##// END OF EJS Templates
util: use tuple accessor to get accurate st_mtime value (issue4836)...
Yuya Nishihara -
r26493:13272104 default
parent child Browse files
Show More
@@ -1,2465 +1,2477 b''
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific 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 specific implementations.
10 """Mercurial utility functions and platform specific 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 import i18n
16 import i18n
17 _ = i18n._
17 _ = i18n._
18 import error, osutil, encoding, parsers
18 import error, osutil, encoding, parsers
19 import errno, shutil, sys, tempfile, traceback
19 import errno, shutil, sys, tempfile, traceback
20 import re as remod
20 import re as remod
21 import os, time, datetime, calendar, textwrap, signal, collections
21 import os, time, datetime, calendar, textwrap, signal, collections
22 import stat
22 import imp, socket, urllib
23 import imp, socket, urllib
23 import gc
24 import gc
24 import bz2
25 import bz2
25 import zlib
26 import zlib
26
27
27 if os.name == 'nt':
28 if os.name == 'nt':
28 import windows as platform
29 import windows as platform
29 else:
30 else:
30 import posix as platform
31 import posix as platform
31
32
32 cachestat = platform.cachestat
33 cachestat = platform.cachestat
33 checkexec = platform.checkexec
34 checkexec = platform.checkexec
34 checklink = platform.checklink
35 checklink = platform.checklink
35 copymode = platform.copymode
36 copymode = platform.copymode
36 executablepath = platform.executablepath
37 executablepath = platform.executablepath
37 expandglobs = platform.expandglobs
38 expandglobs = platform.expandglobs
38 explainexit = platform.explainexit
39 explainexit = platform.explainexit
39 findexe = platform.findexe
40 findexe = platform.findexe
40 gethgcmd = platform.gethgcmd
41 gethgcmd = platform.gethgcmd
41 getuser = platform.getuser
42 getuser = platform.getuser
42 groupmembers = platform.groupmembers
43 groupmembers = platform.groupmembers
43 groupname = platform.groupname
44 groupname = platform.groupname
44 hidewindow = platform.hidewindow
45 hidewindow = platform.hidewindow
45 isexec = platform.isexec
46 isexec = platform.isexec
46 isowner = platform.isowner
47 isowner = platform.isowner
47 localpath = platform.localpath
48 localpath = platform.localpath
48 lookupreg = platform.lookupreg
49 lookupreg = platform.lookupreg
49 makedir = platform.makedir
50 makedir = platform.makedir
50 nlinks = platform.nlinks
51 nlinks = platform.nlinks
51 normpath = platform.normpath
52 normpath = platform.normpath
52 normcase = platform.normcase
53 normcase = platform.normcase
53 normcasespec = platform.normcasespec
54 normcasespec = platform.normcasespec
54 normcasefallback = platform.normcasefallback
55 normcasefallback = platform.normcasefallback
55 openhardlinks = platform.openhardlinks
56 openhardlinks = platform.openhardlinks
56 oslink = platform.oslink
57 oslink = platform.oslink
57 parsepatchoutput = platform.parsepatchoutput
58 parsepatchoutput = platform.parsepatchoutput
58 pconvert = platform.pconvert
59 pconvert = platform.pconvert
59 poll = platform.poll
60 poll = platform.poll
60 popen = platform.popen
61 popen = platform.popen
61 posixfile = platform.posixfile
62 posixfile = platform.posixfile
62 quotecommand = platform.quotecommand
63 quotecommand = platform.quotecommand
63 readpipe = platform.readpipe
64 readpipe = platform.readpipe
64 rename = platform.rename
65 rename = platform.rename
65 removedirs = platform.removedirs
66 removedirs = platform.removedirs
66 samedevice = platform.samedevice
67 samedevice = platform.samedevice
67 samefile = platform.samefile
68 samefile = platform.samefile
68 samestat = platform.samestat
69 samestat = platform.samestat
69 setbinary = platform.setbinary
70 setbinary = platform.setbinary
70 setflags = platform.setflags
71 setflags = platform.setflags
71 setsignalhandler = platform.setsignalhandler
72 setsignalhandler = platform.setsignalhandler
72 shellquote = platform.shellquote
73 shellquote = platform.shellquote
73 spawndetached = platform.spawndetached
74 spawndetached = platform.spawndetached
74 split = platform.split
75 split = platform.split
75 sshargs = platform.sshargs
76 sshargs = platform.sshargs
76 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
77 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
77 statisexec = platform.statisexec
78 statisexec = platform.statisexec
78 statislink = platform.statislink
79 statislink = platform.statislink
79 termwidth = platform.termwidth
80 termwidth = platform.termwidth
80 testpid = platform.testpid
81 testpid = platform.testpid
81 umask = platform.umask
82 umask = platform.umask
82 unlink = platform.unlink
83 unlink = platform.unlink
83 unlinkpath = platform.unlinkpath
84 unlinkpath = platform.unlinkpath
84 username = platform.username
85 username = platform.username
85
86
86 # Python compatibility
87 # Python compatibility
87
88
88 _notset = object()
89 _notset = object()
89
90
90 def safehasattr(thing, attr):
91 def safehasattr(thing, attr):
91 return getattr(thing, attr, _notset) is not _notset
92 return getattr(thing, attr, _notset) is not _notset
92
93
93 def sha1(s=''):
94 def sha1(s=''):
94 '''
95 '''
95 Low-overhead wrapper around Python's SHA support
96 Low-overhead wrapper around Python's SHA support
96
97
97 >>> f = _fastsha1
98 >>> f = _fastsha1
98 >>> a = sha1()
99 >>> a = sha1()
99 >>> a = f()
100 >>> a = f()
100 >>> a.hexdigest()
101 >>> a.hexdigest()
101 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
102 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
102 '''
103 '''
103
104
104 return _fastsha1(s)
105 return _fastsha1(s)
105
106
106 def _fastsha1(s=''):
107 def _fastsha1(s=''):
107 # This function will import sha1 from hashlib or sha (whichever is
108 # This function will import sha1 from hashlib or sha (whichever is
108 # available) and overwrite itself with it on the first call.
109 # available) and overwrite itself with it on the first call.
109 # Subsequent calls will go directly to the imported function.
110 # Subsequent calls will go directly to the imported function.
110 if sys.version_info >= (2, 5):
111 if sys.version_info >= (2, 5):
111 from hashlib import sha1 as _sha1
112 from hashlib import sha1 as _sha1
112 else:
113 else:
113 from sha import sha as _sha1
114 from sha import sha as _sha1
114 global _fastsha1, sha1
115 global _fastsha1, sha1
115 _fastsha1 = sha1 = _sha1
116 _fastsha1 = sha1 = _sha1
116 return _sha1(s)
117 return _sha1(s)
117
118
118 def md5(s=''):
119 def md5(s=''):
119 try:
120 try:
120 from hashlib import md5 as _md5
121 from hashlib import md5 as _md5
121 except ImportError:
122 except ImportError:
122 from md5 import md5 as _md5
123 from md5 import md5 as _md5
123 global md5
124 global md5
124 md5 = _md5
125 md5 = _md5
125 return _md5(s)
126 return _md5(s)
126
127
127 DIGESTS = {
128 DIGESTS = {
128 'md5': md5,
129 'md5': md5,
129 'sha1': sha1,
130 'sha1': sha1,
130 }
131 }
131 # List of digest types from strongest to weakest
132 # List of digest types from strongest to weakest
132 DIGESTS_BY_STRENGTH = ['sha1', 'md5']
133 DIGESTS_BY_STRENGTH = ['sha1', 'md5']
133
134
134 try:
135 try:
135 import hashlib
136 import hashlib
136 DIGESTS.update({
137 DIGESTS.update({
137 'sha512': hashlib.sha512,
138 'sha512': hashlib.sha512,
138 })
139 })
139 DIGESTS_BY_STRENGTH.insert(0, 'sha512')
140 DIGESTS_BY_STRENGTH.insert(0, 'sha512')
140 except ImportError:
141 except ImportError:
141 pass
142 pass
142
143
143 for k in DIGESTS_BY_STRENGTH:
144 for k in DIGESTS_BY_STRENGTH:
144 assert k in DIGESTS
145 assert k in DIGESTS
145
146
146 class digester(object):
147 class digester(object):
147 """helper to compute digests.
148 """helper to compute digests.
148
149
149 This helper can be used to compute one or more digests given their name.
150 This helper can be used to compute one or more digests given their name.
150
151
151 >>> d = digester(['md5', 'sha1'])
152 >>> d = digester(['md5', 'sha1'])
152 >>> d.update('foo')
153 >>> d.update('foo')
153 >>> [k for k in sorted(d)]
154 >>> [k for k in sorted(d)]
154 ['md5', 'sha1']
155 ['md5', 'sha1']
155 >>> d['md5']
156 >>> d['md5']
156 'acbd18db4cc2f85cedef654fccc4a4d8'
157 'acbd18db4cc2f85cedef654fccc4a4d8'
157 >>> d['sha1']
158 >>> d['sha1']
158 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
159 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
159 >>> digester.preferred(['md5', 'sha1'])
160 >>> digester.preferred(['md5', 'sha1'])
160 'sha1'
161 'sha1'
161 """
162 """
162
163
163 def __init__(self, digests, s=''):
164 def __init__(self, digests, s=''):
164 self._hashes = {}
165 self._hashes = {}
165 for k in digests:
166 for k in digests:
166 if k not in DIGESTS:
167 if k not in DIGESTS:
167 raise Abort(_('unknown digest type: %s') % k)
168 raise Abort(_('unknown digest type: %s') % k)
168 self._hashes[k] = DIGESTS[k]()
169 self._hashes[k] = DIGESTS[k]()
169 if s:
170 if s:
170 self.update(s)
171 self.update(s)
171
172
172 def update(self, data):
173 def update(self, data):
173 for h in self._hashes.values():
174 for h in self._hashes.values():
174 h.update(data)
175 h.update(data)
175
176
176 def __getitem__(self, key):
177 def __getitem__(self, key):
177 if key not in DIGESTS:
178 if key not in DIGESTS:
178 raise Abort(_('unknown digest type: %s') % k)
179 raise Abort(_('unknown digest type: %s') % k)
179 return self._hashes[key].hexdigest()
180 return self._hashes[key].hexdigest()
180
181
181 def __iter__(self):
182 def __iter__(self):
182 return iter(self._hashes)
183 return iter(self._hashes)
183
184
184 @staticmethod
185 @staticmethod
185 def preferred(supported):
186 def preferred(supported):
186 """returns the strongest digest type in both supported and DIGESTS."""
187 """returns the strongest digest type in both supported and DIGESTS."""
187
188
188 for k in DIGESTS_BY_STRENGTH:
189 for k in DIGESTS_BY_STRENGTH:
189 if k in supported:
190 if k in supported:
190 return k
191 return k
191 return None
192 return None
192
193
193 class digestchecker(object):
194 class digestchecker(object):
194 """file handle wrapper that additionally checks content against a given
195 """file handle wrapper that additionally checks content against a given
195 size and digests.
196 size and digests.
196
197
197 d = digestchecker(fh, size, {'md5': '...'})
198 d = digestchecker(fh, size, {'md5': '...'})
198
199
199 When multiple digests are given, all of them are validated.
200 When multiple digests are given, all of them are validated.
200 """
201 """
201
202
202 def __init__(self, fh, size, digests):
203 def __init__(self, fh, size, digests):
203 self._fh = fh
204 self._fh = fh
204 self._size = size
205 self._size = size
205 self._got = 0
206 self._got = 0
206 self._digests = dict(digests)
207 self._digests = dict(digests)
207 self._digester = digester(self._digests.keys())
208 self._digester = digester(self._digests.keys())
208
209
209 def read(self, length=-1):
210 def read(self, length=-1):
210 content = self._fh.read(length)
211 content = self._fh.read(length)
211 self._digester.update(content)
212 self._digester.update(content)
212 self._got += len(content)
213 self._got += len(content)
213 return content
214 return content
214
215
215 def validate(self):
216 def validate(self):
216 if self._size != self._got:
217 if self._size != self._got:
217 raise Abort(_('size mismatch: expected %d, got %d') %
218 raise Abort(_('size mismatch: expected %d, got %d') %
218 (self._size, self._got))
219 (self._size, self._got))
219 for k, v in self._digests.items():
220 for k, v in self._digests.items():
220 if v != self._digester[k]:
221 if v != self._digester[k]:
221 # i18n: first parameter is a digest name
222 # i18n: first parameter is a digest name
222 raise Abort(_('%s mismatch: expected %s, got %s') %
223 raise Abort(_('%s mismatch: expected %s, got %s') %
223 (k, v, self._digester[k]))
224 (k, v, self._digester[k]))
224
225
225 try:
226 try:
226 buffer = buffer
227 buffer = buffer
227 except NameError:
228 except NameError:
228 if sys.version_info[0] < 3:
229 if sys.version_info[0] < 3:
229 def buffer(sliceable, offset=0):
230 def buffer(sliceable, offset=0):
230 return sliceable[offset:]
231 return sliceable[offset:]
231 else:
232 else:
232 def buffer(sliceable, offset=0):
233 def buffer(sliceable, offset=0):
233 return memoryview(sliceable)[offset:]
234 return memoryview(sliceable)[offset:]
234
235
235 import subprocess
236 import subprocess
236 closefds = os.name == 'posix'
237 closefds = os.name == 'posix'
237
238
238 _chunksize = 4096
239 _chunksize = 4096
239
240
240 class bufferedinputpipe(object):
241 class bufferedinputpipe(object):
241 """a manually buffered input pipe
242 """a manually buffered input pipe
242
243
243 Python will not let us use buffered IO and lazy reading with 'polling' at
244 Python will not let us use buffered IO and lazy reading with 'polling' at
244 the same time. We cannot probe the buffer state and select will not detect
245 the same time. We cannot probe the buffer state and select will not detect
245 that data are ready to read if they are already buffered.
246 that data are ready to read if they are already buffered.
246
247
247 This class let us work around that by implementing its own buffering
248 This class let us work around that by implementing its own buffering
248 (allowing efficient readline) while offering a way to know if the buffer is
249 (allowing efficient readline) while offering a way to know if the buffer is
249 empty from the output (allowing collaboration of the buffer with polling).
250 empty from the output (allowing collaboration of the buffer with polling).
250
251
251 This class lives in the 'util' module because it makes use of the 'os'
252 This class lives in the 'util' module because it makes use of the 'os'
252 module from the python stdlib.
253 module from the python stdlib.
253 """
254 """
254
255
255 def __init__(self, input):
256 def __init__(self, input):
256 self._input = input
257 self._input = input
257 self._buffer = []
258 self._buffer = []
258 self._eof = False
259 self._eof = False
259 self._lenbuf = 0
260 self._lenbuf = 0
260
261
261 @property
262 @property
262 def hasbuffer(self):
263 def hasbuffer(self):
263 """True is any data is currently buffered
264 """True is any data is currently buffered
264
265
265 This will be used externally a pre-step for polling IO. If there is
266 This will be used externally a pre-step for polling IO. If there is
266 already data then no polling should be set in place."""
267 already data then no polling should be set in place."""
267 return bool(self._buffer)
268 return bool(self._buffer)
268
269
269 @property
270 @property
270 def closed(self):
271 def closed(self):
271 return self._input.closed
272 return self._input.closed
272
273
273 def fileno(self):
274 def fileno(self):
274 return self._input.fileno()
275 return self._input.fileno()
275
276
276 def close(self):
277 def close(self):
277 return self._input.close()
278 return self._input.close()
278
279
279 def read(self, size):
280 def read(self, size):
280 while (not self._eof) and (self._lenbuf < size):
281 while (not self._eof) and (self._lenbuf < size):
281 self._fillbuffer()
282 self._fillbuffer()
282 return self._frombuffer(size)
283 return self._frombuffer(size)
283
284
284 def readline(self, *args, **kwargs):
285 def readline(self, *args, **kwargs):
285 if 1 < len(self._buffer):
286 if 1 < len(self._buffer):
286 # this should not happen because both read and readline end with a
287 # this should not happen because both read and readline end with a
287 # _frombuffer call that collapse it.
288 # _frombuffer call that collapse it.
288 self._buffer = [''.join(self._buffer)]
289 self._buffer = [''.join(self._buffer)]
289 self._lenbuf = len(self._buffer[0])
290 self._lenbuf = len(self._buffer[0])
290 lfi = -1
291 lfi = -1
291 if self._buffer:
292 if self._buffer:
292 lfi = self._buffer[-1].find('\n')
293 lfi = self._buffer[-1].find('\n')
293 while (not self._eof) and lfi < 0:
294 while (not self._eof) and lfi < 0:
294 self._fillbuffer()
295 self._fillbuffer()
295 if self._buffer:
296 if self._buffer:
296 lfi = self._buffer[-1].find('\n')
297 lfi = self._buffer[-1].find('\n')
297 size = lfi + 1
298 size = lfi + 1
298 if lfi < 0: # end of file
299 if lfi < 0: # end of file
299 size = self._lenbuf
300 size = self._lenbuf
300 elif 1 < len(self._buffer):
301 elif 1 < len(self._buffer):
301 # we need to take previous chunks into account
302 # we need to take previous chunks into account
302 size += self._lenbuf - len(self._buffer[-1])
303 size += self._lenbuf - len(self._buffer[-1])
303 return self._frombuffer(size)
304 return self._frombuffer(size)
304
305
305 def _frombuffer(self, size):
306 def _frombuffer(self, size):
306 """return at most 'size' data from the buffer
307 """return at most 'size' data from the buffer
307
308
308 The data are removed from the buffer."""
309 The data are removed from the buffer."""
309 if size == 0 or not self._buffer:
310 if size == 0 or not self._buffer:
310 return ''
311 return ''
311 buf = self._buffer[0]
312 buf = self._buffer[0]
312 if 1 < len(self._buffer):
313 if 1 < len(self._buffer):
313 buf = ''.join(self._buffer)
314 buf = ''.join(self._buffer)
314
315
315 data = buf[:size]
316 data = buf[:size]
316 buf = buf[len(data):]
317 buf = buf[len(data):]
317 if buf:
318 if buf:
318 self._buffer = [buf]
319 self._buffer = [buf]
319 self._lenbuf = len(buf)
320 self._lenbuf = len(buf)
320 else:
321 else:
321 self._buffer = []
322 self._buffer = []
322 self._lenbuf = 0
323 self._lenbuf = 0
323 return data
324 return data
324
325
325 def _fillbuffer(self):
326 def _fillbuffer(self):
326 """read data to the buffer"""
327 """read data to the buffer"""
327 data = os.read(self._input.fileno(), _chunksize)
328 data = os.read(self._input.fileno(), _chunksize)
328 if not data:
329 if not data:
329 self._eof = True
330 self._eof = True
330 else:
331 else:
331 self._lenbuf += len(data)
332 self._lenbuf += len(data)
332 self._buffer.append(data)
333 self._buffer.append(data)
333
334
334 def popen2(cmd, env=None, newlines=False):
335 def popen2(cmd, env=None, newlines=False):
335 # Setting bufsize to -1 lets the system decide the buffer size.
336 # Setting bufsize to -1 lets the system decide the buffer size.
336 # The default for bufsize is 0, meaning unbuffered. This leads to
337 # The default for bufsize is 0, meaning unbuffered. This leads to
337 # poor performance on Mac OS X: http://bugs.python.org/issue4194
338 # poor performance on Mac OS X: http://bugs.python.org/issue4194
338 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
339 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
339 close_fds=closefds,
340 close_fds=closefds,
340 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
341 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
341 universal_newlines=newlines,
342 universal_newlines=newlines,
342 env=env)
343 env=env)
343 return p.stdin, p.stdout
344 return p.stdin, p.stdout
344
345
345 def popen3(cmd, env=None, newlines=False):
346 def popen3(cmd, env=None, newlines=False):
346 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
347 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
347 return stdin, stdout, stderr
348 return stdin, stdout, stderr
348
349
349 def popen4(cmd, env=None, newlines=False, bufsize=-1):
350 def popen4(cmd, env=None, newlines=False, bufsize=-1):
350 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
351 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
351 close_fds=closefds,
352 close_fds=closefds,
352 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
353 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
353 stderr=subprocess.PIPE,
354 stderr=subprocess.PIPE,
354 universal_newlines=newlines,
355 universal_newlines=newlines,
355 env=env)
356 env=env)
356 return p.stdin, p.stdout, p.stderr, p
357 return p.stdin, p.stdout, p.stderr, p
357
358
358 def version():
359 def version():
359 """Return version information if available."""
360 """Return version information if available."""
360 try:
361 try:
361 import __version__
362 import __version__
362 return __version__.version
363 return __version__.version
363 except ImportError:
364 except ImportError:
364 return 'unknown'
365 return 'unknown'
365
366
366 # used by parsedate
367 # used by parsedate
367 defaultdateformats = (
368 defaultdateformats = (
368 '%Y-%m-%d %H:%M:%S',
369 '%Y-%m-%d %H:%M:%S',
369 '%Y-%m-%d %I:%M:%S%p',
370 '%Y-%m-%d %I:%M:%S%p',
370 '%Y-%m-%d %H:%M',
371 '%Y-%m-%d %H:%M',
371 '%Y-%m-%d %I:%M%p',
372 '%Y-%m-%d %I:%M%p',
372 '%Y-%m-%d',
373 '%Y-%m-%d',
373 '%m-%d',
374 '%m-%d',
374 '%m/%d',
375 '%m/%d',
375 '%m/%d/%y',
376 '%m/%d/%y',
376 '%m/%d/%Y',
377 '%m/%d/%Y',
377 '%a %b %d %H:%M:%S %Y',
378 '%a %b %d %H:%M:%S %Y',
378 '%a %b %d %I:%M:%S%p %Y',
379 '%a %b %d %I:%M:%S%p %Y',
379 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
380 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
380 '%b %d %H:%M:%S %Y',
381 '%b %d %H:%M:%S %Y',
381 '%b %d %I:%M:%S%p %Y',
382 '%b %d %I:%M:%S%p %Y',
382 '%b %d %H:%M:%S',
383 '%b %d %H:%M:%S',
383 '%b %d %I:%M:%S%p',
384 '%b %d %I:%M:%S%p',
384 '%b %d %H:%M',
385 '%b %d %H:%M',
385 '%b %d %I:%M%p',
386 '%b %d %I:%M%p',
386 '%b %d %Y',
387 '%b %d %Y',
387 '%b %d',
388 '%b %d',
388 '%H:%M:%S',
389 '%H:%M:%S',
389 '%I:%M:%S%p',
390 '%I:%M:%S%p',
390 '%H:%M',
391 '%H:%M',
391 '%I:%M%p',
392 '%I:%M%p',
392 )
393 )
393
394
394 extendeddateformats = defaultdateformats + (
395 extendeddateformats = defaultdateformats + (
395 "%Y",
396 "%Y",
396 "%Y-%m",
397 "%Y-%m",
397 "%b",
398 "%b",
398 "%b %Y",
399 "%b %Y",
399 )
400 )
400
401
401 def cachefunc(func):
402 def cachefunc(func):
402 '''cache the result of function calls'''
403 '''cache the result of function calls'''
403 # XXX doesn't handle keywords args
404 # XXX doesn't handle keywords args
404 if func.func_code.co_argcount == 0:
405 if func.func_code.co_argcount == 0:
405 cache = []
406 cache = []
406 def f():
407 def f():
407 if len(cache) == 0:
408 if len(cache) == 0:
408 cache.append(func())
409 cache.append(func())
409 return cache[0]
410 return cache[0]
410 return f
411 return f
411 cache = {}
412 cache = {}
412 if func.func_code.co_argcount == 1:
413 if func.func_code.co_argcount == 1:
413 # we gain a small amount of time because
414 # we gain a small amount of time because
414 # we don't need to pack/unpack the list
415 # we don't need to pack/unpack the list
415 def f(arg):
416 def f(arg):
416 if arg not in cache:
417 if arg not in cache:
417 cache[arg] = func(arg)
418 cache[arg] = func(arg)
418 return cache[arg]
419 return cache[arg]
419 else:
420 else:
420 def f(*args):
421 def f(*args):
421 if args not in cache:
422 if args not in cache:
422 cache[args] = func(*args)
423 cache[args] = func(*args)
423 return cache[args]
424 return cache[args]
424
425
425 return f
426 return f
426
427
427 class sortdict(dict):
428 class sortdict(dict):
428 '''a simple sorted dictionary'''
429 '''a simple sorted dictionary'''
429 def __init__(self, data=None):
430 def __init__(self, data=None):
430 self._list = []
431 self._list = []
431 if data:
432 if data:
432 self.update(data)
433 self.update(data)
433 def copy(self):
434 def copy(self):
434 return sortdict(self)
435 return sortdict(self)
435 def __setitem__(self, key, val):
436 def __setitem__(self, key, val):
436 if key in self:
437 if key in self:
437 self._list.remove(key)
438 self._list.remove(key)
438 self._list.append(key)
439 self._list.append(key)
439 dict.__setitem__(self, key, val)
440 dict.__setitem__(self, key, val)
440 def __iter__(self):
441 def __iter__(self):
441 return self._list.__iter__()
442 return self._list.__iter__()
442 def update(self, src):
443 def update(self, src):
443 if isinstance(src, dict):
444 if isinstance(src, dict):
444 src = src.iteritems()
445 src = src.iteritems()
445 for k, v in src:
446 for k, v in src:
446 self[k] = v
447 self[k] = v
447 def clear(self):
448 def clear(self):
448 dict.clear(self)
449 dict.clear(self)
449 self._list = []
450 self._list = []
450 def items(self):
451 def items(self):
451 return [(k, self[k]) for k in self._list]
452 return [(k, self[k]) for k in self._list]
452 def __delitem__(self, key):
453 def __delitem__(self, key):
453 dict.__delitem__(self, key)
454 dict.__delitem__(self, key)
454 self._list.remove(key)
455 self._list.remove(key)
455 def pop(self, key, *args, **kwargs):
456 def pop(self, key, *args, **kwargs):
456 dict.pop(self, key, *args, **kwargs)
457 dict.pop(self, key, *args, **kwargs)
457 try:
458 try:
458 self._list.remove(key)
459 self._list.remove(key)
459 except ValueError:
460 except ValueError:
460 pass
461 pass
461 def keys(self):
462 def keys(self):
462 return self._list
463 return self._list
463 def iterkeys(self):
464 def iterkeys(self):
464 return self._list.__iter__()
465 return self._list.__iter__()
465 def iteritems(self):
466 def iteritems(self):
466 for k in self._list:
467 for k in self._list:
467 yield k, self[k]
468 yield k, self[k]
468 def insert(self, index, key, val):
469 def insert(self, index, key, val):
469 self._list.insert(index, key)
470 self._list.insert(index, key)
470 dict.__setitem__(self, key, val)
471 dict.__setitem__(self, key, val)
471
472
472 class lrucachedict(object):
473 class lrucachedict(object):
473 '''cache most recent gets from or sets to this dictionary'''
474 '''cache most recent gets from or sets to this dictionary'''
474 def __init__(self, maxsize):
475 def __init__(self, maxsize):
475 self._cache = {}
476 self._cache = {}
476 self._maxsize = maxsize
477 self._maxsize = maxsize
477 self._order = collections.deque()
478 self._order = collections.deque()
478
479
479 def __getitem__(self, key):
480 def __getitem__(self, key):
480 value = self._cache[key]
481 value = self._cache[key]
481 self._order.remove(key)
482 self._order.remove(key)
482 self._order.append(key)
483 self._order.append(key)
483 return value
484 return value
484
485
485 def __setitem__(self, key, value):
486 def __setitem__(self, key, value):
486 if key not in self._cache:
487 if key not in self._cache:
487 if len(self._cache) >= self._maxsize:
488 if len(self._cache) >= self._maxsize:
488 del self._cache[self._order.popleft()]
489 del self._cache[self._order.popleft()]
489 else:
490 else:
490 self._order.remove(key)
491 self._order.remove(key)
491 self._cache[key] = value
492 self._cache[key] = value
492 self._order.append(key)
493 self._order.append(key)
493
494
494 def __contains__(self, key):
495 def __contains__(self, key):
495 return key in self._cache
496 return key in self._cache
496
497
497 def clear(self):
498 def clear(self):
498 self._cache.clear()
499 self._cache.clear()
499 self._order = collections.deque()
500 self._order = collections.deque()
500
501
501 def lrucachefunc(func):
502 def lrucachefunc(func):
502 '''cache most recent results of function calls'''
503 '''cache most recent results of function calls'''
503 cache = {}
504 cache = {}
504 order = collections.deque()
505 order = collections.deque()
505 if func.func_code.co_argcount == 1:
506 if func.func_code.co_argcount == 1:
506 def f(arg):
507 def f(arg):
507 if arg not in cache:
508 if arg not in cache:
508 if len(cache) > 20:
509 if len(cache) > 20:
509 del cache[order.popleft()]
510 del cache[order.popleft()]
510 cache[arg] = func(arg)
511 cache[arg] = func(arg)
511 else:
512 else:
512 order.remove(arg)
513 order.remove(arg)
513 order.append(arg)
514 order.append(arg)
514 return cache[arg]
515 return cache[arg]
515 else:
516 else:
516 def f(*args):
517 def f(*args):
517 if args not in cache:
518 if args not in cache:
518 if len(cache) > 20:
519 if len(cache) > 20:
519 del cache[order.popleft()]
520 del cache[order.popleft()]
520 cache[args] = func(*args)
521 cache[args] = func(*args)
521 else:
522 else:
522 order.remove(args)
523 order.remove(args)
523 order.append(args)
524 order.append(args)
524 return cache[args]
525 return cache[args]
525
526
526 return f
527 return f
527
528
528 class propertycache(object):
529 class propertycache(object):
529 def __init__(self, func):
530 def __init__(self, func):
530 self.func = func
531 self.func = func
531 self.name = func.__name__
532 self.name = func.__name__
532 def __get__(self, obj, type=None):
533 def __get__(self, obj, type=None):
533 result = self.func(obj)
534 result = self.func(obj)
534 self.cachevalue(obj, result)
535 self.cachevalue(obj, result)
535 return result
536 return result
536
537
537 def cachevalue(self, obj, value):
538 def cachevalue(self, obj, value):
538 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
539 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
539 obj.__dict__[self.name] = value
540 obj.__dict__[self.name] = value
540
541
541 def pipefilter(s, cmd):
542 def pipefilter(s, cmd):
542 '''filter string S through command CMD, returning its output'''
543 '''filter string S through command CMD, returning its output'''
543 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
544 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
544 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
545 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
545 pout, perr = p.communicate(s)
546 pout, perr = p.communicate(s)
546 return pout
547 return pout
547
548
548 def tempfilter(s, cmd):
549 def tempfilter(s, cmd):
549 '''filter string S through a pair of temporary files with CMD.
550 '''filter string S through a pair of temporary files with CMD.
550 CMD is used as a template to create the real command to be run,
551 CMD is used as a template to create the real command to be run,
551 with the strings INFILE and OUTFILE replaced by the real names of
552 with the strings INFILE and OUTFILE replaced by the real names of
552 the temporary files generated.'''
553 the temporary files generated.'''
553 inname, outname = None, None
554 inname, outname = None, None
554 try:
555 try:
555 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
556 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
556 fp = os.fdopen(infd, 'wb')
557 fp = os.fdopen(infd, 'wb')
557 fp.write(s)
558 fp.write(s)
558 fp.close()
559 fp.close()
559 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
560 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
560 os.close(outfd)
561 os.close(outfd)
561 cmd = cmd.replace('INFILE', inname)
562 cmd = cmd.replace('INFILE', inname)
562 cmd = cmd.replace('OUTFILE', outname)
563 cmd = cmd.replace('OUTFILE', outname)
563 code = os.system(cmd)
564 code = os.system(cmd)
564 if sys.platform == 'OpenVMS' and code & 1:
565 if sys.platform == 'OpenVMS' and code & 1:
565 code = 0
566 code = 0
566 if code:
567 if code:
567 raise Abort(_("command '%s' failed: %s") %
568 raise Abort(_("command '%s' failed: %s") %
568 (cmd, explainexit(code)))
569 (cmd, explainexit(code)))
569 fp = open(outname, 'rb')
570 fp = open(outname, 'rb')
570 r = fp.read()
571 r = fp.read()
571 fp.close()
572 fp.close()
572 return r
573 return r
573 finally:
574 finally:
574 try:
575 try:
575 if inname:
576 if inname:
576 os.unlink(inname)
577 os.unlink(inname)
577 except OSError:
578 except OSError:
578 pass
579 pass
579 try:
580 try:
580 if outname:
581 if outname:
581 os.unlink(outname)
582 os.unlink(outname)
582 except OSError:
583 except OSError:
583 pass
584 pass
584
585
585 filtertable = {
586 filtertable = {
586 'tempfile:': tempfilter,
587 'tempfile:': tempfilter,
587 'pipe:': pipefilter,
588 'pipe:': pipefilter,
588 }
589 }
589
590
590 def filter(s, cmd):
591 def filter(s, cmd):
591 "filter a string through a command that transforms its input to its output"
592 "filter a string through a command that transforms its input to its output"
592 for name, fn in filtertable.iteritems():
593 for name, fn in filtertable.iteritems():
593 if cmd.startswith(name):
594 if cmd.startswith(name):
594 return fn(s, cmd[len(name):].lstrip())
595 return fn(s, cmd[len(name):].lstrip())
595 return pipefilter(s, cmd)
596 return pipefilter(s, cmd)
596
597
597 def binary(s):
598 def binary(s):
598 """return true if a string is binary data"""
599 """return true if a string is binary data"""
599 return bool(s and '\0' in s)
600 return bool(s and '\0' in s)
600
601
601 def increasingchunks(source, min=1024, max=65536):
602 def increasingchunks(source, min=1024, max=65536):
602 '''return no less than min bytes per chunk while data remains,
603 '''return no less than min bytes per chunk while data remains,
603 doubling min after each chunk until it reaches max'''
604 doubling min after each chunk until it reaches max'''
604 def log2(x):
605 def log2(x):
605 if not x:
606 if not x:
606 return 0
607 return 0
607 i = 0
608 i = 0
608 while x:
609 while x:
609 x >>= 1
610 x >>= 1
610 i += 1
611 i += 1
611 return i - 1
612 return i - 1
612
613
613 buf = []
614 buf = []
614 blen = 0
615 blen = 0
615 for chunk in source:
616 for chunk in source:
616 buf.append(chunk)
617 buf.append(chunk)
617 blen += len(chunk)
618 blen += len(chunk)
618 if blen >= min:
619 if blen >= min:
619 if min < max:
620 if min < max:
620 min = min << 1
621 min = min << 1
621 nmin = 1 << log2(blen)
622 nmin = 1 << log2(blen)
622 if nmin > min:
623 if nmin > min:
623 min = nmin
624 min = nmin
624 if min > max:
625 if min > max:
625 min = max
626 min = max
626 yield ''.join(buf)
627 yield ''.join(buf)
627 blen = 0
628 blen = 0
628 buf = []
629 buf = []
629 if buf:
630 if buf:
630 yield ''.join(buf)
631 yield ''.join(buf)
631
632
632 Abort = error.Abort
633 Abort = error.Abort
633
634
634 def always(fn):
635 def always(fn):
635 return True
636 return True
636
637
637 def never(fn):
638 def never(fn):
638 return False
639 return False
639
640
640 def nogc(func):
641 def nogc(func):
641 """disable garbage collector
642 """disable garbage collector
642
643
643 Python's garbage collector triggers a GC each time a certain number of
644 Python's garbage collector triggers a GC each time a certain number of
644 container objects (the number being defined by gc.get_threshold()) are
645 container objects (the number being defined by gc.get_threshold()) are
645 allocated even when marked not to be tracked by the collector. Tracking has
646 allocated even when marked not to be tracked by the collector. Tracking has
646 no effect on when GCs are triggered, only on what objects the GC looks
647 no effect on when GCs are triggered, only on what objects the GC looks
647 into. As a workaround, disable GC while building complex (huge)
648 into. As a workaround, disable GC while building complex (huge)
648 containers.
649 containers.
649
650
650 This garbage collector issue have been fixed in 2.7.
651 This garbage collector issue have been fixed in 2.7.
651 """
652 """
652 def wrapper(*args, **kwargs):
653 def wrapper(*args, **kwargs):
653 gcenabled = gc.isenabled()
654 gcenabled = gc.isenabled()
654 gc.disable()
655 gc.disable()
655 try:
656 try:
656 return func(*args, **kwargs)
657 return func(*args, **kwargs)
657 finally:
658 finally:
658 if gcenabled:
659 if gcenabled:
659 gc.enable()
660 gc.enable()
660 return wrapper
661 return wrapper
661
662
662 def pathto(root, n1, n2):
663 def pathto(root, n1, n2):
663 '''return the relative path from one place to another.
664 '''return the relative path from one place to another.
664 root should use os.sep to separate directories
665 root should use os.sep to separate directories
665 n1 should use os.sep to separate directories
666 n1 should use os.sep to separate directories
666 n2 should use "/" to separate directories
667 n2 should use "/" to separate directories
667 returns an os.sep-separated path.
668 returns an os.sep-separated path.
668
669
669 If n1 is a relative path, it's assumed it's
670 If n1 is a relative path, it's assumed it's
670 relative to root.
671 relative to root.
671 n2 should always be relative to root.
672 n2 should always be relative to root.
672 '''
673 '''
673 if not n1:
674 if not n1:
674 return localpath(n2)
675 return localpath(n2)
675 if os.path.isabs(n1):
676 if os.path.isabs(n1):
676 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
677 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
677 return os.path.join(root, localpath(n2))
678 return os.path.join(root, localpath(n2))
678 n2 = '/'.join((pconvert(root), n2))
679 n2 = '/'.join((pconvert(root), n2))
679 a, b = splitpath(n1), n2.split('/')
680 a, b = splitpath(n1), n2.split('/')
680 a.reverse()
681 a.reverse()
681 b.reverse()
682 b.reverse()
682 while a and b and a[-1] == b[-1]:
683 while a and b and a[-1] == b[-1]:
683 a.pop()
684 a.pop()
684 b.pop()
685 b.pop()
685 b.reverse()
686 b.reverse()
686 return os.sep.join((['..'] * len(a)) + b) or '.'
687 return os.sep.join((['..'] * len(a)) + b) or '.'
687
688
688 def mainfrozen():
689 def mainfrozen():
689 """return True if we are a frozen executable.
690 """return True if we are a frozen executable.
690
691
691 The code supports py2exe (most common, Windows only) and tools/freeze
692 The code supports py2exe (most common, Windows only) and tools/freeze
692 (portable, not much used).
693 (portable, not much used).
693 """
694 """
694 return (safehasattr(sys, "frozen") or # new py2exe
695 return (safehasattr(sys, "frozen") or # new py2exe
695 safehasattr(sys, "importers") or # old py2exe
696 safehasattr(sys, "importers") or # old py2exe
696 imp.is_frozen("__main__")) # tools/freeze
697 imp.is_frozen("__main__")) # tools/freeze
697
698
698 # the location of data files matching the source code
699 # the location of data files matching the source code
699 if mainfrozen():
700 if mainfrozen():
700 # executable version (py2exe) doesn't support __file__
701 # executable version (py2exe) doesn't support __file__
701 datapath = os.path.dirname(sys.executable)
702 datapath = os.path.dirname(sys.executable)
702 else:
703 else:
703 datapath = os.path.dirname(__file__)
704 datapath = os.path.dirname(__file__)
704
705
705 i18n.setdatapath(datapath)
706 i18n.setdatapath(datapath)
706
707
707 _hgexecutable = None
708 _hgexecutable = None
708
709
709 def hgexecutable():
710 def hgexecutable():
710 """return location of the 'hg' executable.
711 """return location of the 'hg' executable.
711
712
712 Defaults to $HG or 'hg' in the search path.
713 Defaults to $HG or 'hg' in the search path.
713 """
714 """
714 if _hgexecutable is None:
715 if _hgexecutable is None:
715 hg = os.environ.get('HG')
716 hg = os.environ.get('HG')
716 mainmod = sys.modules['__main__']
717 mainmod = sys.modules['__main__']
717 if hg:
718 if hg:
718 _sethgexecutable(hg)
719 _sethgexecutable(hg)
719 elif mainfrozen():
720 elif mainfrozen():
720 _sethgexecutable(sys.executable)
721 _sethgexecutable(sys.executable)
721 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
722 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
722 _sethgexecutable(mainmod.__file__)
723 _sethgexecutable(mainmod.__file__)
723 else:
724 else:
724 exe = findexe('hg') or os.path.basename(sys.argv[0])
725 exe = findexe('hg') or os.path.basename(sys.argv[0])
725 _sethgexecutable(exe)
726 _sethgexecutable(exe)
726 return _hgexecutable
727 return _hgexecutable
727
728
728 def _sethgexecutable(path):
729 def _sethgexecutable(path):
729 """set location of the 'hg' executable"""
730 """set location of the 'hg' executable"""
730 global _hgexecutable
731 global _hgexecutable
731 _hgexecutable = path
732 _hgexecutable = path
732
733
733 def _isstdout(f):
734 def _isstdout(f):
734 fileno = getattr(f, 'fileno', None)
735 fileno = getattr(f, 'fileno', None)
735 return fileno and fileno() == sys.__stdout__.fileno()
736 return fileno and fileno() == sys.__stdout__.fileno()
736
737
737 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
738 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
738 '''enhanced shell command execution.
739 '''enhanced shell command execution.
739 run with environment maybe modified, maybe in different dir.
740 run with environment maybe modified, maybe in different dir.
740
741
741 if command fails and onerr is None, return status, else raise onerr
742 if command fails and onerr is None, return status, else raise onerr
742 object as exception.
743 object as exception.
743
744
744 if out is specified, it is assumed to be a file-like object that has a
745 if out is specified, it is assumed to be a file-like object that has a
745 write() method. stdout and stderr will be redirected to out.'''
746 write() method. stdout and stderr will be redirected to out.'''
746 if environ is None:
747 if environ is None:
747 environ = {}
748 environ = {}
748 try:
749 try:
749 sys.stdout.flush()
750 sys.stdout.flush()
750 except Exception:
751 except Exception:
751 pass
752 pass
752 def py2shell(val):
753 def py2shell(val):
753 'convert python object into string that is useful to shell'
754 'convert python object into string that is useful to shell'
754 if val is None or val is False:
755 if val is None or val is False:
755 return '0'
756 return '0'
756 if val is True:
757 if val is True:
757 return '1'
758 return '1'
758 return str(val)
759 return str(val)
759 origcmd = cmd
760 origcmd = cmd
760 cmd = quotecommand(cmd)
761 cmd = quotecommand(cmd)
761 if sys.platform == 'plan9' and (sys.version_info[0] == 2
762 if sys.platform == 'plan9' and (sys.version_info[0] == 2
762 and sys.version_info[1] < 7):
763 and sys.version_info[1] < 7):
763 # subprocess kludge to work around issues in half-baked Python
764 # subprocess kludge to work around issues in half-baked Python
764 # ports, notably bichued/python:
765 # ports, notably bichued/python:
765 if not cwd is None:
766 if not cwd is None:
766 os.chdir(cwd)
767 os.chdir(cwd)
767 rc = os.system(cmd)
768 rc = os.system(cmd)
768 else:
769 else:
769 env = dict(os.environ)
770 env = dict(os.environ)
770 env.update((k, py2shell(v)) for k, v in environ.iteritems())
771 env.update((k, py2shell(v)) for k, v in environ.iteritems())
771 env['HG'] = hgexecutable()
772 env['HG'] = hgexecutable()
772 if out is None or _isstdout(out):
773 if out is None or _isstdout(out):
773 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
774 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
774 env=env, cwd=cwd)
775 env=env, cwd=cwd)
775 else:
776 else:
776 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
777 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
777 env=env, cwd=cwd, stdout=subprocess.PIPE,
778 env=env, cwd=cwd, stdout=subprocess.PIPE,
778 stderr=subprocess.STDOUT)
779 stderr=subprocess.STDOUT)
779 while True:
780 while True:
780 line = proc.stdout.readline()
781 line = proc.stdout.readline()
781 if not line:
782 if not line:
782 break
783 break
783 out.write(line)
784 out.write(line)
784 proc.wait()
785 proc.wait()
785 rc = proc.returncode
786 rc = proc.returncode
786 if sys.platform == 'OpenVMS' and rc & 1:
787 if sys.platform == 'OpenVMS' and rc & 1:
787 rc = 0
788 rc = 0
788 if rc and onerr:
789 if rc and onerr:
789 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
790 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
790 explainexit(rc)[0])
791 explainexit(rc)[0])
791 if errprefix:
792 if errprefix:
792 errmsg = '%s: %s' % (errprefix, errmsg)
793 errmsg = '%s: %s' % (errprefix, errmsg)
793 raise onerr(errmsg)
794 raise onerr(errmsg)
794 return rc
795 return rc
795
796
796 def checksignature(func):
797 def checksignature(func):
797 '''wrap a function with code to check for calling errors'''
798 '''wrap a function with code to check for calling errors'''
798 def check(*args, **kwargs):
799 def check(*args, **kwargs):
799 try:
800 try:
800 return func(*args, **kwargs)
801 return func(*args, **kwargs)
801 except TypeError:
802 except TypeError:
802 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
803 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
803 raise error.SignatureError
804 raise error.SignatureError
804 raise
805 raise
805
806
806 return check
807 return check
807
808
808 def copyfile(src, dest, hardlink=False):
809 def copyfile(src, dest, hardlink=False):
809 "copy a file, preserving mode and atime/mtime"
810 "copy a file, preserving mode and atime/mtime"
810 if os.path.lexists(dest):
811 if os.path.lexists(dest):
811 unlink(dest)
812 unlink(dest)
812 # hardlinks are problematic on CIFS, quietly ignore this flag
813 # hardlinks are problematic on CIFS, quietly ignore this flag
813 # until we find a way to work around it cleanly (issue4546)
814 # until we find a way to work around it cleanly (issue4546)
814 if False and hardlink:
815 if False and hardlink:
815 try:
816 try:
816 oslink(src, dest)
817 oslink(src, dest)
817 return
818 return
818 except (IOError, OSError):
819 except (IOError, OSError):
819 pass # fall back to normal copy
820 pass # fall back to normal copy
820 if os.path.islink(src):
821 if os.path.islink(src):
821 os.symlink(os.readlink(src), dest)
822 os.symlink(os.readlink(src), dest)
822 else:
823 else:
823 try:
824 try:
824 shutil.copyfile(src, dest)
825 shutil.copyfile(src, dest)
825 shutil.copymode(src, dest)
826 shutil.copymode(src, dest)
826 except shutil.Error as inst:
827 except shutil.Error as inst:
827 raise Abort(str(inst))
828 raise Abort(str(inst))
828
829
829 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
830 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
830 """Copy a directory tree using hardlinks if possible."""
831 """Copy a directory tree using hardlinks if possible."""
831 num = 0
832 num = 0
832
833
833 if hardlink is None:
834 if hardlink is None:
834 hardlink = (os.stat(src).st_dev ==
835 hardlink = (os.stat(src).st_dev ==
835 os.stat(os.path.dirname(dst)).st_dev)
836 os.stat(os.path.dirname(dst)).st_dev)
836 if hardlink:
837 if hardlink:
837 topic = _('linking')
838 topic = _('linking')
838 else:
839 else:
839 topic = _('copying')
840 topic = _('copying')
840
841
841 if os.path.isdir(src):
842 if os.path.isdir(src):
842 os.mkdir(dst)
843 os.mkdir(dst)
843 for name, kind in osutil.listdir(src):
844 for name, kind in osutil.listdir(src):
844 srcname = os.path.join(src, name)
845 srcname = os.path.join(src, name)
845 dstname = os.path.join(dst, name)
846 dstname = os.path.join(dst, name)
846 def nprog(t, pos):
847 def nprog(t, pos):
847 if pos is not None:
848 if pos is not None:
848 return progress(t, pos + num)
849 return progress(t, pos + num)
849 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
850 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
850 num += n
851 num += n
851 else:
852 else:
852 if hardlink:
853 if hardlink:
853 try:
854 try:
854 oslink(src, dst)
855 oslink(src, dst)
855 except (IOError, OSError):
856 except (IOError, OSError):
856 hardlink = False
857 hardlink = False
857 shutil.copy(src, dst)
858 shutil.copy(src, dst)
858 else:
859 else:
859 shutil.copy(src, dst)
860 shutil.copy(src, dst)
860 num += 1
861 num += 1
861 progress(topic, num)
862 progress(topic, num)
862 progress(topic, None)
863 progress(topic, None)
863
864
864 return hardlink, num
865 return hardlink, num
865
866
866 _winreservednames = '''con prn aux nul
867 _winreservednames = '''con prn aux nul
867 com1 com2 com3 com4 com5 com6 com7 com8 com9
868 com1 com2 com3 com4 com5 com6 com7 com8 com9
868 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
869 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
869 _winreservedchars = ':*?"<>|'
870 _winreservedchars = ':*?"<>|'
870 def checkwinfilename(path):
871 def checkwinfilename(path):
871 r'''Check that the base-relative path is a valid filename on Windows.
872 r'''Check that the base-relative path is a valid filename on Windows.
872 Returns None if the path is ok, or a UI string describing the problem.
873 Returns None if the path is ok, or a UI string describing the problem.
873
874
874 >>> checkwinfilename("just/a/normal/path")
875 >>> checkwinfilename("just/a/normal/path")
875 >>> checkwinfilename("foo/bar/con.xml")
876 >>> checkwinfilename("foo/bar/con.xml")
876 "filename contains 'con', which is reserved on Windows"
877 "filename contains 'con', which is reserved on Windows"
877 >>> checkwinfilename("foo/con.xml/bar")
878 >>> checkwinfilename("foo/con.xml/bar")
878 "filename contains 'con', which is reserved on Windows"
879 "filename contains 'con', which is reserved on Windows"
879 >>> checkwinfilename("foo/bar/xml.con")
880 >>> checkwinfilename("foo/bar/xml.con")
880 >>> checkwinfilename("foo/bar/AUX/bla.txt")
881 >>> checkwinfilename("foo/bar/AUX/bla.txt")
881 "filename contains 'AUX', which is reserved on Windows"
882 "filename contains 'AUX', which is reserved on Windows"
882 >>> checkwinfilename("foo/bar/bla:.txt")
883 >>> checkwinfilename("foo/bar/bla:.txt")
883 "filename contains ':', which is reserved on Windows"
884 "filename contains ':', which is reserved on Windows"
884 >>> checkwinfilename("foo/bar/b\07la.txt")
885 >>> checkwinfilename("foo/bar/b\07la.txt")
885 "filename contains '\\x07', which is invalid on Windows"
886 "filename contains '\\x07', which is invalid on Windows"
886 >>> checkwinfilename("foo/bar/bla ")
887 >>> checkwinfilename("foo/bar/bla ")
887 "filename ends with ' ', which is not allowed on Windows"
888 "filename ends with ' ', which is not allowed on Windows"
888 >>> checkwinfilename("../bar")
889 >>> checkwinfilename("../bar")
889 >>> checkwinfilename("foo\\")
890 >>> checkwinfilename("foo\\")
890 "filename ends with '\\', which is invalid on Windows"
891 "filename ends with '\\', which is invalid on Windows"
891 >>> checkwinfilename("foo\\/bar")
892 >>> checkwinfilename("foo\\/bar")
892 "directory name ends with '\\', which is invalid on Windows"
893 "directory name ends with '\\', which is invalid on Windows"
893 '''
894 '''
894 if path.endswith('\\'):
895 if path.endswith('\\'):
895 return _("filename ends with '\\', which is invalid on Windows")
896 return _("filename ends with '\\', which is invalid on Windows")
896 if '\\/' in path:
897 if '\\/' in path:
897 return _("directory name ends with '\\', which is invalid on Windows")
898 return _("directory name ends with '\\', which is invalid on Windows")
898 for n in path.replace('\\', '/').split('/'):
899 for n in path.replace('\\', '/').split('/'):
899 if not n:
900 if not n:
900 continue
901 continue
901 for c in n:
902 for c in n:
902 if c in _winreservedchars:
903 if c in _winreservedchars:
903 return _("filename contains '%s', which is reserved "
904 return _("filename contains '%s', which is reserved "
904 "on Windows") % c
905 "on Windows") % c
905 if ord(c) <= 31:
906 if ord(c) <= 31:
906 return _("filename contains %r, which is invalid "
907 return _("filename contains %r, which is invalid "
907 "on Windows") % c
908 "on Windows") % c
908 base = n.split('.')[0]
909 base = n.split('.')[0]
909 if base and base.lower() in _winreservednames:
910 if base and base.lower() in _winreservednames:
910 return _("filename contains '%s', which is reserved "
911 return _("filename contains '%s', which is reserved "
911 "on Windows") % base
912 "on Windows") % base
912 t = n[-1]
913 t = n[-1]
913 if t in '. ' and n not in '..':
914 if t in '. ' and n not in '..':
914 return _("filename ends with '%s', which is not allowed "
915 return _("filename ends with '%s', which is not allowed "
915 "on Windows") % t
916 "on Windows") % t
916
917
917 if os.name == 'nt':
918 if os.name == 'nt':
918 checkosfilename = checkwinfilename
919 checkosfilename = checkwinfilename
919 else:
920 else:
920 checkosfilename = platform.checkosfilename
921 checkosfilename = platform.checkosfilename
921
922
922 def makelock(info, pathname):
923 def makelock(info, pathname):
923 try:
924 try:
924 return os.symlink(info, pathname)
925 return os.symlink(info, pathname)
925 except OSError as why:
926 except OSError as why:
926 if why.errno == errno.EEXIST:
927 if why.errno == errno.EEXIST:
927 raise
928 raise
928 except AttributeError: # no symlink in os
929 except AttributeError: # no symlink in os
929 pass
930 pass
930
931
931 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
932 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
932 os.write(ld, info)
933 os.write(ld, info)
933 os.close(ld)
934 os.close(ld)
934
935
935 def readlock(pathname):
936 def readlock(pathname):
936 try:
937 try:
937 return os.readlink(pathname)
938 return os.readlink(pathname)
938 except OSError as why:
939 except OSError as why:
939 if why.errno not in (errno.EINVAL, errno.ENOSYS):
940 if why.errno not in (errno.EINVAL, errno.ENOSYS):
940 raise
941 raise
941 except AttributeError: # no symlink in os
942 except AttributeError: # no symlink in os
942 pass
943 pass
943 fp = posixfile(pathname)
944 fp = posixfile(pathname)
944 r = fp.read()
945 r = fp.read()
945 fp.close()
946 fp.close()
946 return r
947 return r
947
948
948 def fstat(fp):
949 def fstat(fp):
949 '''stat file object that may not have fileno method.'''
950 '''stat file object that may not have fileno method.'''
950 try:
951 try:
951 return os.fstat(fp.fileno())
952 return os.fstat(fp.fileno())
952 except AttributeError:
953 except AttributeError:
953 return os.stat(fp.name)
954 return os.stat(fp.name)
954
955
955 def statmtimesec(st):
956 def statmtimesec(st):
956 return int(st.st_mtime)
957 """Get mtime as integer of seconds
958
959 'int(st.st_mtime)' cannot be used because st.st_mtime is computed as
960 'sec + 1e-9 * nsec' and double-precision floating-point type is too narrow
961 to represent nanoseconds. If 'nsec' is close to 1 sec, 'int(st.st_mtime)'
962 can be 'sec + 1'. (issue4836)
963 """
964 try:
965 return st[stat.ST_MTIME]
966 except TypeError:
967 # osutil.stat doesn't allow index access and its st_mtime is int
968 return st.st_mtime
957
969
958 # File system features
970 # File system features
959
971
960 def checkcase(path):
972 def checkcase(path):
961 """
973 """
962 Return true if the given path is on a case-sensitive filesystem
974 Return true if the given path is on a case-sensitive filesystem
963
975
964 Requires a path (like /foo/.hg) ending with a foldable final
976 Requires a path (like /foo/.hg) ending with a foldable final
965 directory component.
977 directory component.
966 """
978 """
967 s1 = os.lstat(path)
979 s1 = os.lstat(path)
968 d, b = os.path.split(path)
980 d, b = os.path.split(path)
969 b2 = b.upper()
981 b2 = b.upper()
970 if b == b2:
982 if b == b2:
971 b2 = b.lower()
983 b2 = b.lower()
972 if b == b2:
984 if b == b2:
973 return True # no evidence against case sensitivity
985 return True # no evidence against case sensitivity
974 p2 = os.path.join(d, b2)
986 p2 = os.path.join(d, b2)
975 try:
987 try:
976 s2 = os.lstat(p2)
988 s2 = os.lstat(p2)
977 if s2 == s1:
989 if s2 == s1:
978 return False
990 return False
979 return True
991 return True
980 except OSError:
992 except OSError:
981 return True
993 return True
982
994
983 try:
995 try:
984 import re2
996 import re2
985 _re2 = None
997 _re2 = None
986 except ImportError:
998 except ImportError:
987 _re2 = False
999 _re2 = False
988
1000
989 class _re(object):
1001 class _re(object):
990 def _checkre2(self):
1002 def _checkre2(self):
991 global _re2
1003 global _re2
992 try:
1004 try:
993 # check if match works, see issue3964
1005 # check if match works, see issue3964
994 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1006 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
995 except ImportError:
1007 except ImportError:
996 _re2 = False
1008 _re2 = False
997
1009
998 def compile(self, pat, flags=0):
1010 def compile(self, pat, flags=0):
999 '''Compile a regular expression, using re2 if possible
1011 '''Compile a regular expression, using re2 if possible
1000
1012
1001 For best performance, use only re2-compatible regexp features. The
1013 For best performance, use only re2-compatible regexp features. The
1002 only flags from the re module that are re2-compatible are
1014 only flags from the re module that are re2-compatible are
1003 IGNORECASE and MULTILINE.'''
1015 IGNORECASE and MULTILINE.'''
1004 if _re2 is None:
1016 if _re2 is None:
1005 self._checkre2()
1017 self._checkre2()
1006 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1018 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1007 if flags & remod.IGNORECASE:
1019 if flags & remod.IGNORECASE:
1008 pat = '(?i)' + pat
1020 pat = '(?i)' + pat
1009 if flags & remod.MULTILINE:
1021 if flags & remod.MULTILINE:
1010 pat = '(?m)' + pat
1022 pat = '(?m)' + pat
1011 try:
1023 try:
1012 return re2.compile(pat)
1024 return re2.compile(pat)
1013 except re2.error:
1025 except re2.error:
1014 pass
1026 pass
1015 return remod.compile(pat, flags)
1027 return remod.compile(pat, flags)
1016
1028
1017 @propertycache
1029 @propertycache
1018 def escape(self):
1030 def escape(self):
1019 '''Return the version of escape corresponding to self.compile.
1031 '''Return the version of escape corresponding to self.compile.
1020
1032
1021 This is imperfect because whether re2 or re is used for a particular
1033 This is imperfect because whether re2 or re is used for a particular
1022 function depends on the flags, etc, but it's the best we can do.
1034 function depends on the flags, etc, but it's the best we can do.
1023 '''
1035 '''
1024 global _re2
1036 global _re2
1025 if _re2 is None:
1037 if _re2 is None:
1026 self._checkre2()
1038 self._checkre2()
1027 if _re2:
1039 if _re2:
1028 return re2.escape
1040 return re2.escape
1029 else:
1041 else:
1030 return remod.escape
1042 return remod.escape
1031
1043
1032 re = _re()
1044 re = _re()
1033
1045
1034 _fspathcache = {}
1046 _fspathcache = {}
1035 def fspath(name, root):
1047 def fspath(name, root):
1036 '''Get name in the case stored in the filesystem
1048 '''Get name in the case stored in the filesystem
1037
1049
1038 The name should be relative to root, and be normcase-ed for efficiency.
1050 The name should be relative to root, and be normcase-ed for efficiency.
1039
1051
1040 Note that this function is unnecessary, and should not be
1052 Note that this function is unnecessary, and should not be
1041 called, for case-sensitive filesystems (simply because it's expensive).
1053 called, for case-sensitive filesystems (simply because it's expensive).
1042
1054
1043 The root should be normcase-ed, too.
1055 The root should be normcase-ed, too.
1044 '''
1056 '''
1045 def _makefspathcacheentry(dir):
1057 def _makefspathcacheentry(dir):
1046 return dict((normcase(n), n) for n in os.listdir(dir))
1058 return dict((normcase(n), n) for n in os.listdir(dir))
1047
1059
1048 seps = os.sep
1060 seps = os.sep
1049 if os.altsep:
1061 if os.altsep:
1050 seps = seps + os.altsep
1062 seps = seps + os.altsep
1051 # Protect backslashes. This gets silly very quickly.
1063 # Protect backslashes. This gets silly very quickly.
1052 seps.replace('\\','\\\\')
1064 seps.replace('\\','\\\\')
1053 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1065 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1054 dir = os.path.normpath(root)
1066 dir = os.path.normpath(root)
1055 result = []
1067 result = []
1056 for part, sep in pattern.findall(name):
1068 for part, sep in pattern.findall(name):
1057 if sep:
1069 if sep:
1058 result.append(sep)
1070 result.append(sep)
1059 continue
1071 continue
1060
1072
1061 if dir not in _fspathcache:
1073 if dir not in _fspathcache:
1062 _fspathcache[dir] = _makefspathcacheentry(dir)
1074 _fspathcache[dir] = _makefspathcacheentry(dir)
1063 contents = _fspathcache[dir]
1075 contents = _fspathcache[dir]
1064
1076
1065 found = contents.get(part)
1077 found = contents.get(part)
1066 if not found:
1078 if not found:
1067 # retry "once per directory" per "dirstate.walk" which
1079 # retry "once per directory" per "dirstate.walk" which
1068 # may take place for each patches of "hg qpush", for example
1080 # may take place for each patches of "hg qpush", for example
1069 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1081 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1070 found = contents.get(part)
1082 found = contents.get(part)
1071
1083
1072 result.append(found or part)
1084 result.append(found or part)
1073 dir = os.path.join(dir, part)
1085 dir = os.path.join(dir, part)
1074
1086
1075 return ''.join(result)
1087 return ''.join(result)
1076
1088
1077 def checknlink(testfile):
1089 def checknlink(testfile):
1078 '''check whether hardlink count reporting works properly'''
1090 '''check whether hardlink count reporting works properly'''
1079
1091
1080 # testfile may be open, so we need a separate file for checking to
1092 # testfile may be open, so we need a separate file for checking to
1081 # work around issue2543 (or testfile may get lost on Samba shares)
1093 # work around issue2543 (or testfile may get lost on Samba shares)
1082 f1 = testfile + ".hgtmp1"
1094 f1 = testfile + ".hgtmp1"
1083 if os.path.lexists(f1):
1095 if os.path.lexists(f1):
1084 return False
1096 return False
1085 try:
1097 try:
1086 posixfile(f1, 'w').close()
1098 posixfile(f1, 'w').close()
1087 except IOError:
1099 except IOError:
1088 return False
1100 return False
1089
1101
1090 f2 = testfile + ".hgtmp2"
1102 f2 = testfile + ".hgtmp2"
1091 fd = None
1103 fd = None
1092 try:
1104 try:
1093 oslink(f1, f2)
1105 oslink(f1, f2)
1094 # nlinks() may behave differently for files on Windows shares if
1106 # nlinks() may behave differently for files on Windows shares if
1095 # the file is open.
1107 # the file is open.
1096 fd = posixfile(f2)
1108 fd = posixfile(f2)
1097 return nlinks(f2) > 1
1109 return nlinks(f2) > 1
1098 except OSError:
1110 except OSError:
1099 return False
1111 return False
1100 finally:
1112 finally:
1101 if fd is not None:
1113 if fd is not None:
1102 fd.close()
1114 fd.close()
1103 for f in (f1, f2):
1115 for f in (f1, f2):
1104 try:
1116 try:
1105 os.unlink(f)
1117 os.unlink(f)
1106 except OSError:
1118 except OSError:
1107 pass
1119 pass
1108
1120
1109 def endswithsep(path):
1121 def endswithsep(path):
1110 '''Check path ends with os.sep or os.altsep.'''
1122 '''Check path ends with os.sep or os.altsep.'''
1111 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1123 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1112
1124
1113 def splitpath(path):
1125 def splitpath(path):
1114 '''Split path by os.sep.
1126 '''Split path by os.sep.
1115 Note that this function does not use os.altsep because this is
1127 Note that this function does not use os.altsep because this is
1116 an alternative of simple "xxx.split(os.sep)".
1128 an alternative of simple "xxx.split(os.sep)".
1117 It is recommended to use os.path.normpath() before using this
1129 It is recommended to use os.path.normpath() before using this
1118 function if need.'''
1130 function if need.'''
1119 return path.split(os.sep)
1131 return path.split(os.sep)
1120
1132
1121 def gui():
1133 def gui():
1122 '''Are we running in a GUI?'''
1134 '''Are we running in a GUI?'''
1123 if sys.platform == 'darwin':
1135 if sys.platform == 'darwin':
1124 if 'SSH_CONNECTION' in os.environ:
1136 if 'SSH_CONNECTION' in os.environ:
1125 # handle SSH access to a box where the user is logged in
1137 # handle SSH access to a box where the user is logged in
1126 return False
1138 return False
1127 elif getattr(osutil, 'isgui', None):
1139 elif getattr(osutil, 'isgui', None):
1128 # check if a CoreGraphics session is available
1140 # check if a CoreGraphics session is available
1129 return osutil.isgui()
1141 return osutil.isgui()
1130 else:
1142 else:
1131 # pure build; use a safe default
1143 # pure build; use a safe default
1132 return True
1144 return True
1133 else:
1145 else:
1134 return os.name == "nt" or os.environ.get("DISPLAY")
1146 return os.name == "nt" or os.environ.get("DISPLAY")
1135
1147
1136 def mktempcopy(name, emptyok=False, createmode=None):
1148 def mktempcopy(name, emptyok=False, createmode=None):
1137 """Create a temporary file with the same contents from name
1149 """Create a temporary file with the same contents from name
1138
1150
1139 The permission bits are copied from the original file.
1151 The permission bits are copied from the original file.
1140
1152
1141 If the temporary file is going to be truncated immediately, you
1153 If the temporary file is going to be truncated immediately, you
1142 can use emptyok=True as an optimization.
1154 can use emptyok=True as an optimization.
1143
1155
1144 Returns the name of the temporary file.
1156 Returns the name of the temporary file.
1145 """
1157 """
1146 d, fn = os.path.split(name)
1158 d, fn = os.path.split(name)
1147 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1159 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1148 os.close(fd)
1160 os.close(fd)
1149 # Temporary files are created with mode 0600, which is usually not
1161 # Temporary files are created with mode 0600, which is usually not
1150 # what we want. If the original file already exists, just copy
1162 # what we want. If the original file already exists, just copy
1151 # its mode. Otherwise, manually obey umask.
1163 # its mode. Otherwise, manually obey umask.
1152 copymode(name, temp, createmode)
1164 copymode(name, temp, createmode)
1153 if emptyok:
1165 if emptyok:
1154 return temp
1166 return temp
1155 try:
1167 try:
1156 try:
1168 try:
1157 ifp = posixfile(name, "rb")
1169 ifp = posixfile(name, "rb")
1158 except IOError as inst:
1170 except IOError as inst:
1159 if inst.errno == errno.ENOENT:
1171 if inst.errno == errno.ENOENT:
1160 return temp
1172 return temp
1161 if not getattr(inst, 'filename', None):
1173 if not getattr(inst, 'filename', None):
1162 inst.filename = name
1174 inst.filename = name
1163 raise
1175 raise
1164 ofp = posixfile(temp, "wb")
1176 ofp = posixfile(temp, "wb")
1165 for chunk in filechunkiter(ifp):
1177 for chunk in filechunkiter(ifp):
1166 ofp.write(chunk)
1178 ofp.write(chunk)
1167 ifp.close()
1179 ifp.close()
1168 ofp.close()
1180 ofp.close()
1169 except: # re-raises
1181 except: # re-raises
1170 try: os.unlink(temp)
1182 try: os.unlink(temp)
1171 except OSError: pass
1183 except OSError: pass
1172 raise
1184 raise
1173 return temp
1185 return temp
1174
1186
1175 class atomictempfile(object):
1187 class atomictempfile(object):
1176 '''writable file object that atomically updates a file
1188 '''writable file object that atomically updates a file
1177
1189
1178 All writes will go to a temporary copy of the original file. Call
1190 All writes will go to a temporary copy of the original file. Call
1179 close() when you are done writing, and atomictempfile will rename
1191 close() when you are done writing, and atomictempfile will rename
1180 the temporary copy to the original name, making the changes
1192 the temporary copy to the original name, making the changes
1181 visible. If the object is destroyed without being closed, all your
1193 visible. If the object is destroyed without being closed, all your
1182 writes are discarded.
1194 writes are discarded.
1183 '''
1195 '''
1184 def __init__(self, name, mode='w+b', createmode=None):
1196 def __init__(self, name, mode='w+b', createmode=None):
1185 self.__name = name # permanent name
1197 self.__name = name # permanent name
1186 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1198 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1187 createmode=createmode)
1199 createmode=createmode)
1188 self._fp = posixfile(self._tempname, mode)
1200 self._fp = posixfile(self._tempname, mode)
1189
1201
1190 # delegated methods
1202 # delegated methods
1191 self.write = self._fp.write
1203 self.write = self._fp.write
1192 self.seek = self._fp.seek
1204 self.seek = self._fp.seek
1193 self.tell = self._fp.tell
1205 self.tell = self._fp.tell
1194 self.fileno = self._fp.fileno
1206 self.fileno = self._fp.fileno
1195
1207
1196 def close(self):
1208 def close(self):
1197 if not self._fp.closed:
1209 if not self._fp.closed:
1198 self._fp.close()
1210 self._fp.close()
1199 rename(self._tempname, localpath(self.__name))
1211 rename(self._tempname, localpath(self.__name))
1200
1212
1201 def discard(self):
1213 def discard(self):
1202 if not self._fp.closed:
1214 if not self._fp.closed:
1203 try:
1215 try:
1204 os.unlink(self._tempname)
1216 os.unlink(self._tempname)
1205 except OSError:
1217 except OSError:
1206 pass
1218 pass
1207 self._fp.close()
1219 self._fp.close()
1208
1220
1209 def __del__(self):
1221 def __del__(self):
1210 if safehasattr(self, '_fp'): # constructor actually did something
1222 if safehasattr(self, '_fp'): # constructor actually did something
1211 self.discard()
1223 self.discard()
1212
1224
1213 def makedirs(name, mode=None, notindexed=False):
1225 def makedirs(name, mode=None, notindexed=False):
1214 """recursive directory creation with parent mode inheritance"""
1226 """recursive directory creation with parent mode inheritance"""
1215 try:
1227 try:
1216 makedir(name, notindexed)
1228 makedir(name, notindexed)
1217 except OSError as err:
1229 except OSError as err:
1218 if err.errno == errno.EEXIST:
1230 if err.errno == errno.EEXIST:
1219 return
1231 return
1220 if err.errno != errno.ENOENT or not name:
1232 if err.errno != errno.ENOENT or not name:
1221 raise
1233 raise
1222 parent = os.path.dirname(os.path.abspath(name))
1234 parent = os.path.dirname(os.path.abspath(name))
1223 if parent == name:
1235 if parent == name:
1224 raise
1236 raise
1225 makedirs(parent, mode, notindexed)
1237 makedirs(parent, mode, notindexed)
1226 makedir(name, notindexed)
1238 makedir(name, notindexed)
1227 if mode is not None:
1239 if mode is not None:
1228 os.chmod(name, mode)
1240 os.chmod(name, mode)
1229
1241
1230 def ensuredirs(name, mode=None, notindexed=False):
1242 def ensuredirs(name, mode=None, notindexed=False):
1231 """race-safe recursive directory creation
1243 """race-safe recursive directory creation
1232
1244
1233 Newly created directories are marked as "not to be indexed by
1245 Newly created directories are marked as "not to be indexed by
1234 the content indexing service", if ``notindexed`` is specified
1246 the content indexing service", if ``notindexed`` is specified
1235 for "write" mode access.
1247 for "write" mode access.
1236 """
1248 """
1237 if os.path.isdir(name):
1249 if os.path.isdir(name):
1238 return
1250 return
1239 parent = os.path.dirname(os.path.abspath(name))
1251 parent = os.path.dirname(os.path.abspath(name))
1240 if parent != name:
1252 if parent != name:
1241 ensuredirs(parent, mode, notindexed)
1253 ensuredirs(parent, mode, notindexed)
1242 try:
1254 try:
1243 makedir(name, notindexed)
1255 makedir(name, notindexed)
1244 except OSError as err:
1256 except OSError as err:
1245 if err.errno == errno.EEXIST and os.path.isdir(name):
1257 if err.errno == errno.EEXIST and os.path.isdir(name):
1246 # someone else seems to have won a directory creation race
1258 # someone else seems to have won a directory creation race
1247 return
1259 return
1248 raise
1260 raise
1249 if mode is not None:
1261 if mode is not None:
1250 os.chmod(name, mode)
1262 os.chmod(name, mode)
1251
1263
1252 def readfile(path):
1264 def readfile(path):
1253 fp = open(path, 'rb')
1265 fp = open(path, 'rb')
1254 try:
1266 try:
1255 return fp.read()
1267 return fp.read()
1256 finally:
1268 finally:
1257 fp.close()
1269 fp.close()
1258
1270
1259 def writefile(path, text):
1271 def writefile(path, text):
1260 fp = open(path, 'wb')
1272 fp = open(path, 'wb')
1261 try:
1273 try:
1262 fp.write(text)
1274 fp.write(text)
1263 finally:
1275 finally:
1264 fp.close()
1276 fp.close()
1265
1277
1266 def appendfile(path, text):
1278 def appendfile(path, text):
1267 fp = open(path, 'ab')
1279 fp = open(path, 'ab')
1268 try:
1280 try:
1269 fp.write(text)
1281 fp.write(text)
1270 finally:
1282 finally:
1271 fp.close()
1283 fp.close()
1272
1284
1273 class chunkbuffer(object):
1285 class chunkbuffer(object):
1274 """Allow arbitrary sized chunks of data to be efficiently read from an
1286 """Allow arbitrary sized chunks of data to be efficiently read from an
1275 iterator over chunks of arbitrary size."""
1287 iterator over chunks of arbitrary size."""
1276
1288
1277 def __init__(self, in_iter):
1289 def __init__(self, in_iter):
1278 """in_iter is the iterator that's iterating over the input chunks.
1290 """in_iter is the iterator that's iterating over the input chunks.
1279 targetsize is how big a buffer to try to maintain."""
1291 targetsize is how big a buffer to try to maintain."""
1280 def splitbig(chunks):
1292 def splitbig(chunks):
1281 for chunk in chunks:
1293 for chunk in chunks:
1282 if len(chunk) > 2**20:
1294 if len(chunk) > 2**20:
1283 pos = 0
1295 pos = 0
1284 while pos < len(chunk):
1296 while pos < len(chunk):
1285 end = pos + 2 ** 18
1297 end = pos + 2 ** 18
1286 yield chunk[pos:end]
1298 yield chunk[pos:end]
1287 pos = end
1299 pos = end
1288 else:
1300 else:
1289 yield chunk
1301 yield chunk
1290 self.iter = splitbig(in_iter)
1302 self.iter = splitbig(in_iter)
1291 self._queue = collections.deque()
1303 self._queue = collections.deque()
1292 self._chunkoffset = 0
1304 self._chunkoffset = 0
1293
1305
1294 def read(self, l=None):
1306 def read(self, l=None):
1295 """Read L bytes of data from the iterator of chunks of data.
1307 """Read L bytes of data from the iterator of chunks of data.
1296 Returns less than L bytes if the iterator runs dry.
1308 Returns less than L bytes if the iterator runs dry.
1297
1309
1298 If size parameter is omitted, read everything"""
1310 If size parameter is omitted, read everything"""
1299 if l is None:
1311 if l is None:
1300 return ''.join(self.iter)
1312 return ''.join(self.iter)
1301
1313
1302 left = l
1314 left = l
1303 buf = []
1315 buf = []
1304 queue = self._queue
1316 queue = self._queue
1305 while left > 0:
1317 while left > 0:
1306 # refill the queue
1318 # refill the queue
1307 if not queue:
1319 if not queue:
1308 target = 2**18
1320 target = 2**18
1309 for chunk in self.iter:
1321 for chunk in self.iter:
1310 queue.append(chunk)
1322 queue.append(chunk)
1311 target -= len(chunk)
1323 target -= len(chunk)
1312 if target <= 0:
1324 if target <= 0:
1313 break
1325 break
1314 if not queue:
1326 if not queue:
1315 break
1327 break
1316
1328
1317 # The easy way to do this would be to queue.popleft(), modify the
1329 # The easy way to do this would be to queue.popleft(), modify the
1318 # chunk (if necessary), then queue.appendleft(). However, for cases
1330 # chunk (if necessary), then queue.appendleft(). However, for cases
1319 # where we read partial chunk content, this incurs 2 dequeue
1331 # where we read partial chunk content, this incurs 2 dequeue
1320 # mutations and creates a new str for the remaining chunk in the
1332 # mutations and creates a new str for the remaining chunk in the
1321 # queue. Our code below avoids this overhead.
1333 # queue. Our code below avoids this overhead.
1322
1334
1323 chunk = queue[0]
1335 chunk = queue[0]
1324 chunkl = len(chunk)
1336 chunkl = len(chunk)
1325 offset = self._chunkoffset
1337 offset = self._chunkoffset
1326
1338
1327 # Use full chunk.
1339 # Use full chunk.
1328 if offset == 0 and left >= chunkl:
1340 if offset == 0 and left >= chunkl:
1329 left -= chunkl
1341 left -= chunkl
1330 queue.popleft()
1342 queue.popleft()
1331 buf.append(chunk)
1343 buf.append(chunk)
1332 # self._chunkoffset remains at 0.
1344 # self._chunkoffset remains at 0.
1333 continue
1345 continue
1334
1346
1335 chunkremaining = chunkl - offset
1347 chunkremaining = chunkl - offset
1336
1348
1337 # Use all of unconsumed part of chunk.
1349 # Use all of unconsumed part of chunk.
1338 if left >= chunkremaining:
1350 if left >= chunkremaining:
1339 left -= chunkremaining
1351 left -= chunkremaining
1340 queue.popleft()
1352 queue.popleft()
1341 # offset == 0 is enabled by block above, so this won't merely
1353 # offset == 0 is enabled by block above, so this won't merely
1342 # copy via ``chunk[0:]``.
1354 # copy via ``chunk[0:]``.
1343 buf.append(chunk[offset:])
1355 buf.append(chunk[offset:])
1344 self._chunkoffset = 0
1356 self._chunkoffset = 0
1345
1357
1346 # Partial chunk needed.
1358 # Partial chunk needed.
1347 else:
1359 else:
1348 buf.append(chunk[offset:offset + left])
1360 buf.append(chunk[offset:offset + left])
1349 self._chunkoffset += left
1361 self._chunkoffset += left
1350 left -= chunkremaining
1362 left -= chunkremaining
1351
1363
1352 return ''.join(buf)
1364 return ''.join(buf)
1353
1365
1354 def filechunkiter(f, size=65536, limit=None):
1366 def filechunkiter(f, size=65536, limit=None):
1355 """Create a generator that produces the data in the file size
1367 """Create a generator that produces the data in the file size
1356 (default 65536) bytes at a time, up to optional limit (default is
1368 (default 65536) bytes at a time, up to optional limit (default is
1357 to read all data). Chunks may be less than size bytes if the
1369 to read all data). Chunks may be less than size bytes if the
1358 chunk is the last chunk in the file, or the file is a socket or
1370 chunk is the last chunk in the file, or the file is a socket or
1359 some other type of file that sometimes reads less data than is
1371 some other type of file that sometimes reads less data than is
1360 requested."""
1372 requested."""
1361 assert size >= 0
1373 assert size >= 0
1362 assert limit is None or limit >= 0
1374 assert limit is None or limit >= 0
1363 while True:
1375 while True:
1364 if limit is None:
1376 if limit is None:
1365 nbytes = size
1377 nbytes = size
1366 else:
1378 else:
1367 nbytes = min(limit, size)
1379 nbytes = min(limit, size)
1368 s = nbytes and f.read(nbytes)
1380 s = nbytes and f.read(nbytes)
1369 if not s:
1381 if not s:
1370 break
1382 break
1371 if limit:
1383 if limit:
1372 limit -= len(s)
1384 limit -= len(s)
1373 yield s
1385 yield s
1374
1386
1375 def makedate(timestamp=None):
1387 def makedate(timestamp=None):
1376 '''Return a unix timestamp (or the current time) as a (unixtime,
1388 '''Return a unix timestamp (or the current time) as a (unixtime,
1377 offset) tuple based off the local timezone.'''
1389 offset) tuple based off the local timezone.'''
1378 if timestamp is None:
1390 if timestamp is None:
1379 timestamp = time.time()
1391 timestamp = time.time()
1380 if timestamp < 0:
1392 if timestamp < 0:
1381 hint = _("check your clock")
1393 hint = _("check your clock")
1382 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1394 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1383 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1395 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1384 datetime.datetime.fromtimestamp(timestamp))
1396 datetime.datetime.fromtimestamp(timestamp))
1385 tz = delta.days * 86400 + delta.seconds
1397 tz = delta.days * 86400 + delta.seconds
1386 return timestamp, tz
1398 return timestamp, tz
1387
1399
1388 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1400 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1389 """represent a (unixtime, offset) tuple as a localized time.
1401 """represent a (unixtime, offset) tuple as a localized time.
1390 unixtime is seconds since the epoch, and offset is the time zone's
1402 unixtime is seconds since the epoch, and offset is the time zone's
1391 number of seconds away from UTC. if timezone is false, do not
1403 number of seconds away from UTC. if timezone is false, do not
1392 append time zone to string."""
1404 append time zone to string."""
1393 t, tz = date or makedate()
1405 t, tz = date or makedate()
1394 if t < 0:
1406 if t < 0:
1395 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1407 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1396 tz = 0
1408 tz = 0
1397 if "%1" in format or "%2" in format or "%z" in format:
1409 if "%1" in format or "%2" in format or "%z" in format:
1398 sign = (tz > 0) and "-" or "+"
1410 sign = (tz > 0) and "-" or "+"
1399 minutes = abs(tz) // 60
1411 minutes = abs(tz) // 60
1400 format = format.replace("%z", "%1%2")
1412 format = format.replace("%z", "%1%2")
1401 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1413 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1402 format = format.replace("%2", "%02d" % (minutes % 60))
1414 format = format.replace("%2", "%02d" % (minutes % 60))
1403 try:
1415 try:
1404 t = time.gmtime(float(t) - tz)
1416 t = time.gmtime(float(t) - tz)
1405 except ValueError:
1417 except ValueError:
1406 # time was out of range
1418 # time was out of range
1407 t = time.gmtime(sys.maxint)
1419 t = time.gmtime(sys.maxint)
1408 s = time.strftime(format, t)
1420 s = time.strftime(format, t)
1409 return s
1421 return s
1410
1422
1411 def shortdate(date=None):
1423 def shortdate(date=None):
1412 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1424 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1413 return datestr(date, format='%Y-%m-%d')
1425 return datestr(date, format='%Y-%m-%d')
1414
1426
1415 def parsetimezone(tz):
1427 def parsetimezone(tz):
1416 """parse a timezone string and return an offset integer"""
1428 """parse a timezone string and return an offset integer"""
1417 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1429 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1418 sign = (tz[0] == "+") and 1 or -1
1430 sign = (tz[0] == "+") and 1 or -1
1419 hours = int(tz[1:3])
1431 hours = int(tz[1:3])
1420 minutes = int(tz[3:5])
1432 minutes = int(tz[3:5])
1421 return -sign * (hours * 60 + minutes) * 60
1433 return -sign * (hours * 60 + minutes) * 60
1422 if tz == "GMT" or tz == "UTC":
1434 if tz == "GMT" or tz == "UTC":
1423 return 0
1435 return 0
1424 return None
1436 return None
1425
1437
1426 def strdate(string, format, defaults=[]):
1438 def strdate(string, format, defaults=[]):
1427 """parse a localized time string and return a (unixtime, offset) tuple.
1439 """parse a localized time string and return a (unixtime, offset) tuple.
1428 if the string cannot be parsed, ValueError is raised."""
1440 if the string cannot be parsed, ValueError is raised."""
1429 # NOTE: unixtime = localunixtime + offset
1441 # NOTE: unixtime = localunixtime + offset
1430 offset, date = parsetimezone(string.split()[-1]), string
1442 offset, date = parsetimezone(string.split()[-1]), string
1431 if offset is not None:
1443 if offset is not None:
1432 date = " ".join(string.split()[:-1])
1444 date = " ".join(string.split()[:-1])
1433
1445
1434 # add missing elements from defaults
1446 # add missing elements from defaults
1435 usenow = False # default to using biased defaults
1447 usenow = False # default to using biased defaults
1436 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1448 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1437 found = [True for p in part if ("%"+p) in format]
1449 found = [True for p in part if ("%"+p) in format]
1438 if not found:
1450 if not found:
1439 date += "@" + defaults[part][usenow]
1451 date += "@" + defaults[part][usenow]
1440 format += "@%" + part[0]
1452 format += "@%" + part[0]
1441 else:
1453 else:
1442 # We've found a specific time element, less specific time
1454 # We've found a specific time element, less specific time
1443 # elements are relative to today
1455 # elements are relative to today
1444 usenow = True
1456 usenow = True
1445
1457
1446 timetuple = time.strptime(date, format)
1458 timetuple = time.strptime(date, format)
1447 localunixtime = int(calendar.timegm(timetuple))
1459 localunixtime = int(calendar.timegm(timetuple))
1448 if offset is None:
1460 if offset is None:
1449 # local timezone
1461 # local timezone
1450 unixtime = int(time.mktime(timetuple))
1462 unixtime = int(time.mktime(timetuple))
1451 offset = unixtime - localunixtime
1463 offset = unixtime - localunixtime
1452 else:
1464 else:
1453 unixtime = localunixtime + offset
1465 unixtime = localunixtime + offset
1454 return unixtime, offset
1466 return unixtime, offset
1455
1467
1456 def parsedate(date, formats=None, bias=None):
1468 def parsedate(date, formats=None, bias=None):
1457 """parse a localized date/time and return a (unixtime, offset) tuple.
1469 """parse a localized date/time and return a (unixtime, offset) tuple.
1458
1470
1459 The date may be a "unixtime offset" string or in one of the specified
1471 The date may be a "unixtime offset" string or in one of the specified
1460 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1472 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1461
1473
1462 >>> parsedate(' today ') == parsedate(\
1474 >>> parsedate(' today ') == parsedate(\
1463 datetime.date.today().strftime('%b %d'))
1475 datetime.date.today().strftime('%b %d'))
1464 True
1476 True
1465 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1477 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1466 datetime.timedelta(days=1)\
1478 datetime.timedelta(days=1)\
1467 ).strftime('%b %d'))
1479 ).strftime('%b %d'))
1468 True
1480 True
1469 >>> now, tz = makedate()
1481 >>> now, tz = makedate()
1470 >>> strnow, strtz = parsedate('now')
1482 >>> strnow, strtz = parsedate('now')
1471 >>> (strnow - now) < 1
1483 >>> (strnow - now) < 1
1472 True
1484 True
1473 >>> tz == strtz
1485 >>> tz == strtz
1474 True
1486 True
1475 """
1487 """
1476 if bias is None:
1488 if bias is None:
1477 bias = {}
1489 bias = {}
1478 if not date:
1490 if not date:
1479 return 0, 0
1491 return 0, 0
1480 if isinstance(date, tuple) and len(date) == 2:
1492 if isinstance(date, tuple) and len(date) == 2:
1481 return date
1493 return date
1482 if not formats:
1494 if not formats:
1483 formats = defaultdateformats
1495 formats = defaultdateformats
1484 date = date.strip()
1496 date = date.strip()
1485
1497
1486 if date == 'now' or date == _('now'):
1498 if date == 'now' or date == _('now'):
1487 return makedate()
1499 return makedate()
1488 if date == 'today' or date == _('today'):
1500 if date == 'today' or date == _('today'):
1489 date = datetime.date.today().strftime('%b %d')
1501 date = datetime.date.today().strftime('%b %d')
1490 elif date == 'yesterday' or date == _('yesterday'):
1502 elif date == 'yesterday' or date == _('yesterday'):
1491 date = (datetime.date.today() -
1503 date = (datetime.date.today() -
1492 datetime.timedelta(days=1)).strftime('%b %d')
1504 datetime.timedelta(days=1)).strftime('%b %d')
1493
1505
1494 try:
1506 try:
1495 when, offset = map(int, date.split(' '))
1507 when, offset = map(int, date.split(' '))
1496 except ValueError:
1508 except ValueError:
1497 # fill out defaults
1509 # fill out defaults
1498 now = makedate()
1510 now = makedate()
1499 defaults = {}
1511 defaults = {}
1500 for part in ("d", "mb", "yY", "HI", "M", "S"):
1512 for part in ("d", "mb", "yY", "HI", "M", "S"):
1501 # this piece is for rounding the specific end of unknowns
1513 # this piece is for rounding the specific end of unknowns
1502 b = bias.get(part)
1514 b = bias.get(part)
1503 if b is None:
1515 if b is None:
1504 if part[0] in "HMS":
1516 if part[0] in "HMS":
1505 b = "00"
1517 b = "00"
1506 else:
1518 else:
1507 b = "0"
1519 b = "0"
1508
1520
1509 # this piece is for matching the generic end to today's date
1521 # this piece is for matching the generic end to today's date
1510 n = datestr(now, "%" + part[0])
1522 n = datestr(now, "%" + part[0])
1511
1523
1512 defaults[part] = (b, n)
1524 defaults[part] = (b, n)
1513
1525
1514 for format in formats:
1526 for format in formats:
1515 try:
1527 try:
1516 when, offset = strdate(date, format, defaults)
1528 when, offset = strdate(date, format, defaults)
1517 except (ValueError, OverflowError):
1529 except (ValueError, OverflowError):
1518 pass
1530 pass
1519 else:
1531 else:
1520 break
1532 break
1521 else:
1533 else:
1522 raise Abort(_('invalid date: %r') % date)
1534 raise Abort(_('invalid date: %r') % date)
1523 # validate explicit (probably user-specified) date and
1535 # validate explicit (probably user-specified) date and
1524 # time zone offset. values must fit in signed 32 bits for
1536 # time zone offset. values must fit in signed 32 bits for
1525 # current 32-bit linux runtimes. timezones go from UTC-12
1537 # current 32-bit linux runtimes. timezones go from UTC-12
1526 # to UTC+14
1538 # to UTC+14
1527 if abs(when) > 0x7fffffff:
1539 if abs(when) > 0x7fffffff:
1528 raise Abort(_('date exceeds 32 bits: %d') % when)
1540 raise Abort(_('date exceeds 32 bits: %d') % when)
1529 if when < 0:
1541 if when < 0:
1530 raise Abort(_('negative date value: %d') % when)
1542 raise Abort(_('negative date value: %d') % when)
1531 if offset < -50400 or offset > 43200:
1543 if offset < -50400 or offset > 43200:
1532 raise Abort(_('impossible time zone offset: %d') % offset)
1544 raise Abort(_('impossible time zone offset: %d') % offset)
1533 return when, offset
1545 return when, offset
1534
1546
1535 def matchdate(date):
1547 def matchdate(date):
1536 """Return a function that matches a given date match specifier
1548 """Return a function that matches a given date match specifier
1537
1549
1538 Formats include:
1550 Formats include:
1539
1551
1540 '{date}' match a given date to the accuracy provided
1552 '{date}' match a given date to the accuracy provided
1541
1553
1542 '<{date}' on or before a given date
1554 '<{date}' on or before a given date
1543
1555
1544 '>{date}' on or after a given date
1556 '>{date}' on or after a given date
1545
1557
1546 >>> p1 = parsedate("10:29:59")
1558 >>> p1 = parsedate("10:29:59")
1547 >>> p2 = parsedate("10:30:00")
1559 >>> p2 = parsedate("10:30:00")
1548 >>> p3 = parsedate("10:30:59")
1560 >>> p3 = parsedate("10:30:59")
1549 >>> p4 = parsedate("10:31:00")
1561 >>> p4 = parsedate("10:31:00")
1550 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1562 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1551 >>> f = matchdate("10:30")
1563 >>> f = matchdate("10:30")
1552 >>> f(p1[0])
1564 >>> f(p1[0])
1553 False
1565 False
1554 >>> f(p2[0])
1566 >>> f(p2[0])
1555 True
1567 True
1556 >>> f(p3[0])
1568 >>> f(p3[0])
1557 True
1569 True
1558 >>> f(p4[0])
1570 >>> f(p4[0])
1559 False
1571 False
1560 >>> f(p5[0])
1572 >>> f(p5[0])
1561 False
1573 False
1562 """
1574 """
1563
1575
1564 def lower(date):
1576 def lower(date):
1565 d = {'mb': "1", 'd': "1"}
1577 d = {'mb': "1", 'd': "1"}
1566 return parsedate(date, extendeddateformats, d)[0]
1578 return parsedate(date, extendeddateformats, d)[0]
1567
1579
1568 def upper(date):
1580 def upper(date):
1569 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1581 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1570 for days in ("31", "30", "29"):
1582 for days in ("31", "30", "29"):
1571 try:
1583 try:
1572 d["d"] = days
1584 d["d"] = days
1573 return parsedate(date, extendeddateformats, d)[0]
1585 return parsedate(date, extendeddateformats, d)[0]
1574 except Abort:
1586 except Abort:
1575 pass
1587 pass
1576 d["d"] = "28"
1588 d["d"] = "28"
1577 return parsedate(date, extendeddateformats, d)[0]
1589 return parsedate(date, extendeddateformats, d)[0]
1578
1590
1579 date = date.strip()
1591 date = date.strip()
1580
1592
1581 if not date:
1593 if not date:
1582 raise Abort(_("dates cannot consist entirely of whitespace"))
1594 raise Abort(_("dates cannot consist entirely of whitespace"))
1583 elif date[0] == "<":
1595 elif date[0] == "<":
1584 if not date[1:]:
1596 if not date[1:]:
1585 raise Abort(_("invalid day spec, use '<DATE'"))
1597 raise Abort(_("invalid day spec, use '<DATE'"))
1586 when = upper(date[1:])
1598 when = upper(date[1:])
1587 return lambda x: x <= when
1599 return lambda x: x <= when
1588 elif date[0] == ">":
1600 elif date[0] == ">":
1589 if not date[1:]:
1601 if not date[1:]:
1590 raise Abort(_("invalid day spec, use '>DATE'"))
1602 raise Abort(_("invalid day spec, use '>DATE'"))
1591 when = lower(date[1:])
1603 when = lower(date[1:])
1592 return lambda x: x >= when
1604 return lambda x: x >= when
1593 elif date[0] == "-":
1605 elif date[0] == "-":
1594 try:
1606 try:
1595 days = int(date[1:])
1607 days = int(date[1:])
1596 except ValueError:
1608 except ValueError:
1597 raise Abort(_("invalid day spec: %s") % date[1:])
1609 raise Abort(_("invalid day spec: %s") % date[1:])
1598 if days < 0:
1610 if days < 0:
1599 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1611 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1600 % date[1:])
1612 % date[1:])
1601 when = makedate()[0] - days * 3600 * 24
1613 when = makedate()[0] - days * 3600 * 24
1602 return lambda x: x >= when
1614 return lambda x: x >= when
1603 elif " to " in date:
1615 elif " to " in date:
1604 a, b = date.split(" to ")
1616 a, b = date.split(" to ")
1605 start, stop = lower(a), upper(b)
1617 start, stop = lower(a), upper(b)
1606 return lambda x: x >= start and x <= stop
1618 return lambda x: x >= start and x <= stop
1607 else:
1619 else:
1608 start, stop = lower(date), upper(date)
1620 start, stop = lower(date), upper(date)
1609 return lambda x: x >= start and x <= stop
1621 return lambda x: x >= start and x <= stop
1610
1622
1611 def stringmatcher(pattern):
1623 def stringmatcher(pattern):
1612 """
1624 """
1613 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1625 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1614 returns the matcher name, pattern, and matcher function.
1626 returns the matcher name, pattern, and matcher function.
1615 missing or unknown prefixes are treated as literal matches.
1627 missing or unknown prefixes are treated as literal matches.
1616
1628
1617 helper for tests:
1629 helper for tests:
1618 >>> def test(pattern, *tests):
1630 >>> def test(pattern, *tests):
1619 ... kind, pattern, matcher = stringmatcher(pattern)
1631 ... kind, pattern, matcher = stringmatcher(pattern)
1620 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1632 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1621
1633
1622 exact matching (no prefix):
1634 exact matching (no prefix):
1623 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1635 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1624 ('literal', 'abcdefg', [False, False, True])
1636 ('literal', 'abcdefg', [False, False, True])
1625
1637
1626 regex matching ('re:' prefix)
1638 regex matching ('re:' prefix)
1627 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1639 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1628 ('re', 'a.+b', [False, False, True])
1640 ('re', 'a.+b', [False, False, True])
1629
1641
1630 force exact matches ('literal:' prefix)
1642 force exact matches ('literal:' prefix)
1631 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1643 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1632 ('literal', 're:foobar', [False, True])
1644 ('literal', 're:foobar', [False, True])
1633
1645
1634 unknown prefixes are ignored and treated as literals
1646 unknown prefixes are ignored and treated as literals
1635 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1647 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1636 ('literal', 'foo:bar', [False, False, True])
1648 ('literal', 'foo:bar', [False, False, True])
1637 """
1649 """
1638 if pattern.startswith('re:'):
1650 if pattern.startswith('re:'):
1639 pattern = pattern[3:]
1651 pattern = pattern[3:]
1640 try:
1652 try:
1641 regex = remod.compile(pattern)
1653 regex = remod.compile(pattern)
1642 except remod.error as e:
1654 except remod.error as e:
1643 raise error.ParseError(_('invalid regular expression: %s')
1655 raise error.ParseError(_('invalid regular expression: %s')
1644 % e)
1656 % e)
1645 return 're', pattern, regex.search
1657 return 're', pattern, regex.search
1646 elif pattern.startswith('literal:'):
1658 elif pattern.startswith('literal:'):
1647 pattern = pattern[8:]
1659 pattern = pattern[8:]
1648 return 'literal', pattern, pattern.__eq__
1660 return 'literal', pattern, pattern.__eq__
1649
1661
1650 def shortuser(user):
1662 def shortuser(user):
1651 """Return a short representation of a user name or email address."""
1663 """Return a short representation of a user name or email address."""
1652 f = user.find('@')
1664 f = user.find('@')
1653 if f >= 0:
1665 if f >= 0:
1654 user = user[:f]
1666 user = user[:f]
1655 f = user.find('<')
1667 f = user.find('<')
1656 if f >= 0:
1668 if f >= 0:
1657 user = user[f + 1:]
1669 user = user[f + 1:]
1658 f = user.find(' ')
1670 f = user.find(' ')
1659 if f >= 0:
1671 if f >= 0:
1660 user = user[:f]
1672 user = user[:f]
1661 f = user.find('.')
1673 f = user.find('.')
1662 if f >= 0:
1674 if f >= 0:
1663 user = user[:f]
1675 user = user[:f]
1664 return user
1676 return user
1665
1677
1666 def emailuser(user):
1678 def emailuser(user):
1667 """Return the user portion of an email address."""
1679 """Return the user portion of an email address."""
1668 f = user.find('@')
1680 f = user.find('@')
1669 if f >= 0:
1681 if f >= 0:
1670 user = user[:f]
1682 user = user[:f]
1671 f = user.find('<')
1683 f = user.find('<')
1672 if f >= 0:
1684 if f >= 0:
1673 user = user[f + 1:]
1685 user = user[f + 1:]
1674 return user
1686 return user
1675
1687
1676 def email(author):
1688 def email(author):
1677 '''get email of author.'''
1689 '''get email of author.'''
1678 r = author.find('>')
1690 r = author.find('>')
1679 if r == -1:
1691 if r == -1:
1680 r = None
1692 r = None
1681 return author[author.find('<') + 1:r]
1693 return author[author.find('<') + 1:r]
1682
1694
1683 def ellipsis(text, maxlength=400):
1695 def ellipsis(text, maxlength=400):
1684 """Trim string to at most maxlength (default: 400) columns in display."""
1696 """Trim string to at most maxlength (default: 400) columns in display."""
1685 return encoding.trim(text, maxlength, ellipsis='...')
1697 return encoding.trim(text, maxlength, ellipsis='...')
1686
1698
1687 def unitcountfn(*unittable):
1699 def unitcountfn(*unittable):
1688 '''return a function that renders a readable count of some quantity'''
1700 '''return a function that renders a readable count of some quantity'''
1689
1701
1690 def go(count):
1702 def go(count):
1691 for multiplier, divisor, format in unittable:
1703 for multiplier, divisor, format in unittable:
1692 if count >= divisor * multiplier:
1704 if count >= divisor * multiplier:
1693 return format % (count / float(divisor))
1705 return format % (count / float(divisor))
1694 return unittable[-1][2] % count
1706 return unittable[-1][2] % count
1695
1707
1696 return go
1708 return go
1697
1709
1698 bytecount = unitcountfn(
1710 bytecount = unitcountfn(
1699 (100, 1 << 30, _('%.0f GB')),
1711 (100, 1 << 30, _('%.0f GB')),
1700 (10, 1 << 30, _('%.1f GB')),
1712 (10, 1 << 30, _('%.1f GB')),
1701 (1, 1 << 30, _('%.2f GB')),
1713 (1, 1 << 30, _('%.2f GB')),
1702 (100, 1 << 20, _('%.0f MB')),
1714 (100, 1 << 20, _('%.0f MB')),
1703 (10, 1 << 20, _('%.1f MB')),
1715 (10, 1 << 20, _('%.1f MB')),
1704 (1, 1 << 20, _('%.2f MB')),
1716 (1, 1 << 20, _('%.2f MB')),
1705 (100, 1 << 10, _('%.0f KB')),
1717 (100, 1 << 10, _('%.0f KB')),
1706 (10, 1 << 10, _('%.1f KB')),
1718 (10, 1 << 10, _('%.1f KB')),
1707 (1, 1 << 10, _('%.2f KB')),
1719 (1, 1 << 10, _('%.2f KB')),
1708 (1, 1, _('%.0f bytes')),
1720 (1, 1, _('%.0f bytes')),
1709 )
1721 )
1710
1722
1711 def uirepr(s):
1723 def uirepr(s):
1712 # Avoid double backslash in Windows path repr()
1724 # Avoid double backslash in Windows path repr()
1713 return repr(s).replace('\\\\', '\\')
1725 return repr(s).replace('\\\\', '\\')
1714
1726
1715 # delay import of textwrap
1727 # delay import of textwrap
1716 def MBTextWrapper(**kwargs):
1728 def MBTextWrapper(**kwargs):
1717 class tw(textwrap.TextWrapper):
1729 class tw(textwrap.TextWrapper):
1718 """
1730 """
1719 Extend TextWrapper for width-awareness.
1731 Extend TextWrapper for width-awareness.
1720
1732
1721 Neither number of 'bytes' in any encoding nor 'characters' is
1733 Neither number of 'bytes' in any encoding nor 'characters' is
1722 appropriate to calculate terminal columns for specified string.
1734 appropriate to calculate terminal columns for specified string.
1723
1735
1724 Original TextWrapper implementation uses built-in 'len()' directly,
1736 Original TextWrapper implementation uses built-in 'len()' directly,
1725 so overriding is needed to use width information of each characters.
1737 so overriding is needed to use width information of each characters.
1726
1738
1727 In addition, characters classified into 'ambiguous' width are
1739 In addition, characters classified into 'ambiguous' width are
1728 treated as wide in East Asian area, but as narrow in other.
1740 treated as wide in East Asian area, but as narrow in other.
1729
1741
1730 This requires use decision to determine width of such characters.
1742 This requires use decision to determine width of such characters.
1731 """
1743 """
1732 def _cutdown(self, ucstr, space_left):
1744 def _cutdown(self, ucstr, space_left):
1733 l = 0
1745 l = 0
1734 colwidth = encoding.ucolwidth
1746 colwidth = encoding.ucolwidth
1735 for i in xrange(len(ucstr)):
1747 for i in xrange(len(ucstr)):
1736 l += colwidth(ucstr[i])
1748 l += colwidth(ucstr[i])
1737 if space_left < l:
1749 if space_left < l:
1738 return (ucstr[:i], ucstr[i:])
1750 return (ucstr[:i], ucstr[i:])
1739 return ucstr, ''
1751 return ucstr, ''
1740
1752
1741 # overriding of base class
1753 # overriding of base class
1742 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1754 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1743 space_left = max(width - cur_len, 1)
1755 space_left = max(width - cur_len, 1)
1744
1756
1745 if self.break_long_words:
1757 if self.break_long_words:
1746 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1758 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1747 cur_line.append(cut)
1759 cur_line.append(cut)
1748 reversed_chunks[-1] = res
1760 reversed_chunks[-1] = res
1749 elif not cur_line:
1761 elif not cur_line:
1750 cur_line.append(reversed_chunks.pop())
1762 cur_line.append(reversed_chunks.pop())
1751
1763
1752 # this overriding code is imported from TextWrapper of Python 2.6
1764 # this overriding code is imported from TextWrapper of Python 2.6
1753 # to calculate columns of string by 'encoding.ucolwidth()'
1765 # to calculate columns of string by 'encoding.ucolwidth()'
1754 def _wrap_chunks(self, chunks):
1766 def _wrap_chunks(self, chunks):
1755 colwidth = encoding.ucolwidth
1767 colwidth = encoding.ucolwidth
1756
1768
1757 lines = []
1769 lines = []
1758 if self.width <= 0:
1770 if self.width <= 0:
1759 raise ValueError("invalid width %r (must be > 0)" % self.width)
1771 raise ValueError("invalid width %r (must be > 0)" % self.width)
1760
1772
1761 # Arrange in reverse order so items can be efficiently popped
1773 # Arrange in reverse order so items can be efficiently popped
1762 # from a stack of chucks.
1774 # from a stack of chucks.
1763 chunks.reverse()
1775 chunks.reverse()
1764
1776
1765 while chunks:
1777 while chunks:
1766
1778
1767 # Start the list of chunks that will make up the current line.
1779 # Start the list of chunks that will make up the current line.
1768 # cur_len is just the length of all the chunks in cur_line.
1780 # cur_len is just the length of all the chunks in cur_line.
1769 cur_line = []
1781 cur_line = []
1770 cur_len = 0
1782 cur_len = 0
1771
1783
1772 # Figure out which static string will prefix this line.
1784 # Figure out which static string will prefix this line.
1773 if lines:
1785 if lines:
1774 indent = self.subsequent_indent
1786 indent = self.subsequent_indent
1775 else:
1787 else:
1776 indent = self.initial_indent
1788 indent = self.initial_indent
1777
1789
1778 # Maximum width for this line.
1790 # Maximum width for this line.
1779 width = self.width - len(indent)
1791 width = self.width - len(indent)
1780
1792
1781 # First chunk on line is whitespace -- drop it, unless this
1793 # First chunk on line is whitespace -- drop it, unless this
1782 # is the very beginning of the text (i.e. no lines started yet).
1794 # is the very beginning of the text (i.e. no lines started yet).
1783 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1795 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1784 del chunks[-1]
1796 del chunks[-1]
1785
1797
1786 while chunks:
1798 while chunks:
1787 l = colwidth(chunks[-1])
1799 l = colwidth(chunks[-1])
1788
1800
1789 # Can at least squeeze this chunk onto the current line.
1801 # Can at least squeeze this chunk onto the current line.
1790 if cur_len + l <= width:
1802 if cur_len + l <= width:
1791 cur_line.append(chunks.pop())
1803 cur_line.append(chunks.pop())
1792 cur_len += l
1804 cur_len += l
1793
1805
1794 # Nope, this line is full.
1806 # Nope, this line is full.
1795 else:
1807 else:
1796 break
1808 break
1797
1809
1798 # The current line is full, and the next chunk is too big to
1810 # The current line is full, and the next chunk is too big to
1799 # fit on *any* line (not just this one).
1811 # fit on *any* line (not just this one).
1800 if chunks and colwidth(chunks[-1]) > width:
1812 if chunks and colwidth(chunks[-1]) > width:
1801 self._handle_long_word(chunks, cur_line, cur_len, width)
1813 self._handle_long_word(chunks, cur_line, cur_len, width)
1802
1814
1803 # If the last chunk on this line is all whitespace, drop it.
1815 # If the last chunk on this line is all whitespace, drop it.
1804 if (self.drop_whitespace and
1816 if (self.drop_whitespace and
1805 cur_line and cur_line[-1].strip() == ''):
1817 cur_line and cur_line[-1].strip() == ''):
1806 del cur_line[-1]
1818 del cur_line[-1]
1807
1819
1808 # Convert current line back to a string and store it in list
1820 # Convert current line back to a string and store it in list
1809 # of all lines (return value).
1821 # of all lines (return value).
1810 if cur_line:
1822 if cur_line:
1811 lines.append(indent + ''.join(cur_line))
1823 lines.append(indent + ''.join(cur_line))
1812
1824
1813 return lines
1825 return lines
1814
1826
1815 global MBTextWrapper
1827 global MBTextWrapper
1816 MBTextWrapper = tw
1828 MBTextWrapper = tw
1817 return tw(**kwargs)
1829 return tw(**kwargs)
1818
1830
1819 def wrap(line, width, initindent='', hangindent=''):
1831 def wrap(line, width, initindent='', hangindent=''):
1820 maxindent = max(len(hangindent), len(initindent))
1832 maxindent = max(len(hangindent), len(initindent))
1821 if width <= maxindent:
1833 if width <= maxindent:
1822 # adjust for weird terminal size
1834 # adjust for weird terminal size
1823 width = max(78, maxindent + 1)
1835 width = max(78, maxindent + 1)
1824 line = line.decode(encoding.encoding, encoding.encodingmode)
1836 line = line.decode(encoding.encoding, encoding.encodingmode)
1825 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1837 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1826 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1838 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1827 wrapper = MBTextWrapper(width=width,
1839 wrapper = MBTextWrapper(width=width,
1828 initial_indent=initindent,
1840 initial_indent=initindent,
1829 subsequent_indent=hangindent)
1841 subsequent_indent=hangindent)
1830 return wrapper.fill(line).encode(encoding.encoding)
1842 return wrapper.fill(line).encode(encoding.encoding)
1831
1843
1832 def iterlines(iterator):
1844 def iterlines(iterator):
1833 for chunk in iterator:
1845 for chunk in iterator:
1834 for line in chunk.splitlines():
1846 for line in chunk.splitlines():
1835 yield line
1847 yield line
1836
1848
1837 def expandpath(path):
1849 def expandpath(path):
1838 return os.path.expanduser(os.path.expandvars(path))
1850 return os.path.expanduser(os.path.expandvars(path))
1839
1851
1840 def hgcmd():
1852 def hgcmd():
1841 """Return the command used to execute current hg
1853 """Return the command used to execute current hg
1842
1854
1843 This is different from hgexecutable() because on Windows we want
1855 This is different from hgexecutable() because on Windows we want
1844 to avoid things opening new shell windows like batch files, so we
1856 to avoid things opening new shell windows like batch files, so we
1845 get either the python call or current executable.
1857 get either the python call or current executable.
1846 """
1858 """
1847 if mainfrozen():
1859 if mainfrozen():
1848 return [sys.executable]
1860 return [sys.executable]
1849 return gethgcmd()
1861 return gethgcmd()
1850
1862
1851 def rundetached(args, condfn):
1863 def rundetached(args, condfn):
1852 """Execute the argument list in a detached process.
1864 """Execute the argument list in a detached process.
1853
1865
1854 condfn is a callable which is called repeatedly and should return
1866 condfn is a callable which is called repeatedly and should return
1855 True once the child process is known to have started successfully.
1867 True once the child process is known to have started successfully.
1856 At this point, the child process PID is returned. If the child
1868 At this point, the child process PID is returned. If the child
1857 process fails to start or finishes before condfn() evaluates to
1869 process fails to start or finishes before condfn() evaluates to
1858 True, return -1.
1870 True, return -1.
1859 """
1871 """
1860 # Windows case is easier because the child process is either
1872 # Windows case is easier because the child process is either
1861 # successfully starting and validating the condition or exiting
1873 # successfully starting and validating the condition or exiting
1862 # on failure. We just poll on its PID. On Unix, if the child
1874 # on failure. We just poll on its PID. On Unix, if the child
1863 # process fails to start, it will be left in a zombie state until
1875 # process fails to start, it will be left in a zombie state until
1864 # the parent wait on it, which we cannot do since we expect a long
1876 # the parent wait on it, which we cannot do since we expect a long
1865 # running process on success. Instead we listen for SIGCHLD telling
1877 # running process on success. Instead we listen for SIGCHLD telling
1866 # us our child process terminated.
1878 # us our child process terminated.
1867 terminated = set()
1879 terminated = set()
1868 def handler(signum, frame):
1880 def handler(signum, frame):
1869 terminated.add(os.wait())
1881 terminated.add(os.wait())
1870 prevhandler = None
1882 prevhandler = None
1871 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1883 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1872 if SIGCHLD is not None:
1884 if SIGCHLD is not None:
1873 prevhandler = signal.signal(SIGCHLD, handler)
1885 prevhandler = signal.signal(SIGCHLD, handler)
1874 try:
1886 try:
1875 pid = spawndetached(args)
1887 pid = spawndetached(args)
1876 while not condfn():
1888 while not condfn():
1877 if ((pid in terminated or not testpid(pid))
1889 if ((pid in terminated or not testpid(pid))
1878 and not condfn()):
1890 and not condfn()):
1879 return -1
1891 return -1
1880 time.sleep(0.1)
1892 time.sleep(0.1)
1881 return pid
1893 return pid
1882 finally:
1894 finally:
1883 if prevhandler is not None:
1895 if prevhandler is not None:
1884 signal.signal(signal.SIGCHLD, prevhandler)
1896 signal.signal(signal.SIGCHLD, prevhandler)
1885
1897
1886 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1898 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1887 """Return the result of interpolating items in the mapping into string s.
1899 """Return the result of interpolating items in the mapping into string s.
1888
1900
1889 prefix is a single character string, or a two character string with
1901 prefix is a single character string, or a two character string with
1890 a backslash as the first character if the prefix needs to be escaped in
1902 a backslash as the first character if the prefix needs to be escaped in
1891 a regular expression.
1903 a regular expression.
1892
1904
1893 fn is an optional function that will be applied to the replacement text
1905 fn is an optional function that will be applied to the replacement text
1894 just before replacement.
1906 just before replacement.
1895
1907
1896 escape_prefix is an optional flag that allows using doubled prefix for
1908 escape_prefix is an optional flag that allows using doubled prefix for
1897 its escaping.
1909 its escaping.
1898 """
1910 """
1899 fn = fn or (lambda s: s)
1911 fn = fn or (lambda s: s)
1900 patterns = '|'.join(mapping.keys())
1912 patterns = '|'.join(mapping.keys())
1901 if escape_prefix:
1913 if escape_prefix:
1902 patterns += '|' + prefix
1914 patterns += '|' + prefix
1903 if len(prefix) > 1:
1915 if len(prefix) > 1:
1904 prefix_char = prefix[1:]
1916 prefix_char = prefix[1:]
1905 else:
1917 else:
1906 prefix_char = prefix
1918 prefix_char = prefix
1907 mapping[prefix_char] = prefix_char
1919 mapping[prefix_char] = prefix_char
1908 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1920 r = remod.compile(r'%s(%s)' % (prefix, patterns))
1909 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1921 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1910
1922
1911 def getport(port):
1923 def getport(port):
1912 """Return the port for a given network service.
1924 """Return the port for a given network service.
1913
1925
1914 If port is an integer, it's returned as is. If it's a string, it's
1926 If port is an integer, it's returned as is. If it's a string, it's
1915 looked up using socket.getservbyname(). If there's no matching
1927 looked up using socket.getservbyname(). If there's no matching
1916 service, util.Abort is raised.
1928 service, util.Abort is raised.
1917 """
1929 """
1918 try:
1930 try:
1919 return int(port)
1931 return int(port)
1920 except ValueError:
1932 except ValueError:
1921 pass
1933 pass
1922
1934
1923 try:
1935 try:
1924 return socket.getservbyname(port)
1936 return socket.getservbyname(port)
1925 except socket.error:
1937 except socket.error:
1926 raise Abort(_("no port number associated with service '%s'") % port)
1938 raise Abort(_("no port number associated with service '%s'") % port)
1927
1939
1928 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1940 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1929 '0': False, 'no': False, 'false': False, 'off': False,
1941 '0': False, 'no': False, 'false': False, 'off': False,
1930 'never': False}
1942 'never': False}
1931
1943
1932 def parsebool(s):
1944 def parsebool(s):
1933 """Parse s into a boolean.
1945 """Parse s into a boolean.
1934
1946
1935 If s is not a valid boolean, returns None.
1947 If s is not a valid boolean, returns None.
1936 """
1948 """
1937 return _booleans.get(s.lower(), None)
1949 return _booleans.get(s.lower(), None)
1938
1950
1939 _hexdig = '0123456789ABCDEFabcdef'
1951 _hexdig = '0123456789ABCDEFabcdef'
1940 _hextochr = dict((a + b, chr(int(a + b, 16)))
1952 _hextochr = dict((a + b, chr(int(a + b, 16)))
1941 for a in _hexdig for b in _hexdig)
1953 for a in _hexdig for b in _hexdig)
1942
1954
1943 def _urlunquote(s):
1955 def _urlunquote(s):
1944 """Decode HTTP/HTML % encoding.
1956 """Decode HTTP/HTML % encoding.
1945
1957
1946 >>> _urlunquote('abc%20def')
1958 >>> _urlunquote('abc%20def')
1947 'abc def'
1959 'abc def'
1948 """
1960 """
1949 res = s.split('%')
1961 res = s.split('%')
1950 # fastpath
1962 # fastpath
1951 if len(res) == 1:
1963 if len(res) == 1:
1952 return s
1964 return s
1953 s = res[0]
1965 s = res[0]
1954 for item in res[1:]:
1966 for item in res[1:]:
1955 try:
1967 try:
1956 s += _hextochr[item[:2]] + item[2:]
1968 s += _hextochr[item[:2]] + item[2:]
1957 except KeyError:
1969 except KeyError:
1958 s += '%' + item
1970 s += '%' + item
1959 except UnicodeDecodeError:
1971 except UnicodeDecodeError:
1960 s += unichr(int(item[:2], 16)) + item[2:]
1972 s += unichr(int(item[:2], 16)) + item[2:]
1961 return s
1973 return s
1962
1974
1963 class url(object):
1975 class url(object):
1964 r"""Reliable URL parser.
1976 r"""Reliable URL parser.
1965
1977
1966 This parses URLs and provides attributes for the following
1978 This parses URLs and provides attributes for the following
1967 components:
1979 components:
1968
1980
1969 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1981 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1970
1982
1971 Missing components are set to None. The only exception is
1983 Missing components are set to None. The only exception is
1972 fragment, which is set to '' if present but empty.
1984 fragment, which is set to '' if present but empty.
1973
1985
1974 If parsefragment is False, fragment is included in query. If
1986 If parsefragment is False, fragment is included in query. If
1975 parsequery is False, query is included in path. If both are
1987 parsequery is False, query is included in path. If both are
1976 False, both fragment and query are included in path.
1988 False, both fragment and query are included in path.
1977
1989
1978 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1990 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1979
1991
1980 Note that for backward compatibility reasons, bundle URLs do not
1992 Note that for backward compatibility reasons, bundle URLs do not
1981 take host names. That means 'bundle://../' has a path of '../'.
1993 take host names. That means 'bundle://../' has a path of '../'.
1982
1994
1983 Examples:
1995 Examples:
1984
1996
1985 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1997 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1986 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1998 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1987 >>> url('ssh://[::1]:2200//home/joe/repo')
1999 >>> url('ssh://[::1]:2200//home/joe/repo')
1988 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2000 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1989 >>> url('file:///home/joe/repo')
2001 >>> url('file:///home/joe/repo')
1990 <url scheme: 'file', path: '/home/joe/repo'>
2002 <url scheme: 'file', path: '/home/joe/repo'>
1991 >>> url('file:///c:/temp/foo/')
2003 >>> url('file:///c:/temp/foo/')
1992 <url scheme: 'file', path: 'c:/temp/foo/'>
2004 <url scheme: 'file', path: 'c:/temp/foo/'>
1993 >>> url('bundle:foo')
2005 >>> url('bundle:foo')
1994 <url scheme: 'bundle', path: 'foo'>
2006 <url scheme: 'bundle', path: 'foo'>
1995 >>> url('bundle://../foo')
2007 >>> url('bundle://../foo')
1996 <url scheme: 'bundle', path: '../foo'>
2008 <url scheme: 'bundle', path: '../foo'>
1997 >>> url(r'c:\foo\bar')
2009 >>> url(r'c:\foo\bar')
1998 <url path: 'c:\\foo\\bar'>
2010 <url path: 'c:\\foo\\bar'>
1999 >>> url(r'\\blah\blah\blah')
2011 >>> url(r'\\blah\blah\blah')
2000 <url path: '\\\\blah\\blah\\blah'>
2012 <url path: '\\\\blah\\blah\\blah'>
2001 >>> url(r'\\blah\blah\blah#baz')
2013 >>> url(r'\\blah\blah\blah#baz')
2002 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2014 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2003 >>> url(r'file:///C:\users\me')
2015 >>> url(r'file:///C:\users\me')
2004 <url scheme: 'file', path: 'C:\\users\\me'>
2016 <url scheme: 'file', path: 'C:\\users\\me'>
2005
2017
2006 Authentication credentials:
2018 Authentication credentials:
2007
2019
2008 >>> url('ssh://joe:xyz@x/repo')
2020 >>> url('ssh://joe:xyz@x/repo')
2009 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2021 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2010 >>> url('ssh://joe@x/repo')
2022 >>> url('ssh://joe@x/repo')
2011 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2023 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2012
2024
2013 Query strings and fragments:
2025 Query strings and fragments:
2014
2026
2015 >>> url('http://host/a?b#c')
2027 >>> url('http://host/a?b#c')
2016 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2028 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2017 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2029 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2018 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2030 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2019 """
2031 """
2020
2032
2021 _safechars = "!~*'()+"
2033 _safechars = "!~*'()+"
2022 _safepchars = "/!~*'()+:\\"
2034 _safepchars = "/!~*'()+:\\"
2023 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
2035 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
2024
2036
2025 def __init__(self, path, parsequery=True, parsefragment=True):
2037 def __init__(self, path, parsequery=True, parsefragment=True):
2026 # We slowly chomp away at path until we have only the path left
2038 # We slowly chomp away at path until we have only the path left
2027 self.scheme = self.user = self.passwd = self.host = None
2039 self.scheme = self.user = self.passwd = self.host = None
2028 self.port = self.path = self.query = self.fragment = None
2040 self.port = self.path = self.query = self.fragment = None
2029 self._localpath = True
2041 self._localpath = True
2030 self._hostport = ''
2042 self._hostport = ''
2031 self._origpath = path
2043 self._origpath = path
2032
2044
2033 if parsefragment and '#' in path:
2045 if parsefragment and '#' in path:
2034 path, self.fragment = path.split('#', 1)
2046 path, self.fragment = path.split('#', 1)
2035 if not path:
2047 if not path:
2036 path = None
2048 path = None
2037
2049
2038 # special case for Windows drive letters and UNC paths
2050 # special case for Windows drive letters and UNC paths
2039 if hasdriveletter(path) or path.startswith(r'\\'):
2051 if hasdriveletter(path) or path.startswith(r'\\'):
2040 self.path = path
2052 self.path = path
2041 return
2053 return
2042
2054
2043 # For compatibility reasons, we can't handle bundle paths as
2055 # For compatibility reasons, we can't handle bundle paths as
2044 # normal URLS
2056 # normal URLS
2045 if path.startswith('bundle:'):
2057 if path.startswith('bundle:'):
2046 self.scheme = 'bundle'
2058 self.scheme = 'bundle'
2047 path = path[7:]
2059 path = path[7:]
2048 if path.startswith('//'):
2060 if path.startswith('//'):
2049 path = path[2:]
2061 path = path[2:]
2050 self.path = path
2062 self.path = path
2051 return
2063 return
2052
2064
2053 if self._matchscheme(path):
2065 if self._matchscheme(path):
2054 parts = path.split(':', 1)
2066 parts = path.split(':', 1)
2055 if parts[0]:
2067 if parts[0]:
2056 self.scheme, path = parts
2068 self.scheme, path = parts
2057 self._localpath = False
2069 self._localpath = False
2058
2070
2059 if not path:
2071 if not path:
2060 path = None
2072 path = None
2061 if self._localpath:
2073 if self._localpath:
2062 self.path = ''
2074 self.path = ''
2063 return
2075 return
2064 else:
2076 else:
2065 if self._localpath:
2077 if self._localpath:
2066 self.path = path
2078 self.path = path
2067 return
2079 return
2068
2080
2069 if parsequery and '?' in path:
2081 if parsequery and '?' in path:
2070 path, self.query = path.split('?', 1)
2082 path, self.query = path.split('?', 1)
2071 if not path:
2083 if not path:
2072 path = None
2084 path = None
2073 if not self.query:
2085 if not self.query:
2074 self.query = None
2086 self.query = None
2075
2087
2076 # // is required to specify a host/authority
2088 # // is required to specify a host/authority
2077 if path and path.startswith('//'):
2089 if path and path.startswith('//'):
2078 parts = path[2:].split('/', 1)
2090 parts = path[2:].split('/', 1)
2079 if len(parts) > 1:
2091 if len(parts) > 1:
2080 self.host, path = parts
2092 self.host, path = parts
2081 else:
2093 else:
2082 self.host = parts[0]
2094 self.host = parts[0]
2083 path = None
2095 path = None
2084 if not self.host:
2096 if not self.host:
2085 self.host = None
2097 self.host = None
2086 # path of file:///d is /d
2098 # path of file:///d is /d
2087 # path of file:///d:/ is d:/, not /d:/
2099 # path of file:///d:/ is d:/, not /d:/
2088 if path and not hasdriveletter(path):
2100 if path and not hasdriveletter(path):
2089 path = '/' + path
2101 path = '/' + path
2090
2102
2091 if self.host and '@' in self.host:
2103 if self.host and '@' in self.host:
2092 self.user, self.host = self.host.rsplit('@', 1)
2104 self.user, self.host = self.host.rsplit('@', 1)
2093 if ':' in self.user:
2105 if ':' in self.user:
2094 self.user, self.passwd = self.user.split(':', 1)
2106 self.user, self.passwd = self.user.split(':', 1)
2095 if not self.host:
2107 if not self.host:
2096 self.host = None
2108 self.host = None
2097
2109
2098 # Don't split on colons in IPv6 addresses without ports
2110 # Don't split on colons in IPv6 addresses without ports
2099 if (self.host and ':' in self.host and
2111 if (self.host and ':' in self.host and
2100 not (self.host.startswith('[') and self.host.endswith(']'))):
2112 not (self.host.startswith('[') and self.host.endswith(']'))):
2101 self._hostport = self.host
2113 self._hostport = self.host
2102 self.host, self.port = self.host.rsplit(':', 1)
2114 self.host, self.port = self.host.rsplit(':', 1)
2103 if not self.host:
2115 if not self.host:
2104 self.host = None
2116 self.host = None
2105
2117
2106 if (self.host and self.scheme == 'file' and
2118 if (self.host and self.scheme == 'file' and
2107 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2119 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2108 raise Abort(_('file:// URLs can only refer to localhost'))
2120 raise Abort(_('file:// URLs can only refer to localhost'))
2109
2121
2110 self.path = path
2122 self.path = path
2111
2123
2112 # leave the query string escaped
2124 # leave the query string escaped
2113 for a in ('user', 'passwd', 'host', 'port',
2125 for a in ('user', 'passwd', 'host', 'port',
2114 'path', 'fragment'):
2126 'path', 'fragment'):
2115 v = getattr(self, a)
2127 v = getattr(self, a)
2116 if v is not None:
2128 if v is not None:
2117 setattr(self, a, _urlunquote(v))
2129 setattr(self, a, _urlunquote(v))
2118
2130
2119 def __repr__(self):
2131 def __repr__(self):
2120 attrs = []
2132 attrs = []
2121 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2133 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2122 'query', 'fragment'):
2134 'query', 'fragment'):
2123 v = getattr(self, a)
2135 v = getattr(self, a)
2124 if v is not None:
2136 if v is not None:
2125 attrs.append('%s: %r' % (a, v))
2137 attrs.append('%s: %r' % (a, v))
2126 return '<url %s>' % ', '.join(attrs)
2138 return '<url %s>' % ', '.join(attrs)
2127
2139
2128 def __str__(self):
2140 def __str__(self):
2129 r"""Join the URL's components back into a URL string.
2141 r"""Join the URL's components back into a URL string.
2130
2142
2131 Examples:
2143 Examples:
2132
2144
2133 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2145 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2134 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2146 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2135 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2147 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2136 'http://user:pw@host:80/?foo=bar&baz=42'
2148 'http://user:pw@host:80/?foo=bar&baz=42'
2137 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2149 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2138 'http://user:pw@host:80/?foo=bar%3dbaz'
2150 'http://user:pw@host:80/?foo=bar%3dbaz'
2139 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2151 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2140 'ssh://user:pw@[::1]:2200//home/joe#'
2152 'ssh://user:pw@[::1]:2200//home/joe#'
2141 >>> str(url('http://localhost:80//'))
2153 >>> str(url('http://localhost:80//'))
2142 'http://localhost:80//'
2154 'http://localhost:80//'
2143 >>> str(url('http://localhost:80/'))
2155 >>> str(url('http://localhost:80/'))
2144 'http://localhost:80/'
2156 'http://localhost:80/'
2145 >>> str(url('http://localhost:80'))
2157 >>> str(url('http://localhost:80'))
2146 'http://localhost:80/'
2158 'http://localhost:80/'
2147 >>> str(url('bundle:foo'))
2159 >>> str(url('bundle:foo'))
2148 'bundle:foo'
2160 'bundle:foo'
2149 >>> str(url('bundle://../foo'))
2161 >>> str(url('bundle://../foo'))
2150 'bundle:../foo'
2162 'bundle:../foo'
2151 >>> str(url('path'))
2163 >>> str(url('path'))
2152 'path'
2164 'path'
2153 >>> str(url('file:///tmp/foo/bar'))
2165 >>> str(url('file:///tmp/foo/bar'))
2154 'file:///tmp/foo/bar'
2166 'file:///tmp/foo/bar'
2155 >>> str(url('file:///c:/tmp/foo/bar'))
2167 >>> str(url('file:///c:/tmp/foo/bar'))
2156 'file:///c:/tmp/foo/bar'
2168 'file:///c:/tmp/foo/bar'
2157 >>> print url(r'bundle:foo\bar')
2169 >>> print url(r'bundle:foo\bar')
2158 bundle:foo\bar
2170 bundle:foo\bar
2159 >>> print url(r'file:///D:\data\hg')
2171 >>> print url(r'file:///D:\data\hg')
2160 file:///D:\data\hg
2172 file:///D:\data\hg
2161 """
2173 """
2162 if self._localpath:
2174 if self._localpath:
2163 s = self.path
2175 s = self.path
2164 if self.scheme == 'bundle':
2176 if self.scheme == 'bundle':
2165 s = 'bundle:' + s
2177 s = 'bundle:' + s
2166 if self.fragment:
2178 if self.fragment:
2167 s += '#' + self.fragment
2179 s += '#' + self.fragment
2168 return s
2180 return s
2169
2181
2170 s = self.scheme + ':'
2182 s = self.scheme + ':'
2171 if self.user or self.passwd or self.host:
2183 if self.user or self.passwd or self.host:
2172 s += '//'
2184 s += '//'
2173 elif self.scheme and (not self.path or self.path.startswith('/')
2185 elif self.scheme and (not self.path or self.path.startswith('/')
2174 or hasdriveletter(self.path)):
2186 or hasdriveletter(self.path)):
2175 s += '//'
2187 s += '//'
2176 if hasdriveletter(self.path):
2188 if hasdriveletter(self.path):
2177 s += '/'
2189 s += '/'
2178 if self.user:
2190 if self.user:
2179 s += urllib.quote(self.user, safe=self._safechars)
2191 s += urllib.quote(self.user, safe=self._safechars)
2180 if self.passwd:
2192 if self.passwd:
2181 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
2193 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
2182 if self.user or self.passwd:
2194 if self.user or self.passwd:
2183 s += '@'
2195 s += '@'
2184 if self.host:
2196 if self.host:
2185 if not (self.host.startswith('[') and self.host.endswith(']')):
2197 if not (self.host.startswith('[') and self.host.endswith(']')):
2186 s += urllib.quote(self.host)
2198 s += urllib.quote(self.host)
2187 else:
2199 else:
2188 s += self.host
2200 s += self.host
2189 if self.port:
2201 if self.port:
2190 s += ':' + urllib.quote(self.port)
2202 s += ':' + urllib.quote(self.port)
2191 if self.host:
2203 if self.host:
2192 s += '/'
2204 s += '/'
2193 if self.path:
2205 if self.path:
2194 # TODO: similar to the query string, we should not unescape the
2206 # TODO: similar to the query string, we should not unescape the
2195 # path when we store it, the path might contain '%2f' = '/',
2207 # path when we store it, the path might contain '%2f' = '/',
2196 # which we should *not* escape.
2208 # which we should *not* escape.
2197 s += urllib.quote(self.path, safe=self._safepchars)
2209 s += urllib.quote(self.path, safe=self._safepchars)
2198 if self.query:
2210 if self.query:
2199 # we store the query in escaped form.
2211 # we store the query in escaped form.
2200 s += '?' + self.query
2212 s += '?' + self.query
2201 if self.fragment is not None:
2213 if self.fragment is not None:
2202 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
2214 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
2203 return s
2215 return s
2204
2216
2205 def authinfo(self):
2217 def authinfo(self):
2206 user, passwd = self.user, self.passwd
2218 user, passwd = self.user, self.passwd
2207 try:
2219 try:
2208 self.user, self.passwd = None, None
2220 self.user, self.passwd = None, None
2209 s = str(self)
2221 s = str(self)
2210 finally:
2222 finally:
2211 self.user, self.passwd = user, passwd
2223 self.user, self.passwd = user, passwd
2212 if not self.user:
2224 if not self.user:
2213 return (s, None)
2225 return (s, None)
2214 # authinfo[1] is passed to urllib2 password manager, and its
2226 # authinfo[1] is passed to urllib2 password manager, and its
2215 # URIs must not contain credentials. The host is passed in the
2227 # URIs must not contain credentials. The host is passed in the
2216 # URIs list because Python < 2.4.3 uses only that to search for
2228 # URIs list because Python < 2.4.3 uses only that to search for
2217 # a password.
2229 # a password.
2218 return (s, (None, (s, self.host),
2230 return (s, (None, (s, self.host),
2219 self.user, self.passwd or ''))
2231 self.user, self.passwd or ''))
2220
2232
2221 def isabs(self):
2233 def isabs(self):
2222 if self.scheme and self.scheme != 'file':
2234 if self.scheme and self.scheme != 'file':
2223 return True # remote URL
2235 return True # remote URL
2224 if hasdriveletter(self.path):
2236 if hasdriveletter(self.path):
2225 return True # absolute for our purposes - can't be joined()
2237 return True # absolute for our purposes - can't be joined()
2226 if self.path.startswith(r'\\'):
2238 if self.path.startswith(r'\\'):
2227 return True # Windows UNC path
2239 return True # Windows UNC path
2228 if self.path.startswith('/'):
2240 if self.path.startswith('/'):
2229 return True # POSIX-style
2241 return True # POSIX-style
2230 return False
2242 return False
2231
2243
2232 def localpath(self):
2244 def localpath(self):
2233 if self.scheme == 'file' or self.scheme == 'bundle':
2245 if self.scheme == 'file' or self.scheme == 'bundle':
2234 path = self.path or '/'
2246 path = self.path or '/'
2235 # For Windows, we need to promote hosts containing drive
2247 # For Windows, we need to promote hosts containing drive
2236 # letters to paths with drive letters.
2248 # letters to paths with drive letters.
2237 if hasdriveletter(self._hostport):
2249 if hasdriveletter(self._hostport):
2238 path = self._hostport + '/' + self.path
2250 path = self._hostport + '/' + self.path
2239 elif (self.host is not None and self.path
2251 elif (self.host is not None and self.path
2240 and not hasdriveletter(path)):
2252 and not hasdriveletter(path)):
2241 path = '/' + path
2253 path = '/' + path
2242 return path
2254 return path
2243 return self._origpath
2255 return self._origpath
2244
2256
2245 def islocal(self):
2257 def islocal(self):
2246 '''whether localpath will return something that posixfile can open'''
2258 '''whether localpath will return something that posixfile can open'''
2247 return (not self.scheme or self.scheme == 'file'
2259 return (not self.scheme or self.scheme == 'file'
2248 or self.scheme == 'bundle')
2260 or self.scheme == 'bundle')
2249
2261
2250 def hasscheme(path):
2262 def hasscheme(path):
2251 return bool(url(path).scheme)
2263 return bool(url(path).scheme)
2252
2264
2253 def hasdriveletter(path):
2265 def hasdriveletter(path):
2254 return path and path[1:2] == ':' and path[0:1].isalpha()
2266 return path and path[1:2] == ':' and path[0:1].isalpha()
2255
2267
2256 def urllocalpath(path):
2268 def urllocalpath(path):
2257 return url(path, parsequery=False, parsefragment=False).localpath()
2269 return url(path, parsequery=False, parsefragment=False).localpath()
2258
2270
2259 def hidepassword(u):
2271 def hidepassword(u):
2260 '''hide user credential in a url string'''
2272 '''hide user credential in a url string'''
2261 u = url(u)
2273 u = url(u)
2262 if u.passwd:
2274 if u.passwd:
2263 u.passwd = '***'
2275 u.passwd = '***'
2264 return str(u)
2276 return str(u)
2265
2277
2266 def removeauth(u):
2278 def removeauth(u):
2267 '''remove all authentication information from a url string'''
2279 '''remove all authentication information from a url string'''
2268 u = url(u)
2280 u = url(u)
2269 u.user = u.passwd = None
2281 u.user = u.passwd = None
2270 return str(u)
2282 return str(u)
2271
2283
2272 def isatty(fd):
2284 def isatty(fd):
2273 try:
2285 try:
2274 return fd.isatty()
2286 return fd.isatty()
2275 except AttributeError:
2287 except AttributeError:
2276 return False
2288 return False
2277
2289
2278 timecount = unitcountfn(
2290 timecount = unitcountfn(
2279 (1, 1e3, _('%.0f s')),
2291 (1, 1e3, _('%.0f s')),
2280 (100, 1, _('%.1f s')),
2292 (100, 1, _('%.1f s')),
2281 (10, 1, _('%.2f s')),
2293 (10, 1, _('%.2f s')),
2282 (1, 1, _('%.3f s')),
2294 (1, 1, _('%.3f s')),
2283 (100, 0.001, _('%.1f ms')),
2295 (100, 0.001, _('%.1f ms')),
2284 (10, 0.001, _('%.2f ms')),
2296 (10, 0.001, _('%.2f ms')),
2285 (1, 0.001, _('%.3f ms')),
2297 (1, 0.001, _('%.3f ms')),
2286 (100, 0.000001, _('%.1f us')),
2298 (100, 0.000001, _('%.1f us')),
2287 (10, 0.000001, _('%.2f us')),
2299 (10, 0.000001, _('%.2f us')),
2288 (1, 0.000001, _('%.3f us')),
2300 (1, 0.000001, _('%.3f us')),
2289 (100, 0.000000001, _('%.1f ns')),
2301 (100, 0.000000001, _('%.1f ns')),
2290 (10, 0.000000001, _('%.2f ns')),
2302 (10, 0.000000001, _('%.2f ns')),
2291 (1, 0.000000001, _('%.3f ns')),
2303 (1, 0.000000001, _('%.3f ns')),
2292 )
2304 )
2293
2305
2294 _timenesting = [0]
2306 _timenesting = [0]
2295
2307
2296 def timed(func):
2308 def timed(func):
2297 '''Report the execution time of a function call to stderr.
2309 '''Report the execution time of a function call to stderr.
2298
2310
2299 During development, use as a decorator when you need to measure
2311 During development, use as a decorator when you need to measure
2300 the cost of a function, e.g. as follows:
2312 the cost of a function, e.g. as follows:
2301
2313
2302 @util.timed
2314 @util.timed
2303 def foo(a, b, c):
2315 def foo(a, b, c):
2304 pass
2316 pass
2305 '''
2317 '''
2306
2318
2307 def wrapper(*args, **kwargs):
2319 def wrapper(*args, **kwargs):
2308 start = time.time()
2320 start = time.time()
2309 indent = 2
2321 indent = 2
2310 _timenesting[0] += indent
2322 _timenesting[0] += indent
2311 try:
2323 try:
2312 return func(*args, **kwargs)
2324 return func(*args, **kwargs)
2313 finally:
2325 finally:
2314 elapsed = time.time() - start
2326 elapsed = time.time() - start
2315 _timenesting[0] -= indent
2327 _timenesting[0] -= indent
2316 sys.stderr.write('%s%s: %s\n' %
2328 sys.stderr.write('%s%s: %s\n' %
2317 (' ' * _timenesting[0], func.__name__,
2329 (' ' * _timenesting[0], func.__name__,
2318 timecount(elapsed)))
2330 timecount(elapsed)))
2319 return wrapper
2331 return wrapper
2320
2332
2321 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2333 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2322 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2334 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2323
2335
2324 def sizetoint(s):
2336 def sizetoint(s):
2325 '''Convert a space specifier to a byte count.
2337 '''Convert a space specifier to a byte count.
2326
2338
2327 >>> sizetoint('30')
2339 >>> sizetoint('30')
2328 30
2340 30
2329 >>> sizetoint('2.2kb')
2341 >>> sizetoint('2.2kb')
2330 2252
2342 2252
2331 >>> sizetoint('6M')
2343 >>> sizetoint('6M')
2332 6291456
2344 6291456
2333 '''
2345 '''
2334 t = s.strip().lower()
2346 t = s.strip().lower()
2335 try:
2347 try:
2336 for k, u in _sizeunits:
2348 for k, u in _sizeunits:
2337 if t.endswith(k):
2349 if t.endswith(k):
2338 return int(float(t[:-len(k)]) * u)
2350 return int(float(t[:-len(k)]) * u)
2339 return int(t)
2351 return int(t)
2340 except ValueError:
2352 except ValueError:
2341 raise error.ParseError(_("couldn't parse size: %s") % s)
2353 raise error.ParseError(_("couldn't parse size: %s") % s)
2342
2354
2343 class hooks(object):
2355 class hooks(object):
2344 '''A collection of hook functions that can be used to extend a
2356 '''A collection of hook functions that can be used to extend a
2345 function's behavior. Hooks are called in lexicographic order,
2357 function's behavior. Hooks are called in lexicographic order,
2346 based on the names of their sources.'''
2358 based on the names of their sources.'''
2347
2359
2348 def __init__(self):
2360 def __init__(self):
2349 self._hooks = []
2361 self._hooks = []
2350
2362
2351 def add(self, source, hook):
2363 def add(self, source, hook):
2352 self._hooks.append((source, hook))
2364 self._hooks.append((source, hook))
2353
2365
2354 def __call__(self, *args):
2366 def __call__(self, *args):
2355 self._hooks.sort(key=lambda x: x[0])
2367 self._hooks.sort(key=lambda x: x[0])
2356 results = []
2368 results = []
2357 for source, hook in self._hooks:
2369 for source, hook in self._hooks:
2358 results.append(hook(*args))
2370 results.append(hook(*args))
2359 return results
2371 return results
2360
2372
2361 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2373 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2362 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2374 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2363 Skips the 'skip' last entries. By default it will flush stdout first.
2375 Skips the 'skip' last entries. By default it will flush stdout first.
2364 It can be used everywhere and do intentionally not require an ui object.
2376 It can be used everywhere and do intentionally not require an ui object.
2365 Not be used in production code but very convenient while developing.
2377 Not be used in production code but very convenient while developing.
2366 '''
2378 '''
2367 if otherf:
2379 if otherf:
2368 otherf.flush()
2380 otherf.flush()
2369 f.write('%s at:\n' % msg)
2381 f.write('%s at:\n' % msg)
2370 entries = [('%s:%s' % (fn, ln), func)
2382 entries = [('%s:%s' % (fn, ln), func)
2371 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2383 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2372 if entries:
2384 if entries:
2373 fnmax = max(len(entry[0]) for entry in entries)
2385 fnmax = max(len(entry[0]) for entry in entries)
2374 for fnln, func in entries:
2386 for fnln, func in entries:
2375 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2387 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2376 f.flush()
2388 f.flush()
2377
2389
2378 class dirs(object):
2390 class dirs(object):
2379 '''a multiset of directory names from a dirstate or manifest'''
2391 '''a multiset of directory names from a dirstate or manifest'''
2380
2392
2381 def __init__(self, map, skip=None):
2393 def __init__(self, map, skip=None):
2382 self._dirs = {}
2394 self._dirs = {}
2383 addpath = self.addpath
2395 addpath = self.addpath
2384 if safehasattr(map, 'iteritems') and skip is not None:
2396 if safehasattr(map, 'iteritems') and skip is not None:
2385 for f, s in map.iteritems():
2397 for f, s in map.iteritems():
2386 if s[0] != skip:
2398 if s[0] != skip:
2387 addpath(f)
2399 addpath(f)
2388 else:
2400 else:
2389 for f in map:
2401 for f in map:
2390 addpath(f)
2402 addpath(f)
2391
2403
2392 def addpath(self, path):
2404 def addpath(self, path):
2393 dirs = self._dirs
2405 dirs = self._dirs
2394 for base in finddirs(path):
2406 for base in finddirs(path):
2395 if base in dirs:
2407 if base in dirs:
2396 dirs[base] += 1
2408 dirs[base] += 1
2397 return
2409 return
2398 dirs[base] = 1
2410 dirs[base] = 1
2399
2411
2400 def delpath(self, path):
2412 def delpath(self, path):
2401 dirs = self._dirs
2413 dirs = self._dirs
2402 for base in finddirs(path):
2414 for base in finddirs(path):
2403 if dirs[base] > 1:
2415 if dirs[base] > 1:
2404 dirs[base] -= 1
2416 dirs[base] -= 1
2405 return
2417 return
2406 del dirs[base]
2418 del dirs[base]
2407
2419
2408 def __iter__(self):
2420 def __iter__(self):
2409 return self._dirs.iterkeys()
2421 return self._dirs.iterkeys()
2410
2422
2411 def __contains__(self, d):
2423 def __contains__(self, d):
2412 return d in self._dirs
2424 return d in self._dirs
2413
2425
2414 if safehasattr(parsers, 'dirs'):
2426 if safehasattr(parsers, 'dirs'):
2415 dirs = parsers.dirs
2427 dirs = parsers.dirs
2416
2428
2417 def finddirs(path):
2429 def finddirs(path):
2418 pos = path.rfind('/')
2430 pos = path.rfind('/')
2419 while pos != -1:
2431 while pos != -1:
2420 yield path[:pos]
2432 yield path[:pos]
2421 pos = path.rfind('/', 0, pos)
2433 pos = path.rfind('/', 0, pos)
2422
2434
2423 # compression utility
2435 # compression utility
2424
2436
2425 class nocompress(object):
2437 class nocompress(object):
2426 def compress(self, x):
2438 def compress(self, x):
2427 return x
2439 return x
2428 def flush(self):
2440 def flush(self):
2429 return ""
2441 return ""
2430
2442
2431 compressors = {
2443 compressors = {
2432 None: nocompress,
2444 None: nocompress,
2433 # lambda to prevent early import
2445 # lambda to prevent early import
2434 'BZ': lambda: bz2.BZ2Compressor(),
2446 'BZ': lambda: bz2.BZ2Compressor(),
2435 'GZ': lambda: zlib.compressobj(),
2447 'GZ': lambda: zlib.compressobj(),
2436 }
2448 }
2437 # also support the old form by courtesies
2449 # also support the old form by courtesies
2438 compressors['UN'] = compressors[None]
2450 compressors['UN'] = compressors[None]
2439
2451
2440 def _makedecompressor(decompcls):
2452 def _makedecompressor(decompcls):
2441 def generator(f):
2453 def generator(f):
2442 d = decompcls()
2454 d = decompcls()
2443 for chunk in filechunkiter(f):
2455 for chunk in filechunkiter(f):
2444 yield d.decompress(chunk)
2456 yield d.decompress(chunk)
2445 def func(fh):
2457 def func(fh):
2446 return chunkbuffer(generator(fh))
2458 return chunkbuffer(generator(fh))
2447 return func
2459 return func
2448
2460
2449 def _bz2():
2461 def _bz2():
2450 d = bz2.BZ2Decompressor()
2462 d = bz2.BZ2Decompressor()
2451 # Bzip2 stream start with BZ, but we stripped it.
2463 # Bzip2 stream start with BZ, but we stripped it.
2452 # we put it back for good measure.
2464 # we put it back for good measure.
2453 d.decompress('BZ')
2465 d.decompress('BZ')
2454 return d
2466 return d
2455
2467
2456 decompressors = {None: lambda fh: fh,
2468 decompressors = {None: lambda fh: fh,
2457 '_truncatedBZ': _makedecompressor(_bz2),
2469 '_truncatedBZ': _makedecompressor(_bz2),
2458 'BZ': _makedecompressor(lambda: bz2.BZ2Decompressor()),
2470 'BZ': _makedecompressor(lambda: bz2.BZ2Decompressor()),
2459 'GZ': _makedecompressor(lambda: zlib.decompressobj()),
2471 'GZ': _makedecompressor(lambda: zlib.decompressobj()),
2460 }
2472 }
2461 # also support the old form by courtesies
2473 # also support the old form by courtesies
2462 decompressors['UN'] = decompressors[None]
2474 decompressors['UN'] = decompressors[None]
2463
2475
2464 # convenient shortcut
2476 # convenient shortcut
2465 dst = debugstacktrace
2477 dst = debugstacktrace
General Comments 0
You need to be logged in to leave comments. Login now