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