##// END OF EJS Templates
util: make copyfile avoid ambiguity of file stat if needed...
FUJIWARA Katsunori -
r29204:ce2d81aa default
parent child Browse files
Show More
@@ -1,2821 +1,2831 b''
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specific implementations.
10 """Mercurial utility functions and platform specific implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from __future__ import absolute_import
16 from __future__ import absolute_import
17
17
18 import bz2
18 import bz2
19 import calendar
19 import calendar
20 import collections
20 import collections
21 import datetime
21 import datetime
22 import errno
22 import errno
23 import gc
23 import gc
24 import hashlib
24 import hashlib
25 import imp
25 import imp
26 import os
26 import os
27 import re as remod
27 import re as remod
28 import shutil
28 import shutil
29 import signal
29 import signal
30 import socket
30 import socket
31 import subprocess
31 import subprocess
32 import sys
32 import sys
33 import tempfile
33 import tempfile
34 import textwrap
34 import textwrap
35 import time
35 import time
36 import traceback
36 import traceback
37 import zlib
37 import zlib
38
38
39 from . import (
39 from . import (
40 encoding,
40 encoding,
41 error,
41 error,
42 i18n,
42 i18n,
43 osutil,
43 osutil,
44 parsers,
44 parsers,
45 pycompat,
45 pycompat,
46 )
46 )
47
47
48 for attr in (
48 for attr in (
49 'empty',
49 'empty',
50 'queue',
50 'queue',
51 'urlerr',
51 'urlerr',
52 # we do import urlreq, but we do it outside the loop
52 # we do import urlreq, but we do it outside the loop
53 #'urlreq',
53 #'urlreq',
54 'stringio',
54 'stringio',
55 ):
55 ):
56 globals()[attr] = getattr(pycompat, attr)
56 globals()[attr] = getattr(pycompat, attr)
57
57
58 # This line is to make pyflakes happy:
58 # This line is to make pyflakes happy:
59 urlreq = pycompat.urlreq
59 urlreq = pycompat.urlreq
60
60
61 if os.name == 'nt':
61 if os.name == 'nt':
62 from . import windows as platform
62 from . import windows as platform
63 else:
63 else:
64 from . import posix as platform
64 from . import posix as platform
65
65
66 md5 = hashlib.md5
66 md5 = hashlib.md5
67 sha1 = hashlib.sha1
67 sha1 = hashlib.sha1
68 sha512 = hashlib.sha512
68 sha512 = hashlib.sha512
69 _ = i18n._
69 _ = i18n._
70
70
71 cachestat = platform.cachestat
71 cachestat = platform.cachestat
72 checkexec = platform.checkexec
72 checkexec = platform.checkexec
73 checklink = platform.checklink
73 checklink = platform.checklink
74 copymode = platform.copymode
74 copymode = platform.copymode
75 executablepath = platform.executablepath
75 executablepath = platform.executablepath
76 expandglobs = platform.expandglobs
76 expandglobs = platform.expandglobs
77 explainexit = platform.explainexit
77 explainexit = platform.explainexit
78 findexe = platform.findexe
78 findexe = platform.findexe
79 gethgcmd = platform.gethgcmd
79 gethgcmd = platform.gethgcmd
80 getuser = platform.getuser
80 getuser = platform.getuser
81 getpid = os.getpid
81 getpid = os.getpid
82 groupmembers = platform.groupmembers
82 groupmembers = platform.groupmembers
83 groupname = platform.groupname
83 groupname = platform.groupname
84 hidewindow = platform.hidewindow
84 hidewindow = platform.hidewindow
85 isexec = platform.isexec
85 isexec = platform.isexec
86 isowner = platform.isowner
86 isowner = platform.isowner
87 localpath = platform.localpath
87 localpath = platform.localpath
88 lookupreg = platform.lookupreg
88 lookupreg = platform.lookupreg
89 makedir = platform.makedir
89 makedir = platform.makedir
90 nlinks = platform.nlinks
90 nlinks = platform.nlinks
91 normpath = platform.normpath
91 normpath = platform.normpath
92 normcase = platform.normcase
92 normcase = platform.normcase
93 normcasespec = platform.normcasespec
93 normcasespec = platform.normcasespec
94 normcasefallback = platform.normcasefallback
94 normcasefallback = platform.normcasefallback
95 openhardlinks = platform.openhardlinks
95 openhardlinks = platform.openhardlinks
96 oslink = platform.oslink
96 oslink = platform.oslink
97 parsepatchoutput = platform.parsepatchoutput
97 parsepatchoutput = platform.parsepatchoutput
98 pconvert = platform.pconvert
98 pconvert = platform.pconvert
99 poll = platform.poll
99 poll = platform.poll
100 popen = platform.popen
100 popen = platform.popen
101 posixfile = platform.posixfile
101 posixfile = platform.posixfile
102 quotecommand = platform.quotecommand
102 quotecommand = platform.quotecommand
103 readpipe = platform.readpipe
103 readpipe = platform.readpipe
104 rename = platform.rename
104 rename = platform.rename
105 removedirs = platform.removedirs
105 removedirs = platform.removedirs
106 samedevice = platform.samedevice
106 samedevice = platform.samedevice
107 samefile = platform.samefile
107 samefile = platform.samefile
108 samestat = platform.samestat
108 samestat = platform.samestat
109 setbinary = platform.setbinary
109 setbinary = platform.setbinary
110 setflags = platform.setflags
110 setflags = platform.setflags
111 setsignalhandler = platform.setsignalhandler
111 setsignalhandler = platform.setsignalhandler
112 shellquote = platform.shellquote
112 shellquote = platform.shellquote
113 spawndetached = platform.spawndetached
113 spawndetached = platform.spawndetached
114 split = platform.split
114 split = platform.split
115 sshargs = platform.sshargs
115 sshargs = platform.sshargs
116 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
116 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
117 statisexec = platform.statisexec
117 statisexec = platform.statisexec
118 statislink = platform.statislink
118 statislink = platform.statislink
119 termwidth = platform.termwidth
119 termwidth = platform.termwidth
120 testpid = platform.testpid
120 testpid = platform.testpid
121 umask = platform.umask
121 umask = platform.umask
122 unlink = platform.unlink
122 unlink = platform.unlink
123 unlinkpath = platform.unlinkpath
123 unlinkpath = platform.unlinkpath
124 username = platform.username
124 username = platform.username
125
125
126 # Python compatibility
126 # Python compatibility
127
127
128 _notset = object()
128 _notset = object()
129
129
130 # disable Python's problematic floating point timestamps (issue4836)
130 # disable Python's problematic floating point timestamps (issue4836)
131 # (Python hypocritically says you shouldn't change this behavior in
131 # (Python hypocritically says you shouldn't change this behavior in
132 # libraries, and sure enough Mercurial is not a library.)
132 # libraries, and sure enough Mercurial is not a library.)
133 os.stat_float_times(False)
133 os.stat_float_times(False)
134
134
135 def safehasattr(thing, attr):
135 def safehasattr(thing, attr):
136 return getattr(thing, attr, _notset) is not _notset
136 return getattr(thing, attr, _notset) is not _notset
137
137
138 DIGESTS = {
138 DIGESTS = {
139 'md5': md5,
139 'md5': md5,
140 'sha1': sha1,
140 'sha1': sha1,
141 'sha512': sha512,
141 'sha512': sha512,
142 }
142 }
143 # List of digest types from strongest to weakest
143 # List of digest types from strongest to weakest
144 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
144 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
145
145
146 for k in DIGESTS_BY_STRENGTH:
146 for k in DIGESTS_BY_STRENGTH:
147 assert k in DIGESTS
147 assert k in DIGESTS
148
148
149 class digester(object):
149 class digester(object):
150 """helper to compute digests.
150 """helper to compute digests.
151
151
152 This helper can be used to compute one or more digests given their name.
152 This helper can be used to compute one or more digests given their name.
153
153
154 >>> d = digester(['md5', 'sha1'])
154 >>> d = digester(['md5', 'sha1'])
155 >>> d.update('foo')
155 >>> d.update('foo')
156 >>> [k for k in sorted(d)]
156 >>> [k for k in sorted(d)]
157 ['md5', 'sha1']
157 ['md5', 'sha1']
158 >>> d['md5']
158 >>> d['md5']
159 'acbd18db4cc2f85cedef654fccc4a4d8'
159 'acbd18db4cc2f85cedef654fccc4a4d8'
160 >>> d['sha1']
160 >>> d['sha1']
161 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
161 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
162 >>> digester.preferred(['md5', 'sha1'])
162 >>> digester.preferred(['md5', 'sha1'])
163 'sha1'
163 'sha1'
164 """
164 """
165
165
166 def __init__(self, digests, s=''):
166 def __init__(self, digests, s=''):
167 self._hashes = {}
167 self._hashes = {}
168 for k in digests:
168 for k in digests:
169 if k not in DIGESTS:
169 if k not in DIGESTS:
170 raise Abort(_('unknown digest type: %s') % k)
170 raise Abort(_('unknown digest type: %s') % k)
171 self._hashes[k] = DIGESTS[k]()
171 self._hashes[k] = DIGESTS[k]()
172 if s:
172 if s:
173 self.update(s)
173 self.update(s)
174
174
175 def update(self, data):
175 def update(self, data):
176 for h in self._hashes.values():
176 for h in self._hashes.values():
177 h.update(data)
177 h.update(data)
178
178
179 def __getitem__(self, key):
179 def __getitem__(self, key):
180 if key not in DIGESTS:
180 if key not in DIGESTS:
181 raise Abort(_('unknown digest type: %s') % k)
181 raise Abort(_('unknown digest type: %s') % k)
182 return self._hashes[key].hexdigest()
182 return self._hashes[key].hexdigest()
183
183
184 def __iter__(self):
184 def __iter__(self):
185 return iter(self._hashes)
185 return iter(self._hashes)
186
186
187 @staticmethod
187 @staticmethod
188 def preferred(supported):
188 def preferred(supported):
189 """returns the strongest digest type in both supported and DIGESTS."""
189 """returns the strongest digest type in both supported and DIGESTS."""
190
190
191 for k in DIGESTS_BY_STRENGTH:
191 for k in DIGESTS_BY_STRENGTH:
192 if k in supported:
192 if k in supported:
193 return k
193 return k
194 return None
194 return None
195
195
196 class digestchecker(object):
196 class digestchecker(object):
197 """file handle wrapper that additionally checks content against a given
197 """file handle wrapper that additionally checks content against a given
198 size and digests.
198 size and digests.
199
199
200 d = digestchecker(fh, size, {'md5': '...'})
200 d = digestchecker(fh, size, {'md5': '...'})
201
201
202 When multiple digests are given, all of them are validated.
202 When multiple digests are given, all of them are validated.
203 """
203 """
204
204
205 def __init__(self, fh, size, digests):
205 def __init__(self, fh, size, digests):
206 self._fh = fh
206 self._fh = fh
207 self._size = size
207 self._size = size
208 self._got = 0
208 self._got = 0
209 self._digests = dict(digests)
209 self._digests = dict(digests)
210 self._digester = digester(self._digests.keys())
210 self._digester = digester(self._digests.keys())
211
211
212 def read(self, length=-1):
212 def read(self, length=-1):
213 content = self._fh.read(length)
213 content = self._fh.read(length)
214 self._digester.update(content)
214 self._digester.update(content)
215 self._got += len(content)
215 self._got += len(content)
216 return content
216 return content
217
217
218 def validate(self):
218 def validate(self):
219 if self._size != self._got:
219 if self._size != self._got:
220 raise Abort(_('size mismatch: expected %d, got %d') %
220 raise Abort(_('size mismatch: expected %d, got %d') %
221 (self._size, self._got))
221 (self._size, self._got))
222 for k, v in self._digests.items():
222 for k, v in self._digests.items():
223 if v != self._digester[k]:
223 if v != self._digester[k]:
224 # i18n: first parameter is a digest name
224 # i18n: first parameter is a digest name
225 raise Abort(_('%s mismatch: expected %s, got %s') %
225 raise Abort(_('%s mismatch: expected %s, got %s') %
226 (k, v, self._digester[k]))
226 (k, v, self._digester[k]))
227
227
228 try:
228 try:
229 buffer = buffer
229 buffer = buffer
230 except NameError:
230 except NameError:
231 if sys.version_info[0] < 3:
231 if sys.version_info[0] < 3:
232 def buffer(sliceable, offset=0):
232 def buffer(sliceable, offset=0):
233 return sliceable[offset:]
233 return sliceable[offset:]
234 else:
234 else:
235 def buffer(sliceable, offset=0):
235 def buffer(sliceable, offset=0):
236 return memoryview(sliceable)[offset:]
236 return memoryview(sliceable)[offset:]
237
237
238 closefds = os.name == 'posix'
238 closefds = os.name == 'posix'
239
239
240 _chunksize = 4096
240 _chunksize = 4096
241
241
242 class bufferedinputpipe(object):
242 class bufferedinputpipe(object):
243 """a manually buffered input pipe
243 """a manually buffered input pipe
244
244
245 Python will not let us use buffered IO and lazy reading with 'polling' at
245 Python will not let us use buffered IO and lazy reading with 'polling' at
246 the same time. We cannot probe the buffer state and select will not detect
246 the same time. We cannot probe the buffer state and select will not detect
247 that data are ready to read if they are already buffered.
247 that data are ready to read if they are already buffered.
248
248
249 This class let us work around that by implementing its own buffering
249 This class let us work around that by implementing its own buffering
250 (allowing efficient readline) while offering a way to know if the buffer is
250 (allowing efficient readline) while offering a way to know if the buffer is
251 empty from the output (allowing collaboration of the buffer with polling).
251 empty from the output (allowing collaboration of the buffer with polling).
252
252
253 This class lives in the 'util' module because it makes use of the 'os'
253 This class lives in the 'util' module because it makes use of the 'os'
254 module from the python stdlib.
254 module from the python stdlib.
255 """
255 """
256
256
257 def __init__(self, input):
257 def __init__(self, input):
258 self._input = input
258 self._input = input
259 self._buffer = []
259 self._buffer = []
260 self._eof = False
260 self._eof = False
261 self._lenbuf = 0
261 self._lenbuf = 0
262
262
263 @property
263 @property
264 def hasbuffer(self):
264 def hasbuffer(self):
265 """True is any data is currently buffered
265 """True is any data is currently buffered
266
266
267 This will be used externally a pre-step for polling IO. If there is
267 This will be used externally a pre-step for polling IO. If there is
268 already data then no polling should be set in place."""
268 already data then no polling should be set in place."""
269 return bool(self._buffer)
269 return bool(self._buffer)
270
270
271 @property
271 @property
272 def closed(self):
272 def closed(self):
273 return self._input.closed
273 return self._input.closed
274
274
275 def fileno(self):
275 def fileno(self):
276 return self._input.fileno()
276 return self._input.fileno()
277
277
278 def close(self):
278 def close(self):
279 return self._input.close()
279 return self._input.close()
280
280
281 def read(self, size):
281 def read(self, size):
282 while (not self._eof) and (self._lenbuf < size):
282 while (not self._eof) and (self._lenbuf < size):
283 self._fillbuffer()
283 self._fillbuffer()
284 return self._frombuffer(size)
284 return self._frombuffer(size)
285
285
286 def readline(self, *args, **kwargs):
286 def readline(self, *args, **kwargs):
287 if 1 < len(self._buffer):
287 if 1 < len(self._buffer):
288 # this should not happen because both read and readline end with a
288 # this should not happen because both read and readline end with a
289 # _frombuffer call that collapse it.
289 # _frombuffer call that collapse it.
290 self._buffer = [''.join(self._buffer)]
290 self._buffer = [''.join(self._buffer)]
291 self._lenbuf = len(self._buffer[0])
291 self._lenbuf = len(self._buffer[0])
292 lfi = -1
292 lfi = -1
293 if self._buffer:
293 if self._buffer:
294 lfi = self._buffer[-1].find('\n')
294 lfi = self._buffer[-1].find('\n')
295 while (not self._eof) and lfi < 0:
295 while (not self._eof) and lfi < 0:
296 self._fillbuffer()
296 self._fillbuffer()
297 if self._buffer:
297 if self._buffer:
298 lfi = self._buffer[-1].find('\n')
298 lfi = self._buffer[-1].find('\n')
299 size = lfi + 1
299 size = lfi + 1
300 if lfi < 0: # end of file
300 if lfi < 0: # end of file
301 size = self._lenbuf
301 size = self._lenbuf
302 elif 1 < len(self._buffer):
302 elif 1 < len(self._buffer):
303 # we need to take previous chunks into account
303 # we need to take previous chunks into account
304 size += self._lenbuf - len(self._buffer[-1])
304 size += self._lenbuf - len(self._buffer[-1])
305 return self._frombuffer(size)
305 return self._frombuffer(size)
306
306
307 def _frombuffer(self, size):
307 def _frombuffer(self, size):
308 """return at most 'size' data from the buffer
308 """return at most 'size' data from the buffer
309
309
310 The data are removed from the buffer."""
310 The data are removed from the buffer."""
311 if size == 0 or not self._buffer:
311 if size == 0 or not self._buffer:
312 return ''
312 return ''
313 buf = self._buffer[0]
313 buf = self._buffer[0]
314 if 1 < len(self._buffer):
314 if 1 < len(self._buffer):
315 buf = ''.join(self._buffer)
315 buf = ''.join(self._buffer)
316
316
317 data = buf[:size]
317 data = buf[:size]
318 buf = buf[len(data):]
318 buf = buf[len(data):]
319 if buf:
319 if buf:
320 self._buffer = [buf]
320 self._buffer = [buf]
321 self._lenbuf = len(buf)
321 self._lenbuf = len(buf)
322 else:
322 else:
323 self._buffer = []
323 self._buffer = []
324 self._lenbuf = 0
324 self._lenbuf = 0
325 return data
325 return data
326
326
327 def _fillbuffer(self):
327 def _fillbuffer(self):
328 """read data to the buffer"""
328 """read data to the buffer"""
329 data = os.read(self._input.fileno(), _chunksize)
329 data = os.read(self._input.fileno(), _chunksize)
330 if not data:
330 if not data:
331 self._eof = True
331 self._eof = True
332 else:
332 else:
333 self._lenbuf += len(data)
333 self._lenbuf += len(data)
334 self._buffer.append(data)
334 self._buffer.append(data)
335
335
336 def popen2(cmd, env=None, newlines=False):
336 def popen2(cmd, env=None, newlines=False):
337 # Setting bufsize to -1 lets the system decide the buffer size.
337 # Setting bufsize to -1 lets the system decide the buffer size.
338 # The default for bufsize is 0, meaning unbuffered. This leads to
338 # The default for bufsize is 0, meaning unbuffered. This leads to
339 # poor performance on Mac OS X: http://bugs.python.org/issue4194
339 # poor performance on Mac OS X: http://bugs.python.org/issue4194
340 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
340 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
341 close_fds=closefds,
341 close_fds=closefds,
342 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
342 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
343 universal_newlines=newlines,
343 universal_newlines=newlines,
344 env=env)
344 env=env)
345 return p.stdin, p.stdout
345 return p.stdin, p.stdout
346
346
347 def popen3(cmd, env=None, newlines=False):
347 def popen3(cmd, env=None, newlines=False):
348 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
348 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
349 return stdin, stdout, stderr
349 return stdin, stdout, stderr
350
350
351 def popen4(cmd, env=None, newlines=False, bufsize=-1):
351 def popen4(cmd, env=None, newlines=False, bufsize=-1):
352 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
352 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
353 close_fds=closefds,
353 close_fds=closefds,
354 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
354 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
355 stderr=subprocess.PIPE,
355 stderr=subprocess.PIPE,
356 universal_newlines=newlines,
356 universal_newlines=newlines,
357 env=env)
357 env=env)
358 return p.stdin, p.stdout, p.stderr, p
358 return p.stdin, p.stdout, p.stderr, p
359
359
360 def version():
360 def version():
361 """Return version information if available."""
361 """Return version information if available."""
362 try:
362 try:
363 from . import __version__
363 from . import __version__
364 return __version__.version
364 return __version__.version
365 except ImportError:
365 except ImportError:
366 return 'unknown'
366 return 'unknown'
367
367
368 def versiontuple(v=None, n=4):
368 def versiontuple(v=None, n=4):
369 """Parses a Mercurial version string into an N-tuple.
369 """Parses a Mercurial version string into an N-tuple.
370
370
371 The version string to be parsed is specified with the ``v`` argument.
371 The version string to be parsed is specified with the ``v`` argument.
372 If it isn't defined, the current Mercurial version string will be parsed.
372 If it isn't defined, the current Mercurial version string will be parsed.
373
373
374 ``n`` can be 2, 3, or 4. Here is how some version strings map to
374 ``n`` can be 2, 3, or 4. Here is how some version strings map to
375 returned values:
375 returned values:
376
376
377 >>> v = '3.6.1+190-df9b73d2d444'
377 >>> v = '3.6.1+190-df9b73d2d444'
378 >>> versiontuple(v, 2)
378 >>> versiontuple(v, 2)
379 (3, 6)
379 (3, 6)
380 >>> versiontuple(v, 3)
380 >>> versiontuple(v, 3)
381 (3, 6, 1)
381 (3, 6, 1)
382 >>> versiontuple(v, 4)
382 >>> versiontuple(v, 4)
383 (3, 6, 1, '190-df9b73d2d444')
383 (3, 6, 1, '190-df9b73d2d444')
384
384
385 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
385 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
386 (3, 6, 1, '190-df9b73d2d444+20151118')
386 (3, 6, 1, '190-df9b73d2d444+20151118')
387
387
388 >>> v = '3.6'
388 >>> v = '3.6'
389 >>> versiontuple(v, 2)
389 >>> versiontuple(v, 2)
390 (3, 6)
390 (3, 6)
391 >>> versiontuple(v, 3)
391 >>> versiontuple(v, 3)
392 (3, 6, None)
392 (3, 6, None)
393 >>> versiontuple(v, 4)
393 >>> versiontuple(v, 4)
394 (3, 6, None, None)
394 (3, 6, None, None)
395 """
395 """
396 if not v:
396 if not v:
397 v = version()
397 v = version()
398 parts = v.split('+', 1)
398 parts = v.split('+', 1)
399 if len(parts) == 1:
399 if len(parts) == 1:
400 vparts, extra = parts[0], None
400 vparts, extra = parts[0], None
401 else:
401 else:
402 vparts, extra = parts
402 vparts, extra = parts
403
403
404 vints = []
404 vints = []
405 for i in vparts.split('.'):
405 for i in vparts.split('.'):
406 try:
406 try:
407 vints.append(int(i))
407 vints.append(int(i))
408 except ValueError:
408 except ValueError:
409 break
409 break
410 # (3, 6) -> (3, 6, None)
410 # (3, 6) -> (3, 6, None)
411 while len(vints) < 3:
411 while len(vints) < 3:
412 vints.append(None)
412 vints.append(None)
413
413
414 if n == 2:
414 if n == 2:
415 return (vints[0], vints[1])
415 return (vints[0], vints[1])
416 if n == 3:
416 if n == 3:
417 return (vints[0], vints[1], vints[2])
417 return (vints[0], vints[1], vints[2])
418 if n == 4:
418 if n == 4:
419 return (vints[0], vints[1], vints[2], extra)
419 return (vints[0], vints[1], vints[2], extra)
420
420
421 # used by parsedate
421 # used by parsedate
422 defaultdateformats = (
422 defaultdateformats = (
423 '%Y-%m-%d %H:%M:%S',
423 '%Y-%m-%d %H:%M:%S',
424 '%Y-%m-%d %I:%M:%S%p',
424 '%Y-%m-%d %I:%M:%S%p',
425 '%Y-%m-%d %H:%M',
425 '%Y-%m-%d %H:%M',
426 '%Y-%m-%d %I:%M%p',
426 '%Y-%m-%d %I:%M%p',
427 '%Y-%m-%d',
427 '%Y-%m-%d',
428 '%m-%d',
428 '%m-%d',
429 '%m/%d',
429 '%m/%d',
430 '%m/%d/%y',
430 '%m/%d/%y',
431 '%m/%d/%Y',
431 '%m/%d/%Y',
432 '%a %b %d %H:%M:%S %Y',
432 '%a %b %d %H:%M:%S %Y',
433 '%a %b %d %I:%M:%S%p %Y',
433 '%a %b %d %I:%M:%S%p %Y',
434 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
434 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
435 '%b %d %H:%M:%S %Y',
435 '%b %d %H:%M:%S %Y',
436 '%b %d %I:%M:%S%p %Y',
436 '%b %d %I:%M:%S%p %Y',
437 '%b %d %H:%M:%S',
437 '%b %d %H:%M:%S',
438 '%b %d %I:%M:%S%p',
438 '%b %d %I:%M:%S%p',
439 '%b %d %H:%M',
439 '%b %d %H:%M',
440 '%b %d %I:%M%p',
440 '%b %d %I:%M%p',
441 '%b %d %Y',
441 '%b %d %Y',
442 '%b %d',
442 '%b %d',
443 '%H:%M:%S',
443 '%H:%M:%S',
444 '%I:%M:%S%p',
444 '%I:%M:%S%p',
445 '%H:%M',
445 '%H:%M',
446 '%I:%M%p',
446 '%I:%M%p',
447 )
447 )
448
448
449 extendeddateformats = defaultdateformats + (
449 extendeddateformats = defaultdateformats + (
450 "%Y",
450 "%Y",
451 "%Y-%m",
451 "%Y-%m",
452 "%b",
452 "%b",
453 "%b %Y",
453 "%b %Y",
454 )
454 )
455
455
456 def cachefunc(func):
456 def cachefunc(func):
457 '''cache the result of function calls'''
457 '''cache the result of function calls'''
458 # XXX doesn't handle keywords args
458 # XXX doesn't handle keywords args
459 if func.__code__.co_argcount == 0:
459 if func.__code__.co_argcount == 0:
460 cache = []
460 cache = []
461 def f():
461 def f():
462 if len(cache) == 0:
462 if len(cache) == 0:
463 cache.append(func())
463 cache.append(func())
464 return cache[0]
464 return cache[0]
465 return f
465 return f
466 cache = {}
466 cache = {}
467 if func.__code__.co_argcount == 1:
467 if func.__code__.co_argcount == 1:
468 # we gain a small amount of time because
468 # we gain a small amount of time because
469 # we don't need to pack/unpack the list
469 # we don't need to pack/unpack the list
470 def f(arg):
470 def f(arg):
471 if arg not in cache:
471 if arg not in cache:
472 cache[arg] = func(arg)
472 cache[arg] = func(arg)
473 return cache[arg]
473 return cache[arg]
474 else:
474 else:
475 def f(*args):
475 def f(*args):
476 if args not in cache:
476 if args not in cache:
477 cache[args] = func(*args)
477 cache[args] = func(*args)
478 return cache[args]
478 return cache[args]
479
479
480 return f
480 return f
481
481
482 class sortdict(dict):
482 class sortdict(dict):
483 '''a simple sorted dictionary'''
483 '''a simple sorted dictionary'''
484 def __init__(self, data=None):
484 def __init__(self, data=None):
485 self._list = []
485 self._list = []
486 if data:
486 if data:
487 self.update(data)
487 self.update(data)
488 def copy(self):
488 def copy(self):
489 return sortdict(self)
489 return sortdict(self)
490 def __setitem__(self, key, val):
490 def __setitem__(self, key, val):
491 if key in self:
491 if key in self:
492 self._list.remove(key)
492 self._list.remove(key)
493 self._list.append(key)
493 self._list.append(key)
494 dict.__setitem__(self, key, val)
494 dict.__setitem__(self, key, val)
495 def __iter__(self):
495 def __iter__(self):
496 return self._list.__iter__()
496 return self._list.__iter__()
497 def update(self, src):
497 def update(self, src):
498 if isinstance(src, dict):
498 if isinstance(src, dict):
499 src = src.iteritems()
499 src = src.iteritems()
500 for k, v in src:
500 for k, v in src:
501 self[k] = v
501 self[k] = v
502 def clear(self):
502 def clear(self):
503 dict.clear(self)
503 dict.clear(self)
504 self._list = []
504 self._list = []
505 def items(self):
505 def items(self):
506 return [(k, self[k]) for k in self._list]
506 return [(k, self[k]) for k in self._list]
507 def __delitem__(self, key):
507 def __delitem__(self, key):
508 dict.__delitem__(self, key)
508 dict.__delitem__(self, key)
509 self._list.remove(key)
509 self._list.remove(key)
510 def pop(self, key, *args, **kwargs):
510 def pop(self, key, *args, **kwargs):
511 dict.pop(self, key, *args, **kwargs)
511 dict.pop(self, key, *args, **kwargs)
512 try:
512 try:
513 self._list.remove(key)
513 self._list.remove(key)
514 except ValueError:
514 except ValueError:
515 pass
515 pass
516 def keys(self):
516 def keys(self):
517 return self._list
517 return self._list
518 def iterkeys(self):
518 def iterkeys(self):
519 return self._list.__iter__()
519 return self._list.__iter__()
520 def iteritems(self):
520 def iteritems(self):
521 for k in self._list:
521 for k in self._list:
522 yield k, self[k]
522 yield k, self[k]
523 def insert(self, index, key, val):
523 def insert(self, index, key, val):
524 self._list.insert(index, key)
524 self._list.insert(index, key)
525 dict.__setitem__(self, key, val)
525 dict.__setitem__(self, key, val)
526
526
527 class _lrucachenode(object):
527 class _lrucachenode(object):
528 """A node in a doubly linked list.
528 """A node in a doubly linked list.
529
529
530 Holds a reference to nodes on either side as well as a key-value
530 Holds a reference to nodes on either side as well as a key-value
531 pair for the dictionary entry.
531 pair for the dictionary entry.
532 """
532 """
533 __slots__ = ('next', 'prev', 'key', 'value')
533 __slots__ = ('next', 'prev', 'key', 'value')
534
534
535 def __init__(self):
535 def __init__(self):
536 self.next = None
536 self.next = None
537 self.prev = None
537 self.prev = None
538
538
539 self.key = _notset
539 self.key = _notset
540 self.value = None
540 self.value = None
541
541
542 def markempty(self):
542 def markempty(self):
543 """Mark the node as emptied."""
543 """Mark the node as emptied."""
544 self.key = _notset
544 self.key = _notset
545
545
546 class lrucachedict(object):
546 class lrucachedict(object):
547 """Dict that caches most recent accesses and sets.
547 """Dict that caches most recent accesses and sets.
548
548
549 The dict consists of an actual backing dict - indexed by original
549 The dict consists of an actual backing dict - indexed by original
550 key - and a doubly linked circular list defining the order of entries in
550 key - and a doubly linked circular list defining the order of entries in
551 the cache.
551 the cache.
552
552
553 The head node is the newest entry in the cache. If the cache is full,
553 The head node is the newest entry in the cache. If the cache is full,
554 we recycle head.prev and make it the new head. Cache accesses result in
554 we recycle head.prev and make it the new head. Cache accesses result in
555 the node being moved to before the existing head and being marked as the
555 the node being moved to before the existing head and being marked as the
556 new head node.
556 new head node.
557 """
557 """
558 def __init__(self, max):
558 def __init__(self, max):
559 self._cache = {}
559 self._cache = {}
560
560
561 self._head = head = _lrucachenode()
561 self._head = head = _lrucachenode()
562 head.prev = head
562 head.prev = head
563 head.next = head
563 head.next = head
564 self._size = 1
564 self._size = 1
565 self._capacity = max
565 self._capacity = max
566
566
567 def __len__(self):
567 def __len__(self):
568 return len(self._cache)
568 return len(self._cache)
569
569
570 def __contains__(self, k):
570 def __contains__(self, k):
571 return k in self._cache
571 return k in self._cache
572
572
573 def __iter__(self):
573 def __iter__(self):
574 # We don't have to iterate in cache order, but why not.
574 # We don't have to iterate in cache order, but why not.
575 n = self._head
575 n = self._head
576 for i in range(len(self._cache)):
576 for i in range(len(self._cache)):
577 yield n.key
577 yield n.key
578 n = n.next
578 n = n.next
579
579
580 def __getitem__(self, k):
580 def __getitem__(self, k):
581 node = self._cache[k]
581 node = self._cache[k]
582 self._movetohead(node)
582 self._movetohead(node)
583 return node.value
583 return node.value
584
584
585 def __setitem__(self, k, v):
585 def __setitem__(self, k, v):
586 node = self._cache.get(k)
586 node = self._cache.get(k)
587 # Replace existing value and mark as newest.
587 # Replace existing value and mark as newest.
588 if node is not None:
588 if node is not None:
589 node.value = v
589 node.value = v
590 self._movetohead(node)
590 self._movetohead(node)
591 return
591 return
592
592
593 if self._size < self._capacity:
593 if self._size < self._capacity:
594 node = self._addcapacity()
594 node = self._addcapacity()
595 else:
595 else:
596 # Grab the last/oldest item.
596 # Grab the last/oldest item.
597 node = self._head.prev
597 node = self._head.prev
598
598
599 # At capacity. Kill the old entry.
599 # At capacity. Kill the old entry.
600 if node.key is not _notset:
600 if node.key is not _notset:
601 del self._cache[node.key]
601 del self._cache[node.key]
602
602
603 node.key = k
603 node.key = k
604 node.value = v
604 node.value = v
605 self._cache[k] = node
605 self._cache[k] = node
606 # And mark it as newest entry. No need to adjust order since it
606 # And mark it as newest entry. No need to adjust order since it
607 # is already self._head.prev.
607 # is already self._head.prev.
608 self._head = node
608 self._head = node
609
609
610 def __delitem__(self, k):
610 def __delitem__(self, k):
611 node = self._cache.pop(k)
611 node = self._cache.pop(k)
612 node.markempty()
612 node.markempty()
613
613
614 # Temporarily mark as newest item before re-adjusting head to make
614 # Temporarily mark as newest item before re-adjusting head to make
615 # this node the oldest item.
615 # this node the oldest item.
616 self._movetohead(node)
616 self._movetohead(node)
617 self._head = node.next
617 self._head = node.next
618
618
619 # Additional dict methods.
619 # Additional dict methods.
620
620
621 def get(self, k, default=None):
621 def get(self, k, default=None):
622 try:
622 try:
623 return self._cache[k]
623 return self._cache[k]
624 except KeyError:
624 except KeyError:
625 return default
625 return default
626
626
627 def clear(self):
627 def clear(self):
628 n = self._head
628 n = self._head
629 while n.key is not _notset:
629 while n.key is not _notset:
630 n.markempty()
630 n.markempty()
631 n = n.next
631 n = n.next
632
632
633 self._cache.clear()
633 self._cache.clear()
634
634
635 def copy(self):
635 def copy(self):
636 result = lrucachedict(self._capacity)
636 result = lrucachedict(self._capacity)
637 n = self._head.prev
637 n = self._head.prev
638 # Iterate in oldest-to-newest order, so the copy has the right ordering
638 # Iterate in oldest-to-newest order, so the copy has the right ordering
639 for i in range(len(self._cache)):
639 for i in range(len(self._cache)):
640 result[n.key] = n.value
640 result[n.key] = n.value
641 n = n.prev
641 n = n.prev
642 return result
642 return result
643
643
644 def _movetohead(self, node):
644 def _movetohead(self, node):
645 """Mark a node as the newest, making it the new head.
645 """Mark a node as the newest, making it the new head.
646
646
647 When a node is accessed, it becomes the freshest entry in the LRU
647 When a node is accessed, it becomes the freshest entry in the LRU
648 list, which is denoted by self._head.
648 list, which is denoted by self._head.
649
649
650 Visually, let's make ``N`` the new head node (* denotes head):
650 Visually, let's make ``N`` the new head node (* denotes head):
651
651
652 previous/oldest <-> head <-> next/next newest
652 previous/oldest <-> head <-> next/next newest
653
653
654 ----<->--- A* ---<->-----
654 ----<->--- A* ---<->-----
655 | |
655 | |
656 E <-> D <-> N <-> C <-> B
656 E <-> D <-> N <-> C <-> B
657
657
658 To:
658 To:
659
659
660 ----<->--- N* ---<->-----
660 ----<->--- N* ---<->-----
661 | |
661 | |
662 E <-> D <-> C <-> B <-> A
662 E <-> D <-> C <-> B <-> A
663
663
664 This requires the following moves:
664 This requires the following moves:
665
665
666 C.next = D (node.prev.next = node.next)
666 C.next = D (node.prev.next = node.next)
667 D.prev = C (node.next.prev = node.prev)
667 D.prev = C (node.next.prev = node.prev)
668 E.next = N (head.prev.next = node)
668 E.next = N (head.prev.next = node)
669 N.prev = E (node.prev = head.prev)
669 N.prev = E (node.prev = head.prev)
670 N.next = A (node.next = head)
670 N.next = A (node.next = head)
671 A.prev = N (head.prev = node)
671 A.prev = N (head.prev = node)
672 """
672 """
673 head = self._head
673 head = self._head
674 # C.next = D
674 # C.next = D
675 node.prev.next = node.next
675 node.prev.next = node.next
676 # D.prev = C
676 # D.prev = C
677 node.next.prev = node.prev
677 node.next.prev = node.prev
678 # N.prev = E
678 # N.prev = E
679 node.prev = head.prev
679 node.prev = head.prev
680 # N.next = A
680 # N.next = A
681 # It is tempting to do just "head" here, however if node is
681 # It is tempting to do just "head" here, however if node is
682 # adjacent to head, this will do bad things.
682 # adjacent to head, this will do bad things.
683 node.next = head.prev.next
683 node.next = head.prev.next
684 # E.next = N
684 # E.next = N
685 node.next.prev = node
685 node.next.prev = node
686 # A.prev = N
686 # A.prev = N
687 node.prev.next = node
687 node.prev.next = node
688
688
689 self._head = node
689 self._head = node
690
690
691 def _addcapacity(self):
691 def _addcapacity(self):
692 """Add a node to the circular linked list.
692 """Add a node to the circular linked list.
693
693
694 The new node is inserted before the head node.
694 The new node is inserted before the head node.
695 """
695 """
696 head = self._head
696 head = self._head
697 node = _lrucachenode()
697 node = _lrucachenode()
698 head.prev.next = node
698 head.prev.next = node
699 node.prev = head.prev
699 node.prev = head.prev
700 node.next = head
700 node.next = head
701 head.prev = node
701 head.prev = node
702 self._size += 1
702 self._size += 1
703 return node
703 return node
704
704
705 def lrucachefunc(func):
705 def lrucachefunc(func):
706 '''cache most recent results of function calls'''
706 '''cache most recent results of function calls'''
707 cache = {}
707 cache = {}
708 order = collections.deque()
708 order = collections.deque()
709 if func.__code__.co_argcount == 1:
709 if func.__code__.co_argcount == 1:
710 def f(arg):
710 def f(arg):
711 if arg not in cache:
711 if arg not in cache:
712 if len(cache) > 20:
712 if len(cache) > 20:
713 del cache[order.popleft()]
713 del cache[order.popleft()]
714 cache[arg] = func(arg)
714 cache[arg] = func(arg)
715 else:
715 else:
716 order.remove(arg)
716 order.remove(arg)
717 order.append(arg)
717 order.append(arg)
718 return cache[arg]
718 return cache[arg]
719 else:
719 else:
720 def f(*args):
720 def f(*args):
721 if args not in cache:
721 if args not in cache:
722 if len(cache) > 20:
722 if len(cache) > 20:
723 del cache[order.popleft()]
723 del cache[order.popleft()]
724 cache[args] = func(*args)
724 cache[args] = func(*args)
725 else:
725 else:
726 order.remove(args)
726 order.remove(args)
727 order.append(args)
727 order.append(args)
728 return cache[args]
728 return cache[args]
729
729
730 return f
730 return f
731
731
732 class propertycache(object):
732 class propertycache(object):
733 def __init__(self, func):
733 def __init__(self, func):
734 self.func = func
734 self.func = func
735 self.name = func.__name__
735 self.name = func.__name__
736 def __get__(self, obj, type=None):
736 def __get__(self, obj, type=None):
737 result = self.func(obj)
737 result = self.func(obj)
738 self.cachevalue(obj, result)
738 self.cachevalue(obj, result)
739 return result
739 return result
740
740
741 def cachevalue(self, obj, value):
741 def cachevalue(self, obj, value):
742 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
742 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
743 obj.__dict__[self.name] = value
743 obj.__dict__[self.name] = value
744
744
745 def pipefilter(s, cmd):
745 def pipefilter(s, cmd):
746 '''filter string S through command CMD, returning its output'''
746 '''filter string S through command CMD, returning its output'''
747 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
747 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
748 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
748 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
749 pout, perr = p.communicate(s)
749 pout, perr = p.communicate(s)
750 return pout
750 return pout
751
751
752 def tempfilter(s, cmd):
752 def tempfilter(s, cmd):
753 '''filter string S through a pair of temporary files with CMD.
753 '''filter string S through a pair of temporary files with CMD.
754 CMD is used as a template to create the real command to be run,
754 CMD is used as a template to create the real command to be run,
755 with the strings INFILE and OUTFILE replaced by the real names of
755 with the strings INFILE and OUTFILE replaced by the real names of
756 the temporary files generated.'''
756 the temporary files generated.'''
757 inname, outname = None, None
757 inname, outname = None, None
758 try:
758 try:
759 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
759 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
760 fp = os.fdopen(infd, 'wb')
760 fp = os.fdopen(infd, 'wb')
761 fp.write(s)
761 fp.write(s)
762 fp.close()
762 fp.close()
763 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
763 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
764 os.close(outfd)
764 os.close(outfd)
765 cmd = cmd.replace('INFILE', inname)
765 cmd = cmd.replace('INFILE', inname)
766 cmd = cmd.replace('OUTFILE', outname)
766 cmd = cmd.replace('OUTFILE', outname)
767 code = os.system(cmd)
767 code = os.system(cmd)
768 if sys.platform == 'OpenVMS' and code & 1:
768 if sys.platform == 'OpenVMS' and code & 1:
769 code = 0
769 code = 0
770 if code:
770 if code:
771 raise Abort(_("command '%s' failed: %s") %
771 raise Abort(_("command '%s' failed: %s") %
772 (cmd, explainexit(code)))
772 (cmd, explainexit(code)))
773 return readfile(outname)
773 return readfile(outname)
774 finally:
774 finally:
775 try:
775 try:
776 if inname:
776 if inname:
777 os.unlink(inname)
777 os.unlink(inname)
778 except OSError:
778 except OSError:
779 pass
779 pass
780 try:
780 try:
781 if outname:
781 if outname:
782 os.unlink(outname)
782 os.unlink(outname)
783 except OSError:
783 except OSError:
784 pass
784 pass
785
785
786 filtertable = {
786 filtertable = {
787 'tempfile:': tempfilter,
787 'tempfile:': tempfilter,
788 'pipe:': pipefilter,
788 'pipe:': pipefilter,
789 }
789 }
790
790
791 def filter(s, cmd):
791 def filter(s, cmd):
792 "filter a string through a command that transforms its input to its output"
792 "filter a string through a command that transforms its input to its output"
793 for name, fn in filtertable.iteritems():
793 for name, fn in filtertable.iteritems():
794 if cmd.startswith(name):
794 if cmd.startswith(name):
795 return fn(s, cmd[len(name):].lstrip())
795 return fn(s, cmd[len(name):].lstrip())
796 return pipefilter(s, cmd)
796 return pipefilter(s, cmd)
797
797
798 def binary(s):
798 def binary(s):
799 """return true if a string is binary data"""
799 """return true if a string is binary data"""
800 return bool(s and '\0' in s)
800 return bool(s and '\0' in s)
801
801
802 def increasingchunks(source, min=1024, max=65536):
802 def increasingchunks(source, min=1024, max=65536):
803 '''return no less than min bytes per chunk while data remains,
803 '''return no less than min bytes per chunk while data remains,
804 doubling min after each chunk until it reaches max'''
804 doubling min after each chunk until it reaches max'''
805 def log2(x):
805 def log2(x):
806 if not x:
806 if not x:
807 return 0
807 return 0
808 i = 0
808 i = 0
809 while x:
809 while x:
810 x >>= 1
810 x >>= 1
811 i += 1
811 i += 1
812 return i - 1
812 return i - 1
813
813
814 buf = []
814 buf = []
815 blen = 0
815 blen = 0
816 for chunk in source:
816 for chunk in source:
817 buf.append(chunk)
817 buf.append(chunk)
818 blen += len(chunk)
818 blen += len(chunk)
819 if blen >= min:
819 if blen >= min:
820 if min < max:
820 if min < max:
821 min = min << 1
821 min = min << 1
822 nmin = 1 << log2(blen)
822 nmin = 1 << log2(blen)
823 if nmin > min:
823 if nmin > min:
824 min = nmin
824 min = nmin
825 if min > max:
825 if min > max:
826 min = max
826 min = max
827 yield ''.join(buf)
827 yield ''.join(buf)
828 blen = 0
828 blen = 0
829 buf = []
829 buf = []
830 if buf:
830 if buf:
831 yield ''.join(buf)
831 yield ''.join(buf)
832
832
833 Abort = error.Abort
833 Abort = error.Abort
834
834
835 def always(fn):
835 def always(fn):
836 return True
836 return True
837
837
838 def never(fn):
838 def never(fn):
839 return False
839 return False
840
840
841 def nogc(func):
841 def nogc(func):
842 """disable garbage collector
842 """disable garbage collector
843
843
844 Python's garbage collector triggers a GC each time a certain number of
844 Python's garbage collector triggers a GC each time a certain number of
845 container objects (the number being defined by gc.get_threshold()) are
845 container objects (the number being defined by gc.get_threshold()) are
846 allocated even when marked not to be tracked by the collector. Tracking has
846 allocated even when marked not to be tracked by the collector. Tracking has
847 no effect on when GCs are triggered, only on what objects the GC looks
847 no effect on when GCs are triggered, only on what objects the GC looks
848 into. As a workaround, disable GC while building complex (huge)
848 into. As a workaround, disable GC while building complex (huge)
849 containers.
849 containers.
850
850
851 This garbage collector issue have been fixed in 2.7.
851 This garbage collector issue have been fixed in 2.7.
852 """
852 """
853 def wrapper(*args, **kwargs):
853 def wrapper(*args, **kwargs):
854 gcenabled = gc.isenabled()
854 gcenabled = gc.isenabled()
855 gc.disable()
855 gc.disable()
856 try:
856 try:
857 return func(*args, **kwargs)
857 return func(*args, **kwargs)
858 finally:
858 finally:
859 if gcenabled:
859 if gcenabled:
860 gc.enable()
860 gc.enable()
861 return wrapper
861 return wrapper
862
862
863 def pathto(root, n1, n2):
863 def pathto(root, n1, n2):
864 '''return the relative path from one place to another.
864 '''return the relative path from one place to another.
865 root should use os.sep to separate directories
865 root should use os.sep to separate directories
866 n1 should use os.sep to separate directories
866 n1 should use os.sep to separate directories
867 n2 should use "/" to separate directories
867 n2 should use "/" to separate directories
868 returns an os.sep-separated path.
868 returns an os.sep-separated path.
869
869
870 If n1 is a relative path, it's assumed it's
870 If n1 is a relative path, it's assumed it's
871 relative to root.
871 relative to root.
872 n2 should always be relative to root.
872 n2 should always be relative to root.
873 '''
873 '''
874 if not n1:
874 if not n1:
875 return localpath(n2)
875 return localpath(n2)
876 if os.path.isabs(n1):
876 if os.path.isabs(n1):
877 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
877 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
878 return os.path.join(root, localpath(n2))
878 return os.path.join(root, localpath(n2))
879 n2 = '/'.join((pconvert(root), n2))
879 n2 = '/'.join((pconvert(root), n2))
880 a, b = splitpath(n1), n2.split('/')
880 a, b = splitpath(n1), n2.split('/')
881 a.reverse()
881 a.reverse()
882 b.reverse()
882 b.reverse()
883 while a and b and a[-1] == b[-1]:
883 while a and b and a[-1] == b[-1]:
884 a.pop()
884 a.pop()
885 b.pop()
885 b.pop()
886 b.reverse()
886 b.reverse()
887 return os.sep.join((['..'] * len(a)) + b) or '.'
887 return os.sep.join((['..'] * len(a)) + b) or '.'
888
888
889 def mainfrozen():
889 def mainfrozen():
890 """return True if we are a frozen executable.
890 """return True if we are a frozen executable.
891
891
892 The code supports py2exe (most common, Windows only) and tools/freeze
892 The code supports py2exe (most common, Windows only) and tools/freeze
893 (portable, not much used).
893 (portable, not much used).
894 """
894 """
895 return (safehasattr(sys, "frozen") or # new py2exe
895 return (safehasattr(sys, "frozen") or # new py2exe
896 safehasattr(sys, "importers") or # old py2exe
896 safehasattr(sys, "importers") or # old py2exe
897 imp.is_frozen("__main__")) # tools/freeze
897 imp.is_frozen("__main__")) # tools/freeze
898
898
899 # the location of data files matching the source code
899 # the location of data files matching the source code
900 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
900 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
901 # executable version (py2exe) doesn't support __file__
901 # executable version (py2exe) doesn't support __file__
902 datapath = os.path.dirname(sys.executable)
902 datapath = os.path.dirname(sys.executable)
903 else:
903 else:
904 datapath = os.path.dirname(__file__)
904 datapath = os.path.dirname(__file__)
905
905
906 i18n.setdatapath(datapath)
906 i18n.setdatapath(datapath)
907
907
908 _hgexecutable = None
908 _hgexecutable = None
909
909
910 def hgexecutable():
910 def hgexecutable():
911 """return location of the 'hg' executable.
911 """return location of the 'hg' executable.
912
912
913 Defaults to $HG or 'hg' in the search path.
913 Defaults to $HG or 'hg' in the search path.
914 """
914 """
915 if _hgexecutable is None:
915 if _hgexecutable is None:
916 hg = os.environ.get('HG')
916 hg = os.environ.get('HG')
917 mainmod = sys.modules['__main__']
917 mainmod = sys.modules['__main__']
918 if hg:
918 if hg:
919 _sethgexecutable(hg)
919 _sethgexecutable(hg)
920 elif mainfrozen():
920 elif mainfrozen():
921 if getattr(sys, 'frozen', None) == 'macosx_app':
921 if getattr(sys, 'frozen', None) == 'macosx_app':
922 # Env variable set by py2app
922 # Env variable set by py2app
923 _sethgexecutable(os.environ['EXECUTABLEPATH'])
923 _sethgexecutable(os.environ['EXECUTABLEPATH'])
924 else:
924 else:
925 _sethgexecutable(sys.executable)
925 _sethgexecutable(sys.executable)
926 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
926 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
927 _sethgexecutable(mainmod.__file__)
927 _sethgexecutable(mainmod.__file__)
928 else:
928 else:
929 exe = findexe('hg') or os.path.basename(sys.argv[0])
929 exe = findexe('hg') or os.path.basename(sys.argv[0])
930 _sethgexecutable(exe)
930 _sethgexecutable(exe)
931 return _hgexecutable
931 return _hgexecutable
932
932
933 def _sethgexecutable(path):
933 def _sethgexecutable(path):
934 """set location of the 'hg' executable"""
934 """set location of the 'hg' executable"""
935 global _hgexecutable
935 global _hgexecutable
936 _hgexecutable = path
936 _hgexecutable = path
937
937
938 def _isstdout(f):
938 def _isstdout(f):
939 fileno = getattr(f, 'fileno', None)
939 fileno = getattr(f, 'fileno', None)
940 return fileno and fileno() == sys.__stdout__.fileno()
940 return fileno and fileno() == sys.__stdout__.fileno()
941
941
942 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
942 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
943 '''enhanced shell command execution.
943 '''enhanced shell command execution.
944 run with environment maybe modified, maybe in different dir.
944 run with environment maybe modified, maybe in different dir.
945
945
946 if command fails and onerr is None, return status, else raise onerr
946 if command fails and onerr is None, return status, else raise onerr
947 object as exception.
947 object as exception.
948
948
949 if out is specified, it is assumed to be a file-like object that has a
949 if out is specified, it is assumed to be a file-like object that has a
950 write() method. stdout and stderr will be redirected to out.'''
950 write() method. stdout and stderr will be redirected to out.'''
951 if environ is None:
951 if environ is None:
952 environ = {}
952 environ = {}
953 try:
953 try:
954 sys.stdout.flush()
954 sys.stdout.flush()
955 except Exception:
955 except Exception:
956 pass
956 pass
957 def py2shell(val):
957 def py2shell(val):
958 'convert python object into string that is useful to shell'
958 'convert python object into string that is useful to shell'
959 if val is None or val is False:
959 if val is None or val is False:
960 return '0'
960 return '0'
961 if val is True:
961 if val is True:
962 return '1'
962 return '1'
963 return str(val)
963 return str(val)
964 origcmd = cmd
964 origcmd = cmd
965 cmd = quotecommand(cmd)
965 cmd = quotecommand(cmd)
966 if sys.platform == 'plan9' and (sys.version_info[0] == 2
966 if sys.platform == 'plan9' and (sys.version_info[0] == 2
967 and sys.version_info[1] < 7):
967 and sys.version_info[1] < 7):
968 # subprocess kludge to work around issues in half-baked Python
968 # subprocess kludge to work around issues in half-baked Python
969 # ports, notably bichued/python:
969 # ports, notably bichued/python:
970 if not cwd is None:
970 if not cwd is None:
971 os.chdir(cwd)
971 os.chdir(cwd)
972 rc = os.system(cmd)
972 rc = os.system(cmd)
973 else:
973 else:
974 env = dict(os.environ)
974 env = dict(os.environ)
975 env.update((k, py2shell(v)) for k, v in environ.iteritems())
975 env.update((k, py2shell(v)) for k, v in environ.iteritems())
976 env['HG'] = hgexecutable()
976 env['HG'] = hgexecutable()
977 if out is None or _isstdout(out):
977 if out is None or _isstdout(out):
978 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
978 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
979 env=env, cwd=cwd)
979 env=env, cwd=cwd)
980 else:
980 else:
981 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
981 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
982 env=env, cwd=cwd, stdout=subprocess.PIPE,
982 env=env, cwd=cwd, stdout=subprocess.PIPE,
983 stderr=subprocess.STDOUT)
983 stderr=subprocess.STDOUT)
984 while True:
984 while True:
985 line = proc.stdout.readline()
985 line = proc.stdout.readline()
986 if not line:
986 if not line:
987 break
987 break
988 out.write(line)
988 out.write(line)
989 proc.wait()
989 proc.wait()
990 rc = proc.returncode
990 rc = proc.returncode
991 if sys.platform == 'OpenVMS' and rc & 1:
991 if sys.platform == 'OpenVMS' and rc & 1:
992 rc = 0
992 rc = 0
993 if rc and onerr:
993 if rc and onerr:
994 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
994 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
995 explainexit(rc)[0])
995 explainexit(rc)[0])
996 if errprefix:
996 if errprefix:
997 errmsg = '%s: %s' % (errprefix, errmsg)
997 errmsg = '%s: %s' % (errprefix, errmsg)
998 raise onerr(errmsg)
998 raise onerr(errmsg)
999 return rc
999 return rc
1000
1000
1001 def checksignature(func):
1001 def checksignature(func):
1002 '''wrap a function with code to check for calling errors'''
1002 '''wrap a function with code to check for calling errors'''
1003 def check(*args, **kwargs):
1003 def check(*args, **kwargs):
1004 try:
1004 try:
1005 return func(*args, **kwargs)
1005 return func(*args, **kwargs)
1006 except TypeError:
1006 except TypeError:
1007 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1007 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1008 raise error.SignatureError
1008 raise error.SignatureError
1009 raise
1009 raise
1010
1010
1011 return check
1011 return check
1012
1012
1013 def copyfile(src, dest, hardlink=False, copystat=False):
1013 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1014 '''copy a file, preserving mode and optionally other stat info like
1014 '''copy a file, preserving mode and optionally other stat info like
1015 atime/mtime'''
1015 atime/mtime'''
1016 assert not (copystat and checkambig)
1017 oldstat = None
1016 if os.path.lexists(dest):
1018 if os.path.lexists(dest):
1019 if checkambig:
1020 oldstat = checkambig and filestat(dest)
1017 unlink(dest)
1021 unlink(dest)
1018 # hardlinks are problematic on CIFS, quietly ignore this flag
1022 # hardlinks are problematic on CIFS, quietly ignore this flag
1019 # until we find a way to work around it cleanly (issue4546)
1023 # until we find a way to work around it cleanly (issue4546)
1020 if False and hardlink:
1024 if False and hardlink:
1021 try:
1025 try:
1022 oslink(src, dest)
1026 oslink(src, dest)
1023 return
1027 return
1024 except (IOError, OSError):
1028 except (IOError, OSError):
1025 pass # fall back to normal copy
1029 pass # fall back to normal copy
1026 if os.path.islink(src):
1030 if os.path.islink(src):
1027 os.symlink(os.readlink(src), dest)
1031 os.symlink(os.readlink(src), dest)
1028 # copytime is ignored for symlinks, but in general copytime isn't needed
1032 # copytime is ignored for symlinks, but in general copytime isn't needed
1029 # for them anyway
1033 # for them anyway
1030 else:
1034 else:
1031 try:
1035 try:
1032 shutil.copyfile(src, dest)
1036 shutil.copyfile(src, dest)
1033 if copystat:
1037 if copystat:
1034 # copystat also copies mode
1038 # copystat also copies mode
1035 shutil.copystat(src, dest)
1039 shutil.copystat(src, dest)
1036 else:
1040 else:
1037 shutil.copymode(src, dest)
1041 shutil.copymode(src, dest)
1042 if oldstat and oldstat.stat:
1043 newstat = filestat(dest)
1044 if newstat.isambig(oldstat):
1045 # stat of copied file is ambiguous to original one
1046 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1047 os.utime(dest, (advanced, advanced))
1038 except shutil.Error as inst:
1048 except shutil.Error as inst:
1039 raise Abort(str(inst))
1049 raise Abort(str(inst))
1040
1050
1041 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1051 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1042 """Copy a directory tree using hardlinks if possible."""
1052 """Copy a directory tree using hardlinks if possible."""
1043 num = 0
1053 num = 0
1044
1054
1045 if hardlink is None:
1055 if hardlink is None:
1046 hardlink = (os.stat(src).st_dev ==
1056 hardlink = (os.stat(src).st_dev ==
1047 os.stat(os.path.dirname(dst)).st_dev)
1057 os.stat(os.path.dirname(dst)).st_dev)
1048 if hardlink:
1058 if hardlink:
1049 topic = _('linking')
1059 topic = _('linking')
1050 else:
1060 else:
1051 topic = _('copying')
1061 topic = _('copying')
1052
1062
1053 if os.path.isdir(src):
1063 if os.path.isdir(src):
1054 os.mkdir(dst)
1064 os.mkdir(dst)
1055 for name, kind in osutil.listdir(src):
1065 for name, kind in osutil.listdir(src):
1056 srcname = os.path.join(src, name)
1066 srcname = os.path.join(src, name)
1057 dstname = os.path.join(dst, name)
1067 dstname = os.path.join(dst, name)
1058 def nprog(t, pos):
1068 def nprog(t, pos):
1059 if pos is not None:
1069 if pos is not None:
1060 return progress(t, pos + num)
1070 return progress(t, pos + num)
1061 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1071 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1062 num += n
1072 num += n
1063 else:
1073 else:
1064 if hardlink:
1074 if hardlink:
1065 try:
1075 try:
1066 oslink(src, dst)
1076 oslink(src, dst)
1067 except (IOError, OSError):
1077 except (IOError, OSError):
1068 hardlink = False
1078 hardlink = False
1069 shutil.copy(src, dst)
1079 shutil.copy(src, dst)
1070 else:
1080 else:
1071 shutil.copy(src, dst)
1081 shutil.copy(src, dst)
1072 num += 1
1082 num += 1
1073 progress(topic, num)
1083 progress(topic, num)
1074 progress(topic, None)
1084 progress(topic, None)
1075
1085
1076 return hardlink, num
1086 return hardlink, num
1077
1087
1078 _winreservednames = '''con prn aux nul
1088 _winreservednames = '''con prn aux nul
1079 com1 com2 com3 com4 com5 com6 com7 com8 com9
1089 com1 com2 com3 com4 com5 com6 com7 com8 com9
1080 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1090 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1081 _winreservedchars = ':*?"<>|'
1091 _winreservedchars = ':*?"<>|'
1082 def checkwinfilename(path):
1092 def checkwinfilename(path):
1083 r'''Check that the base-relative path is a valid filename on Windows.
1093 r'''Check that the base-relative path is a valid filename on Windows.
1084 Returns None if the path is ok, or a UI string describing the problem.
1094 Returns None if the path is ok, or a UI string describing the problem.
1085
1095
1086 >>> checkwinfilename("just/a/normal/path")
1096 >>> checkwinfilename("just/a/normal/path")
1087 >>> checkwinfilename("foo/bar/con.xml")
1097 >>> checkwinfilename("foo/bar/con.xml")
1088 "filename contains 'con', which is reserved on Windows"
1098 "filename contains 'con', which is reserved on Windows"
1089 >>> checkwinfilename("foo/con.xml/bar")
1099 >>> checkwinfilename("foo/con.xml/bar")
1090 "filename contains 'con', which is reserved on Windows"
1100 "filename contains 'con', which is reserved on Windows"
1091 >>> checkwinfilename("foo/bar/xml.con")
1101 >>> checkwinfilename("foo/bar/xml.con")
1092 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1102 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1093 "filename contains 'AUX', which is reserved on Windows"
1103 "filename contains 'AUX', which is reserved on Windows"
1094 >>> checkwinfilename("foo/bar/bla:.txt")
1104 >>> checkwinfilename("foo/bar/bla:.txt")
1095 "filename contains ':', which is reserved on Windows"
1105 "filename contains ':', which is reserved on Windows"
1096 >>> checkwinfilename("foo/bar/b\07la.txt")
1106 >>> checkwinfilename("foo/bar/b\07la.txt")
1097 "filename contains '\\x07', which is invalid on Windows"
1107 "filename contains '\\x07', which is invalid on Windows"
1098 >>> checkwinfilename("foo/bar/bla ")
1108 >>> checkwinfilename("foo/bar/bla ")
1099 "filename ends with ' ', which is not allowed on Windows"
1109 "filename ends with ' ', which is not allowed on Windows"
1100 >>> checkwinfilename("../bar")
1110 >>> checkwinfilename("../bar")
1101 >>> checkwinfilename("foo\\")
1111 >>> checkwinfilename("foo\\")
1102 "filename ends with '\\', which is invalid on Windows"
1112 "filename ends with '\\', which is invalid on Windows"
1103 >>> checkwinfilename("foo\\/bar")
1113 >>> checkwinfilename("foo\\/bar")
1104 "directory name ends with '\\', which is invalid on Windows"
1114 "directory name ends with '\\', which is invalid on Windows"
1105 '''
1115 '''
1106 if path.endswith('\\'):
1116 if path.endswith('\\'):
1107 return _("filename ends with '\\', which is invalid on Windows")
1117 return _("filename ends with '\\', which is invalid on Windows")
1108 if '\\/' in path:
1118 if '\\/' in path:
1109 return _("directory name ends with '\\', which is invalid on Windows")
1119 return _("directory name ends with '\\', which is invalid on Windows")
1110 for n in path.replace('\\', '/').split('/'):
1120 for n in path.replace('\\', '/').split('/'):
1111 if not n:
1121 if not n:
1112 continue
1122 continue
1113 for c in n:
1123 for c in n:
1114 if c in _winreservedchars:
1124 if c in _winreservedchars:
1115 return _("filename contains '%s', which is reserved "
1125 return _("filename contains '%s', which is reserved "
1116 "on Windows") % c
1126 "on Windows") % c
1117 if ord(c) <= 31:
1127 if ord(c) <= 31:
1118 return _("filename contains %r, which is invalid "
1128 return _("filename contains %r, which is invalid "
1119 "on Windows") % c
1129 "on Windows") % c
1120 base = n.split('.')[0]
1130 base = n.split('.')[0]
1121 if base and base.lower() in _winreservednames:
1131 if base and base.lower() in _winreservednames:
1122 return _("filename contains '%s', which is reserved "
1132 return _("filename contains '%s', which is reserved "
1123 "on Windows") % base
1133 "on Windows") % base
1124 t = n[-1]
1134 t = n[-1]
1125 if t in '. ' and n not in '..':
1135 if t in '. ' and n not in '..':
1126 return _("filename ends with '%s', which is not allowed "
1136 return _("filename ends with '%s', which is not allowed "
1127 "on Windows") % t
1137 "on Windows") % t
1128
1138
1129 if os.name == 'nt':
1139 if os.name == 'nt':
1130 checkosfilename = checkwinfilename
1140 checkosfilename = checkwinfilename
1131 else:
1141 else:
1132 checkosfilename = platform.checkosfilename
1142 checkosfilename = platform.checkosfilename
1133
1143
1134 def makelock(info, pathname):
1144 def makelock(info, pathname):
1135 try:
1145 try:
1136 return os.symlink(info, pathname)
1146 return os.symlink(info, pathname)
1137 except OSError as why:
1147 except OSError as why:
1138 if why.errno == errno.EEXIST:
1148 if why.errno == errno.EEXIST:
1139 raise
1149 raise
1140 except AttributeError: # no symlink in os
1150 except AttributeError: # no symlink in os
1141 pass
1151 pass
1142
1152
1143 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1153 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1144 os.write(ld, info)
1154 os.write(ld, info)
1145 os.close(ld)
1155 os.close(ld)
1146
1156
1147 def readlock(pathname):
1157 def readlock(pathname):
1148 try:
1158 try:
1149 return os.readlink(pathname)
1159 return os.readlink(pathname)
1150 except OSError as why:
1160 except OSError as why:
1151 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1161 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1152 raise
1162 raise
1153 except AttributeError: # no symlink in os
1163 except AttributeError: # no symlink in os
1154 pass
1164 pass
1155 fp = posixfile(pathname)
1165 fp = posixfile(pathname)
1156 r = fp.read()
1166 r = fp.read()
1157 fp.close()
1167 fp.close()
1158 return r
1168 return r
1159
1169
1160 def fstat(fp):
1170 def fstat(fp):
1161 '''stat file object that may not have fileno method.'''
1171 '''stat file object that may not have fileno method.'''
1162 try:
1172 try:
1163 return os.fstat(fp.fileno())
1173 return os.fstat(fp.fileno())
1164 except AttributeError:
1174 except AttributeError:
1165 return os.stat(fp.name)
1175 return os.stat(fp.name)
1166
1176
1167 # File system features
1177 # File system features
1168
1178
1169 def checkcase(path):
1179 def checkcase(path):
1170 """
1180 """
1171 Return true if the given path is on a case-sensitive filesystem
1181 Return true if the given path is on a case-sensitive filesystem
1172
1182
1173 Requires a path (like /foo/.hg) ending with a foldable final
1183 Requires a path (like /foo/.hg) ending with a foldable final
1174 directory component.
1184 directory component.
1175 """
1185 """
1176 s1 = os.lstat(path)
1186 s1 = os.lstat(path)
1177 d, b = os.path.split(path)
1187 d, b = os.path.split(path)
1178 b2 = b.upper()
1188 b2 = b.upper()
1179 if b == b2:
1189 if b == b2:
1180 b2 = b.lower()
1190 b2 = b.lower()
1181 if b == b2:
1191 if b == b2:
1182 return True # no evidence against case sensitivity
1192 return True # no evidence against case sensitivity
1183 p2 = os.path.join(d, b2)
1193 p2 = os.path.join(d, b2)
1184 try:
1194 try:
1185 s2 = os.lstat(p2)
1195 s2 = os.lstat(p2)
1186 if s2 == s1:
1196 if s2 == s1:
1187 return False
1197 return False
1188 return True
1198 return True
1189 except OSError:
1199 except OSError:
1190 return True
1200 return True
1191
1201
1192 try:
1202 try:
1193 import re2
1203 import re2
1194 _re2 = None
1204 _re2 = None
1195 except ImportError:
1205 except ImportError:
1196 _re2 = False
1206 _re2 = False
1197
1207
1198 class _re(object):
1208 class _re(object):
1199 def _checkre2(self):
1209 def _checkre2(self):
1200 global _re2
1210 global _re2
1201 try:
1211 try:
1202 # check if match works, see issue3964
1212 # check if match works, see issue3964
1203 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1213 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1204 except ImportError:
1214 except ImportError:
1205 _re2 = False
1215 _re2 = False
1206
1216
1207 def compile(self, pat, flags=0):
1217 def compile(self, pat, flags=0):
1208 '''Compile a regular expression, using re2 if possible
1218 '''Compile a regular expression, using re2 if possible
1209
1219
1210 For best performance, use only re2-compatible regexp features. The
1220 For best performance, use only re2-compatible regexp features. The
1211 only flags from the re module that are re2-compatible are
1221 only flags from the re module that are re2-compatible are
1212 IGNORECASE and MULTILINE.'''
1222 IGNORECASE and MULTILINE.'''
1213 if _re2 is None:
1223 if _re2 is None:
1214 self._checkre2()
1224 self._checkre2()
1215 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1225 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1216 if flags & remod.IGNORECASE:
1226 if flags & remod.IGNORECASE:
1217 pat = '(?i)' + pat
1227 pat = '(?i)' + pat
1218 if flags & remod.MULTILINE:
1228 if flags & remod.MULTILINE:
1219 pat = '(?m)' + pat
1229 pat = '(?m)' + pat
1220 try:
1230 try:
1221 return re2.compile(pat)
1231 return re2.compile(pat)
1222 except re2.error:
1232 except re2.error:
1223 pass
1233 pass
1224 return remod.compile(pat, flags)
1234 return remod.compile(pat, flags)
1225
1235
1226 @propertycache
1236 @propertycache
1227 def escape(self):
1237 def escape(self):
1228 '''Return the version of escape corresponding to self.compile.
1238 '''Return the version of escape corresponding to self.compile.
1229
1239
1230 This is imperfect because whether re2 or re is used for a particular
1240 This is imperfect because whether re2 or re is used for a particular
1231 function depends on the flags, etc, but it's the best we can do.
1241 function depends on the flags, etc, but it's the best we can do.
1232 '''
1242 '''
1233 global _re2
1243 global _re2
1234 if _re2 is None:
1244 if _re2 is None:
1235 self._checkre2()
1245 self._checkre2()
1236 if _re2:
1246 if _re2:
1237 return re2.escape
1247 return re2.escape
1238 else:
1248 else:
1239 return remod.escape
1249 return remod.escape
1240
1250
1241 re = _re()
1251 re = _re()
1242
1252
1243 _fspathcache = {}
1253 _fspathcache = {}
1244 def fspath(name, root):
1254 def fspath(name, root):
1245 '''Get name in the case stored in the filesystem
1255 '''Get name in the case stored in the filesystem
1246
1256
1247 The name should be relative to root, and be normcase-ed for efficiency.
1257 The name should be relative to root, and be normcase-ed for efficiency.
1248
1258
1249 Note that this function is unnecessary, and should not be
1259 Note that this function is unnecessary, and should not be
1250 called, for case-sensitive filesystems (simply because it's expensive).
1260 called, for case-sensitive filesystems (simply because it's expensive).
1251
1261
1252 The root should be normcase-ed, too.
1262 The root should be normcase-ed, too.
1253 '''
1263 '''
1254 def _makefspathcacheentry(dir):
1264 def _makefspathcacheentry(dir):
1255 return dict((normcase(n), n) for n in os.listdir(dir))
1265 return dict((normcase(n), n) for n in os.listdir(dir))
1256
1266
1257 seps = os.sep
1267 seps = os.sep
1258 if os.altsep:
1268 if os.altsep:
1259 seps = seps + os.altsep
1269 seps = seps + os.altsep
1260 # Protect backslashes. This gets silly very quickly.
1270 # Protect backslashes. This gets silly very quickly.
1261 seps.replace('\\','\\\\')
1271 seps.replace('\\','\\\\')
1262 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1272 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1263 dir = os.path.normpath(root)
1273 dir = os.path.normpath(root)
1264 result = []
1274 result = []
1265 for part, sep in pattern.findall(name):
1275 for part, sep in pattern.findall(name):
1266 if sep:
1276 if sep:
1267 result.append(sep)
1277 result.append(sep)
1268 continue
1278 continue
1269
1279
1270 if dir not in _fspathcache:
1280 if dir not in _fspathcache:
1271 _fspathcache[dir] = _makefspathcacheentry(dir)
1281 _fspathcache[dir] = _makefspathcacheentry(dir)
1272 contents = _fspathcache[dir]
1282 contents = _fspathcache[dir]
1273
1283
1274 found = contents.get(part)
1284 found = contents.get(part)
1275 if not found:
1285 if not found:
1276 # retry "once per directory" per "dirstate.walk" which
1286 # retry "once per directory" per "dirstate.walk" which
1277 # may take place for each patches of "hg qpush", for example
1287 # may take place for each patches of "hg qpush", for example
1278 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1288 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1279 found = contents.get(part)
1289 found = contents.get(part)
1280
1290
1281 result.append(found or part)
1291 result.append(found or part)
1282 dir = os.path.join(dir, part)
1292 dir = os.path.join(dir, part)
1283
1293
1284 return ''.join(result)
1294 return ''.join(result)
1285
1295
1286 def checknlink(testfile):
1296 def checknlink(testfile):
1287 '''check whether hardlink count reporting works properly'''
1297 '''check whether hardlink count reporting works properly'''
1288
1298
1289 # testfile may be open, so we need a separate file for checking to
1299 # testfile may be open, so we need a separate file for checking to
1290 # work around issue2543 (or testfile may get lost on Samba shares)
1300 # work around issue2543 (or testfile may get lost on Samba shares)
1291 f1 = testfile + ".hgtmp1"
1301 f1 = testfile + ".hgtmp1"
1292 if os.path.lexists(f1):
1302 if os.path.lexists(f1):
1293 return False
1303 return False
1294 try:
1304 try:
1295 posixfile(f1, 'w').close()
1305 posixfile(f1, 'w').close()
1296 except IOError:
1306 except IOError:
1297 return False
1307 return False
1298
1308
1299 f2 = testfile + ".hgtmp2"
1309 f2 = testfile + ".hgtmp2"
1300 fd = None
1310 fd = None
1301 try:
1311 try:
1302 oslink(f1, f2)
1312 oslink(f1, f2)
1303 # nlinks() may behave differently for files on Windows shares if
1313 # nlinks() may behave differently for files on Windows shares if
1304 # the file is open.
1314 # the file is open.
1305 fd = posixfile(f2)
1315 fd = posixfile(f2)
1306 return nlinks(f2) > 1
1316 return nlinks(f2) > 1
1307 except OSError:
1317 except OSError:
1308 return False
1318 return False
1309 finally:
1319 finally:
1310 if fd is not None:
1320 if fd is not None:
1311 fd.close()
1321 fd.close()
1312 for f in (f1, f2):
1322 for f in (f1, f2):
1313 try:
1323 try:
1314 os.unlink(f)
1324 os.unlink(f)
1315 except OSError:
1325 except OSError:
1316 pass
1326 pass
1317
1327
1318 def endswithsep(path):
1328 def endswithsep(path):
1319 '''Check path ends with os.sep or os.altsep.'''
1329 '''Check path ends with os.sep or os.altsep.'''
1320 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1330 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1321
1331
1322 def splitpath(path):
1332 def splitpath(path):
1323 '''Split path by os.sep.
1333 '''Split path by os.sep.
1324 Note that this function does not use os.altsep because this is
1334 Note that this function does not use os.altsep because this is
1325 an alternative of simple "xxx.split(os.sep)".
1335 an alternative of simple "xxx.split(os.sep)".
1326 It is recommended to use os.path.normpath() before using this
1336 It is recommended to use os.path.normpath() before using this
1327 function if need.'''
1337 function if need.'''
1328 return path.split(os.sep)
1338 return path.split(os.sep)
1329
1339
1330 def gui():
1340 def gui():
1331 '''Are we running in a GUI?'''
1341 '''Are we running in a GUI?'''
1332 if sys.platform == 'darwin':
1342 if sys.platform == 'darwin':
1333 if 'SSH_CONNECTION' in os.environ:
1343 if 'SSH_CONNECTION' in os.environ:
1334 # handle SSH access to a box where the user is logged in
1344 # handle SSH access to a box where the user is logged in
1335 return False
1345 return False
1336 elif getattr(osutil, 'isgui', None):
1346 elif getattr(osutil, 'isgui', None):
1337 # check if a CoreGraphics session is available
1347 # check if a CoreGraphics session is available
1338 return osutil.isgui()
1348 return osutil.isgui()
1339 else:
1349 else:
1340 # pure build; use a safe default
1350 # pure build; use a safe default
1341 return True
1351 return True
1342 else:
1352 else:
1343 return os.name == "nt" or os.environ.get("DISPLAY")
1353 return os.name == "nt" or os.environ.get("DISPLAY")
1344
1354
1345 def mktempcopy(name, emptyok=False, createmode=None):
1355 def mktempcopy(name, emptyok=False, createmode=None):
1346 """Create a temporary file with the same contents from name
1356 """Create a temporary file with the same contents from name
1347
1357
1348 The permission bits are copied from the original file.
1358 The permission bits are copied from the original file.
1349
1359
1350 If the temporary file is going to be truncated immediately, you
1360 If the temporary file is going to be truncated immediately, you
1351 can use emptyok=True as an optimization.
1361 can use emptyok=True as an optimization.
1352
1362
1353 Returns the name of the temporary file.
1363 Returns the name of the temporary file.
1354 """
1364 """
1355 d, fn = os.path.split(name)
1365 d, fn = os.path.split(name)
1356 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1366 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1357 os.close(fd)
1367 os.close(fd)
1358 # Temporary files are created with mode 0600, which is usually not
1368 # Temporary files are created with mode 0600, which is usually not
1359 # what we want. If the original file already exists, just copy
1369 # what we want. If the original file already exists, just copy
1360 # its mode. Otherwise, manually obey umask.
1370 # its mode. Otherwise, manually obey umask.
1361 copymode(name, temp, createmode)
1371 copymode(name, temp, createmode)
1362 if emptyok:
1372 if emptyok:
1363 return temp
1373 return temp
1364 try:
1374 try:
1365 try:
1375 try:
1366 ifp = posixfile(name, "rb")
1376 ifp = posixfile(name, "rb")
1367 except IOError as inst:
1377 except IOError as inst:
1368 if inst.errno == errno.ENOENT:
1378 if inst.errno == errno.ENOENT:
1369 return temp
1379 return temp
1370 if not getattr(inst, 'filename', None):
1380 if not getattr(inst, 'filename', None):
1371 inst.filename = name
1381 inst.filename = name
1372 raise
1382 raise
1373 ofp = posixfile(temp, "wb")
1383 ofp = posixfile(temp, "wb")
1374 for chunk in filechunkiter(ifp):
1384 for chunk in filechunkiter(ifp):
1375 ofp.write(chunk)
1385 ofp.write(chunk)
1376 ifp.close()
1386 ifp.close()
1377 ofp.close()
1387 ofp.close()
1378 except: # re-raises
1388 except: # re-raises
1379 try: os.unlink(temp)
1389 try: os.unlink(temp)
1380 except OSError: pass
1390 except OSError: pass
1381 raise
1391 raise
1382 return temp
1392 return temp
1383
1393
1384 class filestat(object):
1394 class filestat(object):
1385 """help to exactly detect change of a file
1395 """help to exactly detect change of a file
1386
1396
1387 'stat' attribute is result of 'os.stat()' if specified 'path'
1397 'stat' attribute is result of 'os.stat()' if specified 'path'
1388 exists. Otherwise, it is None. This can avoid preparative
1398 exists. Otherwise, it is None. This can avoid preparative
1389 'exists()' examination on client side of this class.
1399 'exists()' examination on client side of this class.
1390 """
1400 """
1391 def __init__(self, path):
1401 def __init__(self, path):
1392 try:
1402 try:
1393 self.stat = os.stat(path)
1403 self.stat = os.stat(path)
1394 except OSError as err:
1404 except OSError as err:
1395 if err.errno != errno.ENOENT:
1405 if err.errno != errno.ENOENT:
1396 raise
1406 raise
1397 self.stat = None
1407 self.stat = None
1398
1408
1399 __hash__ = object.__hash__
1409 __hash__ = object.__hash__
1400
1410
1401 def __eq__(self, old):
1411 def __eq__(self, old):
1402 try:
1412 try:
1403 # if ambiguity between stat of new and old file is
1413 # if ambiguity between stat of new and old file is
1404 # avoided, comparision of size, ctime and mtime is enough
1414 # avoided, comparision of size, ctime and mtime is enough
1405 # to exactly detect change of a file regardless of platform
1415 # to exactly detect change of a file regardless of platform
1406 return (self.stat.st_size == old.stat.st_size and
1416 return (self.stat.st_size == old.stat.st_size and
1407 self.stat.st_ctime == old.stat.st_ctime and
1417 self.stat.st_ctime == old.stat.st_ctime and
1408 self.stat.st_mtime == old.stat.st_mtime)
1418 self.stat.st_mtime == old.stat.st_mtime)
1409 except AttributeError:
1419 except AttributeError:
1410 return False
1420 return False
1411
1421
1412 def isambig(self, old):
1422 def isambig(self, old):
1413 """Examine whether new (= self) stat is ambiguous against old one
1423 """Examine whether new (= self) stat is ambiguous against old one
1414
1424
1415 "S[N]" below means stat of a file at N-th change:
1425 "S[N]" below means stat of a file at N-th change:
1416
1426
1417 - S[n-1].ctime < S[n].ctime: can detect change of a file
1427 - S[n-1].ctime < S[n].ctime: can detect change of a file
1418 - S[n-1].ctime == S[n].ctime
1428 - S[n-1].ctime == S[n].ctime
1419 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1429 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1420 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1430 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1421 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1431 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1422 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1432 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1423
1433
1424 Case (*2) above means that a file was changed twice or more at
1434 Case (*2) above means that a file was changed twice or more at
1425 same time in sec (= S[n-1].ctime), and comparison of timestamp
1435 same time in sec (= S[n-1].ctime), and comparison of timestamp
1426 is ambiguous.
1436 is ambiguous.
1427
1437
1428 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1438 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1429 timestamp is ambiguous".
1439 timestamp is ambiguous".
1430
1440
1431 But advancing mtime only in case (*2) doesn't work as
1441 But advancing mtime only in case (*2) doesn't work as
1432 expected, because naturally advanced S[n].mtime in case (*1)
1442 expected, because naturally advanced S[n].mtime in case (*1)
1433 might be equal to manually advanced S[n-1 or earlier].mtime.
1443 might be equal to manually advanced S[n-1 or earlier].mtime.
1434
1444
1435 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1445 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1436 treated as ambiguous regardless of mtime, to avoid overlooking
1446 treated as ambiguous regardless of mtime, to avoid overlooking
1437 by confliction between such mtime.
1447 by confliction between such mtime.
1438
1448
1439 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1449 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1440 S[n].mtime", even if size of a file isn't changed.
1450 S[n].mtime", even if size of a file isn't changed.
1441 """
1451 """
1442 try:
1452 try:
1443 return (self.stat.st_ctime == old.stat.st_ctime)
1453 return (self.stat.st_ctime == old.stat.st_ctime)
1444 except AttributeError:
1454 except AttributeError:
1445 return False
1455 return False
1446
1456
1447 class atomictempfile(object):
1457 class atomictempfile(object):
1448 '''writable file object that atomically updates a file
1458 '''writable file object that atomically updates a file
1449
1459
1450 All writes will go to a temporary copy of the original file. Call
1460 All writes will go to a temporary copy of the original file. Call
1451 close() when you are done writing, and atomictempfile will rename
1461 close() when you are done writing, and atomictempfile will rename
1452 the temporary copy to the original name, making the changes
1462 the temporary copy to the original name, making the changes
1453 visible. If the object is destroyed without being closed, all your
1463 visible. If the object is destroyed without being closed, all your
1454 writes are discarded.
1464 writes are discarded.
1455 '''
1465 '''
1456 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1466 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1457 self.__name = name # permanent name
1467 self.__name = name # permanent name
1458 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1468 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1459 createmode=createmode)
1469 createmode=createmode)
1460 self._fp = posixfile(self._tempname, mode)
1470 self._fp = posixfile(self._tempname, mode)
1461 self._checkambig = checkambig
1471 self._checkambig = checkambig
1462
1472
1463 # delegated methods
1473 # delegated methods
1464 self.write = self._fp.write
1474 self.write = self._fp.write
1465 self.seek = self._fp.seek
1475 self.seek = self._fp.seek
1466 self.tell = self._fp.tell
1476 self.tell = self._fp.tell
1467 self.fileno = self._fp.fileno
1477 self.fileno = self._fp.fileno
1468
1478
1469 def close(self):
1479 def close(self):
1470 if not self._fp.closed:
1480 if not self._fp.closed:
1471 self._fp.close()
1481 self._fp.close()
1472 filename = localpath(self.__name)
1482 filename = localpath(self.__name)
1473 oldstat = self._checkambig and filestat(filename)
1483 oldstat = self._checkambig and filestat(filename)
1474 if oldstat and oldstat.stat:
1484 if oldstat and oldstat.stat:
1475 rename(self._tempname, filename)
1485 rename(self._tempname, filename)
1476 newstat = filestat(filename)
1486 newstat = filestat(filename)
1477 if newstat.isambig(oldstat):
1487 if newstat.isambig(oldstat):
1478 # stat of changed file is ambiguous to original one
1488 # stat of changed file is ambiguous to original one
1479 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1489 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1480 os.utime(filename, (advanced, advanced))
1490 os.utime(filename, (advanced, advanced))
1481 else:
1491 else:
1482 rename(self._tempname, filename)
1492 rename(self._tempname, filename)
1483
1493
1484 def discard(self):
1494 def discard(self):
1485 if not self._fp.closed:
1495 if not self._fp.closed:
1486 try:
1496 try:
1487 os.unlink(self._tempname)
1497 os.unlink(self._tempname)
1488 except OSError:
1498 except OSError:
1489 pass
1499 pass
1490 self._fp.close()
1500 self._fp.close()
1491
1501
1492 def __del__(self):
1502 def __del__(self):
1493 if safehasattr(self, '_fp'): # constructor actually did something
1503 if safehasattr(self, '_fp'): # constructor actually did something
1494 self.discard()
1504 self.discard()
1495
1505
1496 def makedirs(name, mode=None, notindexed=False):
1506 def makedirs(name, mode=None, notindexed=False):
1497 """recursive directory creation with parent mode inheritance
1507 """recursive directory creation with parent mode inheritance
1498
1508
1499 Newly created directories are marked as "not to be indexed by
1509 Newly created directories are marked as "not to be indexed by
1500 the content indexing service", if ``notindexed`` is specified
1510 the content indexing service", if ``notindexed`` is specified
1501 for "write" mode access.
1511 for "write" mode access.
1502 """
1512 """
1503 try:
1513 try:
1504 makedir(name, notindexed)
1514 makedir(name, notindexed)
1505 except OSError as err:
1515 except OSError as err:
1506 if err.errno == errno.EEXIST:
1516 if err.errno == errno.EEXIST:
1507 return
1517 return
1508 if err.errno != errno.ENOENT or not name:
1518 if err.errno != errno.ENOENT or not name:
1509 raise
1519 raise
1510 parent = os.path.dirname(os.path.abspath(name))
1520 parent = os.path.dirname(os.path.abspath(name))
1511 if parent == name:
1521 if parent == name:
1512 raise
1522 raise
1513 makedirs(parent, mode, notindexed)
1523 makedirs(parent, mode, notindexed)
1514 try:
1524 try:
1515 makedir(name, notindexed)
1525 makedir(name, notindexed)
1516 except OSError as err:
1526 except OSError as err:
1517 # Catch EEXIST to handle races
1527 # Catch EEXIST to handle races
1518 if err.errno == errno.EEXIST:
1528 if err.errno == errno.EEXIST:
1519 return
1529 return
1520 raise
1530 raise
1521 if mode is not None:
1531 if mode is not None:
1522 os.chmod(name, mode)
1532 os.chmod(name, mode)
1523
1533
1524 def readfile(path):
1534 def readfile(path):
1525 with open(path, 'rb') as fp:
1535 with open(path, 'rb') as fp:
1526 return fp.read()
1536 return fp.read()
1527
1537
1528 def writefile(path, text):
1538 def writefile(path, text):
1529 with open(path, 'wb') as fp:
1539 with open(path, 'wb') as fp:
1530 fp.write(text)
1540 fp.write(text)
1531
1541
1532 def appendfile(path, text):
1542 def appendfile(path, text):
1533 with open(path, 'ab') as fp:
1543 with open(path, 'ab') as fp:
1534 fp.write(text)
1544 fp.write(text)
1535
1545
1536 class chunkbuffer(object):
1546 class chunkbuffer(object):
1537 """Allow arbitrary sized chunks of data to be efficiently read from an
1547 """Allow arbitrary sized chunks of data to be efficiently read from an
1538 iterator over chunks of arbitrary size."""
1548 iterator over chunks of arbitrary size."""
1539
1549
1540 def __init__(self, in_iter):
1550 def __init__(self, in_iter):
1541 """in_iter is the iterator that's iterating over the input chunks.
1551 """in_iter is the iterator that's iterating over the input chunks.
1542 targetsize is how big a buffer to try to maintain."""
1552 targetsize is how big a buffer to try to maintain."""
1543 def splitbig(chunks):
1553 def splitbig(chunks):
1544 for chunk in chunks:
1554 for chunk in chunks:
1545 if len(chunk) > 2**20:
1555 if len(chunk) > 2**20:
1546 pos = 0
1556 pos = 0
1547 while pos < len(chunk):
1557 while pos < len(chunk):
1548 end = pos + 2 ** 18
1558 end = pos + 2 ** 18
1549 yield chunk[pos:end]
1559 yield chunk[pos:end]
1550 pos = end
1560 pos = end
1551 else:
1561 else:
1552 yield chunk
1562 yield chunk
1553 self.iter = splitbig(in_iter)
1563 self.iter = splitbig(in_iter)
1554 self._queue = collections.deque()
1564 self._queue = collections.deque()
1555 self._chunkoffset = 0
1565 self._chunkoffset = 0
1556
1566
1557 def read(self, l=None):
1567 def read(self, l=None):
1558 """Read L bytes of data from the iterator of chunks of data.
1568 """Read L bytes of data from the iterator of chunks of data.
1559 Returns less than L bytes if the iterator runs dry.
1569 Returns less than L bytes if the iterator runs dry.
1560
1570
1561 If size parameter is omitted, read everything"""
1571 If size parameter is omitted, read everything"""
1562 if l is None:
1572 if l is None:
1563 return ''.join(self.iter)
1573 return ''.join(self.iter)
1564
1574
1565 left = l
1575 left = l
1566 buf = []
1576 buf = []
1567 queue = self._queue
1577 queue = self._queue
1568 while left > 0:
1578 while left > 0:
1569 # refill the queue
1579 # refill the queue
1570 if not queue:
1580 if not queue:
1571 target = 2**18
1581 target = 2**18
1572 for chunk in self.iter:
1582 for chunk in self.iter:
1573 queue.append(chunk)
1583 queue.append(chunk)
1574 target -= len(chunk)
1584 target -= len(chunk)
1575 if target <= 0:
1585 if target <= 0:
1576 break
1586 break
1577 if not queue:
1587 if not queue:
1578 break
1588 break
1579
1589
1580 # The easy way to do this would be to queue.popleft(), modify the
1590 # The easy way to do this would be to queue.popleft(), modify the
1581 # chunk (if necessary), then queue.appendleft(). However, for cases
1591 # chunk (if necessary), then queue.appendleft(). However, for cases
1582 # where we read partial chunk content, this incurs 2 dequeue
1592 # where we read partial chunk content, this incurs 2 dequeue
1583 # mutations and creates a new str for the remaining chunk in the
1593 # mutations and creates a new str for the remaining chunk in the
1584 # queue. Our code below avoids this overhead.
1594 # queue. Our code below avoids this overhead.
1585
1595
1586 chunk = queue[0]
1596 chunk = queue[0]
1587 chunkl = len(chunk)
1597 chunkl = len(chunk)
1588 offset = self._chunkoffset
1598 offset = self._chunkoffset
1589
1599
1590 # Use full chunk.
1600 # Use full chunk.
1591 if offset == 0 and left >= chunkl:
1601 if offset == 0 and left >= chunkl:
1592 left -= chunkl
1602 left -= chunkl
1593 queue.popleft()
1603 queue.popleft()
1594 buf.append(chunk)
1604 buf.append(chunk)
1595 # self._chunkoffset remains at 0.
1605 # self._chunkoffset remains at 0.
1596 continue
1606 continue
1597
1607
1598 chunkremaining = chunkl - offset
1608 chunkremaining = chunkl - offset
1599
1609
1600 # Use all of unconsumed part of chunk.
1610 # Use all of unconsumed part of chunk.
1601 if left >= chunkremaining:
1611 if left >= chunkremaining:
1602 left -= chunkremaining
1612 left -= chunkremaining
1603 queue.popleft()
1613 queue.popleft()
1604 # offset == 0 is enabled by block above, so this won't merely
1614 # offset == 0 is enabled by block above, so this won't merely
1605 # copy via ``chunk[0:]``.
1615 # copy via ``chunk[0:]``.
1606 buf.append(chunk[offset:])
1616 buf.append(chunk[offset:])
1607 self._chunkoffset = 0
1617 self._chunkoffset = 0
1608
1618
1609 # Partial chunk needed.
1619 # Partial chunk needed.
1610 else:
1620 else:
1611 buf.append(chunk[offset:offset + left])
1621 buf.append(chunk[offset:offset + left])
1612 self._chunkoffset += left
1622 self._chunkoffset += left
1613 left -= chunkremaining
1623 left -= chunkremaining
1614
1624
1615 return ''.join(buf)
1625 return ''.join(buf)
1616
1626
1617 def filechunkiter(f, size=65536, limit=None):
1627 def filechunkiter(f, size=65536, limit=None):
1618 """Create a generator that produces the data in the file size
1628 """Create a generator that produces the data in the file size
1619 (default 65536) bytes at a time, up to optional limit (default is
1629 (default 65536) bytes at a time, up to optional limit (default is
1620 to read all data). Chunks may be less than size bytes if the
1630 to read all data). Chunks may be less than size bytes if the
1621 chunk is the last chunk in the file, or the file is a socket or
1631 chunk is the last chunk in the file, or the file is a socket or
1622 some other type of file that sometimes reads less data than is
1632 some other type of file that sometimes reads less data than is
1623 requested."""
1633 requested."""
1624 assert size >= 0
1634 assert size >= 0
1625 assert limit is None or limit >= 0
1635 assert limit is None or limit >= 0
1626 while True:
1636 while True:
1627 if limit is None:
1637 if limit is None:
1628 nbytes = size
1638 nbytes = size
1629 else:
1639 else:
1630 nbytes = min(limit, size)
1640 nbytes = min(limit, size)
1631 s = nbytes and f.read(nbytes)
1641 s = nbytes and f.read(nbytes)
1632 if not s:
1642 if not s:
1633 break
1643 break
1634 if limit:
1644 if limit:
1635 limit -= len(s)
1645 limit -= len(s)
1636 yield s
1646 yield s
1637
1647
1638 def makedate(timestamp=None):
1648 def makedate(timestamp=None):
1639 '''Return a unix timestamp (or the current time) as a (unixtime,
1649 '''Return a unix timestamp (or the current time) as a (unixtime,
1640 offset) tuple based off the local timezone.'''
1650 offset) tuple based off the local timezone.'''
1641 if timestamp is None:
1651 if timestamp is None:
1642 timestamp = time.time()
1652 timestamp = time.time()
1643 if timestamp < 0:
1653 if timestamp < 0:
1644 hint = _("check your clock")
1654 hint = _("check your clock")
1645 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1655 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1646 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1656 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1647 datetime.datetime.fromtimestamp(timestamp))
1657 datetime.datetime.fromtimestamp(timestamp))
1648 tz = delta.days * 86400 + delta.seconds
1658 tz = delta.days * 86400 + delta.seconds
1649 return timestamp, tz
1659 return timestamp, tz
1650
1660
1651 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1661 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1652 """represent a (unixtime, offset) tuple as a localized time.
1662 """represent a (unixtime, offset) tuple as a localized time.
1653 unixtime is seconds since the epoch, and offset is the time zone's
1663 unixtime is seconds since the epoch, and offset is the time zone's
1654 number of seconds away from UTC.
1664 number of seconds away from UTC.
1655
1665
1656 >>> datestr((0, 0))
1666 >>> datestr((0, 0))
1657 'Thu Jan 01 00:00:00 1970 +0000'
1667 'Thu Jan 01 00:00:00 1970 +0000'
1658 >>> datestr((42, 0))
1668 >>> datestr((42, 0))
1659 'Thu Jan 01 00:00:42 1970 +0000'
1669 'Thu Jan 01 00:00:42 1970 +0000'
1660 >>> datestr((-42, 0))
1670 >>> datestr((-42, 0))
1661 'Wed Dec 31 23:59:18 1969 +0000'
1671 'Wed Dec 31 23:59:18 1969 +0000'
1662 >>> datestr((0x7fffffff, 0))
1672 >>> datestr((0x7fffffff, 0))
1663 'Tue Jan 19 03:14:07 2038 +0000'
1673 'Tue Jan 19 03:14:07 2038 +0000'
1664 >>> datestr((-0x80000000, 0))
1674 >>> datestr((-0x80000000, 0))
1665 'Fri Dec 13 20:45:52 1901 +0000'
1675 'Fri Dec 13 20:45:52 1901 +0000'
1666 """
1676 """
1667 t, tz = date or makedate()
1677 t, tz = date or makedate()
1668 if "%1" in format or "%2" in format or "%z" in format:
1678 if "%1" in format or "%2" in format or "%z" in format:
1669 sign = (tz > 0) and "-" or "+"
1679 sign = (tz > 0) and "-" or "+"
1670 minutes = abs(tz) // 60
1680 minutes = abs(tz) // 60
1671 q, r = divmod(minutes, 60)
1681 q, r = divmod(minutes, 60)
1672 format = format.replace("%z", "%1%2")
1682 format = format.replace("%z", "%1%2")
1673 format = format.replace("%1", "%c%02d" % (sign, q))
1683 format = format.replace("%1", "%c%02d" % (sign, q))
1674 format = format.replace("%2", "%02d" % r)
1684 format = format.replace("%2", "%02d" % r)
1675 d = t - tz
1685 d = t - tz
1676 if d > 0x7fffffff:
1686 if d > 0x7fffffff:
1677 d = 0x7fffffff
1687 d = 0x7fffffff
1678 elif d < -0x80000000:
1688 elif d < -0x80000000:
1679 d = -0x80000000
1689 d = -0x80000000
1680 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1690 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1681 # because they use the gmtime() system call which is buggy on Windows
1691 # because they use the gmtime() system call which is buggy on Windows
1682 # for negative values.
1692 # for negative values.
1683 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1693 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1684 s = t.strftime(format)
1694 s = t.strftime(format)
1685 return s
1695 return s
1686
1696
1687 def shortdate(date=None):
1697 def shortdate(date=None):
1688 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1698 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1689 return datestr(date, format='%Y-%m-%d')
1699 return datestr(date, format='%Y-%m-%d')
1690
1700
1691 def parsetimezone(tz):
1701 def parsetimezone(tz):
1692 """parse a timezone string and return an offset integer"""
1702 """parse a timezone string and return an offset integer"""
1693 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1703 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1694 sign = (tz[0] == "+") and 1 or -1
1704 sign = (tz[0] == "+") and 1 or -1
1695 hours = int(tz[1:3])
1705 hours = int(tz[1:3])
1696 minutes = int(tz[3:5])
1706 minutes = int(tz[3:5])
1697 return -sign * (hours * 60 + minutes) * 60
1707 return -sign * (hours * 60 + minutes) * 60
1698 if tz == "GMT" or tz == "UTC":
1708 if tz == "GMT" or tz == "UTC":
1699 return 0
1709 return 0
1700 return None
1710 return None
1701
1711
1702 def strdate(string, format, defaults=[]):
1712 def strdate(string, format, defaults=[]):
1703 """parse a localized time string and return a (unixtime, offset) tuple.
1713 """parse a localized time string and return a (unixtime, offset) tuple.
1704 if the string cannot be parsed, ValueError is raised."""
1714 if the string cannot be parsed, ValueError is raised."""
1705 # NOTE: unixtime = localunixtime + offset
1715 # NOTE: unixtime = localunixtime + offset
1706 offset, date = parsetimezone(string.split()[-1]), string
1716 offset, date = parsetimezone(string.split()[-1]), string
1707 if offset is not None:
1717 if offset is not None:
1708 date = " ".join(string.split()[:-1])
1718 date = " ".join(string.split()[:-1])
1709
1719
1710 # add missing elements from defaults
1720 # add missing elements from defaults
1711 usenow = False # default to using biased defaults
1721 usenow = False # default to using biased defaults
1712 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1722 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1713 found = [True for p in part if ("%"+p) in format]
1723 found = [True for p in part if ("%"+p) in format]
1714 if not found:
1724 if not found:
1715 date += "@" + defaults[part][usenow]
1725 date += "@" + defaults[part][usenow]
1716 format += "@%" + part[0]
1726 format += "@%" + part[0]
1717 else:
1727 else:
1718 # We've found a specific time element, less specific time
1728 # We've found a specific time element, less specific time
1719 # elements are relative to today
1729 # elements are relative to today
1720 usenow = True
1730 usenow = True
1721
1731
1722 timetuple = time.strptime(date, format)
1732 timetuple = time.strptime(date, format)
1723 localunixtime = int(calendar.timegm(timetuple))
1733 localunixtime = int(calendar.timegm(timetuple))
1724 if offset is None:
1734 if offset is None:
1725 # local timezone
1735 # local timezone
1726 unixtime = int(time.mktime(timetuple))
1736 unixtime = int(time.mktime(timetuple))
1727 offset = unixtime - localunixtime
1737 offset = unixtime - localunixtime
1728 else:
1738 else:
1729 unixtime = localunixtime + offset
1739 unixtime = localunixtime + offset
1730 return unixtime, offset
1740 return unixtime, offset
1731
1741
1732 def parsedate(date, formats=None, bias=None):
1742 def parsedate(date, formats=None, bias=None):
1733 """parse a localized date/time and return a (unixtime, offset) tuple.
1743 """parse a localized date/time and return a (unixtime, offset) tuple.
1734
1744
1735 The date may be a "unixtime offset" string or in one of the specified
1745 The date may be a "unixtime offset" string or in one of the specified
1736 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1746 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1737
1747
1738 >>> parsedate(' today ') == parsedate(\
1748 >>> parsedate(' today ') == parsedate(\
1739 datetime.date.today().strftime('%b %d'))
1749 datetime.date.today().strftime('%b %d'))
1740 True
1750 True
1741 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1751 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1742 datetime.timedelta(days=1)\
1752 datetime.timedelta(days=1)\
1743 ).strftime('%b %d'))
1753 ).strftime('%b %d'))
1744 True
1754 True
1745 >>> now, tz = makedate()
1755 >>> now, tz = makedate()
1746 >>> strnow, strtz = parsedate('now')
1756 >>> strnow, strtz = parsedate('now')
1747 >>> (strnow - now) < 1
1757 >>> (strnow - now) < 1
1748 True
1758 True
1749 >>> tz == strtz
1759 >>> tz == strtz
1750 True
1760 True
1751 """
1761 """
1752 if bias is None:
1762 if bias is None:
1753 bias = {}
1763 bias = {}
1754 if not date:
1764 if not date:
1755 return 0, 0
1765 return 0, 0
1756 if isinstance(date, tuple) and len(date) == 2:
1766 if isinstance(date, tuple) and len(date) == 2:
1757 return date
1767 return date
1758 if not formats:
1768 if not formats:
1759 formats = defaultdateformats
1769 formats = defaultdateformats
1760 date = date.strip()
1770 date = date.strip()
1761
1771
1762 if date == 'now' or date == _('now'):
1772 if date == 'now' or date == _('now'):
1763 return makedate()
1773 return makedate()
1764 if date == 'today' or date == _('today'):
1774 if date == 'today' or date == _('today'):
1765 date = datetime.date.today().strftime('%b %d')
1775 date = datetime.date.today().strftime('%b %d')
1766 elif date == 'yesterday' or date == _('yesterday'):
1776 elif date == 'yesterday' or date == _('yesterday'):
1767 date = (datetime.date.today() -
1777 date = (datetime.date.today() -
1768 datetime.timedelta(days=1)).strftime('%b %d')
1778 datetime.timedelta(days=1)).strftime('%b %d')
1769
1779
1770 try:
1780 try:
1771 when, offset = map(int, date.split(' '))
1781 when, offset = map(int, date.split(' '))
1772 except ValueError:
1782 except ValueError:
1773 # fill out defaults
1783 # fill out defaults
1774 now = makedate()
1784 now = makedate()
1775 defaults = {}
1785 defaults = {}
1776 for part in ("d", "mb", "yY", "HI", "M", "S"):
1786 for part in ("d", "mb", "yY", "HI", "M", "S"):
1777 # this piece is for rounding the specific end of unknowns
1787 # this piece is for rounding the specific end of unknowns
1778 b = bias.get(part)
1788 b = bias.get(part)
1779 if b is None:
1789 if b is None:
1780 if part[0] in "HMS":
1790 if part[0] in "HMS":
1781 b = "00"
1791 b = "00"
1782 else:
1792 else:
1783 b = "0"
1793 b = "0"
1784
1794
1785 # this piece is for matching the generic end to today's date
1795 # this piece is for matching the generic end to today's date
1786 n = datestr(now, "%" + part[0])
1796 n = datestr(now, "%" + part[0])
1787
1797
1788 defaults[part] = (b, n)
1798 defaults[part] = (b, n)
1789
1799
1790 for format in formats:
1800 for format in formats:
1791 try:
1801 try:
1792 when, offset = strdate(date, format, defaults)
1802 when, offset = strdate(date, format, defaults)
1793 except (ValueError, OverflowError):
1803 except (ValueError, OverflowError):
1794 pass
1804 pass
1795 else:
1805 else:
1796 break
1806 break
1797 else:
1807 else:
1798 raise Abort(_('invalid date: %r') % date)
1808 raise Abort(_('invalid date: %r') % date)
1799 # validate explicit (probably user-specified) date and
1809 # validate explicit (probably user-specified) date and
1800 # time zone offset. values must fit in signed 32 bits for
1810 # time zone offset. values must fit in signed 32 bits for
1801 # current 32-bit linux runtimes. timezones go from UTC-12
1811 # current 32-bit linux runtimes. timezones go from UTC-12
1802 # to UTC+14
1812 # to UTC+14
1803 if when < -0x80000000 or when > 0x7fffffff:
1813 if when < -0x80000000 or when > 0x7fffffff:
1804 raise Abort(_('date exceeds 32 bits: %d') % when)
1814 raise Abort(_('date exceeds 32 bits: %d') % when)
1805 if offset < -50400 or offset > 43200:
1815 if offset < -50400 or offset > 43200:
1806 raise Abort(_('impossible time zone offset: %d') % offset)
1816 raise Abort(_('impossible time zone offset: %d') % offset)
1807 return when, offset
1817 return when, offset
1808
1818
1809 def matchdate(date):
1819 def matchdate(date):
1810 """Return a function that matches a given date match specifier
1820 """Return a function that matches a given date match specifier
1811
1821
1812 Formats include:
1822 Formats include:
1813
1823
1814 '{date}' match a given date to the accuracy provided
1824 '{date}' match a given date to the accuracy provided
1815
1825
1816 '<{date}' on or before a given date
1826 '<{date}' on or before a given date
1817
1827
1818 '>{date}' on or after a given date
1828 '>{date}' on or after a given date
1819
1829
1820 >>> p1 = parsedate("10:29:59")
1830 >>> p1 = parsedate("10:29:59")
1821 >>> p2 = parsedate("10:30:00")
1831 >>> p2 = parsedate("10:30:00")
1822 >>> p3 = parsedate("10:30:59")
1832 >>> p3 = parsedate("10:30:59")
1823 >>> p4 = parsedate("10:31:00")
1833 >>> p4 = parsedate("10:31:00")
1824 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1834 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1825 >>> f = matchdate("10:30")
1835 >>> f = matchdate("10:30")
1826 >>> f(p1[0])
1836 >>> f(p1[0])
1827 False
1837 False
1828 >>> f(p2[0])
1838 >>> f(p2[0])
1829 True
1839 True
1830 >>> f(p3[0])
1840 >>> f(p3[0])
1831 True
1841 True
1832 >>> f(p4[0])
1842 >>> f(p4[0])
1833 False
1843 False
1834 >>> f(p5[0])
1844 >>> f(p5[0])
1835 False
1845 False
1836 """
1846 """
1837
1847
1838 def lower(date):
1848 def lower(date):
1839 d = {'mb': "1", 'd': "1"}
1849 d = {'mb': "1", 'd': "1"}
1840 return parsedate(date, extendeddateformats, d)[0]
1850 return parsedate(date, extendeddateformats, d)[0]
1841
1851
1842 def upper(date):
1852 def upper(date):
1843 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1853 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1844 for days in ("31", "30", "29"):
1854 for days in ("31", "30", "29"):
1845 try:
1855 try:
1846 d["d"] = days
1856 d["d"] = days
1847 return parsedate(date, extendeddateformats, d)[0]
1857 return parsedate(date, extendeddateformats, d)[0]
1848 except Abort:
1858 except Abort:
1849 pass
1859 pass
1850 d["d"] = "28"
1860 d["d"] = "28"
1851 return parsedate(date, extendeddateformats, d)[0]
1861 return parsedate(date, extendeddateformats, d)[0]
1852
1862
1853 date = date.strip()
1863 date = date.strip()
1854
1864
1855 if not date:
1865 if not date:
1856 raise Abort(_("dates cannot consist entirely of whitespace"))
1866 raise Abort(_("dates cannot consist entirely of whitespace"))
1857 elif date[0] == "<":
1867 elif date[0] == "<":
1858 if not date[1:]:
1868 if not date[1:]:
1859 raise Abort(_("invalid day spec, use '<DATE'"))
1869 raise Abort(_("invalid day spec, use '<DATE'"))
1860 when = upper(date[1:])
1870 when = upper(date[1:])
1861 return lambda x: x <= when
1871 return lambda x: x <= when
1862 elif date[0] == ">":
1872 elif date[0] == ">":
1863 if not date[1:]:
1873 if not date[1:]:
1864 raise Abort(_("invalid day spec, use '>DATE'"))
1874 raise Abort(_("invalid day spec, use '>DATE'"))
1865 when = lower(date[1:])
1875 when = lower(date[1:])
1866 return lambda x: x >= when
1876 return lambda x: x >= when
1867 elif date[0] == "-":
1877 elif date[0] == "-":
1868 try:
1878 try:
1869 days = int(date[1:])
1879 days = int(date[1:])
1870 except ValueError:
1880 except ValueError:
1871 raise Abort(_("invalid day spec: %s") % date[1:])
1881 raise Abort(_("invalid day spec: %s") % date[1:])
1872 if days < 0:
1882 if days < 0:
1873 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1883 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1874 % date[1:])
1884 % date[1:])
1875 when = makedate()[0] - days * 3600 * 24
1885 when = makedate()[0] - days * 3600 * 24
1876 return lambda x: x >= when
1886 return lambda x: x >= when
1877 elif " to " in date:
1887 elif " to " in date:
1878 a, b = date.split(" to ")
1888 a, b = date.split(" to ")
1879 start, stop = lower(a), upper(b)
1889 start, stop = lower(a), upper(b)
1880 return lambda x: x >= start and x <= stop
1890 return lambda x: x >= start and x <= stop
1881 else:
1891 else:
1882 start, stop = lower(date), upper(date)
1892 start, stop = lower(date), upper(date)
1883 return lambda x: x >= start and x <= stop
1893 return lambda x: x >= start and x <= stop
1884
1894
1885 def stringmatcher(pattern):
1895 def stringmatcher(pattern):
1886 """
1896 """
1887 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1897 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1888 returns the matcher name, pattern, and matcher function.
1898 returns the matcher name, pattern, and matcher function.
1889 missing or unknown prefixes are treated as literal matches.
1899 missing or unknown prefixes are treated as literal matches.
1890
1900
1891 helper for tests:
1901 helper for tests:
1892 >>> def test(pattern, *tests):
1902 >>> def test(pattern, *tests):
1893 ... kind, pattern, matcher = stringmatcher(pattern)
1903 ... kind, pattern, matcher = stringmatcher(pattern)
1894 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1904 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1895
1905
1896 exact matching (no prefix):
1906 exact matching (no prefix):
1897 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1907 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1898 ('literal', 'abcdefg', [False, False, True])
1908 ('literal', 'abcdefg', [False, False, True])
1899
1909
1900 regex matching ('re:' prefix)
1910 regex matching ('re:' prefix)
1901 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1911 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1902 ('re', 'a.+b', [False, False, True])
1912 ('re', 'a.+b', [False, False, True])
1903
1913
1904 force exact matches ('literal:' prefix)
1914 force exact matches ('literal:' prefix)
1905 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1915 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1906 ('literal', 're:foobar', [False, True])
1916 ('literal', 're:foobar', [False, True])
1907
1917
1908 unknown prefixes are ignored and treated as literals
1918 unknown prefixes are ignored and treated as literals
1909 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1919 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1910 ('literal', 'foo:bar', [False, False, True])
1920 ('literal', 'foo:bar', [False, False, True])
1911 """
1921 """
1912 if pattern.startswith('re:'):
1922 if pattern.startswith('re:'):
1913 pattern = pattern[3:]
1923 pattern = pattern[3:]
1914 try:
1924 try:
1915 regex = remod.compile(pattern)
1925 regex = remod.compile(pattern)
1916 except remod.error as e:
1926 except remod.error as e:
1917 raise error.ParseError(_('invalid regular expression: %s')
1927 raise error.ParseError(_('invalid regular expression: %s')
1918 % e)
1928 % e)
1919 return 're', pattern, regex.search
1929 return 're', pattern, regex.search
1920 elif pattern.startswith('literal:'):
1930 elif pattern.startswith('literal:'):
1921 pattern = pattern[8:]
1931 pattern = pattern[8:]
1922 return 'literal', pattern, pattern.__eq__
1932 return 'literal', pattern, pattern.__eq__
1923
1933
1924 def shortuser(user):
1934 def shortuser(user):
1925 """Return a short representation of a user name or email address."""
1935 """Return a short representation of a user name or email address."""
1926 f = user.find('@')
1936 f = user.find('@')
1927 if f >= 0:
1937 if f >= 0:
1928 user = user[:f]
1938 user = user[:f]
1929 f = user.find('<')
1939 f = user.find('<')
1930 if f >= 0:
1940 if f >= 0:
1931 user = user[f + 1:]
1941 user = user[f + 1:]
1932 f = user.find(' ')
1942 f = user.find(' ')
1933 if f >= 0:
1943 if f >= 0:
1934 user = user[:f]
1944 user = user[:f]
1935 f = user.find('.')
1945 f = user.find('.')
1936 if f >= 0:
1946 if f >= 0:
1937 user = user[:f]
1947 user = user[:f]
1938 return user
1948 return user
1939
1949
1940 def emailuser(user):
1950 def emailuser(user):
1941 """Return the user portion of an email address."""
1951 """Return the user portion of an email address."""
1942 f = user.find('@')
1952 f = user.find('@')
1943 if f >= 0:
1953 if f >= 0:
1944 user = user[:f]
1954 user = user[:f]
1945 f = user.find('<')
1955 f = user.find('<')
1946 if f >= 0:
1956 if f >= 0:
1947 user = user[f + 1:]
1957 user = user[f + 1:]
1948 return user
1958 return user
1949
1959
1950 def email(author):
1960 def email(author):
1951 '''get email of author.'''
1961 '''get email of author.'''
1952 r = author.find('>')
1962 r = author.find('>')
1953 if r == -1:
1963 if r == -1:
1954 r = None
1964 r = None
1955 return author[author.find('<') + 1:r]
1965 return author[author.find('<') + 1:r]
1956
1966
1957 def ellipsis(text, maxlength=400):
1967 def ellipsis(text, maxlength=400):
1958 """Trim string to at most maxlength (default: 400) columns in display."""
1968 """Trim string to at most maxlength (default: 400) columns in display."""
1959 return encoding.trim(text, maxlength, ellipsis='...')
1969 return encoding.trim(text, maxlength, ellipsis='...')
1960
1970
1961 def unitcountfn(*unittable):
1971 def unitcountfn(*unittable):
1962 '''return a function that renders a readable count of some quantity'''
1972 '''return a function that renders a readable count of some quantity'''
1963
1973
1964 def go(count):
1974 def go(count):
1965 for multiplier, divisor, format in unittable:
1975 for multiplier, divisor, format in unittable:
1966 if count >= divisor * multiplier:
1976 if count >= divisor * multiplier:
1967 return format % (count / float(divisor))
1977 return format % (count / float(divisor))
1968 return unittable[-1][2] % count
1978 return unittable[-1][2] % count
1969
1979
1970 return go
1980 return go
1971
1981
1972 bytecount = unitcountfn(
1982 bytecount = unitcountfn(
1973 (100, 1 << 30, _('%.0f GB')),
1983 (100, 1 << 30, _('%.0f GB')),
1974 (10, 1 << 30, _('%.1f GB')),
1984 (10, 1 << 30, _('%.1f GB')),
1975 (1, 1 << 30, _('%.2f GB')),
1985 (1, 1 << 30, _('%.2f GB')),
1976 (100, 1 << 20, _('%.0f MB')),
1986 (100, 1 << 20, _('%.0f MB')),
1977 (10, 1 << 20, _('%.1f MB')),
1987 (10, 1 << 20, _('%.1f MB')),
1978 (1, 1 << 20, _('%.2f MB')),
1988 (1, 1 << 20, _('%.2f MB')),
1979 (100, 1 << 10, _('%.0f KB')),
1989 (100, 1 << 10, _('%.0f KB')),
1980 (10, 1 << 10, _('%.1f KB')),
1990 (10, 1 << 10, _('%.1f KB')),
1981 (1, 1 << 10, _('%.2f KB')),
1991 (1, 1 << 10, _('%.2f KB')),
1982 (1, 1, _('%.0f bytes')),
1992 (1, 1, _('%.0f bytes')),
1983 )
1993 )
1984
1994
1985 def uirepr(s):
1995 def uirepr(s):
1986 # Avoid double backslash in Windows path repr()
1996 # Avoid double backslash in Windows path repr()
1987 return repr(s).replace('\\\\', '\\')
1997 return repr(s).replace('\\\\', '\\')
1988
1998
1989 # delay import of textwrap
1999 # delay import of textwrap
1990 def MBTextWrapper(**kwargs):
2000 def MBTextWrapper(**kwargs):
1991 class tw(textwrap.TextWrapper):
2001 class tw(textwrap.TextWrapper):
1992 """
2002 """
1993 Extend TextWrapper for width-awareness.
2003 Extend TextWrapper for width-awareness.
1994
2004
1995 Neither number of 'bytes' in any encoding nor 'characters' is
2005 Neither number of 'bytes' in any encoding nor 'characters' is
1996 appropriate to calculate terminal columns for specified string.
2006 appropriate to calculate terminal columns for specified string.
1997
2007
1998 Original TextWrapper implementation uses built-in 'len()' directly,
2008 Original TextWrapper implementation uses built-in 'len()' directly,
1999 so overriding is needed to use width information of each characters.
2009 so overriding is needed to use width information of each characters.
2000
2010
2001 In addition, characters classified into 'ambiguous' width are
2011 In addition, characters classified into 'ambiguous' width are
2002 treated as wide in East Asian area, but as narrow in other.
2012 treated as wide in East Asian area, but as narrow in other.
2003
2013
2004 This requires use decision to determine width of such characters.
2014 This requires use decision to determine width of such characters.
2005 """
2015 """
2006 def _cutdown(self, ucstr, space_left):
2016 def _cutdown(self, ucstr, space_left):
2007 l = 0
2017 l = 0
2008 colwidth = encoding.ucolwidth
2018 colwidth = encoding.ucolwidth
2009 for i in xrange(len(ucstr)):
2019 for i in xrange(len(ucstr)):
2010 l += colwidth(ucstr[i])
2020 l += colwidth(ucstr[i])
2011 if space_left < l:
2021 if space_left < l:
2012 return (ucstr[:i], ucstr[i:])
2022 return (ucstr[:i], ucstr[i:])
2013 return ucstr, ''
2023 return ucstr, ''
2014
2024
2015 # overriding of base class
2025 # overriding of base class
2016 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2026 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2017 space_left = max(width - cur_len, 1)
2027 space_left = max(width - cur_len, 1)
2018
2028
2019 if self.break_long_words:
2029 if self.break_long_words:
2020 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2030 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2021 cur_line.append(cut)
2031 cur_line.append(cut)
2022 reversed_chunks[-1] = res
2032 reversed_chunks[-1] = res
2023 elif not cur_line:
2033 elif not cur_line:
2024 cur_line.append(reversed_chunks.pop())
2034 cur_line.append(reversed_chunks.pop())
2025
2035
2026 # this overriding code is imported from TextWrapper of Python 2.6
2036 # this overriding code is imported from TextWrapper of Python 2.6
2027 # to calculate columns of string by 'encoding.ucolwidth()'
2037 # to calculate columns of string by 'encoding.ucolwidth()'
2028 def _wrap_chunks(self, chunks):
2038 def _wrap_chunks(self, chunks):
2029 colwidth = encoding.ucolwidth
2039 colwidth = encoding.ucolwidth
2030
2040
2031 lines = []
2041 lines = []
2032 if self.width <= 0:
2042 if self.width <= 0:
2033 raise ValueError("invalid width %r (must be > 0)" % self.width)
2043 raise ValueError("invalid width %r (must be > 0)" % self.width)
2034
2044
2035 # Arrange in reverse order so items can be efficiently popped
2045 # Arrange in reverse order so items can be efficiently popped
2036 # from a stack of chucks.
2046 # from a stack of chucks.
2037 chunks.reverse()
2047 chunks.reverse()
2038
2048
2039 while chunks:
2049 while chunks:
2040
2050
2041 # Start the list of chunks that will make up the current line.
2051 # Start the list of chunks that will make up the current line.
2042 # cur_len is just the length of all the chunks in cur_line.
2052 # cur_len is just the length of all the chunks in cur_line.
2043 cur_line = []
2053 cur_line = []
2044 cur_len = 0
2054 cur_len = 0
2045
2055
2046 # Figure out which static string will prefix this line.
2056 # Figure out which static string will prefix this line.
2047 if lines:
2057 if lines:
2048 indent = self.subsequent_indent
2058 indent = self.subsequent_indent
2049 else:
2059 else:
2050 indent = self.initial_indent
2060 indent = self.initial_indent
2051
2061
2052 # Maximum width for this line.
2062 # Maximum width for this line.
2053 width = self.width - len(indent)
2063 width = self.width - len(indent)
2054
2064
2055 # First chunk on line is whitespace -- drop it, unless this
2065 # First chunk on line is whitespace -- drop it, unless this
2056 # is the very beginning of the text (i.e. no lines started yet).
2066 # is the very beginning of the text (i.e. no lines started yet).
2057 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2067 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2058 del chunks[-1]
2068 del chunks[-1]
2059
2069
2060 while chunks:
2070 while chunks:
2061 l = colwidth(chunks[-1])
2071 l = colwidth(chunks[-1])
2062
2072
2063 # Can at least squeeze this chunk onto the current line.
2073 # Can at least squeeze this chunk onto the current line.
2064 if cur_len + l <= width:
2074 if cur_len + l <= width:
2065 cur_line.append(chunks.pop())
2075 cur_line.append(chunks.pop())
2066 cur_len += l
2076 cur_len += l
2067
2077
2068 # Nope, this line is full.
2078 # Nope, this line is full.
2069 else:
2079 else:
2070 break
2080 break
2071
2081
2072 # The current line is full, and the next chunk is too big to
2082 # The current line is full, and the next chunk is too big to
2073 # fit on *any* line (not just this one).
2083 # fit on *any* line (not just this one).
2074 if chunks and colwidth(chunks[-1]) > width:
2084 if chunks and colwidth(chunks[-1]) > width:
2075 self._handle_long_word(chunks, cur_line, cur_len, width)
2085 self._handle_long_word(chunks, cur_line, cur_len, width)
2076
2086
2077 # If the last chunk on this line is all whitespace, drop it.
2087 # If the last chunk on this line is all whitespace, drop it.
2078 if (self.drop_whitespace and
2088 if (self.drop_whitespace and
2079 cur_line and cur_line[-1].strip() == ''):
2089 cur_line and cur_line[-1].strip() == ''):
2080 del cur_line[-1]
2090 del cur_line[-1]
2081
2091
2082 # Convert current line back to a string and store it in list
2092 # Convert current line back to a string and store it in list
2083 # of all lines (return value).
2093 # of all lines (return value).
2084 if cur_line:
2094 if cur_line:
2085 lines.append(indent + ''.join(cur_line))
2095 lines.append(indent + ''.join(cur_line))
2086
2096
2087 return lines
2097 return lines
2088
2098
2089 global MBTextWrapper
2099 global MBTextWrapper
2090 MBTextWrapper = tw
2100 MBTextWrapper = tw
2091 return tw(**kwargs)
2101 return tw(**kwargs)
2092
2102
2093 def wrap(line, width, initindent='', hangindent=''):
2103 def wrap(line, width, initindent='', hangindent=''):
2094 maxindent = max(len(hangindent), len(initindent))
2104 maxindent = max(len(hangindent), len(initindent))
2095 if width <= maxindent:
2105 if width <= maxindent:
2096 # adjust for weird terminal size
2106 # adjust for weird terminal size
2097 width = max(78, maxindent + 1)
2107 width = max(78, maxindent + 1)
2098 line = line.decode(encoding.encoding, encoding.encodingmode)
2108 line = line.decode(encoding.encoding, encoding.encodingmode)
2099 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
2109 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
2100 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
2110 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
2101 wrapper = MBTextWrapper(width=width,
2111 wrapper = MBTextWrapper(width=width,
2102 initial_indent=initindent,
2112 initial_indent=initindent,
2103 subsequent_indent=hangindent)
2113 subsequent_indent=hangindent)
2104 return wrapper.fill(line).encode(encoding.encoding)
2114 return wrapper.fill(line).encode(encoding.encoding)
2105
2115
2106 def iterlines(iterator):
2116 def iterlines(iterator):
2107 for chunk in iterator:
2117 for chunk in iterator:
2108 for line in chunk.splitlines():
2118 for line in chunk.splitlines():
2109 yield line
2119 yield line
2110
2120
2111 def expandpath(path):
2121 def expandpath(path):
2112 return os.path.expanduser(os.path.expandvars(path))
2122 return os.path.expanduser(os.path.expandvars(path))
2113
2123
2114 def hgcmd():
2124 def hgcmd():
2115 """Return the command used to execute current hg
2125 """Return the command used to execute current hg
2116
2126
2117 This is different from hgexecutable() because on Windows we want
2127 This is different from hgexecutable() because on Windows we want
2118 to avoid things opening new shell windows like batch files, so we
2128 to avoid things opening new shell windows like batch files, so we
2119 get either the python call or current executable.
2129 get either the python call or current executable.
2120 """
2130 """
2121 if mainfrozen():
2131 if mainfrozen():
2122 if getattr(sys, 'frozen', None) == 'macosx_app':
2132 if getattr(sys, 'frozen', None) == 'macosx_app':
2123 # Env variable set by py2app
2133 # Env variable set by py2app
2124 return [os.environ['EXECUTABLEPATH']]
2134 return [os.environ['EXECUTABLEPATH']]
2125 else:
2135 else:
2126 return [sys.executable]
2136 return [sys.executable]
2127 return gethgcmd()
2137 return gethgcmd()
2128
2138
2129 def rundetached(args, condfn):
2139 def rundetached(args, condfn):
2130 """Execute the argument list in a detached process.
2140 """Execute the argument list in a detached process.
2131
2141
2132 condfn is a callable which is called repeatedly and should return
2142 condfn is a callable which is called repeatedly and should return
2133 True once the child process is known to have started successfully.
2143 True once the child process is known to have started successfully.
2134 At this point, the child process PID is returned. If the child
2144 At this point, the child process PID is returned. If the child
2135 process fails to start or finishes before condfn() evaluates to
2145 process fails to start or finishes before condfn() evaluates to
2136 True, return -1.
2146 True, return -1.
2137 """
2147 """
2138 # Windows case is easier because the child process is either
2148 # Windows case is easier because the child process is either
2139 # successfully starting and validating the condition or exiting
2149 # successfully starting and validating the condition or exiting
2140 # on failure. We just poll on its PID. On Unix, if the child
2150 # on failure. We just poll on its PID. On Unix, if the child
2141 # process fails to start, it will be left in a zombie state until
2151 # process fails to start, it will be left in a zombie state until
2142 # the parent wait on it, which we cannot do since we expect a long
2152 # the parent wait on it, which we cannot do since we expect a long
2143 # running process on success. Instead we listen for SIGCHLD telling
2153 # running process on success. Instead we listen for SIGCHLD telling
2144 # us our child process terminated.
2154 # us our child process terminated.
2145 terminated = set()
2155 terminated = set()
2146 def handler(signum, frame):
2156 def handler(signum, frame):
2147 terminated.add(os.wait())
2157 terminated.add(os.wait())
2148 prevhandler = None
2158 prevhandler = None
2149 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2159 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2150 if SIGCHLD is not None:
2160 if SIGCHLD is not None:
2151 prevhandler = signal.signal(SIGCHLD, handler)
2161 prevhandler = signal.signal(SIGCHLD, handler)
2152 try:
2162 try:
2153 pid = spawndetached(args)
2163 pid = spawndetached(args)
2154 while not condfn():
2164 while not condfn():
2155 if ((pid in terminated or not testpid(pid))
2165 if ((pid in terminated or not testpid(pid))
2156 and not condfn()):
2166 and not condfn()):
2157 return -1
2167 return -1
2158 time.sleep(0.1)
2168 time.sleep(0.1)
2159 return pid
2169 return pid
2160 finally:
2170 finally:
2161 if prevhandler is not None:
2171 if prevhandler is not None:
2162 signal.signal(signal.SIGCHLD, prevhandler)
2172 signal.signal(signal.SIGCHLD, prevhandler)
2163
2173
2164 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2174 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2165 """Return the result of interpolating items in the mapping into string s.
2175 """Return the result of interpolating items in the mapping into string s.
2166
2176
2167 prefix is a single character string, or a two character string with
2177 prefix is a single character string, or a two character string with
2168 a backslash as the first character if the prefix needs to be escaped in
2178 a backslash as the first character if the prefix needs to be escaped in
2169 a regular expression.
2179 a regular expression.
2170
2180
2171 fn is an optional function that will be applied to the replacement text
2181 fn is an optional function that will be applied to the replacement text
2172 just before replacement.
2182 just before replacement.
2173
2183
2174 escape_prefix is an optional flag that allows using doubled prefix for
2184 escape_prefix is an optional flag that allows using doubled prefix for
2175 its escaping.
2185 its escaping.
2176 """
2186 """
2177 fn = fn or (lambda s: s)
2187 fn = fn or (lambda s: s)
2178 patterns = '|'.join(mapping.keys())
2188 patterns = '|'.join(mapping.keys())
2179 if escape_prefix:
2189 if escape_prefix:
2180 patterns += '|' + prefix
2190 patterns += '|' + prefix
2181 if len(prefix) > 1:
2191 if len(prefix) > 1:
2182 prefix_char = prefix[1:]
2192 prefix_char = prefix[1:]
2183 else:
2193 else:
2184 prefix_char = prefix
2194 prefix_char = prefix
2185 mapping[prefix_char] = prefix_char
2195 mapping[prefix_char] = prefix_char
2186 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2196 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2187 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2197 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2188
2198
2189 def getport(port):
2199 def getport(port):
2190 """Return the port for a given network service.
2200 """Return the port for a given network service.
2191
2201
2192 If port is an integer, it's returned as is. If it's a string, it's
2202 If port is an integer, it's returned as is. If it's a string, it's
2193 looked up using socket.getservbyname(). If there's no matching
2203 looked up using socket.getservbyname(). If there's no matching
2194 service, error.Abort is raised.
2204 service, error.Abort is raised.
2195 """
2205 """
2196 try:
2206 try:
2197 return int(port)
2207 return int(port)
2198 except ValueError:
2208 except ValueError:
2199 pass
2209 pass
2200
2210
2201 try:
2211 try:
2202 return socket.getservbyname(port)
2212 return socket.getservbyname(port)
2203 except socket.error:
2213 except socket.error:
2204 raise Abort(_("no port number associated with service '%s'") % port)
2214 raise Abort(_("no port number associated with service '%s'") % port)
2205
2215
2206 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2216 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2207 '0': False, 'no': False, 'false': False, 'off': False,
2217 '0': False, 'no': False, 'false': False, 'off': False,
2208 'never': False}
2218 'never': False}
2209
2219
2210 def parsebool(s):
2220 def parsebool(s):
2211 """Parse s into a boolean.
2221 """Parse s into a boolean.
2212
2222
2213 If s is not a valid boolean, returns None.
2223 If s is not a valid boolean, returns None.
2214 """
2224 """
2215 return _booleans.get(s.lower(), None)
2225 return _booleans.get(s.lower(), None)
2216
2226
2217 _hexdig = '0123456789ABCDEFabcdef'
2227 _hexdig = '0123456789ABCDEFabcdef'
2218 _hextochr = dict((a + b, chr(int(a + b, 16)))
2228 _hextochr = dict((a + b, chr(int(a + b, 16)))
2219 for a in _hexdig for b in _hexdig)
2229 for a in _hexdig for b in _hexdig)
2220
2230
2221 def _urlunquote(s):
2231 def _urlunquote(s):
2222 """Decode HTTP/HTML % encoding.
2232 """Decode HTTP/HTML % encoding.
2223
2233
2224 >>> _urlunquote('abc%20def')
2234 >>> _urlunquote('abc%20def')
2225 'abc def'
2235 'abc def'
2226 """
2236 """
2227 res = s.split('%')
2237 res = s.split('%')
2228 # fastpath
2238 # fastpath
2229 if len(res) == 1:
2239 if len(res) == 1:
2230 return s
2240 return s
2231 s = res[0]
2241 s = res[0]
2232 for item in res[1:]:
2242 for item in res[1:]:
2233 try:
2243 try:
2234 s += _hextochr[item[:2]] + item[2:]
2244 s += _hextochr[item[:2]] + item[2:]
2235 except KeyError:
2245 except KeyError:
2236 s += '%' + item
2246 s += '%' + item
2237 except UnicodeDecodeError:
2247 except UnicodeDecodeError:
2238 s += unichr(int(item[:2], 16)) + item[2:]
2248 s += unichr(int(item[:2], 16)) + item[2:]
2239 return s
2249 return s
2240
2250
2241 class url(object):
2251 class url(object):
2242 r"""Reliable URL parser.
2252 r"""Reliable URL parser.
2243
2253
2244 This parses URLs and provides attributes for the following
2254 This parses URLs and provides attributes for the following
2245 components:
2255 components:
2246
2256
2247 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2257 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2248
2258
2249 Missing components are set to None. The only exception is
2259 Missing components are set to None. The only exception is
2250 fragment, which is set to '' if present but empty.
2260 fragment, which is set to '' if present but empty.
2251
2261
2252 If parsefragment is False, fragment is included in query. If
2262 If parsefragment is False, fragment is included in query. If
2253 parsequery is False, query is included in path. If both are
2263 parsequery is False, query is included in path. If both are
2254 False, both fragment and query are included in path.
2264 False, both fragment and query are included in path.
2255
2265
2256 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2266 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2257
2267
2258 Note that for backward compatibility reasons, bundle URLs do not
2268 Note that for backward compatibility reasons, bundle URLs do not
2259 take host names. That means 'bundle://../' has a path of '../'.
2269 take host names. That means 'bundle://../' has a path of '../'.
2260
2270
2261 Examples:
2271 Examples:
2262
2272
2263 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2273 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2264 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2274 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2265 >>> url('ssh://[::1]:2200//home/joe/repo')
2275 >>> url('ssh://[::1]:2200//home/joe/repo')
2266 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2276 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2267 >>> url('file:///home/joe/repo')
2277 >>> url('file:///home/joe/repo')
2268 <url scheme: 'file', path: '/home/joe/repo'>
2278 <url scheme: 'file', path: '/home/joe/repo'>
2269 >>> url('file:///c:/temp/foo/')
2279 >>> url('file:///c:/temp/foo/')
2270 <url scheme: 'file', path: 'c:/temp/foo/'>
2280 <url scheme: 'file', path: 'c:/temp/foo/'>
2271 >>> url('bundle:foo')
2281 >>> url('bundle:foo')
2272 <url scheme: 'bundle', path: 'foo'>
2282 <url scheme: 'bundle', path: 'foo'>
2273 >>> url('bundle://../foo')
2283 >>> url('bundle://../foo')
2274 <url scheme: 'bundle', path: '../foo'>
2284 <url scheme: 'bundle', path: '../foo'>
2275 >>> url(r'c:\foo\bar')
2285 >>> url(r'c:\foo\bar')
2276 <url path: 'c:\\foo\\bar'>
2286 <url path: 'c:\\foo\\bar'>
2277 >>> url(r'\\blah\blah\blah')
2287 >>> url(r'\\blah\blah\blah')
2278 <url path: '\\\\blah\\blah\\blah'>
2288 <url path: '\\\\blah\\blah\\blah'>
2279 >>> url(r'\\blah\blah\blah#baz')
2289 >>> url(r'\\blah\blah\blah#baz')
2280 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2290 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2281 >>> url(r'file:///C:\users\me')
2291 >>> url(r'file:///C:\users\me')
2282 <url scheme: 'file', path: 'C:\\users\\me'>
2292 <url scheme: 'file', path: 'C:\\users\\me'>
2283
2293
2284 Authentication credentials:
2294 Authentication credentials:
2285
2295
2286 >>> url('ssh://joe:xyz@x/repo')
2296 >>> url('ssh://joe:xyz@x/repo')
2287 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2297 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2288 >>> url('ssh://joe@x/repo')
2298 >>> url('ssh://joe@x/repo')
2289 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2299 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2290
2300
2291 Query strings and fragments:
2301 Query strings and fragments:
2292
2302
2293 >>> url('http://host/a?b#c')
2303 >>> url('http://host/a?b#c')
2294 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2304 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2295 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2305 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2296 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2306 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2297 """
2307 """
2298
2308
2299 _safechars = "!~*'()+"
2309 _safechars = "!~*'()+"
2300 _safepchars = "/!~*'()+:\\"
2310 _safepchars = "/!~*'()+:\\"
2301 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
2311 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
2302
2312
2303 def __init__(self, path, parsequery=True, parsefragment=True):
2313 def __init__(self, path, parsequery=True, parsefragment=True):
2304 # We slowly chomp away at path until we have only the path left
2314 # We slowly chomp away at path until we have only the path left
2305 self.scheme = self.user = self.passwd = self.host = None
2315 self.scheme = self.user = self.passwd = self.host = None
2306 self.port = self.path = self.query = self.fragment = None
2316 self.port = self.path = self.query = self.fragment = None
2307 self._localpath = True
2317 self._localpath = True
2308 self._hostport = ''
2318 self._hostport = ''
2309 self._origpath = path
2319 self._origpath = path
2310
2320
2311 if parsefragment and '#' in path:
2321 if parsefragment and '#' in path:
2312 path, self.fragment = path.split('#', 1)
2322 path, self.fragment = path.split('#', 1)
2313 if not path:
2323 if not path:
2314 path = None
2324 path = None
2315
2325
2316 # special case for Windows drive letters and UNC paths
2326 # special case for Windows drive letters and UNC paths
2317 if hasdriveletter(path) or path.startswith(r'\\'):
2327 if hasdriveletter(path) or path.startswith(r'\\'):
2318 self.path = path
2328 self.path = path
2319 return
2329 return
2320
2330
2321 # For compatibility reasons, we can't handle bundle paths as
2331 # For compatibility reasons, we can't handle bundle paths as
2322 # normal URLS
2332 # normal URLS
2323 if path.startswith('bundle:'):
2333 if path.startswith('bundle:'):
2324 self.scheme = 'bundle'
2334 self.scheme = 'bundle'
2325 path = path[7:]
2335 path = path[7:]
2326 if path.startswith('//'):
2336 if path.startswith('//'):
2327 path = path[2:]
2337 path = path[2:]
2328 self.path = path
2338 self.path = path
2329 return
2339 return
2330
2340
2331 if self._matchscheme(path):
2341 if self._matchscheme(path):
2332 parts = path.split(':', 1)
2342 parts = path.split(':', 1)
2333 if parts[0]:
2343 if parts[0]:
2334 self.scheme, path = parts
2344 self.scheme, path = parts
2335 self._localpath = False
2345 self._localpath = False
2336
2346
2337 if not path:
2347 if not path:
2338 path = None
2348 path = None
2339 if self._localpath:
2349 if self._localpath:
2340 self.path = ''
2350 self.path = ''
2341 return
2351 return
2342 else:
2352 else:
2343 if self._localpath:
2353 if self._localpath:
2344 self.path = path
2354 self.path = path
2345 return
2355 return
2346
2356
2347 if parsequery and '?' in path:
2357 if parsequery and '?' in path:
2348 path, self.query = path.split('?', 1)
2358 path, self.query = path.split('?', 1)
2349 if not path:
2359 if not path:
2350 path = None
2360 path = None
2351 if not self.query:
2361 if not self.query:
2352 self.query = None
2362 self.query = None
2353
2363
2354 # // is required to specify a host/authority
2364 # // is required to specify a host/authority
2355 if path and path.startswith('//'):
2365 if path and path.startswith('//'):
2356 parts = path[2:].split('/', 1)
2366 parts = path[2:].split('/', 1)
2357 if len(parts) > 1:
2367 if len(parts) > 1:
2358 self.host, path = parts
2368 self.host, path = parts
2359 else:
2369 else:
2360 self.host = parts[0]
2370 self.host = parts[0]
2361 path = None
2371 path = None
2362 if not self.host:
2372 if not self.host:
2363 self.host = None
2373 self.host = None
2364 # path of file:///d is /d
2374 # path of file:///d is /d
2365 # path of file:///d:/ is d:/, not /d:/
2375 # path of file:///d:/ is d:/, not /d:/
2366 if path and not hasdriveletter(path):
2376 if path and not hasdriveletter(path):
2367 path = '/' + path
2377 path = '/' + path
2368
2378
2369 if self.host and '@' in self.host:
2379 if self.host and '@' in self.host:
2370 self.user, self.host = self.host.rsplit('@', 1)
2380 self.user, self.host = self.host.rsplit('@', 1)
2371 if ':' in self.user:
2381 if ':' in self.user:
2372 self.user, self.passwd = self.user.split(':', 1)
2382 self.user, self.passwd = self.user.split(':', 1)
2373 if not self.host:
2383 if not self.host:
2374 self.host = None
2384 self.host = None
2375
2385
2376 # Don't split on colons in IPv6 addresses without ports
2386 # Don't split on colons in IPv6 addresses without ports
2377 if (self.host and ':' in self.host and
2387 if (self.host and ':' in self.host and
2378 not (self.host.startswith('[') and self.host.endswith(']'))):
2388 not (self.host.startswith('[') and self.host.endswith(']'))):
2379 self._hostport = self.host
2389 self._hostport = self.host
2380 self.host, self.port = self.host.rsplit(':', 1)
2390 self.host, self.port = self.host.rsplit(':', 1)
2381 if not self.host:
2391 if not self.host:
2382 self.host = None
2392 self.host = None
2383
2393
2384 if (self.host and self.scheme == 'file' and
2394 if (self.host and self.scheme == 'file' and
2385 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2395 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2386 raise Abort(_('file:// URLs can only refer to localhost'))
2396 raise Abort(_('file:// URLs can only refer to localhost'))
2387
2397
2388 self.path = path
2398 self.path = path
2389
2399
2390 # leave the query string escaped
2400 # leave the query string escaped
2391 for a in ('user', 'passwd', 'host', 'port',
2401 for a in ('user', 'passwd', 'host', 'port',
2392 'path', 'fragment'):
2402 'path', 'fragment'):
2393 v = getattr(self, a)
2403 v = getattr(self, a)
2394 if v is not None:
2404 if v is not None:
2395 setattr(self, a, _urlunquote(v))
2405 setattr(self, a, _urlunquote(v))
2396
2406
2397 def __repr__(self):
2407 def __repr__(self):
2398 attrs = []
2408 attrs = []
2399 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2409 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2400 'query', 'fragment'):
2410 'query', 'fragment'):
2401 v = getattr(self, a)
2411 v = getattr(self, a)
2402 if v is not None:
2412 if v is not None:
2403 attrs.append('%s: %r' % (a, v))
2413 attrs.append('%s: %r' % (a, v))
2404 return '<url %s>' % ', '.join(attrs)
2414 return '<url %s>' % ', '.join(attrs)
2405
2415
2406 def __str__(self):
2416 def __str__(self):
2407 r"""Join the URL's components back into a URL string.
2417 r"""Join the URL's components back into a URL string.
2408
2418
2409 Examples:
2419 Examples:
2410
2420
2411 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2421 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2412 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2422 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2413 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2423 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2414 'http://user:pw@host:80/?foo=bar&baz=42'
2424 'http://user:pw@host:80/?foo=bar&baz=42'
2415 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2425 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2416 'http://user:pw@host:80/?foo=bar%3dbaz'
2426 'http://user:pw@host:80/?foo=bar%3dbaz'
2417 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2427 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2418 'ssh://user:pw@[::1]:2200//home/joe#'
2428 'ssh://user:pw@[::1]:2200//home/joe#'
2419 >>> str(url('http://localhost:80//'))
2429 >>> str(url('http://localhost:80//'))
2420 'http://localhost:80//'
2430 'http://localhost:80//'
2421 >>> str(url('http://localhost:80/'))
2431 >>> str(url('http://localhost:80/'))
2422 'http://localhost:80/'
2432 'http://localhost:80/'
2423 >>> str(url('http://localhost:80'))
2433 >>> str(url('http://localhost:80'))
2424 'http://localhost:80/'
2434 'http://localhost:80/'
2425 >>> str(url('bundle:foo'))
2435 >>> str(url('bundle:foo'))
2426 'bundle:foo'
2436 'bundle:foo'
2427 >>> str(url('bundle://../foo'))
2437 >>> str(url('bundle://../foo'))
2428 'bundle:../foo'
2438 'bundle:../foo'
2429 >>> str(url('path'))
2439 >>> str(url('path'))
2430 'path'
2440 'path'
2431 >>> str(url('file:///tmp/foo/bar'))
2441 >>> str(url('file:///tmp/foo/bar'))
2432 'file:///tmp/foo/bar'
2442 'file:///tmp/foo/bar'
2433 >>> str(url('file:///c:/tmp/foo/bar'))
2443 >>> str(url('file:///c:/tmp/foo/bar'))
2434 'file:///c:/tmp/foo/bar'
2444 'file:///c:/tmp/foo/bar'
2435 >>> print url(r'bundle:foo\bar')
2445 >>> print url(r'bundle:foo\bar')
2436 bundle:foo\bar
2446 bundle:foo\bar
2437 >>> print url(r'file:///D:\data\hg')
2447 >>> print url(r'file:///D:\data\hg')
2438 file:///D:\data\hg
2448 file:///D:\data\hg
2439 """
2449 """
2440 if self._localpath:
2450 if self._localpath:
2441 s = self.path
2451 s = self.path
2442 if self.scheme == 'bundle':
2452 if self.scheme == 'bundle':
2443 s = 'bundle:' + s
2453 s = 'bundle:' + s
2444 if self.fragment:
2454 if self.fragment:
2445 s += '#' + self.fragment
2455 s += '#' + self.fragment
2446 return s
2456 return s
2447
2457
2448 s = self.scheme + ':'
2458 s = self.scheme + ':'
2449 if self.user or self.passwd or self.host:
2459 if self.user or self.passwd or self.host:
2450 s += '//'
2460 s += '//'
2451 elif self.scheme and (not self.path or self.path.startswith('/')
2461 elif self.scheme and (not self.path or self.path.startswith('/')
2452 or hasdriveletter(self.path)):
2462 or hasdriveletter(self.path)):
2453 s += '//'
2463 s += '//'
2454 if hasdriveletter(self.path):
2464 if hasdriveletter(self.path):
2455 s += '/'
2465 s += '/'
2456 if self.user:
2466 if self.user:
2457 s += urlreq.quote(self.user, safe=self._safechars)
2467 s += urlreq.quote(self.user, safe=self._safechars)
2458 if self.passwd:
2468 if self.passwd:
2459 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2469 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2460 if self.user or self.passwd:
2470 if self.user or self.passwd:
2461 s += '@'
2471 s += '@'
2462 if self.host:
2472 if self.host:
2463 if not (self.host.startswith('[') and self.host.endswith(']')):
2473 if not (self.host.startswith('[') and self.host.endswith(']')):
2464 s += urlreq.quote(self.host)
2474 s += urlreq.quote(self.host)
2465 else:
2475 else:
2466 s += self.host
2476 s += self.host
2467 if self.port:
2477 if self.port:
2468 s += ':' + urlreq.quote(self.port)
2478 s += ':' + urlreq.quote(self.port)
2469 if self.host:
2479 if self.host:
2470 s += '/'
2480 s += '/'
2471 if self.path:
2481 if self.path:
2472 # TODO: similar to the query string, we should not unescape the
2482 # TODO: similar to the query string, we should not unescape the
2473 # path when we store it, the path might contain '%2f' = '/',
2483 # path when we store it, the path might contain '%2f' = '/',
2474 # which we should *not* escape.
2484 # which we should *not* escape.
2475 s += urlreq.quote(self.path, safe=self._safepchars)
2485 s += urlreq.quote(self.path, safe=self._safepchars)
2476 if self.query:
2486 if self.query:
2477 # we store the query in escaped form.
2487 # we store the query in escaped form.
2478 s += '?' + self.query
2488 s += '?' + self.query
2479 if self.fragment is not None:
2489 if self.fragment is not None:
2480 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2490 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2481 return s
2491 return s
2482
2492
2483 def authinfo(self):
2493 def authinfo(self):
2484 user, passwd = self.user, self.passwd
2494 user, passwd = self.user, self.passwd
2485 try:
2495 try:
2486 self.user, self.passwd = None, None
2496 self.user, self.passwd = None, None
2487 s = str(self)
2497 s = str(self)
2488 finally:
2498 finally:
2489 self.user, self.passwd = user, passwd
2499 self.user, self.passwd = user, passwd
2490 if not self.user:
2500 if not self.user:
2491 return (s, None)
2501 return (s, None)
2492 # authinfo[1] is passed to urllib2 password manager, and its
2502 # authinfo[1] is passed to urllib2 password manager, and its
2493 # URIs must not contain credentials. The host is passed in the
2503 # URIs must not contain credentials. The host is passed in the
2494 # URIs list because Python < 2.4.3 uses only that to search for
2504 # URIs list because Python < 2.4.3 uses only that to search for
2495 # a password.
2505 # a password.
2496 return (s, (None, (s, self.host),
2506 return (s, (None, (s, self.host),
2497 self.user, self.passwd or ''))
2507 self.user, self.passwd or ''))
2498
2508
2499 def isabs(self):
2509 def isabs(self):
2500 if self.scheme and self.scheme != 'file':
2510 if self.scheme and self.scheme != 'file':
2501 return True # remote URL
2511 return True # remote URL
2502 if hasdriveletter(self.path):
2512 if hasdriveletter(self.path):
2503 return True # absolute for our purposes - can't be joined()
2513 return True # absolute for our purposes - can't be joined()
2504 if self.path.startswith(r'\\'):
2514 if self.path.startswith(r'\\'):
2505 return True # Windows UNC path
2515 return True # Windows UNC path
2506 if self.path.startswith('/'):
2516 if self.path.startswith('/'):
2507 return True # POSIX-style
2517 return True # POSIX-style
2508 return False
2518 return False
2509
2519
2510 def localpath(self):
2520 def localpath(self):
2511 if self.scheme == 'file' or self.scheme == 'bundle':
2521 if self.scheme == 'file' or self.scheme == 'bundle':
2512 path = self.path or '/'
2522 path = self.path or '/'
2513 # For Windows, we need to promote hosts containing drive
2523 # For Windows, we need to promote hosts containing drive
2514 # letters to paths with drive letters.
2524 # letters to paths with drive letters.
2515 if hasdriveletter(self._hostport):
2525 if hasdriveletter(self._hostport):
2516 path = self._hostport + '/' + self.path
2526 path = self._hostport + '/' + self.path
2517 elif (self.host is not None and self.path
2527 elif (self.host is not None and self.path
2518 and not hasdriveletter(path)):
2528 and not hasdriveletter(path)):
2519 path = '/' + path
2529 path = '/' + path
2520 return path
2530 return path
2521 return self._origpath
2531 return self._origpath
2522
2532
2523 def islocal(self):
2533 def islocal(self):
2524 '''whether localpath will return something that posixfile can open'''
2534 '''whether localpath will return something that posixfile can open'''
2525 return (not self.scheme or self.scheme == 'file'
2535 return (not self.scheme or self.scheme == 'file'
2526 or self.scheme == 'bundle')
2536 or self.scheme == 'bundle')
2527
2537
2528 def hasscheme(path):
2538 def hasscheme(path):
2529 return bool(url(path).scheme)
2539 return bool(url(path).scheme)
2530
2540
2531 def hasdriveletter(path):
2541 def hasdriveletter(path):
2532 return path and path[1:2] == ':' and path[0:1].isalpha()
2542 return path and path[1:2] == ':' and path[0:1].isalpha()
2533
2543
2534 def urllocalpath(path):
2544 def urllocalpath(path):
2535 return url(path, parsequery=False, parsefragment=False).localpath()
2545 return url(path, parsequery=False, parsefragment=False).localpath()
2536
2546
2537 def hidepassword(u):
2547 def hidepassword(u):
2538 '''hide user credential in a url string'''
2548 '''hide user credential in a url string'''
2539 u = url(u)
2549 u = url(u)
2540 if u.passwd:
2550 if u.passwd:
2541 u.passwd = '***'
2551 u.passwd = '***'
2542 return str(u)
2552 return str(u)
2543
2553
2544 def removeauth(u):
2554 def removeauth(u):
2545 '''remove all authentication information from a url string'''
2555 '''remove all authentication information from a url string'''
2546 u = url(u)
2556 u = url(u)
2547 u.user = u.passwd = None
2557 u.user = u.passwd = None
2548 return str(u)
2558 return str(u)
2549
2559
2550 def isatty(fp):
2560 def isatty(fp):
2551 try:
2561 try:
2552 return fp.isatty()
2562 return fp.isatty()
2553 except AttributeError:
2563 except AttributeError:
2554 return False
2564 return False
2555
2565
2556 timecount = unitcountfn(
2566 timecount = unitcountfn(
2557 (1, 1e3, _('%.0f s')),
2567 (1, 1e3, _('%.0f s')),
2558 (100, 1, _('%.1f s')),
2568 (100, 1, _('%.1f s')),
2559 (10, 1, _('%.2f s')),
2569 (10, 1, _('%.2f s')),
2560 (1, 1, _('%.3f s')),
2570 (1, 1, _('%.3f s')),
2561 (100, 0.001, _('%.1f ms')),
2571 (100, 0.001, _('%.1f ms')),
2562 (10, 0.001, _('%.2f ms')),
2572 (10, 0.001, _('%.2f ms')),
2563 (1, 0.001, _('%.3f ms')),
2573 (1, 0.001, _('%.3f ms')),
2564 (100, 0.000001, _('%.1f us')),
2574 (100, 0.000001, _('%.1f us')),
2565 (10, 0.000001, _('%.2f us')),
2575 (10, 0.000001, _('%.2f us')),
2566 (1, 0.000001, _('%.3f us')),
2576 (1, 0.000001, _('%.3f us')),
2567 (100, 0.000000001, _('%.1f ns')),
2577 (100, 0.000000001, _('%.1f ns')),
2568 (10, 0.000000001, _('%.2f ns')),
2578 (10, 0.000000001, _('%.2f ns')),
2569 (1, 0.000000001, _('%.3f ns')),
2579 (1, 0.000000001, _('%.3f ns')),
2570 )
2580 )
2571
2581
2572 _timenesting = [0]
2582 _timenesting = [0]
2573
2583
2574 def timed(func):
2584 def timed(func):
2575 '''Report the execution time of a function call to stderr.
2585 '''Report the execution time of a function call to stderr.
2576
2586
2577 During development, use as a decorator when you need to measure
2587 During development, use as a decorator when you need to measure
2578 the cost of a function, e.g. as follows:
2588 the cost of a function, e.g. as follows:
2579
2589
2580 @util.timed
2590 @util.timed
2581 def foo(a, b, c):
2591 def foo(a, b, c):
2582 pass
2592 pass
2583 '''
2593 '''
2584
2594
2585 def wrapper(*args, **kwargs):
2595 def wrapper(*args, **kwargs):
2586 start = time.time()
2596 start = time.time()
2587 indent = 2
2597 indent = 2
2588 _timenesting[0] += indent
2598 _timenesting[0] += indent
2589 try:
2599 try:
2590 return func(*args, **kwargs)
2600 return func(*args, **kwargs)
2591 finally:
2601 finally:
2592 elapsed = time.time() - start
2602 elapsed = time.time() - start
2593 _timenesting[0] -= indent
2603 _timenesting[0] -= indent
2594 sys.stderr.write('%s%s: %s\n' %
2604 sys.stderr.write('%s%s: %s\n' %
2595 (' ' * _timenesting[0], func.__name__,
2605 (' ' * _timenesting[0], func.__name__,
2596 timecount(elapsed)))
2606 timecount(elapsed)))
2597 return wrapper
2607 return wrapper
2598
2608
2599 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2609 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2600 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2610 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2601
2611
2602 def sizetoint(s):
2612 def sizetoint(s):
2603 '''Convert a space specifier to a byte count.
2613 '''Convert a space specifier to a byte count.
2604
2614
2605 >>> sizetoint('30')
2615 >>> sizetoint('30')
2606 30
2616 30
2607 >>> sizetoint('2.2kb')
2617 >>> sizetoint('2.2kb')
2608 2252
2618 2252
2609 >>> sizetoint('6M')
2619 >>> sizetoint('6M')
2610 6291456
2620 6291456
2611 '''
2621 '''
2612 t = s.strip().lower()
2622 t = s.strip().lower()
2613 try:
2623 try:
2614 for k, u in _sizeunits:
2624 for k, u in _sizeunits:
2615 if t.endswith(k):
2625 if t.endswith(k):
2616 return int(float(t[:-len(k)]) * u)
2626 return int(float(t[:-len(k)]) * u)
2617 return int(t)
2627 return int(t)
2618 except ValueError:
2628 except ValueError:
2619 raise error.ParseError(_("couldn't parse size: %s") % s)
2629 raise error.ParseError(_("couldn't parse size: %s") % s)
2620
2630
2621 class hooks(object):
2631 class hooks(object):
2622 '''A collection of hook functions that can be used to extend a
2632 '''A collection of hook functions that can be used to extend a
2623 function's behavior. Hooks are called in lexicographic order,
2633 function's behavior. Hooks are called in lexicographic order,
2624 based on the names of their sources.'''
2634 based on the names of their sources.'''
2625
2635
2626 def __init__(self):
2636 def __init__(self):
2627 self._hooks = []
2637 self._hooks = []
2628
2638
2629 def add(self, source, hook):
2639 def add(self, source, hook):
2630 self._hooks.append((source, hook))
2640 self._hooks.append((source, hook))
2631
2641
2632 def __call__(self, *args):
2642 def __call__(self, *args):
2633 self._hooks.sort(key=lambda x: x[0])
2643 self._hooks.sort(key=lambda x: x[0])
2634 results = []
2644 results = []
2635 for source, hook in self._hooks:
2645 for source, hook in self._hooks:
2636 results.append(hook(*args))
2646 results.append(hook(*args))
2637 return results
2647 return results
2638
2648
2639 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s'):
2649 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s'):
2640 '''Yields lines for a nicely formatted stacktrace.
2650 '''Yields lines for a nicely formatted stacktrace.
2641 Skips the 'skip' last entries.
2651 Skips the 'skip' last entries.
2642 Each file+linenumber is formatted according to fileline.
2652 Each file+linenumber is formatted according to fileline.
2643 Each line is formatted according to line.
2653 Each line is formatted according to line.
2644 If line is None, it yields:
2654 If line is None, it yields:
2645 length of longest filepath+line number,
2655 length of longest filepath+line number,
2646 filepath+linenumber,
2656 filepath+linenumber,
2647 function
2657 function
2648
2658
2649 Not be used in production code but very convenient while developing.
2659 Not be used in production code but very convenient while developing.
2650 '''
2660 '''
2651 entries = [(fileline % (fn, ln), func)
2661 entries = [(fileline % (fn, ln), func)
2652 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2662 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2653 if entries:
2663 if entries:
2654 fnmax = max(len(entry[0]) for entry in entries)
2664 fnmax = max(len(entry[0]) for entry in entries)
2655 for fnln, func in entries:
2665 for fnln, func in entries:
2656 if line is None:
2666 if line is None:
2657 yield (fnmax, fnln, func)
2667 yield (fnmax, fnln, func)
2658 else:
2668 else:
2659 yield line % (fnmax, fnln, func)
2669 yield line % (fnmax, fnln, func)
2660
2670
2661 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2671 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2662 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2672 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2663 Skips the 'skip' last entries. By default it will flush stdout first.
2673 Skips the 'skip' last entries. By default it will flush stdout first.
2664 It can be used everywhere and intentionally does not require an ui object.
2674 It can be used everywhere and intentionally does not require an ui object.
2665 Not be used in production code but very convenient while developing.
2675 Not be used in production code but very convenient while developing.
2666 '''
2676 '''
2667 if otherf:
2677 if otherf:
2668 otherf.flush()
2678 otherf.flush()
2669 f.write('%s at:\n' % msg)
2679 f.write('%s at:\n' % msg)
2670 for line in getstackframes(skip + 1):
2680 for line in getstackframes(skip + 1):
2671 f.write(line)
2681 f.write(line)
2672 f.flush()
2682 f.flush()
2673
2683
2674 class dirs(object):
2684 class dirs(object):
2675 '''a multiset of directory names from a dirstate or manifest'''
2685 '''a multiset of directory names from a dirstate or manifest'''
2676
2686
2677 def __init__(self, map, skip=None):
2687 def __init__(self, map, skip=None):
2678 self._dirs = {}
2688 self._dirs = {}
2679 addpath = self.addpath
2689 addpath = self.addpath
2680 if safehasattr(map, 'iteritems') and skip is not None:
2690 if safehasattr(map, 'iteritems') and skip is not None:
2681 for f, s in map.iteritems():
2691 for f, s in map.iteritems():
2682 if s[0] != skip:
2692 if s[0] != skip:
2683 addpath(f)
2693 addpath(f)
2684 else:
2694 else:
2685 for f in map:
2695 for f in map:
2686 addpath(f)
2696 addpath(f)
2687
2697
2688 def addpath(self, path):
2698 def addpath(self, path):
2689 dirs = self._dirs
2699 dirs = self._dirs
2690 for base in finddirs(path):
2700 for base in finddirs(path):
2691 if base in dirs:
2701 if base in dirs:
2692 dirs[base] += 1
2702 dirs[base] += 1
2693 return
2703 return
2694 dirs[base] = 1
2704 dirs[base] = 1
2695
2705
2696 def delpath(self, path):
2706 def delpath(self, path):
2697 dirs = self._dirs
2707 dirs = self._dirs
2698 for base in finddirs(path):
2708 for base in finddirs(path):
2699 if dirs[base] > 1:
2709 if dirs[base] > 1:
2700 dirs[base] -= 1
2710 dirs[base] -= 1
2701 return
2711 return
2702 del dirs[base]
2712 del dirs[base]
2703
2713
2704 def __iter__(self):
2714 def __iter__(self):
2705 return self._dirs.iterkeys()
2715 return self._dirs.iterkeys()
2706
2716
2707 def __contains__(self, d):
2717 def __contains__(self, d):
2708 return d in self._dirs
2718 return d in self._dirs
2709
2719
2710 if safehasattr(parsers, 'dirs'):
2720 if safehasattr(parsers, 'dirs'):
2711 dirs = parsers.dirs
2721 dirs = parsers.dirs
2712
2722
2713 def finddirs(path):
2723 def finddirs(path):
2714 pos = path.rfind('/')
2724 pos = path.rfind('/')
2715 while pos != -1:
2725 while pos != -1:
2716 yield path[:pos]
2726 yield path[:pos]
2717 pos = path.rfind('/', 0, pos)
2727 pos = path.rfind('/', 0, pos)
2718
2728
2719 # compression utility
2729 # compression utility
2720
2730
2721 class nocompress(object):
2731 class nocompress(object):
2722 def compress(self, x):
2732 def compress(self, x):
2723 return x
2733 return x
2724 def flush(self):
2734 def flush(self):
2725 return ""
2735 return ""
2726
2736
2727 compressors = {
2737 compressors = {
2728 None: nocompress,
2738 None: nocompress,
2729 # lambda to prevent early import
2739 # lambda to prevent early import
2730 'BZ': lambda: bz2.BZ2Compressor(),
2740 'BZ': lambda: bz2.BZ2Compressor(),
2731 'GZ': lambda: zlib.compressobj(),
2741 'GZ': lambda: zlib.compressobj(),
2732 }
2742 }
2733 # also support the old form by courtesies
2743 # also support the old form by courtesies
2734 compressors['UN'] = compressors[None]
2744 compressors['UN'] = compressors[None]
2735
2745
2736 def _makedecompressor(decompcls):
2746 def _makedecompressor(decompcls):
2737 def generator(f):
2747 def generator(f):
2738 d = decompcls()
2748 d = decompcls()
2739 for chunk in filechunkiter(f):
2749 for chunk in filechunkiter(f):
2740 yield d.decompress(chunk)
2750 yield d.decompress(chunk)
2741 def func(fh):
2751 def func(fh):
2742 return chunkbuffer(generator(fh))
2752 return chunkbuffer(generator(fh))
2743 return func
2753 return func
2744
2754
2745 class ctxmanager(object):
2755 class ctxmanager(object):
2746 '''A context manager for use in 'with' blocks to allow multiple
2756 '''A context manager for use in 'with' blocks to allow multiple
2747 contexts to be entered at once. This is both safer and more
2757 contexts to be entered at once. This is both safer and more
2748 flexible than contextlib.nested.
2758 flexible than contextlib.nested.
2749
2759
2750 Once Mercurial supports Python 2.7+, this will become mostly
2760 Once Mercurial supports Python 2.7+, this will become mostly
2751 unnecessary.
2761 unnecessary.
2752 '''
2762 '''
2753
2763
2754 def __init__(self, *args):
2764 def __init__(self, *args):
2755 '''Accepts a list of no-argument functions that return context
2765 '''Accepts a list of no-argument functions that return context
2756 managers. These will be invoked at __call__ time.'''
2766 managers. These will be invoked at __call__ time.'''
2757 self._pending = args
2767 self._pending = args
2758 self._atexit = []
2768 self._atexit = []
2759
2769
2760 def __enter__(self):
2770 def __enter__(self):
2761 return self
2771 return self
2762
2772
2763 def enter(self):
2773 def enter(self):
2764 '''Create and enter context managers in the order in which they were
2774 '''Create and enter context managers in the order in which they were
2765 passed to the constructor.'''
2775 passed to the constructor.'''
2766 values = []
2776 values = []
2767 for func in self._pending:
2777 for func in self._pending:
2768 obj = func()
2778 obj = func()
2769 values.append(obj.__enter__())
2779 values.append(obj.__enter__())
2770 self._atexit.append(obj.__exit__)
2780 self._atexit.append(obj.__exit__)
2771 del self._pending
2781 del self._pending
2772 return values
2782 return values
2773
2783
2774 def atexit(self, func, *args, **kwargs):
2784 def atexit(self, func, *args, **kwargs):
2775 '''Add a function to call when this context manager exits. The
2785 '''Add a function to call when this context manager exits. The
2776 ordering of multiple atexit calls is unspecified, save that
2786 ordering of multiple atexit calls is unspecified, save that
2777 they will happen before any __exit__ functions.'''
2787 they will happen before any __exit__ functions.'''
2778 def wrapper(exc_type, exc_val, exc_tb):
2788 def wrapper(exc_type, exc_val, exc_tb):
2779 func(*args, **kwargs)
2789 func(*args, **kwargs)
2780 self._atexit.append(wrapper)
2790 self._atexit.append(wrapper)
2781 return func
2791 return func
2782
2792
2783 def __exit__(self, exc_type, exc_val, exc_tb):
2793 def __exit__(self, exc_type, exc_val, exc_tb):
2784 '''Context managers are exited in the reverse order from which
2794 '''Context managers are exited in the reverse order from which
2785 they were created.'''
2795 they were created.'''
2786 received = exc_type is not None
2796 received = exc_type is not None
2787 suppressed = False
2797 suppressed = False
2788 pending = None
2798 pending = None
2789 self._atexit.reverse()
2799 self._atexit.reverse()
2790 for exitfunc in self._atexit:
2800 for exitfunc in self._atexit:
2791 try:
2801 try:
2792 if exitfunc(exc_type, exc_val, exc_tb):
2802 if exitfunc(exc_type, exc_val, exc_tb):
2793 suppressed = True
2803 suppressed = True
2794 exc_type = None
2804 exc_type = None
2795 exc_val = None
2805 exc_val = None
2796 exc_tb = None
2806 exc_tb = None
2797 except BaseException:
2807 except BaseException:
2798 pending = sys.exc_info()
2808 pending = sys.exc_info()
2799 exc_type, exc_val, exc_tb = pending = sys.exc_info()
2809 exc_type, exc_val, exc_tb = pending = sys.exc_info()
2800 del self._atexit
2810 del self._atexit
2801 if pending:
2811 if pending:
2802 raise exc_val
2812 raise exc_val
2803 return received and suppressed
2813 return received and suppressed
2804
2814
2805 def _bz2():
2815 def _bz2():
2806 d = bz2.BZ2Decompressor()
2816 d = bz2.BZ2Decompressor()
2807 # Bzip2 stream start with BZ, but we stripped it.
2817 # Bzip2 stream start with BZ, but we stripped it.
2808 # we put it back for good measure.
2818 # we put it back for good measure.
2809 d.decompress('BZ')
2819 d.decompress('BZ')
2810 return d
2820 return d
2811
2821
2812 decompressors = {None: lambda fh: fh,
2822 decompressors = {None: lambda fh: fh,
2813 '_truncatedBZ': _makedecompressor(_bz2),
2823 '_truncatedBZ': _makedecompressor(_bz2),
2814 'BZ': _makedecompressor(lambda: bz2.BZ2Decompressor()),
2824 'BZ': _makedecompressor(lambda: bz2.BZ2Decompressor()),
2815 'GZ': _makedecompressor(lambda: zlib.decompressobj()),
2825 'GZ': _makedecompressor(lambda: zlib.decompressobj()),
2816 }
2826 }
2817 # also support the old form by courtesies
2827 # also support the old form by courtesies
2818 decompressors['UN'] = decompressors[None]
2828 decompressors['UN'] = decompressors[None]
2819
2829
2820 # convenient shortcut
2830 # convenient shortcut
2821 dst = debugstacktrace
2831 dst = debugstacktrace
General Comments 0
You need to be logged in to leave comments. Login now