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