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