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