##// END OF EJS Templates
util.re: move check for re2 into a separate method...
Siddharth Agarwal -
r21913:50aad460 default
parent child Browse files
Show More
@@ -1,2043 +1,2046 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 i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import errno, shutil, sys, tempfile, traceback
18 import errno, shutil, sys, tempfile, traceback
19 import re as remod
19 import re as remod
20 import os, time, datetime, calendar, textwrap, signal, collections
20 import os, time, datetime, calendar, textwrap, signal, collections
21 import imp, socket, urllib
21 import imp, socket, urllib
22
22
23 if os.name == 'nt':
23 if os.name == 'nt':
24 import windows as platform
24 import windows as platform
25 else:
25 else:
26 import posix as platform
26 import posix as platform
27
27
28 cachestat = platform.cachestat
28 cachestat = platform.cachestat
29 checkexec = platform.checkexec
29 checkexec = platform.checkexec
30 checklink = platform.checklink
30 checklink = platform.checklink
31 copymode = platform.copymode
31 copymode = platform.copymode
32 executablepath = platform.executablepath
32 executablepath = platform.executablepath
33 expandglobs = platform.expandglobs
33 expandglobs = platform.expandglobs
34 explainexit = platform.explainexit
34 explainexit = platform.explainexit
35 findexe = platform.findexe
35 findexe = platform.findexe
36 gethgcmd = platform.gethgcmd
36 gethgcmd = platform.gethgcmd
37 getuser = platform.getuser
37 getuser = platform.getuser
38 groupmembers = platform.groupmembers
38 groupmembers = platform.groupmembers
39 groupname = platform.groupname
39 groupname = platform.groupname
40 hidewindow = platform.hidewindow
40 hidewindow = platform.hidewindow
41 isexec = platform.isexec
41 isexec = platform.isexec
42 isowner = platform.isowner
42 isowner = platform.isowner
43 localpath = platform.localpath
43 localpath = platform.localpath
44 lookupreg = platform.lookupreg
44 lookupreg = platform.lookupreg
45 makedir = platform.makedir
45 makedir = platform.makedir
46 nlinks = platform.nlinks
46 nlinks = platform.nlinks
47 normpath = platform.normpath
47 normpath = platform.normpath
48 normcase = platform.normcase
48 normcase = platform.normcase
49 openhardlinks = platform.openhardlinks
49 openhardlinks = platform.openhardlinks
50 oslink = platform.oslink
50 oslink = platform.oslink
51 parsepatchoutput = platform.parsepatchoutput
51 parsepatchoutput = platform.parsepatchoutput
52 pconvert = platform.pconvert
52 pconvert = platform.pconvert
53 popen = platform.popen
53 popen = platform.popen
54 posixfile = platform.posixfile
54 posixfile = platform.posixfile
55 quotecommand = platform.quotecommand
55 quotecommand = platform.quotecommand
56 rename = platform.rename
56 rename = platform.rename
57 samedevice = platform.samedevice
57 samedevice = platform.samedevice
58 samefile = platform.samefile
58 samefile = platform.samefile
59 samestat = platform.samestat
59 samestat = platform.samestat
60 setbinary = platform.setbinary
60 setbinary = platform.setbinary
61 setflags = platform.setflags
61 setflags = platform.setflags
62 setsignalhandler = platform.setsignalhandler
62 setsignalhandler = platform.setsignalhandler
63 shellquote = platform.shellquote
63 shellquote = platform.shellquote
64 spawndetached = platform.spawndetached
64 spawndetached = platform.spawndetached
65 split = platform.split
65 split = platform.split
66 sshargs = platform.sshargs
66 sshargs = platform.sshargs
67 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
67 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
68 statisexec = platform.statisexec
68 statisexec = platform.statisexec
69 statislink = platform.statislink
69 statislink = platform.statislink
70 termwidth = platform.termwidth
70 termwidth = platform.termwidth
71 testpid = platform.testpid
71 testpid = platform.testpid
72 umask = platform.umask
72 umask = platform.umask
73 unlink = platform.unlink
73 unlink = platform.unlink
74 unlinkpath = platform.unlinkpath
74 unlinkpath = platform.unlinkpath
75 username = platform.username
75 username = platform.username
76
76
77 # Python compatibility
77 # Python compatibility
78
78
79 _notset = object()
79 _notset = object()
80
80
81 def safehasattr(thing, attr):
81 def safehasattr(thing, attr):
82 return getattr(thing, attr, _notset) is not _notset
82 return getattr(thing, attr, _notset) is not _notset
83
83
84 def sha1(s=''):
84 def sha1(s=''):
85 '''
85 '''
86 Low-overhead wrapper around Python's SHA support
86 Low-overhead wrapper around Python's SHA support
87
87
88 >>> f = _fastsha1
88 >>> f = _fastsha1
89 >>> a = sha1()
89 >>> a = sha1()
90 >>> a = f()
90 >>> a = f()
91 >>> a.hexdigest()
91 >>> a.hexdigest()
92 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
92 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
93 '''
93 '''
94
94
95 return _fastsha1(s)
95 return _fastsha1(s)
96
96
97 def _fastsha1(s=''):
97 def _fastsha1(s=''):
98 # This function will import sha1 from hashlib or sha (whichever is
98 # This function will import sha1 from hashlib or sha (whichever is
99 # available) and overwrite itself with it on the first call.
99 # available) and overwrite itself with it on the first call.
100 # Subsequent calls will go directly to the imported function.
100 # Subsequent calls will go directly to the imported function.
101 if sys.version_info >= (2, 5):
101 if sys.version_info >= (2, 5):
102 from hashlib import sha1 as _sha1
102 from hashlib import sha1 as _sha1
103 else:
103 else:
104 from sha import sha as _sha1
104 from sha import sha as _sha1
105 global _fastsha1, sha1
105 global _fastsha1, sha1
106 _fastsha1 = sha1 = _sha1
106 _fastsha1 = sha1 = _sha1
107 return _sha1(s)
107 return _sha1(s)
108
108
109 try:
109 try:
110 buffer = buffer
110 buffer = buffer
111 except NameError:
111 except NameError:
112 if sys.version_info[0] < 3:
112 if sys.version_info[0] < 3:
113 def buffer(sliceable, offset=0):
113 def buffer(sliceable, offset=0):
114 return sliceable[offset:]
114 return sliceable[offset:]
115 else:
115 else:
116 def buffer(sliceable, offset=0):
116 def buffer(sliceable, offset=0):
117 return memoryview(sliceable)[offset:]
117 return memoryview(sliceable)[offset:]
118
118
119 import subprocess
119 import subprocess
120 closefds = os.name == 'posix'
120 closefds = os.name == 'posix'
121
121
122 def popen2(cmd, env=None, newlines=False):
122 def popen2(cmd, env=None, newlines=False):
123 # Setting bufsize to -1 lets the system decide the buffer size.
123 # Setting bufsize to -1 lets the system decide the buffer size.
124 # The default for bufsize is 0, meaning unbuffered. This leads to
124 # The default for bufsize is 0, meaning unbuffered. This leads to
125 # poor performance on Mac OS X: http://bugs.python.org/issue4194
125 # poor performance on Mac OS X: http://bugs.python.org/issue4194
126 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
126 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
127 close_fds=closefds,
127 close_fds=closefds,
128 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
128 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
129 universal_newlines=newlines,
129 universal_newlines=newlines,
130 env=env)
130 env=env)
131 return p.stdin, p.stdout
131 return p.stdin, p.stdout
132
132
133 def popen3(cmd, env=None, newlines=False):
133 def popen3(cmd, env=None, newlines=False):
134 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
134 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
135 return stdin, stdout, stderr
135 return stdin, stdout, stderr
136
136
137 def popen4(cmd, env=None, newlines=False):
137 def popen4(cmd, env=None, newlines=False):
138 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
138 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
139 close_fds=closefds,
139 close_fds=closefds,
140 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
140 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
141 stderr=subprocess.PIPE,
141 stderr=subprocess.PIPE,
142 universal_newlines=newlines,
142 universal_newlines=newlines,
143 env=env)
143 env=env)
144 return p.stdin, p.stdout, p.stderr, p
144 return p.stdin, p.stdout, p.stderr, p
145
145
146 def version():
146 def version():
147 """Return version information if available."""
147 """Return version information if available."""
148 try:
148 try:
149 import __version__
149 import __version__
150 return __version__.version
150 return __version__.version
151 except ImportError:
151 except ImportError:
152 return 'unknown'
152 return 'unknown'
153
153
154 # used by parsedate
154 # used by parsedate
155 defaultdateformats = (
155 defaultdateformats = (
156 '%Y-%m-%d %H:%M:%S',
156 '%Y-%m-%d %H:%M:%S',
157 '%Y-%m-%d %I:%M:%S%p',
157 '%Y-%m-%d %I:%M:%S%p',
158 '%Y-%m-%d %H:%M',
158 '%Y-%m-%d %H:%M',
159 '%Y-%m-%d %I:%M%p',
159 '%Y-%m-%d %I:%M%p',
160 '%Y-%m-%d',
160 '%Y-%m-%d',
161 '%m-%d',
161 '%m-%d',
162 '%m/%d',
162 '%m/%d',
163 '%m/%d/%y',
163 '%m/%d/%y',
164 '%m/%d/%Y',
164 '%m/%d/%Y',
165 '%a %b %d %H:%M:%S %Y',
165 '%a %b %d %H:%M:%S %Y',
166 '%a %b %d %I:%M:%S%p %Y',
166 '%a %b %d %I:%M:%S%p %Y',
167 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
167 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
168 '%b %d %H:%M:%S %Y',
168 '%b %d %H:%M:%S %Y',
169 '%b %d %I:%M:%S%p %Y',
169 '%b %d %I:%M:%S%p %Y',
170 '%b %d %H:%M:%S',
170 '%b %d %H:%M:%S',
171 '%b %d %I:%M:%S%p',
171 '%b %d %I:%M:%S%p',
172 '%b %d %H:%M',
172 '%b %d %H:%M',
173 '%b %d %I:%M%p',
173 '%b %d %I:%M%p',
174 '%b %d %Y',
174 '%b %d %Y',
175 '%b %d',
175 '%b %d',
176 '%H:%M:%S',
176 '%H:%M:%S',
177 '%I:%M:%S%p',
177 '%I:%M:%S%p',
178 '%H:%M',
178 '%H:%M',
179 '%I:%M%p',
179 '%I:%M%p',
180 )
180 )
181
181
182 extendeddateformats = defaultdateformats + (
182 extendeddateformats = defaultdateformats + (
183 "%Y",
183 "%Y",
184 "%Y-%m",
184 "%Y-%m",
185 "%b",
185 "%b",
186 "%b %Y",
186 "%b %Y",
187 )
187 )
188
188
189 def cachefunc(func):
189 def cachefunc(func):
190 '''cache the result of function calls'''
190 '''cache the result of function calls'''
191 # XXX doesn't handle keywords args
191 # XXX doesn't handle keywords args
192 if func.func_code.co_argcount == 0:
192 if func.func_code.co_argcount == 0:
193 cache = []
193 cache = []
194 def f():
194 def f():
195 if len(cache) == 0:
195 if len(cache) == 0:
196 cache.append(func())
196 cache.append(func())
197 return cache[0]
197 return cache[0]
198 return f
198 return f
199 cache = {}
199 cache = {}
200 if func.func_code.co_argcount == 1:
200 if func.func_code.co_argcount == 1:
201 # we gain a small amount of time because
201 # we gain a small amount of time because
202 # we don't need to pack/unpack the list
202 # we don't need to pack/unpack the list
203 def f(arg):
203 def f(arg):
204 if arg not in cache:
204 if arg not in cache:
205 cache[arg] = func(arg)
205 cache[arg] = func(arg)
206 return cache[arg]
206 return cache[arg]
207 else:
207 else:
208 def f(*args):
208 def f(*args):
209 if args not in cache:
209 if args not in cache:
210 cache[args] = func(*args)
210 cache[args] = func(*args)
211 return cache[args]
211 return cache[args]
212
212
213 return f
213 return f
214
214
215 try:
215 try:
216 collections.deque.remove
216 collections.deque.remove
217 deque = collections.deque
217 deque = collections.deque
218 except AttributeError:
218 except AttributeError:
219 # python 2.4 lacks deque.remove
219 # python 2.4 lacks deque.remove
220 class deque(collections.deque):
220 class deque(collections.deque):
221 def remove(self, val):
221 def remove(self, val):
222 for i, v in enumerate(self):
222 for i, v in enumerate(self):
223 if v == val:
223 if v == val:
224 del self[i]
224 del self[i]
225 break
225 break
226
226
227 class sortdict(dict):
227 class sortdict(dict):
228 '''a simple sorted dictionary'''
228 '''a simple sorted dictionary'''
229 def __init__(self, data=None):
229 def __init__(self, data=None):
230 self._list = []
230 self._list = []
231 if data:
231 if data:
232 self.update(data)
232 self.update(data)
233 def copy(self):
233 def copy(self):
234 return sortdict(self)
234 return sortdict(self)
235 def __setitem__(self, key, val):
235 def __setitem__(self, key, val):
236 if key in self:
236 if key in self:
237 self._list.remove(key)
237 self._list.remove(key)
238 self._list.append(key)
238 self._list.append(key)
239 dict.__setitem__(self, key, val)
239 dict.__setitem__(self, key, val)
240 def __iter__(self):
240 def __iter__(self):
241 return self._list.__iter__()
241 return self._list.__iter__()
242 def update(self, src):
242 def update(self, src):
243 for k in src:
243 for k in src:
244 self[k] = src[k]
244 self[k] = src[k]
245 def clear(self):
245 def clear(self):
246 dict.clear(self)
246 dict.clear(self)
247 self._list = []
247 self._list = []
248 def items(self):
248 def items(self):
249 return [(k, self[k]) for k in self._list]
249 return [(k, self[k]) for k in self._list]
250 def __delitem__(self, key):
250 def __delitem__(self, key):
251 dict.__delitem__(self, key)
251 dict.__delitem__(self, key)
252 self._list.remove(key)
252 self._list.remove(key)
253 def keys(self):
253 def keys(self):
254 return self._list
254 return self._list
255 def iterkeys(self):
255 def iterkeys(self):
256 return self._list.__iter__()
256 return self._list.__iter__()
257
257
258 class lrucachedict(object):
258 class lrucachedict(object):
259 '''cache most recent gets from or sets to this dictionary'''
259 '''cache most recent gets from or sets to this dictionary'''
260 def __init__(self, maxsize):
260 def __init__(self, maxsize):
261 self._cache = {}
261 self._cache = {}
262 self._maxsize = maxsize
262 self._maxsize = maxsize
263 self._order = deque()
263 self._order = deque()
264
264
265 def __getitem__(self, key):
265 def __getitem__(self, key):
266 value = self._cache[key]
266 value = self._cache[key]
267 self._order.remove(key)
267 self._order.remove(key)
268 self._order.append(key)
268 self._order.append(key)
269 return value
269 return value
270
270
271 def __setitem__(self, key, value):
271 def __setitem__(self, key, value):
272 if key not in self._cache:
272 if key not in self._cache:
273 if len(self._cache) >= self._maxsize:
273 if len(self._cache) >= self._maxsize:
274 del self._cache[self._order.popleft()]
274 del self._cache[self._order.popleft()]
275 else:
275 else:
276 self._order.remove(key)
276 self._order.remove(key)
277 self._cache[key] = value
277 self._cache[key] = value
278 self._order.append(key)
278 self._order.append(key)
279
279
280 def __contains__(self, key):
280 def __contains__(self, key):
281 return key in self._cache
281 return key in self._cache
282
282
283 def clear(self):
283 def clear(self):
284 self._cache.clear()
284 self._cache.clear()
285 self._order = deque()
285 self._order = deque()
286
286
287 def lrucachefunc(func):
287 def lrucachefunc(func):
288 '''cache most recent results of function calls'''
288 '''cache most recent results of function calls'''
289 cache = {}
289 cache = {}
290 order = deque()
290 order = deque()
291 if func.func_code.co_argcount == 1:
291 if func.func_code.co_argcount == 1:
292 def f(arg):
292 def f(arg):
293 if arg not in cache:
293 if arg not in cache:
294 if len(cache) > 20:
294 if len(cache) > 20:
295 del cache[order.popleft()]
295 del cache[order.popleft()]
296 cache[arg] = func(arg)
296 cache[arg] = func(arg)
297 else:
297 else:
298 order.remove(arg)
298 order.remove(arg)
299 order.append(arg)
299 order.append(arg)
300 return cache[arg]
300 return cache[arg]
301 else:
301 else:
302 def f(*args):
302 def f(*args):
303 if args not in cache:
303 if args not in cache:
304 if len(cache) > 20:
304 if len(cache) > 20:
305 del cache[order.popleft()]
305 del cache[order.popleft()]
306 cache[args] = func(*args)
306 cache[args] = func(*args)
307 else:
307 else:
308 order.remove(args)
308 order.remove(args)
309 order.append(args)
309 order.append(args)
310 return cache[args]
310 return cache[args]
311
311
312 return f
312 return f
313
313
314 class propertycache(object):
314 class propertycache(object):
315 def __init__(self, func):
315 def __init__(self, func):
316 self.func = func
316 self.func = func
317 self.name = func.__name__
317 self.name = func.__name__
318 def __get__(self, obj, type=None):
318 def __get__(self, obj, type=None):
319 result = self.func(obj)
319 result = self.func(obj)
320 self.cachevalue(obj, result)
320 self.cachevalue(obj, result)
321 return result
321 return result
322
322
323 def cachevalue(self, obj, value):
323 def cachevalue(self, obj, value):
324 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
324 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
325 obj.__dict__[self.name] = value
325 obj.__dict__[self.name] = value
326
326
327 def pipefilter(s, cmd):
327 def pipefilter(s, cmd):
328 '''filter string S through command CMD, returning its output'''
328 '''filter string S through command CMD, returning its output'''
329 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
329 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
330 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
330 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
331 pout, perr = p.communicate(s)
331 pout, perr = p.communicate(s)
332 return pout
332 return pout
333
333
334 def tempfilter(s, cmd):
334 def tempfilter(s, cmd):
335 '''filter string S through a pair of temporary files with CMD.
335 '''filter string S through a pair of temporary files with CMD.
336 CMD is used as a template to create the real command to be run,
336 CMD is used as a template to create the real command to be run,
337 with the strings INFILE and OUTFILE replaced by the real names of
337 with the strings INFILE and OUTFILE replaced by the real names of
338 the temporary files generated.'''
338 the temporary files generated.'''
339 inname, outname = None, None
339 inname, outname = None, None
340 try:
340 try:
341 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
341 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
342 fp = os.fdopen(infd, 'wb')
342 fp = os.fdopen(infd, 'wb')
343 fp.write(s)
343 fp.write(s)
344 fp.close()
344 fp.close()
345 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
345 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
346 os.close(outfd)
346 os.close(outfd)
347 cmd = cmd.replace('INFILE', inname)
347 cmd = cmd.replace('INFILE', inname)
348 cmd = cmd.replace('OUTFILE', outname)
348 cmd = cmd.replace('OUTFILE', outname)
349 code = os.system(cmd)
349 code = os.system(cmd)
350 if sys.platform == 'OpenVMS' and code & 1:
350 if sys.platform == 'OpenVMS' and code & 1:
351 code = 0
351 code = 0
352 if code:
352 if code:
353 raise Abort(_("command '%s' failed: %s") %
353 raise Abort(_("command '%s' failed: %s") %
354 (cmd, explainexit(code)))
354 (cmd, explainexit(code)))
355 fp = open(outname, 'rb')
355 fp = open(outname, 'rb')
356 r = fp.read()
356 r = fp.read()
357 fp.close()
357 fp.close()
358 return r
358 return r
359 finally:
359 finally:
360 try:
360 try:
361 if inname:
361 if inname:
362 os.unlink(inname)
362 os.unlink(inname)
363 except OSError:
363 except OSError:
364 pass
364 pass
365 try:
365 try:
366 if outname:
366 if outname:
367 os.unlink(outname)
367 os.unlink(outname)
368 except OSError:
368 except OSError:
369 pass
369 pass
370
370
371 filtertable = {
371 filtertable = {
372 'tempfile:': tempfilter,
372 'tempfile:': tempfilter,
373 'pipe:': pipefilter,
373 'pipe:': pipefilter,
374 }
374 }
375
375
376 def filter(s, cmd):
376 def filter(s, cmd):
377 "filter a string through a command that transforms its input to its output"
377 "filter a string through a command that transforms its input to its output"
378 for name, fn in filtertable.iteritems():
378 for name, fn in filtertable.iteritems():
379 if cmd.startswith(name):
379 if cmd.startswith(name):
380 return fn(s, cmd[len(name):].lstrip())
380 return fn(s, cmd[len(name):].lstrip())
381 return pipefilter(s, cmd)
381 return pipefilter(s, cmd)
382
382
383 def binary(s):
383 def binary(s):
384 """return true if a string is binary data"""
384 """return true if a string is binary data"""
385 return bool(s and '\0' in s)
385 return bool(s and '\0' in s)
386
386
387 def increasingchunks(source, min=1024, max=65536):
387 def increasingchunks(source, min=1024, max=65536):
388 '''return no less than min bytes per chunk while data remains,
388 '''return no less than min bytes per chunk while data remains,
389 doubling min after each chunk until it reaches max'''
389 doubling min after each chunk until it reaches max'''
390 def log2(x):
390 def log2(x):
391 if not x:
391 if not x:
392 return 0
392 return 0
393 i = 0
393 i = 0
394 while x:
394 while x:
395 x >>= 1
395 x >>= 1
396 i += 1
396 i += 1
397 return i - 1
397 return i - 1
398
398
399 buf = []
399 buf = []
400 blen = 0
400 blen = 0
401 for chunk in source:
401 for chunk in source:
402 buf.append(chunk)
402 buf.append(chunk)
403 blen += len(chunk)
403 blen += len(chunk)
404 if blen >= min:
404 if blen >= min:
405 if min < max:
405 if min < max:
406 min = min << 1
406 min = min << 1
407 nmin = 1 << log2(blen)
407 nmin = 1 << log2(blen)
408 if nmin > min:
408 if nmin > min:
409 min = nmin
409 min = nmin
410 if min > max:
410 if min > max:
411 min = max
411 min = max
412 yield ''.join(buf)
412 yield ''.join(buf)
413 blen = 0
413 blen = 0
414 buf = []
414 buf = []
415 if buf:
415 if buf:
416 yield ''.join(buf)
416 yield ''.join(buf)
417
417
418 Abort = error.Abort
418 Abort = error.Abort
419
419
420 def always(fn):
420 def always(fn):
421 return True
421 return True
422
422
423 def never(fn):
423 def never(fn):
424 return False
424 return False
425
425
426 def pathto(root, n1, n2):
426 def pathto(root, n1, n2):
427 '''return the relative path from one place to another.
427 '''return the relative path from one place to another.
428 root should use os.sep to separate directories
428 root should use os.sep to separate directories
429 n1 should use os.sep to separate directories
429 n1 should use os.sep to separate directories
430 n2 should use "/" to separate directories
430 n2 should use "/" to separate directories
431 returns an os.sep-separated path.
431 returns an os.sep-separated path.
432
432
433 If n1 is a relative path, it's assumed it's
433 If n1 is a relative path, it's assumed it's
434 relative to root.
434 relative to root.
435 n2 should always be relative to root.
435 n2 should always be relative to root.
436 '''
436 '''
437 if not n1:
437 if not n1:
438 return localpath(n2)
438 return localpath(n2)
439 if os.path.isabs(n1):
439 if os.path.isabs(n1):
440 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
440 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
441 return os.path.join(root, localpath(n2))
441 return os.path.join(root, localpath(n2))
442 n2 = '/'.join((pconvert(root), n2))
442 n2 = '/'.join((pconvert(root), n2))
443 a, b = splitpath(n1), n2.split('/')
443 a, b = splitpath(n1), n2.split('/')
444 a.reverse()
444 a.reverse()
445 b.reverse()
445 b.reverse()
446 while a and b and a[-1] == b[-1]:
446 while a and b and a[-1] == b[-1]:
447 a.pop()
447 a.pop()
448 b.pop()
448 b.pop()
449 b.reverse()
449 b.reverse()
450 return os.sep.join((['..'] * len(a)) + b) or '.'
450 return os.sep.join((['..'] * len(a)) + b) or '.'
451
451
452 _hgexecutable = None
452 _hgexecutable = None
453
453
454 def mainfrozen():
454 def mainfrozen():
455 """return True if we are a frozen executable.
455 """return True if we are a frozen executable.
456
456
457 The code supports py2exe (most common, Windows only) and tools/freeze
457 The code supports py2exe (most common, Windows only) and tools/freeze
458 (portable, not much used).
458 (portable, not much used).
459 """
459 """
460 return (safehasattr(sys, "frozen") or # new py2exe
460 return (safehasattr(sys, "frozen") or # new py2exe
461 safehasattr(sys, "importers") or # old py2exe
461 safehasattr(sys, "importers") or # old py2exe
462 imp.is_frozen("__main__")) # tools/freeze
462 imp.is_frozen("__main__")) # tools/freeze
463
463
464 def hgexecutable():
464 def hgexecutable():
465 """return location of the 'hg' executable.
465 """return location of the 'hg' executable.
466
466
467 Defaults to $HG or 'hg' in the search path.
467 Defaults to $HG or 'hg' in the search path.
468 """
468 """
469 if _hgexecutable is None:
469 if _hgexecutable is None:
470 hg = os.environ.get('HG')
470 hg = os.environ.get('HG')
471 mainmod = sys.modules['__main__']
471 mainmod = sys.modules['__main__']
472 if hg:
472 if hg:
473 _sethgexecutable(hg)
473 _sethgexecutable(hg)
474 elif mainfrozen():
474 elif mainfrozen():
475 _sethgexecutable(sys.executable)
475 _sethgexecutable(sys.executable)
476 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
476 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
477 _sethgexecutable(mainmod.__file__)
477 _sethgexecutable(mainmod.__file__)
478 else:
478 else:
479 exe = findexe('hg') or os.path.basename(sys.argv[0])
479 exe = findexe('hg') or os.path.basename(sys.argv[0])
480 _sethgexecutable(exe)
480 _sethgexecutable(exe)
481 return _hgexecutable
481 return _hgexecutable
482
482
483 def _sethgexecutable(path):
483 def _sethgexecutable(path):
484 """set location of the 'hg' executable"""
484 """set location of the 'hg' executable"""
485 global _hgexecutable
485 global _hgexecutable
486 _hgexecutable = path
486 _hgexecutable = path
487
487
488 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
488 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
489 '''enhanced shell command execution.
489 '''enhanced shell command execution.
490 run with environment maybe modified, maybe in different dir.
490 run with environment maybe modified, maybe in different dir.
491
491
492 if command fails and onerr is None, return status. if ui object,
492 if command fails and onerr is None, return status. if ui object,
493 print error message and return status, else raise onerr object as
493 print error message and return status, else raise onerr object as
494 exception.
494 exception.
495
495
496 if out is specified, it is assumed to be a file-like object that has a
496 if out is specified, it is assumed to be a file-like object that has a
497 write() method. stdout and stderr will be redirected to out.'''
497 write() method. stdout and stderr will be redirected to out.'''
498 try:
498 try:
499 sys.stdout.flush()
499 sys.stdout.flush()
500 except Exception:
500 except Exception:
501 pass
501 pass
502 def py2shell(val):
502 def py2shell(val):
503 'convert python object into string that is useful to shell'
503 'convert python object into string that is useful to shell'
504 if val is None or val is False:
504 if val is None or val is False:
505 return '0'
505 return '0'
506 if val is True:
506 if val is True:
507 return '1'
507 return '1'
508 return str(val)
508 return str(val)
509 origcmd = cmd
509 origcmd = cmd
510 cmd = quotecommand(cmd)
510 cmd = quotecommand(cmd)
511 if sys.platform == 'plan9' and (sys.version_info[0] == 2
511 if sys.platform == 'plan9' and (sys.version_info[0] == 2
512 and sys.version_info[1] < 7):
512 and sys.version_info[1] < 7):
513 # subprocess kludge to work around issues in half-baked Python
513 # subprocess kludge to work around issues in half-baked Python
514 # ports, notably bichued/python:
514 # ports, notably bichued/python:
515 if not cwd is None:
515 if not cwd is None:
516 os.chdir(cwd)
516 os.chdir(cwd)
517 rc = os.system(cmd)
517 rc = os.system(cmd)
518 else:
518 else:
519 env = dict(os.environ)
519 env = dict(os.environ)
520 env.update((k, py2shell(v)) for k, v in environ.iteritems())
520 env.update((k, py2shell(v)) for k, v in environ.iteritems())
521 env['HG'] = hgexecutable()
521 env['HG'] = hgexecutable()
522 if out is None or out == sys.__stdout__:
522 if out is None or out == sys.__stdout__:
523 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
523 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
524 env=env, cwd=cwd)
524 env=env, cwd=cwd)
525 else:
525 else:
526 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
526 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
527 env=env, cwd=cwd, stdout=subprocess.PIPE,
527 env=env, cwd=cwd, stdout=subprocess.PIPE,
528 stderr=subprocess.STDOUT)
528 stderr=subprocess.STDOUT)
529 for line in proc.stdout:
529 for line in proc.stdout:
530 out.write(line)
530 out.write(line)
531 proc.wait()
531 proc.wait()
532 rc = proc.returncode
532 rc = proc.returncode
533 if sys.platform == 'OpenVMS' and rc & 1:
533 if sys.platform == 'OpenVMS' and rc & 1:
534 rc = 0
534 rc = 0
535 if rc and onerr:
535 if rc and onerr:
536 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
536 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
537 explainexit(rc)[0])
537 explainexit(rc)[0])
538 if errprefix:
538 if errprefix:
539 errmsg = '%s: %s' % (errprefix, errmsg)
539 errmsg = '%s: %s' % (errprefix, errmsg)
540 try:
540 try:
541 onerr.warn(errmsg + '\n')
541 onerr.warn(errmsg + '\n')
542 except AttributeError:
542 except AttributeError:
543 raise onerr(errmsg)
543 raise onerr(errmsg)
544 return rc
544 return rc
545
545
546 def checksignature(func):
546 def checksignature(func):
547 '''wrap a function with code to check for calling errors'''
547 '''wrap a function with code to check for calling errors'''
548 def check(*args, **kwargs):
548 def check(*args, **kwargs):
549 try:
549 try:
550 return func(*args, **kwargs)
550 return func(*args, **kwargs)
551 except TypeError:
551 except TypeError:
552 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
552 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
553 raise error.SignatureError
553 raise error.SignatureError
554 raise
554 raise
555
555
556 return check
556 return check
557
557
558 def copyfile(src, dest):
558 def copyfile(src, dest):
559 "copy a file, preserving mode and atime/mtime"
559 "copy a file, preserving mode and atime/mtime"
560 if os.path.lexists(dest):
560 if os.path.lexists(dest):
561 unlink(dest)
561 unlink(dest)
562 if os.path.islink(src):
562 if os.path.islink(src):
563 os.symlink(os.readlink(src), dest)
563 os.symlink(os.readlink(src), dest)
564 else:
564 else:
565 try:
565 try:
566 shutil.copyfile(src, dest)
566 shutil.copyfile(src, dest)
567 shutil.copymode(src, dest)
567 shutil.copymode(src, dest)
568 except shutil.Error, inst:
568 except shutil.Error, inst:
569 raise Abort(str(inst))
569 raise Abort(str(inst))
570
570
571 def copyfiles(src, dst, hardlink=None):
571 def copyfiles(src, dst, hardlink=None):
572 """Copy a directory tree using hardlinks if possible"""
572 """Copy a directory tree using hardlinks if possible"""
573
573
574 if hardlink is None:
574 if hardlink is None:
575 hardlink = (os.stat(src).st_dev ==
575 hardlink = (os.stat(src).st_dev ==
576 os.stat(os.path.dirname(dst)).st_dev)
576 os.stat(os.path.dirname(dst)).st_dev)
577
577
578 num = 0
578 num = 0
579 if os.path.isdir(src):
579 if os.path.isdir(src):
580 os.mkdir(dst)
580 os.mkdir(dst)
581 for name, kind in osutil.listdir(src):
581 for name, kind in osutil.listdir(src):
582 srcname = os.path.join(src, name)
582 srcname = os.path.join(src, name)
583 dstname = os.path.join(dst, name)
583 dstname = os.path.join(dst, name)
584 hardlink, n = copyfiles(srcname, dstname, hardlink)
584 hardlink, n = copyfiles(srcname, dstname, hardlink)
585 num += n
585 num += n
586 else:
586 else:
587 if hardlink:
587 if hardlink:
588 try:
588 try:
589 oslink(src, dst)
589 oslink(src, dst)
590 except (IOError, OSError):
590 except (IOError, OSError):
591 hardlink = False
591 hardlink = False
592 shutil.copy(src, dst)
592 shutil.copy(src, dst)
593 else:
593 else:
594 shutil.copy(src, dst)
594 shutil.copy(src, dst)
595 num += 1
595 num += 1
596
596
597 return hardlink, num
597 return hardlink, num
598
598
599 _winreservednames = '''con prn aux nul
599 _winreservednames = '''con prn aux nul
600 com1 com2 com3 com4 com5 com6 com7 com8 com9
600 com1 com2 com3 com4 com5 com6 com7 com8 com9
601 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
601 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
602 _winreservedchars = ':*?"<>|'
602 _winreservedchars = ':*?"<>|'
603 def checkwinfilename(path):
603 def checkwinfilename(path):
604 r'''Check that the base-relative path is a valid filename on Windows.
604 r'''Check that the base-relative path is a valid filename on Windows.
605 Returns None if the path is ok, or a UI string describing the problem.
605 Returns None if the path is ok, or a UI string describing the problem.
606
606
607 >>> checkwinfilename("just/a/normal/path")
607 >>> checkwinfilename("just/a/normal/path")
608 >>> checkwinfilename("foo/bar/con.xml")
608 >>> checkwinfilename("foo/bar/con.xml")
609 "filename contains 'con', which is reserved on Windows"
609 "filename contains 'con', which is reserved on Windows"
610 >>> checkwinfilename("foo/con.xml/bar")
610 >>> checkwinfilename("foo/con.xml/bar")
611 "filename contains 'con', which is reserved on Windows"
611 "filename contains 'con', which is reserved on Windows"
612 >>> checkwinfilename("foo/bar/xml.con")
612 >>> checkwinfilename("foo/bar/xml.con")
613 >>> checkwinfilename("foo/bar/AUX/bla.txt")
613 >>> checkwinfilename("foo/bar/AUX/bla.txt")
614 "filename contains 'AUX', which is reserved on Windows"
614 "filename contains 'AUX', which is reserved on Windows"
615 >>> checkwinfilename("foo/bar/bla:.txt")
615 >>> checkwinfilename("foo/bar/bla:.txt")
616 "filename contains ':', which is reserved on Windows"
616 "filename contains ':', which is reserved on Windows"
617 >>> checkwinfilename("foo/bar/b\07la.txt")
617 >>> checkwinfilename("foo/bar/b\07la.txt")
618 "filename contains '\\x07', which is invalid on Windows"
618 "filename contains '\\x07', which is invalid on Windows"
619 >>> checkwinfilename("foo/bar/bla ")
619 >>> checkwinfilename("foo/bar/bla ")
620 "filename ends with ' ', which is not allowed on Windows"
620 "filename ends with ' ', which is not allowed on Windows"
621 >>> checkwinfilename("../bar")
621 >>> checkwinfilename("../bar")
622 >>> checkwinfilename("foo\\")
622 >>> checkwinfilename("foo\\")
623 "filename ends with '\\', which is invalid on Windows"
623 "filename ends with '\\', which is invalid on Windows"
624 >>> checkwinfilename("foo\\/bar")
624 >>> checkwinfilename("foo\\/bar")
625 "directory name ends with '\\', which is invalid on Windows"
625 "directory name ends with '\\', which is invalid on Windows"
626 '''
626 '''
627 if path.endswith('\\'):
627 if path.endswith('\\'):
628 return _("filename ends with '\\', which is invalid on Windows")
628 return _("filename ends with '\\', which is invalid on Windows")
629 if '\\/' in path:
629 if '\\/' in path:
630 return _("directory name ends with '\\', which is invalid on Windows")
630 return _("directory name ends with '\\', which is invalid on Windows")
631 for n in path.replace('\\', '/').split('/'):
631 for n in path.replace('\\', '/').split('/'):
632 if not n:
632 if not n:
633 continue
633 continue
634 for c in n:
634 for c in n:
635 if c in _winreservedchars:
635 if c in _winreservedchars:
636 return _("filename contains '%s', which is reserved "
636 return _("filename contains '%s', which is reserved "
637 "on Windows") % c
637 "on Windows") % c
638 if ord(c) <= 31:
638 if ord(c) <= 31:
639 return _("filename contains %r, which is invalid "
639 return _("filename contains %r, which is invalid "
640 "on Windows") % c
640 "on Windows") % c
641 base = n.split('.')[0]
641 base = n.split('.')[0]
642 if base and base.lower() in _winreservednames:
642 if base and base.lower() in _winreservednames:
643 return _("filename contains '%s', which is reserved "
643 return _("filename contains '%s', which is reserved "
644 "on Windows") % base
644 "on Windows") % base
645 t = n[-1]
645 t = n[-1]
646 if t in '. ' and n not in '..':
646 if t in '. ' and n not in '..':
647 return _("filename ends with '%s', which is not allowed "
647 return _("filename ends with '%s', which is not allowed "
648 "on Windows") % t
648 "on Windows") % t
649
649
650 if os.name == 'nt':
650 if os.name == 'nt':
651 checkosfilename = checkwinfilename
651 checkosfilename = checkwinfilename
652 else:
652 else:
653 checkosfilename = platform.checkosfilename
653 checkosfilename = platform.checkosfilename
654
654
655 def makelock(info, pathname):
655 def makelock(info, pathname):
656 try:
656 try:
657 return os.symlink(info, pathname)
657 return os.symlink(info, pathname)
658 except OSError, why:
658 except OSError, why:
659 if why.errno == errno.EEXIST:
659 if why.errno == errno.EEXIST:
660 raise
660 raise
661 except AttributeError: # no symlink in os
661 except AttributeError: # no symlink in os
662 pass
662 pass
663
663
664 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
664 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
665 os.write(ld, info)
665 os.write(ld, info)
666 os.close(ld)
666 os.close(ld)
667
667
668 def readlock(pathname):
668 def readlock(pathname):
669 try:
669 try:
670 return os.readlink(pathname)
670 return os.readlink(pathname)
671 except OSError, why:
671 except OSError, why:
672 if why.errno not in (errno.EINVAL, errno.ENOSYS):
672 if why.errno not in (errno.EINVAL, errno.ENOSYS):
673 raise
673 raise
674 except AttributeError: # no symlink in os
674 except AttributeError: # no symlink in os
675 pass
675 pass
676 fp = posixfile(pathname)
676 fp = posixfile(pathname)
677 r = fp.read()
677 r = fp.read()
678 fp.close()
678 fp.close()
679 return r
679 return r
680
680
681 def fstat(fp):
681 def fstat(fp):
682 '''stat file object that may not have fileno method.'''
682 '''stat file object that may not have fileno method.'''
683 try:
683 try:
684 return os.fstat(fp.fileno())
684 return os.fstat(fp.fileno())
685 except AttributeError:
685 except AttributeError:
686 return os.stat(fp.name)
686 return os.stat(fp.name)
687
687
688 # File system features
688 # File system features
689
689
690 def checkcase(path):
690 def checkcase(path):
691 """
691 """
692 Return true if the given path is on a case-sensitive filesystem
692 Return true if the given path is on a case-sensitive filesystem
693
693
694 Requires a path (like /foo/.hg) ending with a foldable final
694 Requires a path (like /foo/.hg) ending with a foldable final
695 directory component.
695 directory component.
696 """
696 """
697 s1 = os.stat(path)
697 s1 = os.stat(path)
698 d, b = os.path.split(path)
698 d, b = os.path.split(path)
699 b2 = b.upper()
699 b2 = b.upper()
700 if b == b2:
700 if b == b2:
701 b2 = b.lower()
701 b2 = b.lower()
702 if b == b2:
702 if b == b2:
703 return True # no evidence against case sensitivity
703 return True # no evidence against case sensitivity
704 p2 = os.path.join(d, b2)
704 p2 = os.path.join(d, b2)
705 try:
705 try:
706 s2 = os.stat(p2)
706 s2 = os.stat(p2)
707 if s2 == s1:
707 if s2 == s1:
708 return False
708 return False
709 return True
709 return True
710 except OSError:
710 except OSError:
711 return True
711 return True
712
712
713 try:
713 try:
714 import re2
714 import re2
715 _re2 = None
715 _re2 = None
716 except ImportError:
716 except ImportError:
717 _re2 = False
717 _re2 = False
718
718
719 class _re(object):
719 class _re(object):
720 def _checkre2(self):
721 global _re2
722 try:
723 # check if match works, see issue3964
724 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
725 except ImportError:
726 _re2 = False
727
720 def compile(self, pat, flags=0):
728 def compile(self, pat, flags=0):
721 '''Compile a regular expression, using re2 if possible
729 '''Compile a regular expression, using re2 if possible
722
730
723 For best performance, use only re2-compatible regexp features. The
731 For best performance, use only re2-compatible regexp features. The
724 only flags from the re module that are re2-compatible are
732 only flags from the re module that are re2-compatible are
725 IGNORECASE and MULTILINE.'''
733 IGNORECASE and MULTILINE.'''
726 global _re2
727 if _re2 is None:
734 if _re2 is None:
728 try:
735 self._checkre2()
729 # check if match works, see issue3964
730 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
731 except ImportError:
732 _re2 = False
733 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
736 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
734 if flags & remod.IGNORECASE:
737 if flags & remod.IGNORECASE:
735 pat = '(?i)' + pat
738 pat = '(?i)' + pat
736 if flags & remod.MULTILINE:
739 if flags & remod.MULTILINE:
737 pat = '(?m)' + pat
740 pat = '(?m)' + pat
738 try:
741 try:
739 return re2.compile(pat)
742 return re2.compile(pat)
740 except re2.error:
743 except re2.error:
741 pass
744 pass
742 return remod.compile(pat, flags)
745 return remod.compile(pat, flags)
743
746
744 re = _re()
747 re = _re()
745
748
746 _fspathcache = {}
749 _fspathcache = {}
747 def fspath(name, root):
750 def fspath(name, root):
748 '''Get name in the case stored in the filesystem
751 '''Get name in the case stored in the filesystem
749
752
750 The name should be relative to root, and be normcase-ed for efficiency.
753 The name should be relative to root, and be normcase-ed for efficiency.
751
754
752 Note that this function is unnecessary, and should not be
755 Note that this function is unnecessary, and should not be
753 called, for case-sensitive filesystems (simply because it's expensive).
756 called, for case-sensitive filesystems (simply because it's expensive).
754
757
755 The root should be normcase-ed, too.
758 The root should be normcase-ed, too.
756 '''
759 '''
757 def find(p, contents):
760 def find(p, contents):
758 for n in contents:
761 for n in contents:
759 if normcase(n) == p:
762 if normcase(n) == p:
760 return n
763 return n
761 return None
764 return None
762
765
763 seps = os.sep
766 seps = os.sep
764 if os.altsep:
767 if os.altsep:
765 seps = seps + os.altsep
768 seps = seps + os.altsep
766 # Protect backslashes. This gets silly very quickly.
769 # Protect backslashes. This gets silly very quickly.
767 seps.replace('\\','\\\\')
770 seps.replace('\\','\\\\')
768 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
771 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
769 dir = os.path.normpath(root)
772 dir = os.path.normpath(root)
770 result = []
773 result = []
771 for part, sep in pattern.findall(name):
774 for part, sep in pattern.findall(name):
772 if sep:
775 if sep:
773 result.append(sep)
776 result.append(sep)
774 continue
777 continue
775
778
776 if dir not in _fspathcache:
779 if dir not in _fspathcache:
777 _fspathcache[dir] = os.listdir(dir)
780 _fspathcache[dir] = os.listdir(dir)
778 contents = _fspathcache[dir]
781 contents = _fspathcache[dir]
779
782
780 found = find(part, contents)
783 found = find(part, contents)
781 if not found:
784 if not found:
782 # retry "once per directory" per "dirstate.walk" which
785 # retry "once per directory" per "dirstate.walk" which
783 # may take place for each patches of "hg qpush", for example
786 # may take place for each patches of "hg qpush", for example
784 contents = os.listdir(dir)
787 contents = os.listdir(dir)
785 _fspathcache[dir] = contents
788 _fspathcache[dir] = contents
786 found = find(part, contents)
789 found = find(part, contents)
787
790
788 result.append(found or part)
791 result.append(found or part)
789 dir = os.path.join(dir, part)
792 dir = os.path.join(dir, part)
790
793
791 return ''.join(result)
794 return ''.join(result)
792
795
793 def checknlink(testfile):
796 def checknlink(testfile):
794 '''check whether hardlink count reporting works properly'''
797 '''check whether hardlink count reporting works properly'''
795
798
796 # testfile may be open, so we need a separate file for checking to
799 # testfile may be open, so we need a separate file for checking to
797 # work around issue2543 (or testfile may get lost on Samba shares)
800 # work around issue2543 (or testfile may get lost on Samba shares)
798 f1 = testfile + ".hgtmp1"
801 f1 = testfile + ".hgtmp1"
799 if os.path.lexists(f1):
802 if os.path.lexists(f1):
800 return False
803 return False
801 try:
804 try:
802 posixfile(f1, 'w').close()
805 posixfile(f1, 'w').close()
803 except IOError:
806 except IOError:
804 return False
807 return False
805
808
806 f2 = testfile + ".hgtmp2"
809 f2 = testfile + ".hgtmp2"
807 fd = None
810 fd = None
808 try:
811 try:
809 try:
812 try:
810 oslink(f1, f2)
813 oslink(f1, f2)
811 except OSError:
814 except OSError:
812 return False
815 return False
813
816
814 # nlinks() may behave differently for files on Windows shares if
817 # nlinks() may behave differently for files on Windows shares if
815 # the file is open.
818 # the file is open.
816 fd = posixfile(f2)
819 fd = posixfile(f2)
817 return nlinks(f2) > 1
820 return nlinks(f2) > 1
818 finally:
821 finally:
819 if fd is not None:
822 if fd is not None:
820 fd.close()
823 fd.close()
821 for f in (f1, f2):
824 for f in (f1, f2):
822 try:
825 try:
823 os.unlink(f)
826 os.unlink(f)
824 except OSError:
827 except OSError:
825 pass
828 pass
826
829
827 def endswithsep(path):
830 def endswithsep(path):
828 '''Check path ends with os.sep or os.altsep.'''
831 '''Check path ends with os.sep or os.altsep.'''
829 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
832 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
830
833
831 def splitpath(path):
834 def splitpath(path):
832 '''Split path by os.sep.
835 '''Split path by os.sep.
833 Note that this function does not use os.altsep because this is
836 Note that this function does not use os.altsep because this is
834 an alternative of simple "xxx.split(os.sep)".
837 an alternative of simple "xxx.split(os.sep)".
835 It is recommended to use os.path.normpath() before using this
838 It is recommended to use os.path.normpath() before using this
836 function if need.'''
839 function if need.'''
837 return path.split(os.sep)
840 return path.split(os.sep)
838
841
839 def gui():
842 def gui():
840 '''Are we running in a GUI?'''
843 '''Are we running in a GUI?'''
841 if sys.platform == 'darwin':
844 if sys.platform == 'darwin':
842 if 'SSH_CONNECTION' in os.environ:
845 if 'SSH_CONNECTION' in os.environ:
843 # handle SSH access to a box where the user is logged in
846 # handle SSH access to a box where the user is logged in
844 return False
847 return False
845 elif getattr(osutil, 'isgui', None):
848 elif getattr(osutil, 'isgui', None):
846 # check if a CoreGraphics session is available
849 # check if a CoreGraphics session is available
847 return osutil.isgui()
850 return osutil.isgui()
848 else:
851 else:
849 # pure build; use a safe default
852 # pure build; use a safe default
850 return True
853 return True
851 else:
854 else:
852 return os.name == "nt" or os.environ.get("DISPLAY")
855 return os.name == "nt" or os.environ.get("DISPLAY")
853
856
854 def mktempcopy(name, emptyok=False, createmode=None):
857 def mktempcopy(name, emptyok=False, createmode=None):
855 """Create a temporary file with the same contents from name
858 """Create a temporary file with the same contents from name
856
859
857 The permission bits are copied from the original file.
860 The permission bits are copied from the original file.
858
861
859 If the temporary file is going to be truncated immediately, you
862 If the temporary file is going to be truncated immediately, you
860 can use emptyok=True as an optimization.
863 can use emptyok=True as an optimization.
861
864
862 Returns the name of the temporary file.
865 Returns the name of the temporary file.
863 """
866 """
864 d, fn = os.path.split(name)
867 d, fn = os.path.split(name)
865 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
868 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
866 os.close(fd)
869 os.close(fd)
867 # Temporary files are created with mode 0600, which is usually not
870 # Temporary files are created with mode 0600, which is usually not
868 # what we want. If the original file already exists, just copy
871 # what we want. If the original file already exists, just copy
869 # its mode. Otherwise, manually obey umask.
872 # its mode. Otherwise, manually obey umask.
870 copymode(name, temp, createmode)
873 copymode(name, temp, createmode)
871 if emptyok:
874 if emptyok:
872 return temp
875 return temp
873 try:
876 try:
874 try:
877 try:
875 ifp = posixfile(name, "rb")
878 ifp = posixfile(name, "rb")
876 except IOError, inst:
879 except IOError, inst:
877 if inst.errno == errno.ENOENT:
880 if inst.errno == errno.ENOENT:
878 return temp
881 return temp
879 if not getattr(inst, 'filename', None):
882 if not getattr(inst, 'filename', None):
880 inst.filename = name
883 inst.filename = name
881 raise
884 raise
882 ofp = posixfile(temp, "wb")
885 ofp = posixfile(temp, "wb")
883 for chunk in filechunkiter(ifp):
886 for chunk in filechunkiter(ifp):
884 ofp.write(chunk)
887 ofp.write(chunk)
885 ifp.close()
888 ifp.close()
886 ofp.close()
889 ofp.close()
887 except: # re-raises
890 except: # re-raises
888 try: os.unlink(temp)
891 try: os.unlink(temp)
889 except OSError: pass
892 except OSError: pass
890 raise
893 raise
891 return temp
894 return temp
892
895
893 class atomictempfile(object):
896 class atomictempfile(object):
894 '''writable file object that atomically updates a file
897 '''writable file object that atomically updates a file
895
898
896 All writes will go to a temporary copy of the original file. Call
899 All writes will go to a temporary copy of the original file. Call
897 close() when you are done writing, and atomictempfile will rename
900 close() when you are done writing, and atomictempfile will rename
898 the temporary copy to the original name, making the changes
901 the temporary copy to the original name, making the changes
899 visible. If the object is destroyed without being closed, all your
902 visible. If the object is destroyed without being closed, all your
900 writes are discarded.
903 writes are discarded.
901 '''
904 '''
902 def __init__(self, name, mode='w+b', createmode=None):
905 def __init__(self, name, mode='w+b', createmode=None):
903 self.__name = name # permanent name
906 self.__name = name # permanent name
904 self._tempname = mktempcopy(name, emptyok=('w' in mode),
907 self._tempname = mktempcopy(name, emptyok=('w' in mode),
905 createmode=createmode)
908 createmode=createmode)
906 self._fp = posixfile(self._tempname, mode)
909 self._fp = posixfile(self._tempname, mode)
907
910
908 # delegated methods
911 # delegated methods
909 self.write = self._fp.write
912 self.write = self._fp.write
910 self.seek = self._fp.seek
913 self.seek = self._fp.seek
911 self.tell = self._fp.tell
914 self.tell = self._fp.tell
912 self.fileno = self._fp.fileno
915 self.fileno = self._fp.fileno
913
916
914 def close(self):
917 def close(self):
915 if not self._fp.closed:
918 if not self._fp.closed:
916 self._fp.close()
919 self._fp.close()
917 rename(self._tempname, localpath(self.__name))
920 rename(self._tempname, localpath(self.__name))
918
921
919 def discard(self):
922 def discard(self):
920 if not self._fp.closed:
923 if not self._fp.closed:
921 try:
924 try:
922 os.unlink(self._tempname)
925 os.unlink(self._tempname)
923 except OSError:
926 except OSError:
924 pass
927 pass
925 self._fp.close()
928 self._fp.close()
926
929
927 def __del__(self):
930 def __del__(self):
928 if safehasattr(self, '_fp'): # constructor actually did something
931 if safehasattr(self, '_fp'): # constructor actually did something
929 self.discard()
932 self.discard()
930
933
931 def makedirs(name, mode=None, notindexed=False):
934 def makedirs(name, mode=None, notindexed=False):
932 """recursive directory creation with parent mode inheritance"""
935 """recursive directory creation with parent mode inheritance"""
933 try:
936 try:
934 makedir(name, notindexed)
937 makedir(name, notindexed)
935 except OSError, err:
938 except OSError, err:
936 if err.errno == errno.EEXIST:
939 if err.errno == errno.EEXIST:
937 return
940 return
938 if err.errno != errno.ENOENT or not name:
941 if err.errno != errno.ENOENT or not name:
939 raise
942 raise
940 parent = os.path.dirname(os.path.abspath(name))
943 parent = os.path.dirname(os.path.abspath(name))
941 if parent == name:
944 if parent == name:
942 raise
945 raise
943 makedirs(parent, mode, notindexed)
946 makedirs(parent, mode, notindexed)
944 makedir(name, notindexed)
947 makedir(name, notindexed)
945 if mode is not None:
948 if mode is not None:
946 os.chmod(name, mode)
949 os.chmod(name, mode)
947
950
948 def ensuredirs(name, mode=None):
951 def ensuredirs(name, mode=None):
949 """race-safe recursive directory creation"""
952 """race-safe recursive directory creation"""
950 if os.path.isdir(name):
953 if os.path.isdir(name):
951 return
954 return
952 parent = os.path.dirname(os.path.abspath(name))
955 parent = os.path.dirname(os.path.abspath(name))
953 if parent != name:
956 if parent != name:
954 ensuredirs(parent, mode)
957 ensuredirs(parent, mode)
955 try:
958 try:
956 os.mkdir(name)
959 os.mkdir(name)
957 except OSError, err:
960 except OSError, err:
958 if err.errno == errno.EEXIST and os.path.isdir(name):
961 if err.errno == errno.EEXIST and os.path.isdir(name):
959 # someone else seems to have won a directory creation race
962 # someone else seems to have won a directory creation race
960 return
963 return
961 raise
964 raise
962 if mode is not None:
965 if mode is not None:
963 os.chmod(name, mode)
966 os.chmod(name, mode)
964
967
965 def readfile(path):
968 def readfile(path):
966 fp = open(path, 'rb')
969 fp = open(path, 'rb')
967 try:
970 try:
968 return fp.read()
971 return fp.read()
969 finally:
972 finally:
970 fp.close()
973 fp.close()
971
974
972 def writefile(path, text):
975 def writefile(path, text):
973 fp = open(path, 'wb')
976 fp = open(path, 'wb')
974 try:
977 try:
975 fp.write(text)
978 fp.write(text)
976 finally:
979 finally:
977 fp.close()
980 fp.close()
978
981
979 def appendfile(path, text):
982 def appendfile(path, text):
980 fp = open(path, 'ab')
983 fp = open(path, 'ab')
981 try:
984 try:
982 fp.write(text)
985 fp.write(text)
983 finally:
986 finally:
984 fp.close()
987 fp.close()
985
988
986 class chunkbuffer(object):
989 class chunkbuffer(object):
987 """Allow arbitrary sized chunks of data to be efficiently read from an
990 """Allow arbitrary sized chunks of data to be efficiently read from an
988 iterator over chunks of arbitrary size."""
991 iterator over chunks of arbitrary size."""
989
992
990 def __init__(self, in_iter):
993 def __init__(self, in_iter):
991 """in_iter is the iterator that's iterating over the input chunks.
994 """in_iter is the iterator that's iterating over the input chunks.
992 targetsize is how big a buffer to try to maintain."""
995 targetsize is how big a buffer to try to maintain."""
993 def splitbig(chunks):
996 def splitbig(chunks):
994 for chunk in chunks:
997 for chunk in chunks:
995 if len(chunk) > 2**20:
998 if len(chunk) > 2**20:
996 pos = 0
999 pos = 0
997 while pos < len(chunk):
1000 while pos < len(chunk):
998 end = pos + 2 ** 18
1001 end = pos + 2 ** 18
999 yield chunk[pos:end]
1002 yield chunk[pos:end]
1000 pos = end
1003 pos = end
1001 else:
1004 else:
1002 yield chunk
1005 yield chunk
1003 self.iter = splitbig(in_iter)
1006 self.iter = splitbig(in_iter)
1004 self._queue = deque()
1007 self._queue = deque()
1005
1008
1006 def read(self, l=None):
1009 def read(self, l=None):
1007 """Read L bytes of data from the iterator of chunks of data.
1010 """Read L bytes of data from the iterator of chunks of data.
1008 Returns less than L bytes if the iterator runs dry.
1011 Returns less than L bytes if the iterator runs dry.
1009
1012
1010 If size parameter is ommited, read everything"""
1013 If size parameter is ommited, read everything"""
1011 left = l
1014 left = l
1012 buf = []
1015 buf = []
1013 queue = self._queue
1016 queue = self._queue
1014 while left is None or left > 0:
1017 while left is None or left > 0:
1015 # refill the queue
1018 # refill the queue
1016 if not queue:
1019 if not queue:
1017 target = 2**18
1020 target = 2**18
1018 for chunk in self.iter:
1021 for chunk in self.iter:
1019 queue.append(chunk)
1022 queue.append(chunk)
1020 target -= len(chunk)
1023 target -= len(chunk)
1021 if target <= 0:
1024 if target <= 0:
1022 break
1025 break
1023 if not queue:
1026 if not queue:
1024 break
1027 break
1025
1028
1026 chunk = queue.popleft()
1029 chunk = queue.popleft()
1027 if left is not None:
1030 if left is not None:
1028 left -= len(chunk)
1031 left -= len(chunk)
1029 if left is not None and left < 0:
1032 if left is not None and left < 0:
1030 queue.appendleft(chunk[left:])
1033 queue.appendleft(chunk[left:])
1031 buf.append(chunk[:left])
1034 buf.append(chunk[:left])
1032 else:
1035 else:
1033 buf.append(chunk)
1036 buf.append(chunk)
1034
1037
1035 return ''.join(buf)
1038 return ''.join(buf)
1036
1039
1037 def filechunkiter(f, size=65536, limit=None):
1040 def filechunkiter(f, size=65536, limit=None):
1038 """Create a generator that produces the data in the file size
1041 """Create a generator that produces the data in the file size
1039 (default 65536) bytes at a time, up to optional limit (default is
1042 (default 65536) bytes at a time, up to optional limit (default is
1040 to read all data). Chunks may be less than size bytes if the
1043 to read all data). Chunks may be less than size bytes if the
1041 chunk is the last chunk in the file, or the file is a socket or
1044 chunk is the last chunk in the file, or the file is a socket or
1042 some other type of file that sometimes reads less data than is
1045 some other type of file that sometimes reads less data than is
1043 requested."""
1046 requested."""
1044 assert size >= 0
1047 assert size >= 0
1045 assert limit is None or limit >= 0
1048 assert limit is None or limit >= 0
1046 while True:
1049 while True:
1047 if limit is None:
1050 if limit is None:
1048 nbytes = size
1051 nbytes = size
1049 else:
1052 else:
1050 nbytes = min(limit, size)
1053 nbytes = min(limit, size)
1051 s = nbytes and f.read(nbytes)
1054 s = nbytes and f.read(nbytes)
1052 if not s:
1055 if not s:
1053 break
1056 break
1054 if limit:
1057 if limit:
1055 limit -= len(s)
1058 limit -= len(s)
1056 yield s
1059 yield s
1057
1060
1058 def makedate(timestamp=None):
1061 def makedate(timestamp=None):
1059 '''Return a unix timestamp (or the current time) as a (unixtime,
1062 '''Return a unix timestamp (or the current time) as a (unixtime,
1060 offset) tuple based off the local timezone.'''
1063 offset) tuple based off the local timezone.'''
1061 if timestamp is None:
1064 if timestamp is None:
1062 timestamp = time.time()
1065 timestamp = time.time()
1063 if timestamp < 0:
1066 if timestamp < 0:
1064 hint = _("check your clock")
1067 hint = _("check your clock")
1065 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1068 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1066 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1069 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1067 datetime.datetime.fromtimestamp(timestamp))
1070 datetime.datetime.fromtimestamp(timestamp))
1068 tz = delta.days * 86400 + delta.seconds
1071 tz = delta.days * 86400 + delta.seconds
1069 return timestamp, tz
1072 return timestamp, tz
1070
1073
1071 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1074 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1072 """represent a (unixtime, offset) tuple as a localized time.
1075 """represent a (unixtime, offset) tuple as a localized time.
1073 unixtime is seconds since the epoch, and offset is the time zone's
1076 unixtime is seconds since the epoch, and offset is the time zone's
1074 number of seconds away from UTC. if timezone is false, do not
1077 number of seconds away from UTC. if timezone is false, do not
1075 append time zone to string."""
1078 append time zone to string."""
1076 t, tz = date or makedate()
1079 t, tz = date or makedate()
1077 if t < 0:
1080 if t < 0:
1078 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1081 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1079 tz = 0
1082 tz = 0
1080 if "%1" in format or "%2" in format or "%z" in format:
1083 if "%1" in format or "%2" in format or "%z" in format:
1081 sign = (tz > 0) and "-" or "+"
1084 sign = (tz > 0) and "-" or "+"
1082 minutes = abs(tz) // 60
1085 minutes = abs(tz) // 60
1083 format = format.replace("%z", "%1%2")
1086 format = format.replace("%z", "%1%2")
1084 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1087 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1085 format = format.replace("%2", "%02d" % (minutes % 60))
1088 format = format.replace("%2", "%02d" % (minutes % 60))
1086 try:
1089 try:
1087 t = time.gmtime(float(t) - tz)
1090 t = time.gmtime(float(t) - tz)
1088 except ValueError:
1091 except ValueError:
1089 # time was out of range
1092 # time was out of range
1090 t = time.gmtime(sys.maxint)
1093 t = time.gmtime(sys.maxint)
1091 s = time.strftime(format, t)
1094 s = time.strftime(format, t)
1092 return s
1095 return s
1093
1096
1094 def shortdate(date=None):
1097 def shortdate(date=None):
1095 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1098 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1096 return datestr(date, format='%Y-%m-%d')
1099 return datestr(date, format='%Y-%m-%d')
1097
1100
1098 def strdate(string, format, defaults=[]):
1101 def strdate(string, format, defaults=[]):
1099 """parse a localized time string and return a (unixtime, offset) tuple.
1102 """parse a localized time string and return a (unixtime, offset) tuple.
1100 if the string cannot be parsed, ValueError is raised."""
1103 if the string cannot be parsed, ValueError is raised."""
1101 def timezone(string):
1104 def timezone(string):
1102 tz = string.split()[-1]
1105 tz = string.split()[-1]
1103 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1106 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1104 sign = (tz[0] == "+") and 1 or -1
1107 sign = (tz[0] == "+") and 1 or -1
1105 hours = int(tz[1:3])
1108 hours = int(tz[1:3])
1106 minutes = int(tz[3:5])
1109 minutes = int(tz[3:5])
1107 return -sign * (hours * 60 + minutes) * 60
1110 return -sign * (hours * 60 + minutes) * 60
1108 if tz == "GMT" or tz == "UTC":
1111 if tz == "GMT" or tz == "UTC":
1109 return 0
1112 return 0
1110 return None
1113 return None
1111
1114
1112 # NOTE: unixtime = localunixtime + offset
1115 # NOTE: unixtime = localunixtime + offset
1113 offset, date = timezone(string), string
1116 offset, date = timezone(string), string
1114 if offset is not None:
1117 if offset is not None:
1115 date = " ".join(string.split()[:-1])
1118 date = " ".join(string.split()[:-1])
1116
1119
1117 # add missing elements from defaults
1120 # add missing elements from defaults
1118 usenow = False # default to using biased defaults
1121 usenow = False # default to using biased defaults
1119 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1122 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1120 found = [True for p in part if ("%"+p) in format]
1123 found = [True for p in part if ("%"+p) in format]
1121 if not found:
1124 if not found:
1122 date += "@" + defaults[part][usenow]
1125 date += "@" + defaults[part][usenow]
1123 format += "@%" + part[0]
1126 format += "@%" + part[0]
1124 else:
1127 else:
1125 # We've found a specific time element, less specific time
1128 # We've found a specific time element, less specific time
1126 # elements are relative to today
1129 # elements are relative to today
1127 usenow = True
1130 usenow = True
1128
1131
1129 timetuple = time.strptime(date, format)
1132 timetuple = time.strptime(date, format)
1130 localunixtime = int(calendar.timegm(timetuple))
1133 localunixtime = int(calendar.timegm(timetuple))
1131 if offset is None:
1134 if offset is None:
1132 # local timezone
1135 # local timezone
1133 unixtime = int(time.mktime(timetuple))
1136 unixtime = int(time.mktime(timetuple))
1134 offset = unixtime - localunixtime
1137 offset = unixtime - localunixtime
1135 else:
1138 else:
1136 unixtime = localunixtime + offset
1139 unixtime = localunixtime + offset
1137 return unixtime, offset
1140 return unixtime, offset
1138
1141
1139 def parsedate(date, formats=None, bias={}):
1142 def parsedate(date, formats=None, bias={}):
1140 """parse a localized date/time and return a (unixtime, offset) tuple.
1143 """parse a localized date/time and return a (unixtime, offset) tuple.
1141
1144
1142 The date may be a "unixtime offset" string or in one of the specified
1145 The date may be a "unixtime offset" string or in one of the specified
1143 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1146 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1144
1147
1145 >>> parsedate(' today ') == parsedate(\
1148 >>> parsedate(' today ') == parsedate(\
1146 datetime.date.today().strftime('%b %d'))
1149 datetime.date.today().strftime('%b %d'))
1147 True
1150 True
1148 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1151 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1149 datetime.timedelta(days=1)\
1152 datetime.timedelta(days=1)\
1150 ).strftime('%b %d'))
1153 ).strftime('%b %d'))
1151 True
1154 True
1152 >>> now, tz = makedate()
1155 >>> now, tz = makedate()
1153 >>> strnow, strtz = parsedate('now')
1156 >>> strnow, strtz = parsedate('now')
1154 >>> (strnow - now) < 1
1157 >>> (strnow - now) < 1
1155 True
1158 True
1156 >>> tz == strtz
1159 >>> tz == strtz
1157 True
1160 True
1158 """
1161 """
1159 if not date:
1162 if not date:
1160 return 0, 0
1163 return 0, 0
1161 if isinstance(date, tuple) and len(date) == 2:
1164 if isinstance(date, tuple) and len(date) == 2:
1162 return date
1165 return date
1163 if not formats:
1166 if not formats:
1164 formats = defaultdateformats
1167 formats = defaultdateformats
1165 date = date.strip()
1168 date = date.strip()
1166
1169
1167 if date == _('now'):
1170 if date == _('now'):
1168 return makedate()
1171 return makedate()
1169 if date == _('today'):
1172 if date == _('today'):
1170 date = datetime.date.today().strftime('%b %d')
1173 date = datetime.date.today().strftime('%b %d')
1171 elif date == _('yesterday'):
1174 elif date == _('yesterday'):
1172 date = (datetime.date.today() -
1175 date = (datetime.date.today() -
1173 datetime.timedelta(days=1)).strftime('%b %d')
1176 datetime.timedelta(days=1)).strftime('%b %d')
1174
1177
1175 try:
1178 try:
1176 when, offset = map(int, date.split(' '))
1179 when, offset = map(int, date.split(' '))
1177 except ValueError:
1180 except ValueError:
1178 # fill out defaults
1181 # fill out defaults
1179 now = makedate()
1182 now = makedate()
1180 defaults = {}
1183 defaults = {}
1181 for part in ("d", "mb", "yY", "HI", "M", "S"):
1184 for part in ("d", "mb", "yY", "HI", "M", "S"):
1182 # this piece is for rounding the specific end of unknowns
1185 # this piece is for rounding the specific end of unknowns
1183 b = bias.get(part)
1186 b = bias.get(part)
1184 if b is None:
1187 if b is None:
1185 if part[0] in "HMS":
1188 if part[0] in "HMS":
1186 b = "00"
1189 b = "00"
1187 else:
1190 else:
1188 b = "0"
1191 b = "0"
1189
1192
1190 # this piece is for matching the generic end to today's date
1193 # this piece is for matching the generic end to today's date
1191 n = datestr(now, "%" + part[0])
1194 n = datestr(now, "%" + part[0])
1192
1195
1193 defaults[part] = (b, n)
1196 defaults[part] = (b, n)
1194
1197
1195 for format in formats:
1198 for format in formats:
1196 try:
1199 try:
1197 when, offset = strdate(date, format, defaults)
1200 when, offset = strdate(date, format, defaults)
1198 except (ValueError, OverflowError):
1201 except (ValueError, OverflowError):
1199 pass
1202 pass
1200 else:
1203 else:
1201 break
1204 break
1202 else:
1205 else:
1203 raise Abort(_('invalid date: %r') % date)
1206 raise Abort(_('invalid date: %r') % date)
1204 # validate explicit (probably user-specified) date and
1207 # validate explicit (probably user-specified) date and
1205 # time zone offset. values must fit in signed 32 bits for
1208 # time zone offset. values must fit in signed 32 bits for
1206 # current 32-bit linux runtimes. timezones go from UTC-12
1209 # current 32-bit linux runtimes. timezones go from UTC-12
1207 # to UTC+14
1210 # to UTC+14
1208 if abs(when) > 0x7fffffff:
1211 if abs(when) > 0x7fffffff:
1209 raise Abort(_('date exceeds 32 bits: %d') % when)
1212 raise Abort(_('date exceeds 32 bits: %d') % when)
1210 if when < 0:
1213 if when < 0:
1211 raise Abort(_('negative date value: %d') % when)
1214 raise Abort(_('negative date value: %d') % when)
1212 if offset < -50400 or offset > 43200:
1215 if offset < -50400 or offset > 43200:
1213 raise Abort(_('impossible time zone offset: %d') % offset)
1216 raise Abort(_('impossible time zone offset: %d') % offset)
1214 return when, offset
1217 return when, offset
1215
1218
1216 def matchdate(date):
1219 def matchdate(date):
1217 """Return a function that matches a given date match specifier
1220 """Return a function that matches a given date match specifier
1218
1221
1219 Formats include:
1222 Formats include:
1220
1223
1221 '{date}' match a given date to the accuracy provided
1224 '{date}' match a given date to the accuracy provided
1222
1225
1223 '<{date}' on or before a given date
1226 '<{date}' on or before a given date
1224
1227
1225 '>{date}' on or after a given date
1228 '>{date}' on or after a given date
1226
1229
1227 >>> p1 = parsedate("10:29:59")
1230 >>> p1 = parsedate("10:29:59")
1228 >>> p2 = parsedate("10:30:00")
1231 >>> p2 = parsedate("10:30:00")
1229 >>> p3 = parsedate("10:30:59")
1232 >>> p3 = parsedate("10:30:59")
1230 >>> p4 = parsedate("10:31:00")
1233 >>> p4 = parsedate("10:31:00")
1231 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1234 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1232 >>> f = matchdate("10:30")
1235 >>> f = matchdate("10:30")
1233 >>> f(p1[0])
1236 >>> f(p1[0])
1234 False
1237 False
1235 >>> f(p2[0])
1238 >>> f(p2[0])
1236 True
1239 True
1237 >>> f(p3[0])
1240 >>> f(p3[0])
1238 True
1241 True
1239 >>> f(p4[0])
1242 >>> f(p4[0])
1240 False
1243 False
1241 >>> f(p5[0])
1244 >>> f(p5[0])
1242 False
1245 False
1243 """
1246 """
1244
1247
1245 def lower(date):
1248 def lower(date):
1246 d = {'mb': "1", 'd': "1"}
1249 d = {'mb': "1", 'd': "1"}
1247 return parsedate(date, extendeddateformats, d)[0]
1250 return parsedate(date, extendeddateformats, d)[0]
1248
1251
1249 def upper(date):
1252 def upper(date):
1250 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1253 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1251 for days in ("31", "30", "29"):
1254 for days in ("31", "30", "29"):
1252 try:
1255 try:
1253 d["d"] = days
1256 d["d"] = days
1254 return parsedate(date, extendeddateformats, d)[0]
1257 return parsedate(date, extendeddateformats, d)[0]
1255 except Abort:
1258 except Abort:
1256 pass
1259 pass
1257 d["d"] = "28"
1260 d["d"] = "28"
1258 return parsedate(date, extendeddateformats, d)[0]
1261 return parsedate(date, extendeddateformats, d)[0]
1259
1262
1260 date = date.strip()
1263 date = date.strip()
1261
1264
1262 if not date:
1265 if not date:
1263 raise Abort(_("dates cannot consist entirely of whitespace"))
1266 raise Abort(_("dates cannot consist entirely of whitespace"))
1264 elif date[0] == "<":
1267 elif date[0] == "<":
1265 if not date[1:]:
1268 if not date[1:]:
1266 raise Abort(_("invalid day spec, use '<DATE'"))
1269 raise Abort(_("invalid day spec, use '<DATE'"))
1267 when = upper(date[1:])
1270 when = upper(date[1:])
1268 return lambda x: x <= when
1271 return lambda x: x <= when
1269 elif date[0] == ">":
1272 elif date[0] == ">":
1270 if not date[1:]:
1273 if not date[1:]:
1271 raise Abort(_("invalid day spec, use '>DATE'"))
1274 raise Abort(_("invalid day spec, use '>DATE'"))
1272 when = lower(date[1:])
1275 when = lower(date[1:])
1273 return lambda x: x >= when
1276 return lambda x: x >= when
1274 elif date[0] == "-":
1277 elif date[0] == "-":
1275 try:
1278 try:
1276 days = int(date[1:])
1279 days = int(date[1:])
1277 except ValueError:
1280 except ValueError:
1278 raise Abort(_("invalid day spec: %s") % date[1:])
1281 raise Abort(_("invalid day spec: %s") % date[1:])
1279 if days < 0:
1282 if days < 0:
1280 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1283 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1281 % date[1:])
1284 % date[1:])
1282 when = makedate()[0] - days * 3600 * 24
1285 when = makedate()[0] - days * 3600 * 24
1283 return lambda x: x >= when
1286 return lambda x: x >= when
1284 elif " to " in date:
1287 elif " to " in date:
1285 a, b = date.split(" to ")
1288 a, b = date.split(" to ")
1286 start, stop = lower(a), upper(b)
1289 start, stop = lower(a), upper(b)
1287 return lambda x: x >= start and x <= stop
1290 return lambda x: x >= start and x <= stop
1288 else:
1291 else:
1289 start, stop = lower(date), upper(date)
1292 start, stop = lower(date), upper(date)
1290 return lambda x: x >= start and x <= stop
1293 return lambda x: x >= start and x <= stop
1291
1294
1292 def shortuser(user):
1295 def shortuser(user):
1293 """Return a short representation of a user name or email address."""
1296 """Return a short representation of a user name or email address."""
1294 f = user.find('@')
1297 f = user.find('@')
1295 if f >= 0:
1298 if f >= 0:
1296 user = user[:f]
1299 user = user[:f]
1297 f = user.find('<')
1300 f = user.find('<')
1298 if f >= 0:
1301 if f >= 0:
1299 user = user[f + 1:]
1302 user = user[f + 1:]
1300 f = user.find(' ')
1303 f = user.find(' ')
1301 if f >= 0:
1304 if f >= 0:
1302 user = user[:f]
1305 user = user[:f]
1303 f = user.find('.')
1306 f = user.find('.')
1304 if f >= 0:
1307 if f >= 0:
1305 user = user[:f]
1308 user = user[:f]
1306 return user
1309 return user
1307
1310
1308 def emailuser(user):
1311 def emailuser(user):
1309 """Return the user portion of an email address."""
1312 """Return the user portion of an email address."""
1310 f = user.find('@')
1313 f = user.find('@')
1311 if f >= 0:
1314 if f >= 0:
1312 user = user[:f]
1315 user = user[:f]
1313 f = user.find('<')
1316 f = user.find('<')
1314 if f >= 0:
1317 if f >= 0:
1315 user = user[f + 1:]
1318 user = user[f + 1:]
1316 return user
1319 return user
1317
1320
1318 def email(author):
1321 def email(author):
1319 '''get email of author.'''
1322 '''get email of author.'''
1320 r = author.find('>')
1323 r = author.find('>')
1321 if r == -1:
1324 if r == -1:
1322 r = None
1325 r = None
1323 return author[author.find('<') + 1:r]
1326 return author[author.find('<') + 1:r]
1324
1327
1325 def ellipsis(text, maxlength=400):
1328 def ellipsis(text, maxlength=400):
1326 """Trim string to at most maxlength (default: 400) columns in display."""
1329 """Trim string to at most maxlength (default: 400) columns in display."""
1327 return encoding.trim(text, maxlength, ellipsis='...')
1330 return encoding.trim(text, maxlength, ellipsis='...')
1328
1331
1329 def unitcountfn(*unittable):
1332 def unitcountfn(*unittable):
1330 '''return a function that renders a readable count of some quantity'''
1333 '''return a function that renders a readable count of some quantity'''
1331
1334
1332 def go(count):
1335 def go(count):
1333 for multiplier, divisor, format in unittable:
1336 for multiplier, divisor, format in unittable:
1334 if count >= divisor * multiplier:
1337 if count >= divisor * multiplier:
1335 return format % (count / float(divisor))
1338 return format % (count / float(divisor))
1336 return unittable[-1][2] % count
1339 return unittable[-1][2] % count
1337
1340
1338 return go
1341 return go
1339
1342
1340 bytecount = unitcountfn(
1343 bytecount = unitcountfn(
1341 (100, 1 << 30, _('%.0f GB')),
1344 (100, 1 << 30, _('%.0f GB')),
1342 (10, 1 << 30, _('%.1f GB')),
1345 (10, 1 << 30, _('%.1f GB')),
1343 (1, 1 << 30, _('%.2f GB')),
1346 (1, 1 << 30, _('%.2f GB')),
1344 (100, 1 << 20, _('%.0f MB')),
1347 (100, 1 << 20, _('%.0f MB')),
1345 (10, 1 << 20, _('%.1f MB')),
1348 (10, 1 << 20, _('%.1f MB')),
1346 (1, 1 << 20, _('%.2f MB')),
1349 (1, 1 << 20, _('%.2f MB')),
1347 (100, 1 << 10, _('%.0f KB')),
1350 (100, 1 << 10, _('%.0f KB')),
1348 (10, 1 << 10, _('%.1f KB')),
1351 (10, 1 << 10, _('%.1f KB')),
1349 (1, 1 << 10, _('%.2f KB')),
1352 (1, 1 << 10, _('%.2f KB')),
1350 (1, 1, _('%.0f bytes')),
1353 (1, 1, _('%.0f bytes')),
1351 )
1354 )
1352
1355
1353 def uirepr(s):
1356 def uirepr(s):
1354 # Avoid double backslash in Windows path repr()
1357 # Avoid double backslash in Windows path repr()
1355 return repr(s).replace('\\\\', '\\')
1358 return repr(s).replace('\\\\', '\\')
1356
1359
1357 # delay import of textwrap
1360 # delay import of textwrap
1358 def MBTextWrapper(**kwargs):
1361 def MBTextWrapper(**kwargs):
1359 class tw(textwrap.TextWrapper):
1362 class tw(textwrap.TextWrapper):
1360 """
1363 """
1361 Extend TextWrapper for width-awareness.
1364 Extend TextWrapper for width-awareness.
1362
1365
1363 Neither number of 'bytes' in any encoding nor 'characters' is
1366 Neither number of 'bytes' in any encoding nor 'characters' is
1364 appropriate to calculate terminal columns for specified string.
1367 appropriate to calculate terminal columns for specified string.
1365
1368
1366 Original TextWrapper implementation uses built-in 'len()' directly,
1369 Original TextWrapper implementation uses built-in 'len()' directly,
1367 so overriding is needed to use width information of each characters.
1370 so overriding is needed to use width information of each characters.
1368
1371
1369 In addition, characters classified into 'ambiguous' width are
1372 In addition, characters classified into 'ambiguous' width are
1370 treated as wide in East Asian area, but as narrow in other.
1373 treated as wide in East Asian area, but as narrow in other.
1371
1374
1372 This requires use decision to determine width of such characters.
1375 This requires use decision to determine width of such characters.
1373 """
1376 """
1374 def __init__(self, **kwargs):
1377 def __init__(self, **kwargs):
1375 textwrap.TextWrapper.__init__(self, **kwargs)
1378 textwrap.TextWrapper.__init__(self, **kwargs)
1376
1379
1377 # for compatibility between 2.4 and 2.6
1380 # for compatibility between 2.4 and 2.6
1378 if getattr(self, 'drop_whitespace', None) is None:
1381 if getattr(self, 'drop_whitespace', None) is None:
1379 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1382 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1380
1383
1381 def _cutdown(self, ucstr, space_left):
1384 def _cutdown(self, ucstr, space_left):
1382 l = 0
1385 l = 0
1383 colwidth = encoding.ucolwidth
1386 colwidth = encoding.ucolwidth
1384 for i in xrange(len(ucstr)):
1387 for i in xrange(len(ucstr)):
1385 l += colwidth(ucstr[i])
1388 l += colwidth(ucstr[i])
1386 if space_left < l:
1389 if space_left < l:
1387 return (ucstr[:i], ucstr[i:])
1390 return (ucstr[:i], ucstr[i:])
1388 return ucstr, ''
1391 return ucstr, ''
1389
1392
1390 # overriding of base class
1393 # overriding of base class
1391 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1394 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1392 space_left = max(width - cur_len, 1)
1395 space_left = max(width - cur_len, 1)
1393
1396
1394 if self.break_long_words:
1397 if self.break_long_words:
1395 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1398 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1396 cur_line.append(cut)
1399 cur_line.append(cut)
1397 reversed_chunks[-1] = res
1400 reversed_chunks[-1] = res
1398 elif not cur_line:
1401 elif not cur_line:
1399 cur_line.append(reversed_chunks.pop())
1402 cur_line.append(reversed_chunks.pop())
1400
1403
1401 # this overriding code is imported from TextWrapper of python 2.6
1404 # this overriding code is imported from TextWrapper of python 2.6
1402 # to calculate columns of string by 'encoding.ucolwidth()'
1405 # to calculate columns of string by 'encoding.ucolwidth()'
1403 def _wrap_chunks(self, chunks):
1406 def _wrap_chunks(self, chunks):
1404 colwidth = encoding.ucolwidth
1407 colwidth = encoding.ucolwidth
1405
1408
1406 lines = []
1409 lines = []
1407 if self.width <= 0:
1410 if self.width <= 0:
1408 raise ValueError("invalid width %r (must be > 0)" % self.width)
1411 raise ValueError("invalid width %r (must be > 0)" % self.width)
1409
1412
1410 # Arrange in reverse order so items can be efficiently popped
1413 # Arrange in reverse order so items can be efficiently popped
1411 # from a stack of chucks.
1414 # from a stack of chucks.
1412 chunks.reverse()
1415 chunks.reverse()
1413
1416
1414 while chunks:
1417 while chunks:
1415
1418
1416 # Start the list of chunks that will make up the current line.
1419 # Start the list of chunks that will make up the current line.
1417 # cur_len is just the length of all the chunks in cur_line.
1420 # cur_len is just the length of all the chunks in cur_line.
1418 cur_line = []
1421 cur_line = []
1419 cur_len = 0
1422 cur_len = 0
1420
1423
1421 # Figure out which static string will prefix this line.
1424 # Figure out which static string will prefix this line.
1422 if lines:
1425 if lines:
1423 indent = self.subsequent_indent
1426 indent = self.subsequent_indent
1424 else:
1427 else:
1425 indent = self.initial_indent
1428 indent = self.initial_indent
1426
1429
1427 # Maximum width for this line.
1430 # Maximum width for this line.
1428 width = self.width - len(indent)
1431 width = self.width - len(indent)
1429
1432
1430 # First chunk on line is whitespace -- drop it, unless this
1433 # First chunk on line is whitespace -- drop it, unless this
1431 # is the very beginning of the text (i.e. no lines started yet).
1434 # is the very beginning of the text (i.e. no lines started yet).
1432 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1435 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1433 del chunks[-1]
1436 del chunks[-1]
1434
1437
1435 while chunks:
1438 while chunks:
1436 l = colwidth(chunks[-1])
1439 l = colwidth(chunks[-1])
1437
1440
1438 # Can at least squeeze this chunk onto the current line.
1441 # Can at least squeeze this chunk onto the current line.
1439 if cur_len + l <= width:
1442 if cur_len + l <= width:
1440 cur_line.append(chunks.pop())
1443 cur_line.append(chunks.pop())
1441 cur_len += l
1444 cur_len += l
1442
1445
1443 # Nope, this line is full.
1446 # Nope, this line is full.
1444 else:
1447 else:
1445 break
1448 break
1446
1449
1447 # The current line is full, and the next chunk is too big to
1450 # The current line is full, and the next chunk is too big to
1448 # fit on *any* line (not just this one).
1451 # fit on *any* line (not just this one).
1449 if chunks and colwidth(chunks[-1]) > width:
1452 if chunks and colwidth(chunks[-1]) > width:
1450 self._handle_long_word(chunks, cur_line, cur_len, width)
1453 self._handle_long_word(chunks, cur_line, cur_len, width)
1451
1454
1452 # If the last chunk on this line is all whitespace, drop it.
1455 # If the last chunk on this line is all whitespace, drop it.
1453 if (self.drop_whitespace and
1456 if (self.drop_whitespace and
1454 cur_line and cur_line[-1].strip() == ''):
1457 cur_line and cur_line[-1].strip() == ''):
1455 del cur_line[-1]
1458 del cur_line[-1]
1456
1459
1457 # Convert current line back to a string and store it in list
1460 # Convert current line back to a string and store it in list
1458 # of all lines (return value).
1461 # of all lines (return value).
1459 if cur_line:
1462 if cur_line:
1460 lines.append(indent + ''.join(cur_line))
1463 lines.append(indent + ''.join(cur_line))
1461
1464
1462 return lines
1465 return lines
1463
1466
1464 global MBTextWrapper
1467 global MBTextWrapper
1465 MBTextWrapper = tw
1468 MBTextWrapper = tw
1466 return tw(**kwargs)
1469 return tw(**kwargs)
1467
1470
1468 def wrap(line, width, initindent='', hangindent=''):
1471 def wrap(line, width, initindent='', hangindent=''):
1469 maxindent = max(len(hangindent), len(initindent))
1472 maxindent = max(len(hangindent), len(initindent))
1470 if width <= maxindent:
1473 if width <= maxindent:
1471 # adjust for weird terminal size
1474 # adjust for weird terminal size
1472 width = max(78, maxindent + 1)
1475 width = max(78, maxindent + 1)
1473 line = line.decode(encoding.encoding, encoding.encodingmode)
1476 line = line.decode(encoding.encoding, encoding.encodingmode)
1474 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1477 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1475 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1478 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1476 wrapper = MBTextWrapper(width=width,
1479 wrapper = MBTextWrapper(width=width,
1477 initial_indent=initindent,
1480 initial_indent=initindent,
1478 subsequent_indent=hangindent)
1481 subsequent_indent=hangindent)
1479 return wrapper.fill(line).encode(encoding.encoding)
1482 return wrapper.fill(line).encode(encoding.encoding)
1480
1483
1481 def iterlines(iterator):
1484 def iterlines(iterator):
1482 for chunk in iterator:
1485 for chunk in iterator:
1483 for line in chunk.splitlines():
1486 for line in chunk.splitlines():
1484 yield line
1487 yield line
1485
1488
1486 def expandpath(path):
1489 def expandpath(path):
1487 return os.path.expanduser(os.path.expandvars(path))
1490 return os.path.expanduser(os.path.expandvars(path))
1488
1491
1489 def hgcmd():
1492 def hgcmd():
1490 """Return the command used to execute current hg
1493 """Return the command used to execute current hg
1491
1494
1492 This is different from hgexecutable() because on Windows we want
1495 This is different from hgexecutable() because on Windows we want
1493 to avoid things opening new shell windows like batch files, so we
1496 to avoid things opening new shell windows like batch files, so we
1494 get either the python call or current executable.
1497 get either the python call or current executable.
1495 """
1498 """
1496 if mainfrozen():
1499 if mainfrozen():
1497 return [sys.executable]
1500 return [sys.executable]
1498 return gethgcmd()
1501 return gethgcmd()
1499
1502
1500 def rundetached(args, condfn):
1503 def rundetached(args, condfn):
1501 """Execute the argument list in a detached process.
1504 """Execute the argument list in a detached process.
1502
1505
1503 condfn is a callable which is called repeatedly and should return
1506 condfn is a callable which is called repeatedly and should return
1504 True once the child process is known to have started successfully.
1507 True once the child process is known to have started successfully.
1505 At this point, the child process PID is returned. If the child
1508 At this point, the child process PID is returned. If the child
1506 process fails to start or finishes before condfn() evaluates to
1509 process fails to start or finishes before condfn() evaluates to
1507 True, return -1.
1510 True, return -1.
1508 """
1511 """
1509 # Windows case is easier because the child process is either
1512 # Windows case is easier because the child process is either
1510 # successfully starting and validating the condition or exiting
1513 # successfully starting and validating the condition or exiting
1511 # on failure. We just poll on its PID. On Unix, if the child
1514 # on failure. We just poll on its PID. On Unix, if the child
1512 # process fails to start, it will be left in a zombie state until
1515 # process fails to start, it will be left in a zombie state until
1513 # the parent wait on it, which we cannot do since we expect a long
1516 # the parent wait on it, which we cannot do since we expect a long
1514 # running process on success. Instead we listen for SIGCHLD telling
1517 # running process on success. Instead we listen for SIGCHLD telling
1515 # us our child process terminated.
1518 # us our child process terminated.
1516 terminated = set()
1519 terminated = set()
1517 def handler(signum, frame):
1520 def handler(signum, frame):
1518 terminated.add(os.wait())
1521 terminated.add(os.wait())
1519 prevhandler = None
1522 prevhandler = None
1520 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1523 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1521 if SIGCHLD is not None:
1524 if SIGCHLD is not None:
1522 prevhandler = signal.signal(SIGCHLD, handler)
1525 prevhandler = signal.signal(SIGCHLD, handler)
1523 try:
1526 try:
1524 pid = spawndetached(args)
1527 pid = spawndetached(args)
1525 while not condfn():
1528 while not condfn():
1526 if ((pid in terminated or not testpid(pid))
1529 if ((pid in terminated or not testpid(pid))
1527 and not condfn()):
1530 and not condfn()):
1528 return -1
1531 return -1
1529 time.sleep(0.1)
1532 time.sleep(0.1)
1530 return pid
1533 return pid
1531 finally:
1534 finally:
1532 if prevhandler is not None:
1535 if prevhandler is not None:
1533 signal.signal(signal.SIGCHLD, prevhandler)
1536 signal.signal(signal.SIGCHLD, prevhandler)
1534
1537
1535 try:
1538 try:
1536 any, all = any, all
1539 any, all = any, all
1537 except NameError:
1540 except NameError:
1538 def any(iterable):
1541 def any(iterable):
1539 for i in iterable:
1542 for i in iterable:
1540 if i:
1543 if i:
1541 return True
1544 return True
1542 return False
1545 return False
1543
1546
1544 def all(iterable):
1547 def all(iterable):
1545 for i in iterable:
1548 for i in iterable:
1546 if not i:
1549 if not i:
1547 return False
1550 return False
1548 return True
1551 return True
1549
1552
1550 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1553 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1551 """Return the result of interpolating items in the mapping into string s.
1554 """Return the result of interpolating items in the mapping into string s.
1552
1555
1553 prefix is a single character string, or a two character string with
1556 prefix is a single character string, or a two character string with
1554 a backslash as the first character if the prefix needs to be escaped in
1557 a backslash as the first character if the prefix needs to be escaped in
1555 a regular expression.
1558 a regular expression.
1556
1559
1557 fn is an optional function that will be applied to the replacement text
1560 fn is an optional function that will be applied to the replacement text
1558 just before replacement.
1561 just before replacement.
1559
1562
1560 escape_prefix is an optional flag that allows using doubled prefix for
1563 escape_prefix is an optional flag that allows using doubled prefix for
1561 its escaping.
1564 its escaping.
1562 """
1565 """
1563 fn = fn or (lambda s: s)
1566 fn = fn or (lambda s: s)
1564 patterns = '|'.join(mapping.keys())
1567 patterns = '|'.join(mapping.keys())
1565 if escape_prefix:
1568 if escape_prefix:
1566 patterns += '|' + prefix
1569 patterns += '|' + prefix
1567 if len(prefix) > 1:
1570 if len(prefix) > 1:
1568 prefix_char = prefix[1:]
1571 prefix_char = prefix[1:]
1569 else:
1572 else:
1570 prefix_char = prefix
1573 prefix_char = prefix
1571 mapping[prefix_char] = prefix_char
1574 mapping[prefix_char] = prefix_char
1572 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1575 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1573 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1576 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1574
1577
1575 def getport(port):
1578 def getport(port):
1576 """Return the port for a given network service.
1579 """Return the port for a given network service.
1577
1580
1578 If port is an integer, it's returned as is. If it's a string, it's
1581 If port is an integer, it's returned as is. If it's a string, it's
1579 looked up using socket.getservbyname(). If there's no matching
1582 looked up using socket.getservbyname(). If there's no matching
1580 service, util.Abort is raised.
1583 service, util.Abort is raised.
1581 """
1584 """
1582 try:
1585 try:
1583 return int(port)
1586 return int(port)
1584 except ValueError:
1587 except ValueError:
1585 pass
1588 pass
1586
1589
1587 try:
1590 try:
1588 return socket.getservbyname(port)
1591 return socket.getservbyname(port)
1589 except socket.error:
1592 except socket.error:
1590 raise Abort(_("no port number associated with service '%s'") % port)
1593 raise Abort(_("no port number associated with service '%s'") % port)
1591
1594
1592 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1595 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1593 '0': False, 'no': False, 'false': False, 'off': False,
1596 '0': False, 'no': False, 'false': False, 'off': False,
1594 'never': False}
1597 'never': False}
1595
1598
1596 def parsebool(s):
1599 def parsebool(s):
1597 """Parse s into a boolean.
1600 """Parse s into a boolean.
1598
1601
1599 If s is not a valid boolean, returns None.
1602 If s is not a valid boolean, returns None.
1600 """
1603 """
1601 return _booleans.get(s.lower(), None)
1604 return _booleans.get(s.lower(), None)
1602
1605
1603 _hexdig = '0123456789ABCDEFabcdef'
1606 _hexdig = '0123456789ABCDEFabcdef'
1604 _hextochr = dict((a + b, chr(int(a + b, 16)))
1607 _hextochr = dict((a + b, chr(int(a + b, 16)))
1605 for a in _hexdig for b in _hexdig)
1608 for a in _hexdig for b in _hexdig)
1606
1609
1607 def _urlunquote(s):
1610 def _urlunquote(s):
1608 """Decode HTTP/HTML % encoding.
1611 """Decode HTTP/HTML % encoding.
1609
1612
1610 >>> _urlunquote('abc%20def')
1613 >>> _urlunquote('abc%20def')
1611 'abc def'
1614 'abc def'
1612 """
1615 """
1613 res = s.split('%')
1616 res = s.split('%')
1614 # fastpath
1617 # fastpath
1615 if len(res) == 1:
1618 if len(res) == 1:
1616 return s
1619 return s
1617 s = res[0]
1620 s = res[0]
1618 for item in res[1:]:
1621 for item in res[1:]:
1619 try:
1622 try:
1620 s += _hextochr[item[:2]] + item[2:]
1623 s += _hextochr[item[:2]] + item[2:]
1621 except KeyError:
1624 except KeyError:
1622 s += '%' + item
1625 s += '%' + item
1623 except UnicodeDecodeError:
1626 except UnicodeDecodeError:
1624 s += unichr(int(item[:2], 16)) + item[2:]
1627 s += unichr(int(item[:2], 16)) + item[2:]
1625 return s
1628 return s
1626
1629
1627 class url(object):
1630 class url(object):
1628 r"""Reliable URL parser.
1631 r"""Reliable URL parser.
1629
1632
1630 This parses URLs and provides attributes for the following
1633 This parses URLs and provides attributes for the following
1631 components:
1634 components:
1632
1635
1633 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1636 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1634
1637
1635 Missing components are set to None. The only exception is
1638 Missing components are set to None. The only exception is
1636 fragment, which is set to '' if present but empty.
1639 fragment, which is set to '' if present but empty.
1637
1640
1638 If parsefragment is False, fragment is included in query. If
1641 If parsefragment is False, fragment is included in query. If
1639 parsequery is False, query is included in path. If both are
1642 parsequery is False, query is included in path. If both are
1640 False, both fragment and query are included in path.
1643 False, both fragment and query are included in path.
1641
1644
1642 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1645 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1643
1646
1644 Note that for backward compatibility reasons, bundle URLs do not
1647 Note that for backward compatibility reasons, bundle URLs do not
1645 take host names. That means 'bundle://../' has a path of '../'.
1648 take host names. That means 'bundle://../' has a path of '../'.
1646
1649
1647 Examples:
1650 Examples:
1648
1651
1649 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1652 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1650 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1653 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1651 >>> url('ssh://[::1]:2200//home/joe/repo')
1654 >>> url('ssh://[::1]:2200//home/joe/repo')
1652 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1655 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1653 >>> url('file:///home/joe/repo')
1656 >>> url('file:///home/joe/repo')
1654 <url scheme: 'file', path: '/home/joe/repo'>
1657 <url scheme: 'file', path: '/home/joe/repo'>
1655 >>> url('file:///c:/temp/foo/')
1658 >>> url('file:///c:/temp/foo/')
1656 <url scheme: 'file', path: 'c:/temp/foo/'>
1659 <url scheme: 'file', path: 'c:/temp/foo/'>
1657 >>> url('bundle:foo')
1660 >>> url('bundle:foo')
1658 <url scheme: 'bundle', path: 'foo'>
1661 <url scheme: 'bundle', path: 'foo'>
1659 >>> url('bundle://../foo')
1662 >>> url('bundle://../foo')
1660 <url scheme: 'bundle', path: '../foo'>
1663 <url scheme: 'bundle', path: '../foo'>
1661 >>> url(r'c:\foo\bar')
1664 >>> url(r'c:\foo\bar')
1662 <url path: 'c:\\foo\\bar'>
1665 <url path: 'c:\\foo\\bar'>
1663 >>> url(r'\\blah\blah\blah')
1666 >>> url(r'\\blah\blah\blah')
1664 <url path: '\\\\blah\\blah\\blah'>
1667 <url path: '\\\\blah\\blah\\blah'>
1665 >>> url(r'\\blah\blah\blah#baz')
1668 >>> url(r'\\blah\blah\blah#baz')
1666 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1669 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1667 >>> url(r'file:///C:\users\me')
1670 >>> url(r'file:///C:\users\me')
1668 <url scheme: 'file', path: 'C:\\users\\me'>
1671 <url scheme: 'file', path: 'C:\\users\\me'>
1669
1672
1670 Authentication credentials:
1673 Authentication credentials:
1671
1674
1672 >>> url('ssh://joe:xyz@x/repo')
1675 >>> url('ssh://joe:xyz@x/repo')
1673 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1676 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1674 >>> url('ssh://joe@x/repo')
1677 >>> url('ssh://joe@x/repo')
1675 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1678 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1676
1679
1677 Query strings and fragments:
1680 Query strings and fragments:
1678
1681
1679 >>> url('http://host/a?b#c')
1682 >>> url('http://host/a?b#c')
1680 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1683 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1681 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1684 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1682 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1685 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1683 """
1686 """
1684
1687
1685 _safechars = "!~*'()+"
1688 _safechars = "!~*'()+"
1686 _safepchars = "/!~*'()+:\\"
1689 _safepchars = "/!~*'()+:\\"
1687 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
1690 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
1688
1691
1689 def __init__(self, path, parsequery=True, parsefragment=True):
1692 def __init__(self, path, parsequery=True, parsefragment=True):
1690 # We slowly chomp away at path until we have only the path left
1693 # We slowly chomp away at path until we have only the path left
1691 self.scheme = self.user = self.passwd = self.host = None
1694 self.scheme = self.user = self.passwd = self.host = None
1692 self.port = self.path = self.query = self.fragment = None
1695 self.port = self.path = self.query = self.fragment = None
1693 self._localpath = True
1696 self._localpath = True
1694 self._hostport = ''
1697 self._hostport = ''
1695 self._origpath = path
1698 self._origpath = path
1696
1699
1697 if parsefragment and '#' in path:
1700 if parsefragment and '#' in path:
1698 path, self.fragment = path.split('#', 1)
1701 path, self.fragment = path.split('#', 1)
1699 if not path:
1702 if not path:
1700 path = None
1703 path = None
1701
1704
1702 # special case for Windows drive letters and UNC paths
1705 # special case for Windows drive letters and UNC paths
1703 if hasdriveletter(path) or path.startswith(r'\\'):
1706 if hasdriveletter(path) or path.startswith(r'\\'):
1704 self.path = path
1707 self.path = path
1705 return
1708 return
1706
1709
1707 # For compatibility reasons, we can't handle bundle paths as
1710 # For compatibility reasons, we can't handle bundle paths as
1708 # normal URLS
1711 # normal URLS
1709 if path.startswith('bundle:'):
1712 if path.startswith('bundle:'):
1710 self.scheme = 'bundle'
1713 self.scheme = 'bundle'
1711 path = path[7:]
1714 path = path[7:]
1712 if path.startswith('//'):
1715 if path.startswith('//'):
1713 path = path[2:]
1716 path = path[2:]
1714 self.path = path
1717 self.path = path
1715 return
1718 return
1716
1719
1717 if self._matchscheme(path):
1720 if self._matchscheme(path):
1718 parts = path.split(':', 1)
1721 parts = path.split(':', 1)
1719 if parts[0]:
1722 if parts[0]:
1720 self.scheme, path = parts
1723 self.scheme, path = parts
1721 self._localpath = False
1724 self._localpath = False
1722
1725
1723 if not path:
1726 if not path:
1724 path = None
1727 path = None
1725 if self._localpath:
1728 if self._localpath:
1726 self.path = ''
1729 self.path = ''
1727 return
1730 return
1728 else:
1731 else:
1729 if self._localpath:
1732 if self._localpath:
1730 self.path = path
1733 self.path = path
1731 return
1734 return
1732
1735
1733 if parsequery and '?' in path:
1736 if parsequery and '?' in path:
1734 path, self.query = path.split('?', 1)
1737 path, self.query = path.split('?', 1)
1735 if not path:
1738 if not path:
1736 path = None
1739 path = None
1737 if not self.query:
1740 if not self.query:
1738 self.query = None
1741 self.query = None
1739
1742
1740 # // is required to specify a host/authority
1743 # // is required to specify a host/authority
1741 if path and path.startswith('//'):
1744 if path and path.startswith('//'):
1742 parts = path[2:].split('/', 1)
1745 parts = path[2:].split('/', 1)
1743 if len(parts) > 1:
1746 if len(parts) > 1:
1744 self.host, path = parts
1747 self.host, path = parts
1745 else:
1748 else:
1746 self.host = parts[0]
1749 self.host = parts[0]
1747 path = None
1750 path = None
1748 if not self.host:
1751 if not self.host:
1749 self.host = None
1752 self.host = None
1750 # path of file:///d is /d
1753 # path of file:///d is /d
1751 # path of file:///d:/ is d:/, not /d:/
1754 # path of file:///d:/ is d:/, not /d:/
1752 if path and not hasdriveletter(path):
1755 if path and not hasdriveletter(path):
1753 path = '/' + path
1756 path = '/' + path
1754
1757
1755 if self.host and '@' in self.host:
1758 if self.host and '@' in self.host:
1756 self.user, self.host = self.host.rsplit('@', 1)
1759 self.user, self.host = self.host.rsplit('@', 1)
1757 if ':' in self.user:
1760 if ':' in self.user:
1758 self.user, self.passwd = self.user.split(':', 1)
1761 self.user, self.passwd = self.user.split(':', 1)
1759 if not self.host:
1762 if not self.host:
1760 self.host = None
1763 self.host = None
1761
1764
1762 # Don't split on colons in IPv6 addresses without ports
1765 # Don't split on colons in IPv6 addresses without ports
1763 if (self.host and ':' in self.host and
1766 if (self.host and ':' in self.host and
1764 not (self.host.startswith('[') and self.host.endswith(']'))):
1767 not (self.host.startswith('[') and self.host.endswith(']'))):
1765 self._hostport = self.host
1768 self._hostport = self.host
1766 self.host, self.port = self.host.rsplit(':', 1)
1769 self.host, self.port = self.host.rsplit(':', 1)
1767 if not self.host:
1770 if not self.host:
1768 self.host = None
1771 self.host = None
1769
1772
1770 if (self.host and self.scheme == 'file' and
1773 if (self.host and self.scheme == 'file' and
1771 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1774 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1772 raise Abort(_('file:// URLs can only refer to localhost'))
1775 raise Abort(_('file:// URLs can only refer to localhost'))
1773
1776
1774 self.path = path
1777 self.path = path
1775
1778
1776 # leave the query string escaped
1779 # leave the query string escaped
1777 for a in ('user', 'passwd', 'host', 'port',
1780 for a in ('user', 'passwd', 'host', 'port',
1778 'path', 'fragment'):
1781 'path', 'fragment'):
1779 v = getattr(self, a)
1782 v = getattr(self, a)
1780 if v is not None:
1783 if v is not None:
1781 setattr(self, a, _urlunquote(v))
1784 setattr(self, a, _urlunquote(v))
1782
1785
1783 def __repr__(self):
1786 def __repr__(self):
1784 attrs = []
1787 attrs = []
1785 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1788 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1786 'query', 'fragment'):
1789 'query', 'fragment'):
1787 v = getattr(self, a)
1790 v = getattr(self, a)
1788 if v is not None:
1791 if v is not None:
1789 attrs.append('%s: %r' % (a, v))
1792 attrs.append('%s: %r' % (a, v))
1790 return '<url %s>' % ', '.join(attrs)
1793 return '<url %s>' % ', '.join(attrs)
1791
1794
1792 def __str__(self):
1795 def __str__(self):
1793 r"""Join the URL's components back into a URL string.
1796 r"""Join the URL's components back into a URL string.
1794
1797
1795 Examples:
1798 Examples:
1796
1799
1797 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1800 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1798 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
1801 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
1799 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1802 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1800 'http://user:pw@host:80/?foo=bar&baz=42'
1803 'http://user:pw@host:80/?foo=bar&baz=42'
1801 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1804 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1802 'http://user:pw@host:80/?foo=bar%3dbaz'
1805 'http://user:pw@host:80/?foo=bar%3dbaz'
1803 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1806 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1804 'ssh://user:pw@[::1]:2200//home/joe#'
1807 'ssh://user:pw@[::1]:2200//home/joe#'
1805 >>> str(url('http://localhost:80//'))
1808 >>> str(url('http://localhost:80//'))
1806 'http://localhost:80//'
1809 'http://localhost:80//'
1807 >>> str(url('http://localhost:80/'))
1810 >>> str(url('http://localhost:80/'))
1808 'http://localhost:80/'
1811 'http://localhost:80/'
1809 >>> str(url('http://localhost:80'))
1812 >>> str(url('http://localhost:80'))
1810 'http://localhost:80/'
1813 'http://localhost:80/'
1811 >>> str(url('bundle:foo'))
1814 >>> str(url('bundle:foo'))
1812 'bundle:foo'
1815 'bundle:foo'
1813 >>> str(url('bundle://../foo'))
1816 >>> str(url('bundle://../foo'))
1814 'bundle:../foo'
1817 'bundle:../foo'
1815 >>> str(url('path'))
1818 >>> str(url('path'))
1816 'path'
1819 'path'
1817 >>> str(url('file:///tmp/foo/bar'))
1820 >>> str(url('file:///tmp/foo/bar'))
1818 'file:///tmp/foo/bar'
1821 'file:///tmp/foo/bar'
1819 >>> str(url('file:///c:/tmp/foo/bar'))
1822 >>> str(url('file:///c:/tmp/foo/bar'))
1820 'file:///c:/tmp/foo/bar'
1823 'file:///c:/tmp/foo/bar'
1821 >>> print url(r'bundle:foo\bar')
1824 >>> print url(r'bundle:foo\bar')
1822 bundle:foo\bar
1825 bundle:foo\bar
1823 >>> print url(r'file:///D:\data\hg')
1826 >>> print url(r'file:///D:\data\hg')
1824 file:///D:\data\hg
1827 file:///D:\data\hg
1825 """
1828 """
1826 if self._localpath:
1829 if self._localpath:
1827 s = self.path
1830 s = self.path
1828 if self.scheme == 'bundle':
1831 if self.scheme == 'bundle':
1829 s = 'bundle:' + s
1832 s = 'bundle:' + s
1830 if self.fragment:
1833 if self.fragment:
1831 s += '#' + self.fragment
1834 s += '#' + self.fragment
1832 return s
1835 return s
1833
1836
1834 s = self.scheme + ':'
1837 s = self.scheme + ':'
1835 if self.user or self.passwd or self.host:
1838 if self.user or self.passwd or self.host:
1836 s += '//'
1839 s += '//'
1837 elif self.scheme and (not self.path or self.path.startswith('/')
1840 elif self.scheme and (not self.path or self.path.startswith('/')
1838 or hasdriveletter(self.path)):
1841 or hasdriveletter(self.path)):
1839 s += '//'
1842 s += '//'
1840 if hasdriveletter(self.path):
1843 if hasdriveletter(self.path):
1841 s += '/'
1844 s += '/'
1842 if self.user:
1845 if self.user:
1843 s += urllib.quote(self.user, safe=self._safechars)
1846 s += urllib.quote(self.user, safe=self._safechars)
1844 if self.passwd:
1847 if self.passwd:
1845 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1848 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1846 if self.user or self.passwd:
1849 if self.user or self.passwd:
1847 s += '@'
1850 s += '@'
1848 if self.host:
1851 if self.host:
1849 if not (self.host.startswith('[') and self.host.endswith(']')):
1852 if not (self.host.startswith('[') and self.host.endswith(']')):
1850 s += urllib.quote(self.host)
1853 s += urllib.quote(self.host)
1851 else:
1854 else:
1852 s += self.host
1855 s += self.host
1853 if self.port:
1856 if self.port:
1854 s += ':' + urllib.quote(self.port)
1857 s += ':' + urllib.quote(self.port)
1855 if self.host:
1858 if self.host:
1856 s += '/'
1859 s += '/'
1857 if self.path:
1860 if self.path:
1858 # TODO: similar to the query string, we should not unescape the
1861 # TODO: similar to the query string, we should not unescape the
1859 # path when we store it, the path might contain '%2f' = '/',
1862 # path when we store it, the path might contain '%2f' = '/',
1860 # which we should *not* escape.
1863 # which we should *not* escape.
1861 s += urllib.quote(self.path, safe=self._safepchars)
1864 s += urllib.quote(self.path, safe=self._safepchars)
1862 if self.query:
1865 if self.query:
1863 # we store the query in escaped form.
1866 # we store the query in escaped form.
1864 s += '?' + self.query
1867 s += '?' + self.query
1865 if self.fragment is not None:
1868 if self.fragment is not None:
1866 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1869 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1867 return s
1870 return s
1868
1871
1869 def authinfo(self):
1872 def authinfo(self):
1870 user, passwd = self.user, self.passwd
1873 user, passwd = self.user, self.passwd
1871 try:
1874 try:
1872 self.user, self.passwd = None, None
1875 self.user, self.passwd = None, None
1873 s = str(self)
1876 s = str(self)
1874 finally:
1877 finally:
1875 self.user, self.passwd = user, passwd
1878 self.user, self.passwd = user, passwd
1876 if not self.user:
1879 if not self.user:
1877 return (s, None)
1880 return (s, None)
1878 # authinfo[1] is passed to urllib2 password manager, and its
1881 # authinfo[1] is passed to urllib2 password manager, and its
1879 # URIs must not contain credentials. The host is passed in the
1882 # URIs must not contain credentials. The host is passed in the
1880 # URIs list because Python < 2.4.3 uses only that to search for
1883 # URIs list because Python < 2.4.3 uses only that to search for
1881 # a password.
1884 # a password.
1882 return (s, (None, (s, self.host),
1885 return (s, (None, (s, self.host),
1883 self.user, self.passwd or ''))
1886 self.user, self.passwd or ''))
1884
1887
1885 def isabs(self):
1888 def isabs(self):
1886 if self.scheme and self.scheme != 'file':
1889 if self.scheme and self.scheme != 'file':
1887 return True # remote URL
1890 return True # remote URL
1888 if hasdriveletter(self.path):
1891 if hasdriveletter(self.path):
1889 return True # absolute for our purposes - can't be joined()
1892 return True # absolute for our purposes - can't be joined()
1890 if self.path.startswith(r'\\'):
1893 if self.path.startswith(r'\\'):
1891 return True # Windows UNC path
1894 return True # Windows UNC path
1892 if self.path.startswith('/'):
1895 if self.path.startswith('/'):
1893 return True # POSIX-style
1896 return True # POSIX-style
1894 return False
1897 return False
1895
1898
1896 def localpath(self):
1899 def localpath(self):
1897 if self.scheme == 'file' or self.scheme == 'bundle':
1900 if self.scheme == 'file' or self.scheme == 'bundle':
1898 path = self.path or '/'
1901 path = self.path or '/'
1899 # For Windows, we need to promote hosts containing drive
1902 # For Windows, we need to promote hosts containing drive
1900 # letters to paths with drive letters.
1903 # letters to paths with drive letters.
1901 if hasdriveletter(self._hostport):
1904 if hasdriveletter(self._hostport):
1902 path = self._hostport + '/' + self.path
1905 path = self._hostport + '/' + self.path
1903 elif (self.host is not None and self.path
1906 elif (self.host is not None and self.path
1904 and not hasdriveletter(path)):
1907 and not hasdriveletter(path)):
1905 path = '/' + path
1908 path = '/' + path
1906 return path
1909 return path
1907 return self._origpath
1910 return self._origpath
1908
1911
1909 def islocal(self):
1912 def islocal(self):
1910 '''whether localpath will return something that posixfile can open'''
1913 '''whether localpath will return something that posixfile can open'''
1911 return (not self.scheme or self.scheme == 'file'
1914 return (not self.scheme or self.scheme == 'file'
1912 or self.scheme == 'bundle')
1915 or self.scheme == 'bundle')
1913
1916
1914 def hasscheme(path):
1917 def hasscheme(path):
1915 return bool(url(path).scheme)
1918 return bool(url(path).scheme)
1916
1919
1917 def hasdriveletter(path):
1920 def hasdriveletter(path):
1918 return path and path[1:2] == ':' and path[0:1].isalpha()
1921 return path and path[1:2] == ':' and path[0:1].isalpha()
1919
1922
1920 def urllocalpath(path):
1923 def urllocalpath(path):
1921 return url(path, parsequery=False, parsefragment=False).localpath()
1924 return url(path, parsequery=False, parsefragment=False).localpath()
1922
1925
1923 def hidepassword(u):
1926 def hidepassword(u):
1924 '''hide user credential in a url string'''
1927 '''hide user credential in a url string'''
1925 u = url(u)
1928 u = url(u)
1926 if u.passwd:
1929 if u.passwd:
1927 u.passwd = '***'
1930 u.passwd = '***'
1928 return str(u)
1931 return str(u)
1929
1932
1930 def removeauth(u):
1933 def removeauth(u):
1931 '''remove all authentication information from a url string'''
1934 '''remove all authentication information from a url string'''
1932 u = url(u)
1935 u = url(u)
1933 u.user = u.passwd = None
1936 u.user = u.passwd = None
1934 return str(u)
1937 return str(u)
1935
1938
1936 def isatty(fd):
1939 def isatty(fd):
1937 try:
1940 try:
1938 return fd.isatty()
1941 return fd.isatty()
1939 except AttributeError:
1942 except AttributeError:
1940 return False
1943 return False
1941
1944
1942 timecount = unitcountfn(
1945 timecount = unitcountfn(
1943 (1, 1e3, _('%.0f s')),
1946 (1, 1e3, _('%.0f s')),
1944 (100, 1, _('%.1f s')),
1947 (100, 1, _('%.1f s')),
1945 (10, 1, _('%.2f s')),
1948 (10, 1, _('%.2f s')),
1946 (1, 1, _('%.3f s')),
1949 (1, 1, _('%.3f s')),
1947 (100, 0.001, _('%.1f ms')),
1950 (100, 0.001, _('%.1f ms')),
1948 (10, 0.001, _('%.2f ms')),
1951 (10, 0.001, _('%.2f ms')),
1949 (1, 0.001, _('%.3f ms')),
1952 (1, 0.001, _('%.3f ms')),
1950 (100, 0.000001, _('%.1f us')),
1953 (100, 0.000001, _('%.1f us')),
1951 (10, 0.000001, _('%.2f us')),
1954 (10, 0.000001, _('%.2f us')),
1952 (1, 0.000001, _('%.3f us')),
1955 (1, 0.000001, _('%.3f us')),
1953 (100, 0.000000001, _('%.1f ns')),
1956 (100, 0.000000001, _('%.1f ns')),
1954 (10, 0.000000001, _('%.2f ns')),
1957 (10, 0.000000001, _('%.2f ns')),
1955 (1, 0.000000001, _('%.3f ns')),
1958 (1, 0.000000001, _('%.3f ns')),
1956 )
1959 )
1957
1960
1958 _timenesting = [0]
1961 _timenesting = [0]
1959
1962
1960 def timed(func):
1963 def timed(func):
1961 '''Report the execution time of a function call to stderr.
1964 '''Report the execution time of a function call to stderr.
1962
1965
1963 During development, use as a decorator when you need to measure
1966 During development, use as a decorator when you need to measure
1964 the cost of a function, e.g. as follows:
1967 the cost of a function, e.g. as follows:
1965
1968
1966 @util.timed
1969 @util.timed
1967 def foo(a, b, c):
1970 def foo(a, b, c):
1968 pass
1971 pass
1969 '''
1972 '''
1970
1973
1971 def wrapper(*args, **kwargs):
1974 def wrapper(*args, **kwargs):
1972 start = time.time()
1975 start = time.time()
1973 indent = 2
1976 indent = 2
1974 _timenesting[0] += indent
1977 _timenesting[0] += indent
1975 try:
1978 try:
1976 return func(*args, **kwargs)
1979 return func(*args, **kwargs)
1977 finally:
1980 finally:
1978 elapsed = time.time() - start
1981 elapsed = time.time() - start
1979 _timenesting[0] -= indent
1982 _timenesting[0] -= indent
1980 sys.stderr.write('%s%s: %s\n' %
1983 sys.stderr.write('%s%s: %s\n' %
1981 (' ' * _timenesting[0], func.__name__,
1984 (' ' * _timenesting[0], func.__name__,
1982 timecount(elapsed)))
1985 timecount(elapsed)))
1983 return wrapper
1986 return wrapper
1984
1987
1985 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
1988 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
1986 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
1989 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
1987
1990
1988 def sizetoint(s):
1991 def sizetoint(s):
1989 '''Convert a space specifier to a byte count.
1992 '''Convert a space specifier to a byte count.
1990
1993
1991 >>> sizetoint('30')
1994 >>> sizetoint('30')
1992 30
1995 30
1993 >>> sizetoint('2.2kb')
1996 >>> sizetoint('2.2kb')
1994 2252
1997 2252
1995 >>> sizetoint('6M')
1998 >>> sizetoint('6M')
1996 6291456
1999 6291456
1997 '''
2000 '''
1998 t = s.strip().lower()
2001 t = s.strip().lower()
1999 try:
2002 try:
2000 for k, u in _sizeunits:
2003 for k, u in _sizeunits:
2001 if t.endswith(k):
2004 if t.endswith(k):
2002 return int(float(t[:-len(k)]) * u)
2005 return int(float(t[:-len(k)]) * u)
2003 return int(t)
2006 return int(t)
2004 except ValueError:
2007 except ValueError:
2005 raise error.ParseError(_("couldn't parse size: %s") % s)
2008 raise error.ParseError(_("couldn't parse size: %s") % s)
2006
2009
2007 class hooks(object):
2010 class hooks(object):
2008 '''A collection of hook functions that can be used to extend a
2011 '''A collection of hook functions that can be used to extend a
2009 function's behaviour. Hooks are called in lexicographic order,
2012 function's behaviour. Hooks are called in lexicographic order,
2010 based on the names of their sources.'''
2013 based on the names of their sources.'''
2011
2014
2012 def __init__(self):
2015 def __init__(self):
2013 self._hooks = []
2016 self._hooks = []
2014
2017
2015 def add(self, source, hook):
2018 def add(self, source, hook):
2016 self._hooks.append((source, hook))
2019 self._hooks.append((source, hook))
2017
2020
2018 def __call__(self, *args):
2021 def __call__(self, *args):
2019 self._hooks.sort(key=lambda x: x[0])
2022 self._hooks.sort(key=lambda x: x[0])
2020 results = []
2023 results = []
2021 for source, hook in self._hooks:
2024 for source, hook in self._hooks:
2022 results.append(hook(*args))
2025 results.append(hook(*args))
2023 return results
2026 return results
2024
2027
2025 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2028 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2026 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2029 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2027 Skips the 'skip' last entries. By default it will flush stdout first.
2030 Skips the 'skip' last entries. By default it will flush stdout first.
2028 It can be used everywhere and do intentionally not require an ui object.
2031 It can be used everywhere and do intentionally not require an ui object.
2029 Not be used in production code but very convenient while developing.
2032 Not be used in production code but very convenient while developing.
2030 '''
2033 '''
2031 if otherf:
2034 if otherf:
2032 otherf.flush()
2035 otherf.flush()
2033 f.write('%s at:\n' % msg)
2036 f.write('%s at:\n' % msg)
2034 entries = [('%s:%s' % (fn, ln), func)
2037 entries = [('%s:%s' % (fn, ln), func)
2035 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2038 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2036 if entries:
2039 if entries:
2037 fnmax = max(len(entry[0]) for entry in entries)
2040 fnmax = max(len(entry[0]) for entry in entries)
2038 for fnln, func in entries:
2041 for fnln, func in entries:
2039 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2042 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2040 f.flush()
2043 f.flush()
2041
2044
2042 # convenient shortcut
2045 # convenient shortcut
2043 dst = debugstacktrace
2046 dst = debugstacktrace
General Comments 0
You need to be logged in to leave comments. Login now