##// END OF EJS Templates
util: move compilere to a class...
Siddharth Agarwal -
r21908:cad9dadc default
parent child Browse files
Show More
@@ -1,2040 +1,2044
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 def compilere(pat, flags=0):
719 class _re(object):
720 '''Compile a regular expression, using re2 if possible
720 def compile(self, pat, flags=0):
721 '''Compile a regular expression, using re2 if possible
721
722
722 For best performance, use only re2-compatible regexp features. The
723 For best performance, use only re2-compatible regexp features. The
723 only flags from the re module that are re2-compatible are
724 only flags from the re module that are re2-compatible are
724 IGNORECASE and MULTILINE.'''
725 IGNORECASE and MULTILINE.'''
725 global _re2
726 global _re2
726 if _re2 is None:
727 if _re2 is None:
727 try:
728 try:
728 # check if match works, see issue3964
729 # check if match works, see issue3964
729 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
730 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
730 except ImportError:
731 except ImportError:
731 _re2 = False
732 _re2 = False
732 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
733 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
733 if flags & remod.IGNORECASE:
734 if flags & remod.IGNORECASE:
734 pat = '(?i)' + pat
735 pat = '(?i)' + pat
735 if flags & remod.MULTILINE:
736 if flags & remod.MULTILINE:
736 pat = '(?m)' + pat
737 pat = '(?m)' + pat
737 try:
738 try:
738 return re2.compile(pat)
739 return re2.compile(pat)
739 except re2.error:
740 except re2.error:
740 pass
741 pass
741 return remod.compile(pat, flags)
742 return remod.compile(pat, flags)
743
744 re = _re()
745 compilere = re.compile
742
746
743 _fspathcache = {}
747 _fspathcache = {}
744 def fspath(name, root):
748 def fspath(name, root):
745 '''Get name in the case stored in the filesystem
749 '''Get name in the case stored in the filesystem
746
750
747 The name should be relative to root, and be normcase-ed for efficiency.
751 The name should be relative to root, and be normcase-ed for efficiency.
748
752
749 Note that this function is unnecessary, and should not be
753 Note that this function is unnecessary, and should not be
750 called, for case-sensitive filesystems (simply because it's expensive).
754 called, for case-sensitive filesystems (simply because it's expensive).
751
755
752 The root should be normcase-ed, too.
756 The root should be normcase-ed, too.
753 '''
757 '''
754 def find(p, contents):
758 def find(p, contents):
755 for n in contents:
759 for n in contents:
756 if normcase(n) == p:
760 if normcase(n) == p:
757 return n
761 return n
758 return None
762 return None
759
763
760 seps = os.sep
764 seps = os.sep
761 if os.altsep:
765 if os.altsep:
762 seps = seps + os.altsep
766 seps = seps + os.altsep
763 # Protect backslashes. This gets silly very quickly.
767 # Protect backslashes. This gets silly very quickly.
764 seps.replace('\\','\\\\')
768 seps.replace('\\','\\\\')
765 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
769 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
766 dir = os.path.normpath(root)
770 dir = os.path.normpath(root)
767 result = []
771 result = []
768 for part, sep in pattern.findall(name):
772 for part, sep in pattern.findall(name):
769 if sep:
773 if sep:
770 result.append(sep)
774 result.append(sep)
771 continue
775 continue
772
776
773 if dir not in _fspathcache:
777 if dir not in _fspathcache:
774 _fspathcache[dir] = os.listdir(dir)
778 _fspathcache[dir] = os.listdir(dir)
775 contents = _fspathcache[dir]
779 contents = _fspathcache[dir]
776
780
777 found = find(part, contents)
781 found = find(part, contents)
778 if not found:
782 if not found:
779 # retry "once per directory" per "dirstate.walk" which
783 # retry "once per directory" per "dirstate.walk" which
780 # may take place for each patches of "hg qpush", for example
784 # may take place for each patches of "hg qpush", for example
781 contents = os.listdir(dir)
785 contents = os.listdir(dir)
782 _fspathcache[dir] = contents
786 _fspathcache[dir] = contents
783 found = find(part, contents)
787 found = find(part, contents)
784
788
785 result.append(found or part)
789 result.append(found or part)
786 dir = os.path.join(dir, part)
790 dir = os.path.join(dir, part)
787
791
788 return ''.join(result)
792 return ''.join(result)
789
793
790 def checknlink(testfile):
794 def checknlink(testfile):
791 '''check whether hardlink count reporting works properly'''
795 '''check whether hardlink count reporting works properly'''
792
796
793 # testfile may be open, so we need a separate file for checking to
797 # testfile may be open, so we need a separate file for checking to
794 # work around issue2543 (or testfile may get lost on Samba shares)
798 # work around issue2543 (or testfile may get lost on Samba shares)
795 f1 = testfile + ".hgtmp1"
799 f1 = testfile + ".hgtmp1"
796 if os.path.lexists(f1):
800 if os.path.lexists(f1):
797 return False
801 return False
798 try:
802 try:
799 posixfile(f1, 'w').close()
803 posixfile(f1, 'w').close()
800 except IOError:
804 except IOError:
801 return False
805 return False
802
806
803 f2 = testfile + ".hgtmp2"
807 f2 = testfile + ".hgtmp2"
804 fd = None
808 fd = None
805 try:
809 try:
806 try:
810 try:
807 oslink(f1, f2)
811 oslink(f1, f2)
808 except OSError:
812 except OSError:
809 return False
813 return False
810
814
811 # nlinks() may behave differently for files on Windows shares if
815 # nlinks() may behave differently for files on Windows shares if
812 # the file is open.
816 # the file is open.
813 fd = posixfile(f2)
817 fd = posixfile(f2)
814 return nlinks(f2) > 1
818 return nlinks(f2) > 1
815 finally:
819 finally:
816 if fd is not None:
820 if fd is not None:
817 fd.close()
821 fd.close()
818 for f in (f1, f2):
822 for f in (f1, f2):
819 try:
823 try:
820 os.unlink(f)
824 os.unlink(f)
821 except OSError:
825 except OSError:
822 pass
826 pass
823
827
824 def endswithsep(path):
828 def endswithsep(path):
825 '''Check path ends with os.sep or os.altsep.'''
829 '''Check path ends with os.sep or os.altsep.'''
826 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
830 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
827
831
828 def splitpath(path):
832 def splitpath(path):
829 '''Split path by os.sep.
833 '''Split path by os.sep.
830 Note that this function does not use os.altsep because this is
834 Note that this function does not use os.altsep because this is
831 an alternative of simple "xxx.split(os.sep)".
835 an alternative of simple "xxx.split(os.sep)".
832 It is recommended to use os.path.normpath() before using this
836 It is recommended to use os.path.normpath() before using this
833 function if need.'''
837 function if need.'''
834 return path.split(os.sep)
838 return path.split(os.sep)
835
839
836 def gui():
840 def gui():
837 '''Are we running in a GUI?'''
841 '''Are we running in a GUI?'''
838 if sys.platform == 'darwin':
842 if sys.platform == 'darwin':
839 if 'SSH_CONNECTION' in os.environ:
843 if 'SSH_CONNECTION' in os.environ:
840 # handle SSH access to a box where the user is logged in
844 # handle SSH access to a box where the user is logged in
841 return False
845 return False
842 elif getattr(osutil, 'isgui', None):
846 elif getattr(osutil, 'isgui', None):
843 # check if a CoreGraphics session is available
847 # check if a CoreGraphics session is available
844 return osutil.isgui()
848 return osutil.isgui()
845 else:
849 else:
846 # pure build; use a safe default
850 # pure build; use a safe default
847 return True
851 return True
848 else:
852 else:
849 return os.name == "nt" or os.environ.get("DISPLAY")
853 return os.name == "nt" or os.environ.get("DISPLAY")
850
854
851 def mktempcopy(name, emptyok=False, createmode=None):
855 def mktempcopy(name, emptyok=False, createmode=None):
852 """Create a temporary file with the same contents from name
856 """Create a temporary file with the same contents from name
853
857
854 The permission bits are copied from the original file.
858 The permission bits are copied from the original file.
855
859
856 If the temporary file is going to be truncated immediately, you
860 If the temporary file is going to be truncated immediately, you
857 can use emptyok=True as an optimization.
861 can use emptyok=True as an optimization.
858
862
859 Returns the name of the temporary file.
863 Returns the name of the temporary file.
860 """
864 """
861 d, fn = os.path.split(name)
865 d, fn = os.path.split(name)
862 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
866 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
863 os.close(fd)
867 os.close(fd)
864 # Temporary files are created with mode 0600, which is usually not
868 # Temporary files are created with mode 0600, which is usually not
865 # what we want. If the original file already exists, just copy
869 # what we want. If the original file already exists, just copy
866 # its mode. Otherwise, manually obey umask.
870 # its mode. Otherwise, manually obey umask.
867 copymode(name, temp, createmode)
871 copymode(name, temp, createmode)
868 if emptyok:
872 if emptyok:
869 return temp
873 return temp
870 try:
874 try:
871 try:
875 try:
872 ifp = posixfile(name, "rb")
876 ifp = posixfile(name, "rb")
873 except IOError, inst:
877 except IOError, inst:
874 if inst.errno == errno.ENOENT:
878 if inst.errno == errno.ENOENT:
875 return temp
879 return temp
876 if not getattr(inst, 'filename', None):
880 if not getattr(inst, 'filename', None):
877 inst.filename = name
881 inst.filename = name
878 raise
882 raise
879 ofp = posixfile(temp, "wb")
883 ofp = posixfile(temp, "wb")
880 for chunk in filechunkiter(ifp):
884 for chunk in filechunkiter(ifp):
881 ofp.write(chunk)
885 ofp.write(chunk)
882 ifp.close()
886 ifp.close()
883 ofp.close()
887 ofp.close()
884 except: # re-raises
888 except: # re-raises
885 try: os.unlink(temp)
889 try: os.unlink(temp)
886 except OSError: pass
890 except OSError: pass
887 raise
891 raise
888 return temp
892 return temp
889
893
890 class atomictempfile(object):
894 class atomictempfile(object):
891 '''writable file object that atomically updates a file
895 '''writable file object that atomically updates a file
892
896
893 All writes will go to a temporary copy of the original file. Call
897 All writes will go to a temporary copy of the original file. Call
894 close() when you are done writing, and atomictempfile will rename
898 close() when you are done writing, and atomictempfile will rename
895 the temporary copy to the original name, making the changes
899 the temporary copy to the original name, making the changes
896 visible. If the object is destroyed without being closed, all your
900 visible. If the object is destroyed without being closed, all your
897 writes are discarded.
901 writes are discarded.
898 '''
902 '''
899 def __init__(self, name, mode='w+b', createmode=None):
903 def __init__(self, name, mode='w+b', createmode=None):
900 self.__name = name # permanent name
904 self.__name = name # permanent name
901 self._tempname = mktempcopy(name, emptyok=('w' in mode),
905 self._tempname = mktempcopy(name, emptyok=('w' in mode),
902 createmode=createmode)
906 createmode=createmode)
903 self._fp = posixfile(self._tempname, mode)
907 self._fp = posixfile(self._tempname, mode)
904
908
905 # delegated methods
909 # delegated methods
906 self.write = self._fp.write
910 self.write = self._fp.write
907 self.seek = self._fp.seek
911 self.seek = self._fp.seek
908 self.tell = self._fp.tell
912 self.tell = self._fp.tell
909 self.fileno = self._fp.fileno
913 self.fileno = self._fp.fileno
910
914
911 def close(self):
915 def close(self):
912 if not self._fp.closed:
916 if not self._fp.closed:
913 self._fp.close()
917 self._fp.close()
914 rename(self._tempname, localpath(self.__name))
918 rename(self._tempname, localpath(self.__name))
915
919
916 def discard(self):
920 def discard(self):
917 if not self._fp.closed:
921 if not self._fp.closed:
918 try:
922 try:
919 os.unlink(self._tempname)
923 os.unlink(self._tempname)
920 except OSError:
924 except OSError:
921 pass
925 pass
922 self._fp.close()
926 self._fp.close()
923
927
924 def __del__(self):
928 def __del__(self):
925 if safehasattr(self, '_fp'): # constructor actually did something
929 if safehasattr(self, '_fp'): # constructor actually did something
926 self.discard()
930 self.discard()
927
931
928 def makedirs(name, mode=None, notindexed=False):
932 def makedirs(name, mode=None, notindexed=False):
929 """recursive directory creation with parent mode inheritance"""
933 """recursive directory creation with parent mode inheritance"""
930 try:
934 try:
931 makedir(name, notindexed)
935 makedir(name, notindexed)
932 except OSError, err:
936 except OSError, err:
933 if err.errno == errno.EEXIST:
937 if err.errno == errno.EEXIST:
934 return
938 return
935 if err.errno != errno.ENOENT or not name:
939 if err.errno != errno.ENOENT or not name:
936 raise
940 raise
937 parent = os.path.dirname(os.path.abspath(name))
941 parent = os.path.dirname(os.path.abspath(name))
938 if parent == name:
942 if parent == name:
939 raise
943 raise
940 makedirs(parent, mode, notindexed)
944 makedirs(parent, mode, notindexed)
941 makedir(name, notindexed)
945 makedir(name, notindexed)
942 if mode is not None:
946 if mode is not None:
943 os.chmod(name, mode)
947 os.chmod(name, mode)
944
948
945 def ensuredirs(name, mode=None):
949 def ensuredirs(name, mode=None):
946 """race-safe recursive directory creation"""
950 """race-safe recursive directory creation"""
947 if os.path.isdir(name):
951 if os.path.isdir(name):
948 return
952 return
949 parent = os.path.dirname(os.path.abspath(name))
953 parent = os.path.dirname(os.path.abspath(name))
950 if parent != name:
954 if parent != name:
951 ensuredirs(parent, mode)
955 ensuredirs(parent, mode)
952 try:
956 try:
953 os.mkdir(name)
957 os.mkdir(name)
954 except OSError, err:
958 except OSError, err:
955 if err.errno == errno.EEXIST and os.path.isdir(name):
959 if err.errno == errno.EEXIST and os.path.isdir(name):
956 # someone else seems to have won a directory creation race
960 # someone else seems to have won a directory creation race
957 return
961 return
958 raise
962 raise
959 if mode is not None:
963 if mode is not None:
960 os.chmod(name, mode)
964 os.chmod(name, mode)
961
965
962 def readfile(path):
966 def readfile(path):
963 fp = open(path, 'rb')
967 fp = open(path, 'rb')
964 try:
968 try:
965 return fp.read()
969 return fp.read()
966 finally:
970 finally:
967 fp.close()
971 fp.close()
968
972
969 def writefile(path, text):
973 def writefile(path, text):
970 fp = open(path, 'wb')
974 fp = open(path, 'wb')
971 try:
975 try:
972 fp.write(text)
976 fp.write(text)
973 finally:
977 finally:
974 fp.close()
978 fp.close()
975
979
976 def appendfile(path, text):
980 def appendfile(path, text):
977 fp = open(path, 'ab')
981 fp = open(path, 'ab')
978 try:
982 try:
979 fp.write(text)
983 fp.write(text)
980 finally:
984 finally:
981 fp.close()
985 fp.close()
982
986
983 class chunkbuffer(object):
987 class chunkbuffer(object):
984 """Allow arbitrary sized chunks of data to be efficiently read from an
988 """Allow arbitrary sized chunks of data to be efficiently read from an
985 iterator over chunks of arbitrary size."""
989 iterator over chunks of arbitrary size."""
986
990
987 def __init__(self, in_iter):
991 def __init__(self, in_iter):
988 """in_iter is the iterator that's iterating over the input chunks.
992 """in_iter is the iterator that's iterating over the input chunks.
989 targetsize is how big a buffer to try to maintain."""
993 targetsize is how big a buffer to try to maintain."""
990 def splitbig(chunks):
994 def splitbig(chunks):
991 for chunk in chunks:
995 for chunk in chunks:
992 if len(chunk) > 2**20:
996 if len(chunk) > 2**20:
993 pos = 0
997 pos = 0
994 while pos < len(chunk):
998 while pos < len(chunk):
995 end = pos + 2 ** 18
999 end = pos + 2 ** 18
996 yield chunk[pos:end]
1000 yield chunk[pos:end]
997 pos = end
1001 pos = end
998 else:
1002 else:
999 yield chunk
1003 yield chunk
1000 self.iter = splitbig(in_iter)
1004 self.iter = splitbig(in_iter)
1001 self._queue = deque()
1005 self._queue = deque()
1002
1006
1003 def read(self, l=None):
1007 def read(self, l=None):
1004 """Read L bytes of data from the iterator of chunks of data.
1008 """Read L bytes of data from the iterator of chunks of data.
1005 Returns less than L bytes if the iterator runs dry.
1009 Returns less than L bytes if the iterator runs dry.
1006
1010
1007 If size parameter is ommited, read everything"""
1011 If size parameter is ommited, read everything"""
1008 left = l
1012 left = l
1009 buf = []
1013 buf = []
1010 queue = self._queue
1014 queue = self._queue
1011 while left is None or left > 0:
1015 while left is None or left > 0:
1012 # refill the queue
1016 # refill the queue
1013 if not queue:
1017 if not queue:
1014 target = 2**18
1018 target = 2**18
1015 for chunk in self.iter:
1019 for chunk in self.iter:
1016 queue.append(chunk)
1020 queue.append(chunk)
1017 target -= len(chunk)
1021 target -= len(chunk)
1018 if target <= 0:
1022 if target <= 0:
1019 break
1023 break
1020 if not queue:
1024 if not queue:
1021 break
1025 break
1022
1026
1023 chunk = queue.popleft()
1027 chunk = queue.popleft()
1024 if left is not None:
1028 if left is not None:
1025 left -= len(chunk)
1029 left -= len(chunk)
1026 if left is not None and left < 0:
1030 if left is not None and left < 0:
1027 queue.appendleft(chunk[left:])
1031 queue.appendleft(chunk[left:])
1028 buf.append(chunk[:left])
1032 buf.append(chunk[:left])
1029 else:
1033 else:
1030 buf.append(chunk)
1034 buf.append(chunk)
1031
1035
1032 return ''.join(buf)
1036 return ''.join(buf)
1033
1037
1034 def filechunkiter(f, size=65536, limit=None):
1038 def filechunkiter(f, size=65536, limit=None):
1035 """Create a generator that produces the data in the file size
1039 """Create a generator that produces the data in the file size
1036 (default 65536) bytes at a time, up to optional limit (default is
1040 (default 65536) bytes at a time, up to optional limit (default is
1037 to read all data). Chunks may be less than size bytes if the
1041 to read all data). Chunks may be less than size bytes if the
1038 chunk is the last chunk in the file, or the file is a socket or
1042 chunk is the last chunk in the file, or the file is a socket or
1039 some other type of file that sometimes reads less data than is
1043 some other type of file that sometimes reads less data than is
1040 requested."""
1044 requested."""
1041 assert size >= 0
1045 assert size >= 0
1042 assert limit is None or limit >= 0
1046 assert limit is None or limit >= 0
1043 while True:
1047 while True:
1044 if limit is None:
1048 if limit is None:
1045 nbytes = size
1049 nbytes = size
1046 else:
1050 else:
1047 nbytes = min(limit, size)
1051 nbytes = min(limit, size)
1048 s = nbytes and f.read(nbytes)
1052 s = nbytes and f.read(nbytes)
1049 if not s:
1053 if not s:
1050 break
1054 break
1051 if limit:
1055 if limit:
1052 limit -= len(s)
1056 limit -= len(s)
1053 yield s
1057 yield s
1054
1058
1055 def makedate(timestamp=None):
1059 def makedate(timestamp=None):
1056 '''Return a unix timestamp (or the current time) as a (unixtime,
1060 '''Return a unix timestamp (or the current time) as a (unixtime,
1057 offset) tuple based off the local timezone.'''
1061 offset) tuple based off the local timezone.'''
1058 if timestamp is None:
1062 if timestamp is None:
1059 timestamp = time.time()
1063 timestamp = time.time()
1060 if timestamp < 0:
1064 if timestamp < 0:
1061 hint = _("check your clock")
1065 hint = _("check your clock")
1062 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1066 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1063 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1067 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1064 datetime.datetime.fromtimestamp(timestamp))
1068 datetime.datetime.fromtimestamp(timestamp))
1065 tz = delta.days * 86400 + delta.seconds
1069 tz = delta.days * 86400 + delta.seconds
1066 return timestamp, tz
1070 return timestamp, tz
1067
1071
1068 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1072 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1069 """represent a (unixtime, offset) tuple as a localized time.
1073 """represent a (unixtime, offset) tuple as a localized time.
1070 unixtime is seconds since the epoch, and offset is the time zone's
1074 unixtime is seconds since the epoch, and offset is the time zone's
1071 number of seconds away from UTC. if timezone is false, do not
1075 number of seconds away from UTC. if timezone is false, do not
1072 append time zone to string."""
1076 append time zone to string."""
1073 t, tz = date or makedate()
1077 t, tz = date or makedate()
1074 if t < 0:
1078 if t < 0:
1075 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1079 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1076 tz = 0
1080 tz = 0
1077 if "%1" in format or "%2" in format or "%z" in format:
1081 if "%1" in format or "%2" in format or "%z" in format:
1078 sign = (tz > 0) and "-" or "+"
1082 sign = (tz > 0) and "-" or "+"
1079 minutes = abs(tz) // 60
1083 minutes = abs(tz) // 60
1080 format = format.replace("%z", "%1%2")
1084 format = format.replace("%z", "%1%2")
1081 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1085 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1082 format = format.replace("%2", "%02d" % (minutes % 60))
1086 format = format.replace("%2", "%02d" % (minutes % 60))
1083 try:
1087 try:
1084 t = time.gmtime(float(t) - tz)
1088 t = time.gmtime(float(t) - tz)
1085 except ValueError:
1089 except ValueError:
1086 # time was out of range
1090 # time was out of range
1087 t = time.gmtime(sys.maxint)
1091 t = time.gmtime(sys.maxint)
1088 s = time.strftime(format, t)
1092 s = time.strftime(format, t)
1089 return s
1093 return s
1090
1094
1091 def shortdate(date=None):
1095 def shortdate(date=None):
1092 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1096 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1093 return datestr(date, format='%Y-%m-%d')
1097 return datestr(date, format='%Y-%m-%d')
1094
1098
1095 def strdate(string, format, defaults=[]):
1099 def strdate(string, format, defaults=[]):
1096 """parse a localized time string and return a (unixtime, offset) tuple.
1100 """parse a localized time string and return a (unixtime, offset) tuple.
1097 if the string cannot be parsed, ValueError is raised."""
1101 if the string cannot be parsed, ValueError is raised."""
1098 def timezone(string):
1102 def timezone(string):
1099 tz = string.split()[-1]
1103 tz = string.split()[-1]
1100 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1104 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1101 sign = (tz[0] == "+") and 1 or -1
1105 sign = (tz[0] == "+") and 1 or -1
1102 hours = int(tz[1:3])
1106 hours = int(tz[1:3])
1103 minutes = int(tz[3:5])
1107 minutes = int(tz[3:5])
1104 return -sign * (hours * 60 + minutes) * 60
1108 return -sign * (hours * 60 + minutes) * 60
1105 if tz == "GMT" or tz == "UTC":
1109 if tz == "GMT" or tz == "UTC":
1106 return 0
1110 return 0
1107 return None
1111 return None
1108
1112
1109 # NOTE: unixtime = localunixtime + offset
1113 # NOTE: unixtime = localunixtime + offset
1110 offset, date = timezone(string), string
1114 offset, date = timezone(string), string
1111 if offset is not None:
1115 if offset is not None:
1112 date = " ".join(string.split()[:-1])
1116 date = " ".join(string.split()[:-1])
1113
1117
1114 # add missing elements from defaults
1118 # add missing elements from defaults
1115 usenow = False # default to using biased defaults
1119 usenow = False # default to using biased defaults
1116 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1120 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1117 found = [True for p in part if ("%"+p) in format]
1121 found = [True for p in part if ("%"+p) in format]
1118 if not found:
1122 if not found:
1119 date += "@" + defaults[part][usenow]
1123 date += "@" + defaults[part][usenow]
1120 format += "@%" + part[0]
1124 format += "@%" + part[0]
1121 else:
1125 else:
1122 # We've found a specific time element, less specific time
1126 # We've found a specific time element, less specific time
1123 # elements are relative to today
1127 # elements are relative to today
1124 usenow = True
1128 usenow = True
1125
1129
1126 timetuple = time.strptime(date, format)
1130 timetuple = time.strptime(date, format)
1127 localunixtime = int(calendar.timegm(timetuple))
1131 localunixtime = int(calendar.timegm(timetuple))
1128 if offset is None:
1132 if offset is None:
1129 # local timezone
1133 # local timezone
1130 unixtime = int(time.mktime(timetuple))
1134 unixtime = int(time.mktime(timetuple))
1131 offset = unixtime - localunixtime
1135 offset = unixtime - localunixtime
1132 else:
1136 else:
1133 unixtime = localunixtime + offset
1137 unixtime = localunixtime + offset
1134 return unixtime, offset
1138 return unixtime, offset
1135
1139
1136 def parsedate(date, formats=None, bias={}):
1140 def parsedate(date, formats=None, bias={}):
1137 """parse a localized date/time and return a (unixtime, offset) tuple.
1141 """parse a localized date/time and return a (unixtime, offset) tuple.
1138
1142
1139 The date may be a "unixtime offset" string or in one of the specified
1143 The date may be a "unixtime offset" string or in one of the specified
1140 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1144 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1141
1145
1142 >>> parsedate(' today ') == parsedate(\
1146 >>> parsedate(' today ') == parsedate(\
1143 datetime.date.today().strftime('%b %d'))
1147 datetime.date.today().strftime('%b %d'))
1144 True
1148 True
1145 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1149 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1146 datetime.timedelta(days=1)\
1150 datetime.timedelta(days=1)\
1147 ).strftime('%b %d'))
1151 ).strftime('%b %d'))
1148 True
1152 True
1149 >>> now, tz = makedate()
1153 >>> now, tz = makedate()
1150 >>> strnow, strtz = parsedate('now')
1154 >>> strnow, strtz = parsedate('now')
1151 >>> (strnow - now) < 1
1155 >>> (strnow - now) < 1
1152 True
1156 True
1153 >>> tz == strtz
1157 >>> tz == strtz
1154 True
1158 True
1155 """
1159 """
1156 if not date:
1160 if not date:
1157 return 0, 0
1161 return 0, 0
1158 if isinstance(date, tuple) and len(date) == 2:
1162 if isinstance(date, tuple) and len(date) == 2:
1159 return date
1163 return date
1160 if not formats:
1164 if not formats:
1161 formats = defaultdateformats
1165 formats = defaultdateformats
1162 date = date.strip()
1166 date = date.strip()
1163
1167
1164 if date == _('now'):
1168 if date == _('now'):
1165 return makedate()
1169 return makedate()
1166 if date == _('today'):
1170 if date == _('today'):
1167 date = datetime.date.today().strftime('%b %d')
1171 date = datetime.date.today().strftime('%b %d')
1168 elif date == _('yesterday'):
1172 elif date == _('yesterday'):
1169 date = (datetime.date.today() -
1173 date = (datetime.date.today() -
1170 datetime.timedelta(days=1)).strftime('%b %d')
1174 datetime.timedelta(days=1)).strftime('%b %d')
1171
1175
1172 try:
1176 try:
1173 when, offset = map(int, date.split(' '))
1177 when, offset = map(int, date.split(' '))
1174 except ValueError:
1178 except ValueError:
1175 # fill out defaults
1179 # fill out defaults
1176 now = makedate()
1180 now = makedate()
1177 defaults = {}
1181 defaults = {}
1178 for part in ("d", "mb", "yY", "HI", "M", "S"):
1182 for part in ("d", "mb", "yY", "HI", "M", "S"):
1179 # this piece is for rounding the specific end of unknowns
1183 # this piece is for rounding the specific end of unknowns
1180 b = bias.get(part)
1184 b = bias.get(part)
1181 if b is None:
1185 if b is None:
1182 if part[0] in "HMS":
1186 if part[0] in "HMS":
1183 b = "00"
1187 b = "00"
1184 else:
1188 else:
1185 b = "0"
1189 b = "0"
1186
1190
1187 # this piece is for matching the generic end to today's date
1191 # this piece is for matching the generic end to today's date
1188 n = datestr(now, "%" + part[0])
1192 n = datestr(now, "%" + part[0])
1189
1193
1190 defaults[part] = (b, n)
1194 defaults[part] = (b, n)
1191
1195
1192 for format in formats:
1196 for format in formats:
1193 try:
1197 try:
1194 when, offset = strdate(date, format, defaults)
1198 when, offset = strdate(date, format, defaults)
1195 except (ValueError, OverflowError):
1199 except (ValueError, OverflowError):
1196 pass
1200 pass
1197 else:
1201 else:
1198 break
1202 break
1199 else:
1203 else:
1200 raise Abort(_('invalid date: %r') % date)
1204 raise Abort(_('invalid date: %r') % date)
1201 # validate explicit (probably user-specified) date and
1205 # validate explicit (probably user-specified) date and
1202 # time zone offset. values must fit in signed 32 bits for
1206 # time zone offset. values must fit in signed 32 bits for
1203 # current 32-bit linux runtimes. timezones go from UTC-12
1207 # current 32-bit linux runtimes. timezones go from UTC-12
1204 # to UTC+14
1208 # to UTC+14
1205 if abs(when) > 0x7fffffff:
1209 if abs(when) > 0x7fffffff:
1206 raise Abort(_('date exceeds 32 bits: %d') % when)
1210 raise Abort(_('date exceeds 32 bits: %d') % when)
1207 if when < 0:
1211 if when < 0:
1208 raise Abort(_('negative date value: %d') % when)
1212 raise Abort(_('negative date value: %d') % when)
1209 if offset < -50400 or offset > 43200:
1213 if offset < -50400 or offset > 43200:
1210 raise Abort(_('impossible time zone offset: %d') % offset)
1214 raise Abort(_('impossible time zone offset: %d') % offset)
1211 return when, offset
1215 return when, offset
1212
1216
1213 def matchdate(date):
1217 def matchdate(date):
1214 """Return a function that matches a given date match specifier
1218 """Return a function that matches a given date match specifier
1215
1219
1216 Formats include:
1220 Formats include:
1217
1221
1218 '{date}' match a given date to the accuracy provided
1222 '{date}' match a given date to the accuracy provided
1219
1223
1220 '<{date}' on or before a given date
1224 '<{date}' on or before a given date
1221
1225
1222 '>{date}' on or after a given date
1226 '>{date}' on or after a given date
1223
1227
1224 >>> p1 = parsedate("10:29:59")
1228 >>> p1 = parsedate("10:29:59")
1225 >>> p2 = parsedate("10:30:00")
1229 >>> p2 = parsedate("10:30:00")
1226 >>> p3 = parsedate("10:30:59")
1230 >>> p3 = parsedate("10:30:59")
1227 >>> p4 = parsedate("10:31:00")
1231 >>> p4 = parsedate("10:31:00")
1228 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1232 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1229 >>> f = matchdate("10:30")
1233 >>> f = matchdate("10:30")
1230 >>> f(p1[0])
1234 >>> f(p1[0])
1231 False
1235 False
1232 >>> f(p2[0])
1236 >>> f(p2[0])
1233 True
1237 True
1234 >>> f(p3[0])
1238 >>> f(p3[0])
1235 True
1239 True
1236 >>> f(p4[0])
1240 >>> f(p4[0])
1237 False
1241 False
1238 >>> f(p5[0])
1242 >>> f(p5[0])
1239 False
1243 False
1240 """
1244 """
1241
1245
1242 def lower(date):
1246 def lower(date):
1243 d = {'mb': "1", 'd': "1"}
1247 d = {'mb': "1", 'd': "1"}
1244 return parsedate(date, extendeddateformats, d)[0]
1248 return parsedate(date, extendeddateformats, d)[0]
1245
1249
1246 def upper(date):
1250 def upper(date):
1247 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1251 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1248 for days in ("31", "30", "29"):
1252 for days in ("31", "30", "29"):
1249 try:
1253 try:
1250 d["d"] = days
1254 d["d"] = days
1251 return parsedate(date, extendeddateformats, d)[0]
1255 return parsedate(date, extendeddateformats, d)[0]
1252 except Abort:
1256 except Abort:
1253 pass
1257 pass
1254 d["d"] = "28"
1258 d["d"] = "28"
1255 return parsedate(date, extendeddateformats, d)[0]
1259 return parsedate(date, extendeddateformats, d)[0]
1256
1260
1257 date = date.strip()
1261 date = date.strip()
1258
1262
1259 if not date:
1263 if not date:
1260 raise Abort(_("dates cannot consist entirely of whitespace"))
1264 raise Abort(_("dates cannot consist entirely of whitespace"))
1261 elif date[0] == "<":
1265 elif date[0] == "<":
1262 if not date[1:]:
1266 if not date[1:]:
1263 raise Abort(_("invalid day spec, use '<DATE'"))
1267 raise Abort(_("invalid day spec, use '<DATE'"))
1264 when = upper(date[1:])
1268 when = upper(date[1:])
1265 return lambda x: x <= when
1269 return lambda x: x <= when
1266 elif date[0] == ">":
1270 elif date[0] == ">":
1267 if not date[1:]:
1271 if not date[1:]:
1268 raise Abort(_("invalid day spec, use '>DATE'"))
1272 raise Abort(_("invalid day spec, use '>DATE'"))
1269 when = lower(date[1:])
1273 when = lower(date[1:])
1270 return lambda x: x >= when
1274 return lambda x: x >= when
1271 elif date[0] == "-":
1275 elif date[0] == "-":
1272 try:
1276 try:
1273 days = int(date[1:])
1277 days = int(date[1:])
1274 except ValueError:
1278 except ValueError:
1275 raise Abort(_("invalid day spec: %s") % date[1:])
1279 raise Abort(_("invalid day spec: %s") % date[1:])
1276 if days < 0:
1280 if days < 0:
1277 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1281 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1278 % date[1:])
1282 % date[1:])
1279 when = makedate()[0] - days * 3600 * 24
1283 when = makedate()[0] - days * 3600 * 24
1280 return lambda x: x >= when
1284 return lambda x: x >= when
1281 elif " to " in date:
1285 elif " to " in date:
1282 a, b = date.split(" to ")
1286 a, b = date.split(" to ")
1283 start, stop = lower(a), upper(b)
1287 start, stop = lower(a), upper(b)
1284 return lambda x: x >= start and x <= stop
1288 return lambda x: x >= start and x <= stop
1285 else:
1289 else:
1286 start, stop = lower(date), upper(date)
1290 start, stop = lower(date), upper(date)
1287 return lambda x: x >= start and x <= stop
1291 return lambda x: x >= start and x <= stop
1288
1292
1289 def shortuser(user):
1293 def shortuser(user):
1290 """Return a short representation of a user name or email address."""
1294 """Return a short representation of a user name or email address."""
1291 f = user.find('@')
1295 f = user.find('@')
1292 if f >= 0:
1296 if f >= 0:
1293 user = user[:f]
1297 user = user[:f]
1294 f = user.find('<')
1298 f = user.find('<')
1295 if f >= 0:
1299 if f >= 0:
1296 user = user[f + 1:]
1300 user = user[f + 1:]
1297 f = user.find(' ')
1301 f = user.find(' ')
1298 if f >= 0:
1302 if f >= 0:
1299 user = user[:f]
1303 user = user[:f]
1300 f = user.find('.')
1304 f = user.find('.')
1301 if f >= 0:
1305 if f >= 0:
1302 user = user[:f]
1306 user = user[:f]
1303 return user
1307 return user
1304
1308
1305 def emailuser(user):
1309 def emailuser(user):
1306 """Return the user portion of an email address."""
1310 """Return the user portion of an email address."""
1307 f = user.find('@')
1311 f = user.find('@')
1308 if f >= 0:
1312 if f >= 0:
1309 user = user[:f]
1313 user = user[:f]
1310 f = user.find('<')
1314 f = user.find('<')
1311 if f >= 0:
1315 if f >= 0:
1312 user = user[f + 1:]
1316 user = user[f + 1:]
1313 return user
1317 return user
1314
1318
1315 def email(author):
1319 def email(author):
1316 '''get email of author.'''
1320 '''get email of author.'''
1317 r = author.find('>')
1321 r = author.find('>')
1318 if r == -1:
1322 if r == -1:
1319 r = None
1323 r = None
1320 return author[author.find('<') + 1:r]
1324 return author[author.find('<') + 1:r]
1321
1325
1322 def ellipsis(text, maxlength=400):
1326 def ellipsis(text, maxlength=400):
1323 """Trim string to at most maxlength (default: 400) columns in display."""
1327 """Trim string to at most maxlength (default: 400) columns in display."""
1324 return encoding.trim(text, maxlength, ellipsis='...')
1328 return encoding.trim(text, maxlength, ellipsis='...')
1325
1329
1326 def unitcountfn(*unittable):
1330 def unitcountfn(*unittable):
1327 '''return a function that renders a readable count of some quantity'''
1331 '''return a function that renders a readable count of some quantity'''
1328
1332
1329 def go(count):
1333 def go(count):
1330 for multiplier, divisor, format in unittable:
1334 for multiplier, divisor, format in unittable:
1331 if count >= divisor * multiplier:
1335 if count >= divisor * multiplier:
1332 return format % (count / float(divisor))
1336 return format % (count / float(divisor))
1333 return unittable[-1][2] % count
1337 return unittable[-1][2] % count
1334
1338
1335 return go
1339 return go
1336
1340
1337 bytecount = unitcountfn(
1341 bytecount = unitcountfn(
1338 (100, 1 << 30, _('%.0f GB')),
1342 (100, 1 << 30, _('%.0f GB')),
1339 (10, 1 << 30, _('%.1f GB')),
1343 (10, 1 << 30, _('%.1f GB')),
1340 (1, 1 << 30, _('%.2f GB')),
1344 (1, 1 << 30, _('%.2f GB')),
1341 (100, 1 << 20, _('%.0f MB')),
1345 (100, 1 << 20, _('%.0f MB')),
1342 (10, 1 << 20, _('%.1f MB')),
1346 (10, 1 << 20, _('%.1f MB')),
1343 (1, 1 << 20, _('%.2f MB')),
1347 (1, 1 << 20, _('%.2f MB')),
1344 (100, 1 << 10, _('%.0f KB')),
1348 (100, 1 << 10, _('%.0f KB')),
1345 (10, 1 << 10, _('%.1f KB')),
1349 (10, 1 << 10, _('%.1f KB')),
1346 (1, 1 << 10, _('%.2f KB')),
1350 (1, 1 << 10, _('%.2f KB')),
1347 (1, 1, _('%.0f bytes')),
1351 (1, 1, _('%.0f bytes')),
1348 )
1352 )
1349
1353
1350 def uirepr(s):
1354 def uirepr(s):
1351 # Avoid double backslash in Windows path repr()
1355 # Avoid double backslash in Windows path repr()
1352 return repr(s).replace('\\\\', '\\')
1356 return repr(s).replace('\\\\', '\\')
1353
1357
1354 # delay import of textwrap
1358 # delay import of textwrap
1355 def MBTextWrapper(**kwargs):
1359 def MBTextWrapper(**kwargs):
1356 class tw(textwrap.TextWrapper):
1360 class tw(textwrap.TextWrapper):
1357 """
1361 """
1358 Extend TextWrapper for width-awareness.
1362 Extend TextWrapper for width-awareness.
1359
1363
1360 Neither number of 'bytes' in any encoding nor 'characters' is
1364 Neither number of 'bytes' in any encoding nor 'characters' is
1361 appropriate to calculate terminal columns for specified string.
1365 appropriate to calculate terminal columns for specified string.
1362
1366
1363 Original TextWrapper implementation uses built-in 'len()' directly,
1367 Original TextWrapper implementation uses built-in 'len()' directly,
1364 so overriding is needed to use width information of each characters.
1368 so overriding is needed to use width information of each characters.
1365
1369
1366 In addition, characters classified into 'ambiguous' width are
1370 In addition, characters classified into 'ambiguous' width are
1367 treated as wide in East Asian area, but as narrow in other.
1371 treated as wide in East Asian area, but as narrow in other.
1368
1372
1369 This requires use decision to determine width of such characters.
1373 This requires use decision to determine width of such characters.
1370 """
1374 """
1371 def __init__(self, **kwargs):
1375 def __init__(self, **kwargs):
1372 textwrap.TextWrapper.__init__(self, **kwargs)
1376 textwrap.TextWrapper.__init__(self, **kwargs)
1373
1377
1374 # for compatibility between 2.4 and 2.6
1378 # for compatibility between 2.4 and 2.6
1375 if getattr(self, 'drop_whitespace', None) is None:
1379 if getattr(self, 'drop_whitespace', None) is None:
1376 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1380 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1377
1381
1378 def _cutdown(self, ucstr, space_left):
1382 def _cutdown(self, ucstr, space_left):
1379 l = 0
1383 l = 0
1380 colwidth = encoding.ucolwidth
1384 colwidth = encoding.ucolwidth
1381 for i in xrange(len(ucstr)):
1385 for i in xrange(len(ucstr)):
1382 l += colwidth(ucstr[i])
1386 l += colwidth(ucstr[i])
1383 if space_left < l:
1387 if space_left < l:
1384 return (ucstr[:i], ucstr[i:])
1388 return (ucstr[:i], ucstr[i:])
1385 return ucstr, ''
1389 return ucstr, ''
1386
1390
1387 # overriding of base class
1391 # overriding of base class
1388 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1392 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1389 space_left = max(width - cur_len, 1)
1393 space_left = max(width - cur_len, 1)
1390
1394
1391 if self.break_long_words:
1395 if self.break_long_words:
1392 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1396 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1393 cur_line.append(cut)
1397 cur_line.append(cut)
1394 reversed_chunks[-1] = res
1398 reversed_chunks[-1] = res
1395 elif not cur_line:
1399 elif not cur_line:
1396 cur_line.append(reversed_chunks.pop())
1400 cur_line.append(reversed_chunks.pop())
1397
1401
1398 # this overriding code is imported from TextWrapper of python 2.6
1402 # this overriding code is imported from TextWrapper of python 2.6
1399 # to calculate columns of string by 'encoding.ucolwidth()'
1403 # to calculate columns of string by 'encoding.ucolwidth()'
1400 def _wrap_chunks(self, chunks):
1404 def _wrap_chunks(self, chunks):
1401 colwidth = encoding.ucolwidth
1405 colwidth = encoding.ucolwidth
1402
1406
1403 lines = []
1407 lines = []
1404 if self.width <= 0:
1408 if self.width <= 0:
1405 raise ValueError("invalid width %r (must be > 0)" % self.width)
1409 raise ValueError("invalid width %r (must be > 0)" % self.width)
1406
1410
1407 # Arrange in reverse order so items can be efficiently popped
1411 # Arrange in reverse order so items can be efficiently popped
1408 # from a stack of chucks.
1412 # from a stack of chucks.
1409 chunks.reverse()
1413 chunks.reverse()
1410
1414
1411 while chunks:
1415 while chunks:
1412
1416
1413 # Start the list of chunks that will make up the current line.
1417 # Start the list of chunks that will make up the current line.
1414 # cur_len is just the length of all the chunks in cur_line.
1418 # cur_len is just the length of all the chunks in cur_line.
1415 cur_line = []
1419 cur_line = []
1416 cur_len = 0
1420 cur_len = 0
1417
1421
1418 # Figure out which static string will prefix this line.
1422 # Figure out which static string will prefix this line.
1419 if lines:
1423 if lines:
1420 indent = self.subsequent_indent
1424 indent = self.subsequent_indent
1421 else:
1425 else:
1422 indent = self.initial_indent
1426 indent = self.initial_indent
1423
1427
1424 # Maximum width for this line.
1428 # Maximum width for this line.
1425 width = self.width - len(indent)
1429 width = self.width - len(indent)
1426
1430
1427 # First chunk on line is whitespace -- drop it, unless this
1431 # First chunk on line is whitespace -- drop it, unless this
1428 # is the very beginning of the text (i.e. no lines started yet).
1432 # is the very beginning of the text (i.e. no lines started yet).
1429 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1433 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1430 del chunks[-1]
1434 del chunks[-1]
1431
1435
1432 while chunks:
1436 while chunks:
1433 l = colwidth(chunks[-1])
1437 l = colwidth(chunks[-1])
1434
1438
1435 # Can at least squeeze this chunk onto the current line.
1439 # Can at least squeeze this chunk onto the current line.
1436 if cur_len + l <= width:
1440 if cur_len + l <= width:
1437 cur_line.append(chunks.pop())
1441 cur_line.append(chunks.pop())
1438 cur_len += l
1442 cur_len += l
1439
1443
1440 # Nope, this line is full.
1444 # Nope, this line is full.
1441 else:
1445 else:
1442 break
1446 break
1443
1447
1444 # The current line is full, and the next chunk is too big to
1448 # The current line is full, and the next chunk is too big to
1445 # fit on *any* line (not just this one).
1449 # fit on *any* line (not just this one).
1446 if chunks and colwidth(chunks[-1]) > width:
1450 if chunks and colwidth(chunks[-1]) > width:
1447 self._handle_long_word(chunks, cur_line, cur_len, width)
1451 self._handle_long_word(chunks, cur_line, cur_len, width)
1448
1452
1449 # If the last chunk on this line is all whitespace, drop it.
1453 # If the last chunk on this line is all whitespace, drop it.
1450 if (self.drop_whitespace and
1454 if (self.drop_whitespace and
1451 cur_line and cur_line[-1].strip() == ''):
1455 cur_line and cur_line[-1].strip() == ''):
1452 del cur_line[-1]
1456 del cur_line[-1]
1453
1457
1454 # Convert current line back to a string and store it in list
1458 # Convert current line back to a string and store it in list
1455 # of all lines (return value).
1459 # of all lines (return value).
1456 if cur_line:
1460 if cur_line:
1457 lines.append(indent + ''.join(cur_line))
1461 lines.append(indent + ''.join(cur_line))
1458
1462
1459 return lines
1463 return lines
1460
1464
1461 global MBTextWrapper
1465 global MBTextWrapper
1462 MBTextWrapper = tw
1466 MBTextWrapper = tw
1463 return tw(**kwargs)
1467 return tw(**kwargs)
1464
1468
1465 def wrap(line, width, initindent='', hangindent=''):
1469 def wrap(line, width, initindent='', hangindent=''):
1466 maxindent = max(len(hangindent), len(initindent))
1470 maxindent = max(len(hangindent), len(initindent))
1467 if width <= maxindent:
1471 if width <= maxindent:
1468 # adjust for weird terminal size
1472 # adjust for weird terminal size
1469 width = max(78, maxindent + 1)
1473 width = max(78, maxindent + 1)
1470 line = line.decode(encoding.encoding, encoding.encodingmode)
1474 line = line.decode(encoding.encoding, encoding.encodingmode)
1471 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1475 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1472 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1476 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1473 wrapper = MBTextWrapper(width=width,
1477 wrapper = MBTextWrapper(width=width,
1474 initial_indent=initindent,
1478 initial_indent=initindent,
1475 subsequent_indent=hangindent)
1479 subsequent_indent=hangindent)
1476 return wrapper.fill(line).encode(encoding.encoding)
1480 return wrapper.fill(line).encode(encoding.encoding)
1477
1481
1478 def iterlines(iterator):
1482 def iterlines(iterator):
1479 for chunk in iterator:
1483 for chunk in iterator:
1480 for line in chunk.splitlines():
1484 for line in chunk.splitlines():
1481 yield line
1485 yield line
1482
1486
1483 def expandpath(path):
1487 def expandpath(path):
1484 return os.path.expanduser(os.path.expandvars(path))
1488 return os.path.expanduser(os.path.expandvars(path))
1485
1489
1486 def hgcmd():
1490 def hgcmd():
1487 """Return the command used to execute current hg
1491 """Return the command used to execute current hg
1488
1492
1489 This is different from hgexecutable() because on Windows we want
1493 This is different from hgexecutable() because on Windows we want
1490 to avoid things opening new shell windows like batch files, so we
1494 to avoid things opening new shell windows like batch files, so we
1491 get either the python call or current executable.
1495 get either the python call or current executable.
1492 """
1496 """
1493 if mainfrozen():
1497 if mainfrozen():
1494 return [sys.executable]
1498 return [sys.executable]
1495 return gethgcmd()
1499 return gethgcmd()
1496
1500
1497 def rundetached(args, condfn):
1501 def rundetached(args, condfn):
1498 """Execute the argument list in a detached process.
1502 """Execute the argument list in a detached process.
1499
1503
1500 condfn is a callable which is called repeatedly and should return
1504 condfn is a callable which is called repeatedly and should return
1501 True once the child process is known to have started successfully.
1505 True once the child process is known to have started successfully.
1502 At this point, the child process PID is returned. If the child
1506 At this point, the child process PID is returned. If the child
1503 process fails to start or finishes before condfn() evaluates to
1507 process fails to start or finishes before condfn() evaluates to
1504 True, return -1.
1508 True, return -1.
1505 """
1509 """
1506 # Windows case is easier because the child process is either
1510 # Windows case is easier because the child process is either
1507 # successfully starting and validating the condition or exiting
1511 # successfully starting and validating the condition or exiting
1508 # on failure. We just poll on its PID. On Unix, if the child
1512 # on failure. We just poll on its PID. On Unix, if the child
1509 # process fails to start, it will be left in a zombie state until
1513 # process fails to start, it will be left in a zombie state until
1510 # the parent wait on it, which we cannot do since we expect a long
1514 # the parent wait on it, which we cannot do since we expect a long
1511 # running process on success. Instead we listen for SIGCHLD telling
1515 # running process on success. Instead we listen for SIGCHLD telling
1512 # us our child process terminated.
1516 # us our child process terminated.
1513 terminated = set()
1517 terminated = set()
1514 def handler(signum, frame):
1518 def handler(signum, frame):
1515 terminated.add(os.wait())
1519 terminated.add(os.wait())
1516 prevhandler = None
1520 prevhandler = None
1517 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1521 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1518 if SIGCHLD is not None:
1522 if SIGCHLD is not None:
1519 prevhandler = signal.signal(SIGCHLD, handler)
1523 prevhandler = signal.signal(SIGCHLD, handler)
1520 try:
1524 try:
1521 pid = spawndetached(args)
1525 pid = spawndetached(args)
1522 while not condfn():
1526 while not condfn():
1523 if ((pid in terminated or not testpid(pid))
1527 if ((pid in terminated or not testpid(pid))
1524 and not condfn()):
1528 and not condfn()):
1525 return -1
1529 return -1
1526 time.sleep(0.1)
1530 time.sleep(0.1)
1527 return pid
1531 return pid
1528 finally:
1532 finally:
1529 if prevhandler is not None:
1533 if prevhandler is not None:
1530 signal.signal(signal.SIGCHLD, prevhandler)
1534 signal.signal(signal.SIGCHLD, prevhandler)
1531
1535
1532 try:
1536 try:
1533 any, all = any, all
1537 any, all = any, all
1534 except NameError:
1538 except NameError:
1535 def any(iterable):
1539 def any(iterable):
1536 for i in iterable:
1540 for i in iterable:
1537 if i:
1541 if i:
1538 return True
1542 return True
1539 return False
1543 return False
1540
1544
1541 def all(iterable):
1545 def all(iterable):
1542 for i in iterable:
1546 for i in iterable:
1543 if not i:
1547 if not i:
1544 return False
1548 return False
1545 return True
1549 return True
1546
1550
1547 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1551 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1548 """Return the result of interpolating items in the mapping into string s.
1552 """Return the result of interpolating items in the mapping into string s.
1549
1553
1550 prefix is a single character string, or a two character string with
1554 prefix is a single character string, or a two character string with
1551 a backslash as the first character if the prefix needs to be escaped in
1555 a backslash as the first character if the prefix needs to be escaped in
1552 a regular expression.
1556 a regular expression.
1553
1557
1554 fn is an optional function that will be applied to the replacement text
1558 fn is an optional function that will be applied to the replacement text
1555 just before replacement.
1559 just before replacement.
1556
1560
1557 escape_prefix is an optional flag that allows using doubled prefix for
1561 escape_prefix is an optional flag that allows using doubled prefix for
1558 its escaping.
1562 its escaping.
1559 """
1563 """
1560 fn = fn or (lambda s: s)
1564 fn = fn or (lambda s: s)
1561 patterns = '|'.join(mapping.keys())
1565 patterns = '|'.join(mapping.keys())
1562 if escape_prefix:
1566 if escape_prefix:
1563 patterns += '|' + prefix
1567 patterns += '|' + prefix
1564 if len(prefix) > 1:
1568 if len(prefix) > 1:
1565 prefix_char = prefix[1:]
1569 prefix_char = prefix[1:]
1566 else:
1570 else:
1567 prefix_char = prefix
1571 prefix_char = prefix
1568 mapping[prefix_char] = prefix_char
1572 mapping[prefix_char] = prefix_char
1569 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1573 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1570 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1574 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1571
1575
1572 def getport(port):
1576 def getport(port):
1573 """Return the port for a given network service.
1577 """Return the port for a given network service.
1574
1578
1575 If port is an integer, it's returned as is. If it's a string, it's
1579 If port is an integer, it's returned as is. If it's a string, it's
1576 looked up using socket.getservbyname(). If there's no matching
1580 looked up using socket.getservbyname(). If there's no matching
1577 service, util.Abort is raised.
1581 service, util.Abort is raised.
1578 """
1582 """
1579 try:
1583 try:
1580 return int(port)
1584 return int(port)
1581 except ValueError:
1585 except ValueError:
1582 pass
1586 pass
1583
1587
1584 try:
1588 try:
1585 return socket.getservbyname(port)
1589 return socket.getservbyname(port)
1586 except socket.error:
1590 except socket.error:
1587 raise Abort(_("no port number associated with service '%s'") % port)
1591 raise Abort(_("no port number associated with service '%s'") % port)
1588
1592
1589 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1593 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1590 '0': False, 'no': False, 'false': False, 'off': False,
1594 '0': False, 'no': False, 'false': False, 'off': False,
1591 'never': False}
1595 'never': False}
1592
1596
1593 def parsebool(s):
1597 def parsebool(s):
1594 """Parse s into a boolean.
1598 """Parse s into a boolean.
1595
1599
1596 If s is not a valid boolean, returns None.
1600 If s is not a valid boolean, returns None.
1597 """
1601 """
1598 return _booleans.get(s.lower(), None)
1602 return _booleans.get(s.lower(), None)
1599
1603
1600 _hexdig = '0123456789ABCDEFabcdef'
1604 _hexdig = '0123456789ABCDEFabcdef'
1601 _hextochr = dict((a + b, chr(int(a + b, 16)))
1605 _hextochr = dict((a + b, chr(int(a + b, 16)))
1602 for a in _hexdig for b in _hexdig)
1606 for a in _hexdig for b in _hexdig)
1603
1607
1604 def _urlunquote(s):
1608 def _urlunquote(s):
1605 """Decode HTTP/HTML % encoding.
1609 """Decode HTTP/HTML % encoding.
1606
1610
1607 >>> _urlunquote('abc%20def')
1611 >>> _urlunquote('abc%20def')
1608 'abc def'
1612 'abc def'
1609 """
1613 """
1610 res = s.split('%')
1614 res = s.split('%')
1611 # fastpath
1615 # fastpath
1612 if len(res) == 1:
1616 if len(res) == 1:
1613 return s
1617 return s
1614 s = res[0]
1618 s = res[0]
1615 for item in res[1:]:
1619 for item in res[1:]:
1616 try:
1620 try:
1617 s += _hextochr[item[:2]] + item[2:]
1621 s += _hextochr[item[:2]] + item[2:]
1618 except KeyError:
1622 except KeyError:
1619 s += '%' + item
1623 s += '%' + item
1620 except UnicodeDecodeError:
1624 except UnicodeDecodeError:
1621 s += unichr(int(item[:2], 16)) + item[2:]
1625 s += unichr(int(item[:2], 16)) + item[2:]
1622 return s
1626 return s
1623
1627
1624 class url(object):
1628 class url(object):
1625 r"""Reliable URL parser.
1629 r"""Reliable URL parser.
1626
1630
1627 This parses URLs and provides attributes for the following
1631 This parses URLs and provides attributes for the following
1628 components:
1632 components:
1629
1633
1630 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1634 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1631
1635
1632 Missing components are set to None. The only exception is
1636 Missing components are set to None. The only exception is
1633 fragment, which is set to '' if present but empty.
1637 fragment, which is set to '' if present but empty.
1634
1638
1635 If parsefragment is False, fragment is included in query. If
1639 If parsefragment is False, fragment is included in query. If
1636 parsequery is False, query is included in path. If both are
1640 parsequery is False, query is included in path. If both are
1637 False, both fragment and query are included in path.
1641 False, both fragment and query are included in path.
1638
1642
1639 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1643 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1640
1644
1641 Note that for backward compatibility reasons, bundle URLs do not
1645 Note that for backward compatibility reasons, bundle URLs do not
1642 take host names. That means 'bundle://../' has a path of '../'.
1646 take host names. That means 'bundle://../' has a path of '../'.
1643
1647
1644 Examples:
1648 Examples:
1645
1649
1646 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1650 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1647 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1651 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1648 >>> url('ssh://[::1]:2200//home/joe/repo')
1652 >>> url('ssh://[::1]:2200//home/joe/repo')
1649 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1653 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1650 >>> url('file:///home/joe/repo')
1654 >>> url('file:///home/joe/repo')
1651 <url scheme: 'file', path: '/home/joe/repo'>
1655 <url scheme: 'file', path: '/home/joe/repo'>
1652 >>> url('file:///c:/temp/foo/')
1656 >>> url('file:///c:/temp/foo/')
1653 <url scheme: 'file', path: 'c:/temp/foo/'>
1657 <url scheme: 'file', path: 'c:/temp/foo/'>
1654 >>> url('bundle:foo')
1658 >>> url('bundle:foo')
1655 <url scheme: 'bundle', path: 'foo'>
1659 <url scheme: 'bundle', path: 'foo'>
1656 >>> url('bundle://../foo')
1660 >>> url('bundle://../foo')
1657 <url scheme: 'bundle', path: '../foo'>
1661 <url scheme: 'bundle', path: '../foo'>
1658 >>> url(r'c:\foo\bar')
1662 >>> url(r'c:\foo\bar')
1659 <url path: 'c:\\foo\\bar'>
1663 <url path: 'c:\\foo\\bar'>
1660 >>> url(r'\\blah\blah\blah')
1664 >>> url(r'\\blah\blah\blah')
1661 <url path: '\\\\blah\\blah\\blah'>
1665 <url path: '\\\\blah\\blah\\blah'>
1662 >>> url(r'\\blah\blah\blah#baz')
1666 >>> url(r'\\blah\blah\blah#baz')
1663 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1667 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1664 >>> url(r'file:///C:\users\me')
1668 >>> url(r'file:///C:\users\me')
1665 <url scheme: 'file', path: 'C:\\users\\me'>
1669 <url scheme: 'file', path: 'C:\\users\\me'>
1666
1670
1667 Authentication credentials:
1671 Authentication credentials:
1668
1672
1669 >>> url('ssh://joe:xyz@x/repo')
1673 >>> url('ssh://joe:xyz@x/repo')
1670 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1674 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1671 >>> url('ssh://joe@x/repo')
1675 >>> url('ssh://joe@x/repo')
1672 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1676 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1673
1677
1674 Query strings and fragments:
1678 Query strings and fragments:
1675
1679
1676 >>> url('http://host/a?b#c')
1680 >>> url('http://host/a?b#c')
1677 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1681 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1678 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1682 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1679 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1683 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1680 """
1684 """
1681
1685
1682 _safechars = "!~*'()+"
1686 _safechars = "!~*'()+"
1683 _safepchars = "/!~*'()+:\\"
1687 _safepchars = "/!~*'()+:\\"
1684 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
1688 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
1685
1689
1686 def __init__(self, path, parsequery=True, parsefragment=True):
1690 def __init__(self, path, parsequery=True, parsefragment=True):
1687 # We slowly chomp away at path until we have only the path left
1691 # We slowly chomp away at path until we have only the path left
1688 self.scheme = self.user = self.passwd = self.host = None
1692 self.scheme = self.user = self.passwd = self.host = None
1689 self.port = self.path = self.query = self.fragment = None
1693 self.port = self.path = self.query = self.fragment = None
1690 self._localpath = True
1694 self._localpath = True
1691 self._hostport = ''
1695 self._hostport = ''
1692 self._origpath = path
1696 self._origpath = path
1693
1697
1694 if parsefragment and '#' in path:
1698 if parsefragment and '#' in path:
1695 path, self.fragment = path.split('#', 1)
1699 path, self.fragment = path.split('#', 1)
1696 if not path:
1700 if not path:
1697 path = None
1701 path = None
1698
1702
1699 # special case for Windows drive letters and UNC paths
1703 # special case for Windows drive letters and UNC paths
1700 if hasdriveletter(path) or path.startswith(r'\\'):
1704 if hasdriveletter(path) or path.startswith(r'\\'):
1701 self.path = path
1705 self.path = path
1702 return
1706 return
1703
1707
1704 # For compatibility reasons, we can't handle bundle paths as
1708 # For compatibility reasons, we can't handle bundle paths as
1705 # normal URLS
1709 # normal URLS
1706 if path.startswith('bundle:'):
1710 if path.startswith('bundle:'):
1707 self.scheme = 'bundle'
1711 self.scheme = 'bundle'
1708 path = path[7:]
1712 path = path[7:]
1709 if path.startswith('//'):
1713 if path.startswith('//'):
1710 path = path[2:]
1714 path = path[2:]
1711 self.path = path
1715 self.path = path
1712 return
1716 return
1713
1717
1714 if self._matchscheme(path):
1718 if self._matchscheme(path):
1715 parts = path.split(':', 1)
1719 parts = path.split(':', 1)
1716 if parts[0]:
1720 if parts[0]:
1717 self.scheme, path = parts
1721 self.scheme, path = parts
1718 self._localpath = False
1722 self._localpath = False
1719
1723
1720 if not path:
1724 if not path:
1721 path = None
1725 path = None
1722 if self._localpath:
1726 if self._localpath:
1723 self.path = ''
1727 self.path = ''
1724 return
1728 return
1725 else:
1729 else:
1726 if self._localpath:
1730 if self._localpath:
1727 self.path = path
1731 self.path = path
1728 return
1732 return
1729
1733
1730 if parsequery and '?' in path:
1734 if parsequery and '?' in path:
1731 path, self.query = path.split('?', 1)
1735 path, self.query = path.split('?', 1)
1732 if not path:
1736 if not path:
1733 path = None
1737 path = None
1734 if not self.query:
1738 if not self.query:
1735 self.query = None
1739 self.query = None
1736
1740
1737 # // is required to specify a host/authority
1741 # // is required to specify a host/authority
1738 if path and path.startswith('//'):
1742 if path and path.startswith('//'):
1739 parts = path[2:].split('/', 1)
1743 parts = path[2:].split('/', 1)
1740 if len(parts) > 1:
1744 if len(parts) > 1:
1741 self.host, path = parts
1745 self.host, path = parts
1742 else:
1746 else:
1743 self.host = parts[0]
1747 self.host = parts[0]
1744 path = None
1748 path = None
1745 if not self.host:
1749 if not self.host:
1746 self.host = None
1750 self.host = None
1747 # path of file:///d is /d
1751 # path of file:///d is /d
1748 # path of file:///d:/ is d:/, not /d:/
1752 # path of file:///d:/ is d:/, not /d:/
1749 if path and not hasdriveletter(path):
1753 if path and not hasdriveletter(path):
1750 path = '/' + path
1754 path = '/' + path
1751
1755
1752 if self.host and '@' in self.host:
1756 if self.host and '@' in self.host:
1753 self.user, self.host = self.host.rsplit('@', 1)
1757 self.user, self.host = self.host.rsplit('@', 1)
1754 if ':' in self.user:
1758 if ':' in self.user:
1755 self.user, self.passwd = self.user.split(':', 1)
1759 self.user, self.passwd = self.user.split(':', 1)
1756 if not self.host:
1760 if not self.host:
1757 self.host = None
1761 self.host = None
1758
1762
1759 # Don't split on colons in IPv6 addresses without ports
1763 # Don't split on colons in IPv6 addresses without ports
1760 if (self.host and ':' in self.host and
1764 if (self.host and ':' in self.host and
1761 not (self.host.startswith('[') and self.host.endswith(']'))):
1765 not (self.host.startswith('[') and self.host.endswith(']'))):
1762 self._hostport = self.host
1766 self._hostport = self.host
1763 self.host, self.port = self.host.rsplit(':', 1)
1767 self.host, self.port = self.host.rsplit(':', 1)
1764 if not self.host:
1768 if not self.host:
1765 self.host = None
1769 self.host = None
1766
1770
1767 if (self.host and self.scheme == 'file' and
1771 if (self.host and self.scheme == 'file' and
1768 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1772 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1769 raise Abort(_('file:// URLs can only refer to localhost'))
1773 raise Abort(_('file:// URLs can only refer to localhost'))
1770
1774
1771 self.path = path
1775 self.path = path
1772
1776
1773 # leave the query string escaped
1777 # leave the query string escaped
1774 for a in ('user', 'passwd', 'host', 'port',
1778 for a in ('user', 'passwd', 'host', 'port',
1775 'path', 'fragment'):
1779 'path', 'fragment'):
1776 v = getattr(self, a)
1780 v = getattr(self, a)
1777 if v is not None:
1781 if v is not None:
1778 setattr(self, a, _urlunquote(v))
1782 setattr(self, a, _urlunquote(v))
1779
1783
1780 def __repr__(self):
1784 def __repr__(self):
1781 attrs = []
1785 attrs = []
1782 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1786 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1783 'query', 'fragment'):
1787 'query', 'fragment'):
1784 v = getattr(self, a)
1788 v = getattr(self, a)
1785 if v is not None:
1789 if v is not None:
1786 attrs.append('%s: %r' % (a, v))
1790 attrs.append('%s: %r' % (a, v))
1787 return '<url %s>' % ', '.join(attrs)
1791 return '<url %s>' % ', '.join(attrs)
1788
1792
1789 def __str__(self):
1793 def __str__(self):
1790 r"""Join the URL's components back into a URL string.
1794 r"""Join the URL's components back into a URL string.
1791
1795
1792 Examples:
1796 Examples:
1793
1797
1794 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1798 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1795 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
1799 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
1796 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1800 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1797 'http://user:pw@host:80/?foo=bar&baz=42'
1801 'http://user:pw@host:80/?foo=bar&baz=42'
1798 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1802 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1799 'http://user:pw@host:80/?foo=bar%3dbaz'
1803 'http://user:pw@host:80/?foo=bar%3dbaz'
1800 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1804 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1801 'ssh://user:pw@[::1]:2200//home/joe#'
1805 'ssh://user:pw@[::1]:2200//home/joe#'
1802 >>> str(url('http://localhost:80//'))
1806 >>> str(url('http://localhost:80//'))
1803 'http://localhost:80//'
1807 'http://localhost:80//'
1804 >>> str(url('http://localhost:80/'))
1808 >>> str(url('http://localhost:80/'))
1805 'http://localhost:80/'
1809 'http://localhost:80/'
1806 >>> str(url('http://localhost:80'))
1810 >>> str(url('http://localhost:80'))
1807 'http://localhost:80/'
1811 'http://localhost:80/'
1808 >>> str(url('bundle:foo'))
1812 >>> str(url('bundle:foo'))
1809 'bundle:foo'
1813 'bundle:foo'
1810 >>> str(url('bundle://../foo'))
1814 >>> str(url('bundle://../foo'))
1811 'bundle:../foo'
1815 'bundle:../foo'
1812 >>> str(url('path'))
1816 >>> str(url('path'))
1813 'path'
1817 'path'
1814 >>> str(url('file:///tmp/foo/bar'))
1818 >>> str(url('file:///tmp/foo/bar'))
1815 'file:///tmp/foo/bar'
1819 'file:///tmp/foo/bar'
1816 >>> str(url('file:///c:/tmp/foo/bar'))
1820 >>> str(url('file:///c:/tmp/foo/bar'))
1817 'file:///c:/tmp/foo/bar'
1821 'file:///c:/tmp/foo/bar'
1818 >>> print url(r'bundle:foo\bar')
1822 >>> print url(r'bundle:foo\bar')
1819 bundle:foo\bar
1823 bundle:foo\bar
1820 >>> print url(r'file:///D:\data\hg')
1824 >>> print url(r'file:///D:\data\hg')
1821 file:///D:\data\hg
1825 file:///D:\data\hg
1822 """
1826 """
1823 if self._localpath:
1827 if self._localpath:
1824 s = self.path
1828 s = self.path
1825 if self.scheme == 'bundle':
1829 if self.scheme == 'bundle':
1826 s = 'bundle:' + s
1830 s = 'bundle:' + s
1827 if self.fragment:
1831 if self.fragment:
1828 s += '#' + self.fragment
1832 s += '#' + self.fragment
1829 return s
1833 return s
1830
1834
1831 s = self.scheme + ':'
1835 s = self.scheme + ':'
1832 if self.user or self.passwd or self.host:
1836 if self.user or self.passwd or self.host:
1833 s += '//'
1837 s += '//'
1834 elif self.scheme and (not self.path or self.path.startswith('/')
1838 elif self.scheme and (not self.path or self.path.startswith('/')
1835 or hasdriveletter(self.path)):
1839 or hasdriveletter(self.path)):
1836 s += '//'
1840 s += '//'
1837 if hasdriveletter(self.path):
1841 if hasdriveletter(self.path):
1838 s += '/'
1842 s += '/'
1839 if self.user:
1843 if self.user:
1840 s += urllib.quote(self.user, safe=self._safechars)
1844 s += urllib.quote(self.user, safe=self._safechars)
1841 if self.passwd:
1845 if self.passwd:
1842 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1846 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1843 if self.user or self.passwd:
1847 if self.user or self.passwd:
1844 s += '@'
1848 s += '@'
1845 if self.host:
1849 if self.host:
1846 if not (self.host.startswith('[') and self.host.endswith(']')):
1850 if not (self.host.startswith('[') and self.host.endswith(']')):
1847 s += urllib.quote(self.host)
1851 s += urllib.quote(self.host)
1848 else:
1852 else:
1849 s += self.host
1853 s += self.host
1850 if self.port:
1854 if self.port:
1851 s += ':' + urllib.quote(self.port)
1855 s += ':' + urllib.quote(self.port)
1852 if self.host:
1856 if self.host:
1853 s += '/'
1857 s += '/'
1854 if self.path:
1858 if self.path:
1855 # TODO: similar to the query string, we should not unescape the
1859 # TODO: similar to the query string, we should not unescape the
1856 # path when we store it, the path might contain '%2f' = '/',
1860 # path when we store it, the path might contain '%2f' = '/',
1857 # which we should *not* escape.
1861 # which we should *not* escape.
1858 s += urllib.quote(self.path, safe=self._safepchars)
1862 s += urllib.quote(self.path, safe=self._safepchars)
1859 if self.query:
1863 if self.query:
1860 # we store the query in escaped form.
1864 # we store the query in escaped form.
1861 s += '?' + self.query
1865 s += '?' + self.query
1862 if self.fragment is not None:
1866 if self.fragment is not None:
1863 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1867 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1864 return s
1868 return s
1865
1869
1866 def authinfo(self):
1870 def authinfo(self):
1867 user, passwd = self.user, self.passwd
1871 user, passwd = self.user, self.passwd
1868 try:
1872 try:
1869 self.user, self.passwd = None, None
1873 self.user, self.passwd = None, None
1870 s = str(self)
1874 s = str(self)
1871 finally:
1875 finally:
1872 self.user, self.passwd = user, passwd
1876 self.user, self.passwd = user, passwd
1873 if not self.user:
1877 if not self.user:
1874 return (s, None)
1878 return (s, None)
1875 # authinfo[1] is passed to urllib2 password manager, and its
1879 # authinfo[1] is passed to urllib2 password manager, and its
1876 # URIs must not contain credentials. The host is passed in the
1880 # URIs must not contain credentials. The host is passed in the
1877 # URIs list because Python < 2.4.3 uses only that to search for
1881 # URIs list because Python < 2.4.3 uses only that to search for
1878 # a password.
1882 # a password.
1879 return (s, (None, (s, self.host),
1883 return (s, (None, (s, self.host),
1880 self.user, self.passwd or ''))
1884 self.user, self.passwd or ''))
1881
1885
1882 def isabs(self):
1886 def isabs(self):
1883 if self.scheme and self.scheme != 'file':
1887 if self.scheme and self.scheme != 'file':
1884 return True # remote URL
1888 return True # remote URL
1885 if hasdriveletter(self.path):
1889 if hasdriveletter(self.path):
1886 return True # absolute for our purposes - can't be joined()
1890 return True # absolute for our purposes - can't be joined()
1887 if self.path.startswith(r'\\'):
1891 if self.path.startswith(r'\\'):
1888 return True # Windows UNC path
1892 return True # Windows UNC path
1889 if self.path.startswith('/'):
1893 if self.path.startswith('/'):
1890 return True # POSIX-style
1894 return True # POSIX-style
1891 return False
1895 return False
1892
1896
1893 def localpath(self):
1897 def localpath(self):
1894 if self.scheme == 'file' or self.scheme == 'bundle':
1898 if self.scheme == 'file' or self.scheme == 'bundle':
1895 path = self.path or '/'
1899 path = self.path or '/'
1896 # For Windows, we need to promote hosts containing drive
1900 # For Windows, we need to promote hosts containing drive
1897 # letters to paths with drive letters.
1901 # letters to paths with drive letters.
1898 if hasdriveletter(self._hostport):
1902 if hasdriveletter(self._hostport):
1899 path = self._hostport + '/' + self.path
1903 path = self._hostport + '/' + self.path
1900 elif (self.host is not None and self.path
1904 elif (self.host is not None and self.path
1901 and not hasdriveletter(path)):
1905 and not hasdriveletter(path)):
1902 path = '/' + path
1906 path = '/' + path
1903 return path
1907 return path
1904 return self._origpath
1908 return self._origpath
1905
1909
1906 def islocal(self):
1910 def islocal(self):
1907 '''whether localpath will return something that posixfile can open'''
1911 '''whether localpath will return something that posixfile can open'''
1908 return (not self.scheme or self.scheme == 'file'
1912 return (not self.scheme or self.scheme == 'file'
1909 or self.scheme == 'bundle')
1913 or self.scheme == 'bundle')
1910
1914
1911 def hasscheme(path):
1915 def hasscheme(path):
1912 return bool(url(path).scheme)
1916 return bool(url(path).scheme)
1913
1917
1914 def hasdriveletter(path):
1918 def hasdriveletter(path):
1915 return path and path[1:2] == ':' and path[0:1].isalpha()
1919 return path and path[1:2] == ':' and path[0:1].isalpha()
1916
1920
1917 def urllocalpath(path):
1921 def urllocalpath(path):
1918 return url(path, parsequery=False, parsefragment=False).localpath()
1922 return url(path, parsequery=False, parsefragment=False).localpath()
1919
1923
1920 def hidepassword(u):
1924 def hidepassword(u):
1921 '''hide user credential in a url string'''
1925 '''hide user credential in a url string'''
1922 u = url(u)
1926 u = url(u)
1923 if u.passwd:
1927 if u.passwd:
1924 u.passwd = '***'
1928 u.passwd = '***'
1925 return str(u)
1929 return str(u)
1926
1930
1927 def removeauth(u):
1931 def removeauth(u):
1928 '''remove all authentication information from a url string'''
1932 '''remove all authentication information from a url string'''
1929 u = url(u)
1933 u = url(u)
1930 u.user = u.passwd = None
1934 u.user = u.passwd = None
1931 return str(u)
1935 return str(u)
1932
1936
1933 def isatty(fd):
1937 def isatty(fd):
1934 try:
1938 try:
1935 return fd.isatty()
1939 return fd.isatty()
1936 except AttributeError:
1940 except AttributeError:
1937 return False
1941 return False
1938
1942
1939 timecount = unitcountfn(
1943 timecount = unitcountfn(
1940 (1, 1e3, _('%.0f s')),
1944 (1, 1e3, _('%.0f s')),
1941 (100, 1, _('%.1f s')),
1945 (100, 1, _('%.1f s')),
1942 (10, 1, _('%.2f s')),
1946 (10, 1, _('%.2f s')),
1943 (1, 1, _('%.3f s')),
1947 (1, 1, _('%.3f s')),
1944 (100, 0.001, _('%.1f ms')),
1948 (100, 0.001, _('%.1f ms')),
1945 (10, 0.001, _('%.2f ms')),
1949 (10, 0.001, _('%.2f ms')),
1946 (1, 0.001, _('%.3f ms')),
1950 (1, 0.001, _('%.3f ms')),
1947 (100, 0.000001, _('%.1f us')),
1951 (100, 0.000001, _('%.1f us')),
1948 (10, 0.000001, _('%.2f us')),
1952 (10, 0.000001, _('%.2f us')),
1949 (1, 0.000001, _('%.3f us')),
1953 (1, 0.000001, _('%.3f us')),
1950 (100, 0.000000001, _('%.1f ns')),
1954 (100, 0.000000001, _('%.1f ns')),
1951 (10, 0.000000001, _('%.2f ns')),
1955 (10, 0.000000001, _('%.2f ns')),
1952 (1, 0.000000001, _('%.3f ns')),
1956 (1, 0.000000001, _('%.3f ns')),
1953 )
1957 )
1954
1958
1955 _timenesting = [0]
1959 _timenesting = [0]
1956
1960
1957 def timed(func):
1961 def timed(func):
1958 '''Report the execution time of a function call to stderr.
1962 '''Report the execution time of a function call to stderr.
1959
1963
1960 During development, use as a decorator when you need to measure
1964 During development, use as a decorator when you need to measure
1961 the cost of a function, e.g. as follows:
1965 the cost of a function, e.g. as follows:
1962
1966
1963 @util.timed
1967 @util.timed
1964 def foo(a, b, c):
1968 def foo(a, b, c):
1965 pass
1969 pass
1966 '''
1970 '''
1967
1971
1968 def wrapper(*args, **kwargs):
1972 def wrapper(*args, **kwargs):
1969 start = time.time()
1973 start = time.time()
1970 indent = 2
1974 indent = 2
1971 _timenesting[0] += indent
1975 _timenesting[0] += indent
1972 try:
1976 try:
1973 return func(*args, **kwargs)
1977 return func(*args, **kwargs)
1974 finally:
1978 finally:
1975 elapsed = time.time() - start
1979 elapsed = time.time() - start
1976 _timenesting[0] -= indent
1980 _timenesting[0] -= indent
1977 sys.stderr.write('%s%s: %s\n' %
1981 sys.stderr.write('%s%s: %s\n' %
1978 (' ' * _timenesting[0], func.__name__,
1982 (' ' * _timenesting[0], func.__name__,
1979 timecount(elapsed)))
1983 timecount(elapsed)))
1980 return wrapper
1984 return wrapper
1981
1985
1982 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
1986 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
1983 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
1987 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
1984
1988
1985 def sizetoint(s):
1989 def sizetoint(s):
1986 '''Convert a space specifier to a byte count.
1990 '''Convert a space specifier to a byte count.
1987
1991
1988 >>> sizetoint('30')
1992 >>> sizetoint('30')
1989 30
1993 30
1990 >>> sizetoint('2.2kb')
1994 >>> sizetoint('2.2kb')
1991 2252
1995 2252
1992 >>> sizetoint('6M')
1996 >>> sizetoint('6M')
1993 6291456
1997 6291456
1994 '''
1998 '''
1995 t = s.strip().lower()
1999 t = s.strip().lower()
1996 try:
2000 try:
1997 for k, u in _sizeunits:
2001 for k, u in _sizeunits:
1998 if t.endswith(k):
2002 if t.endswith(k):
1999 return int(float(t[:-len(k)]) * u)
2003 return int(float(t[:-len(k)]) * u)
2000 return int(t)
2004 return int(t)
2001 except ValueError:
2005 except ValueError:
2002 raise error.ParseError(_("couldn't parse size: %s") % s)
2006 raise error.ParseError(_("couldn't parse size: %s") % s)
2003
2007
2004 class hooks(object):
2008 class hooks(object):
2005 '''A collection of hook functions that can be used to extend a
2009 '''A collection of hook functions that can be used to extend a
2006 function's behaviour. Hooks are called in lexicographic order,
2010 function's behaviour. Hooks are called in lexicographic order,
2007 based on the names of their sources.'''
2011 based on the names of their sources.'''
2008
2012
2009 def __init__(self):
2013 def __init__(self):
2010 self._hooks = []
2014 self._hooks = []
2011
2015
2012 def add(self, source, hook):
2016 def add(self, source, hook):
2013 self._hooks.append((source, hook))
2017 self._hooks.append((source, hook))
2014
2018
2015 def __call__(self, *args):
2019 def __call__(self, *args):
2016 self._hooks.sort(key=lambda x: x[0])
2020 self._hooks.sort(key=lambda x: x[0])
2017 results = []
2021 results = []
2018 for source, hook in self._hooks:
2022 for source, hook in self._hooks:
2019 results.append(hook(*args))
2023 results.append(hook(*args))
2020 return results
2024 return results
2021
2025
2022 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2026 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2023 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2027 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2024 Skips the 'skip' last entries. By default it will flush stdout first.
2028 Skips the 'skip' last entries. By default it will flush stdout first.
2025 It can be used everywhere and do intentionally not require an ui object.
2029 It can be used everywhere and do intentionally not require an ui object.
2026 Not be used in production code but very convenient while developing.
2030 Not be used in production code but very convenient while developing.
2027 '''
2031 '''
2028 if otherf:
2032 if otherf:
2029 otherf.flush()
2033 otherf.flush()
2030 f.write('%s at:\n' % msg)
2034 f.write('%s at:\n' % msg)
2031 entries = [('%s:%s' % (fn, ln), func)
2035 entries = [('%s:%s' % (fn, ln), func)
2032 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2036 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2033 if entries:
2037 if entries:
2034 fnmax = max(len(entry[0]) for entry in entries)
2038 fnmax = max(len(entry[0]) for entry in entries)
2035 for fnln, func in entries:
2039 for fnln, func in entries:
2036 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2040 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2037 f.flush()
2041 f.flush()
2038
2042
2039 # convenient shortcut
2043 # convenient shortcut
2040 dst = debugstacktrace
2044 dst = debugstacktrace
General Comments 0
You need to be logged in to leave comments. Login now