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