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