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