##// END OF EJS Templates
util: rewrite pycompat imports to make pyflakes always happy...
Yuya Nishihara -
r30471:00c9ac4c default
parent child Browse files
Show More
@@ -1,3246 +1,3238 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 from __future__ import absolute_import
16 from __future__ import absolute_import
17
17
18 import bz2
18 import bz2
19 import calendar
19 import calendar
20 import collections
20 import collections
21 import datetime
21 import datetime
22 import errno
22 import errno
23 import gc
23 import gc
24 import hashlib
24 import hashlib
25 import imp
25 import imp
26 import os
26 import os
27 import platform as pyplatform
27 import platform as pyplatform
28 import re as remod
28 import re as remod
29 import shutil
29 import shutil
30 import signal
30 import signal
31 import socket
31 import socket
32 import stat
32 import stat
33 import string
33 import string
34 import subprocess
34 import subprocess
35 import sys
35 import sys
36 import tempfile
36 import tempfile
37 import textwrap
37 import textwrap
38 import time
38 import time
39 import traceback
39 import traceback
40 import zlib
40 import zlib
41
41
42 from . import (
42 from . import (
43 encoding,
43 encoding,
44 error,
44 error,
45 i18n,
45 i18n,
46 osutil,
46 osutil,
47 parsers,
47 parsers,
48 pycompat,
48 pycompat,
49 )
49 )
50
50
51 for attr in (
51 empty = pycompat.empty
52 'empty',
52 httplib = pycompat.httplib
53 'httplib',
53 httpserver = pycompat.httpserver
54 'httpserver',
54 pickle = pycompat.pickle
55 'pickle',
55 queue = pycompat.queue
56 'queue',
56 socketserver = pycompat.socketserver
57 'urlerr',
57 stringio = pycompat.stringio
58 'urlparse',
58 urlerr = pycompat.urlerr
59 # we do import urlreq, but we do it outside the loop
59 urlparse = pycompat.urlparse
60 #'urlreq',
61 'stringio',
62 'socketserver',
63 'xmlrpclib',
64 ):
65 a = pycompat.sysstr(attr)
66 globals()[a] = getattr(pycompat, a)
67
68 # This line is to make pyflakes happy:
69 urlreq = pycompat.urlreq
60 urlreq = pycompat.urlreq
61 xmlrpclib = pycompat.xmlrpclib
70
62
71 if os.name == 'nt':
63 if os.name == 'nt':
72 from . import windows as platform
64 from . import windows as platform
73 else:
65 else:
74 from . import posix as platform
66 from . import posix as platform
75
67
76 _ = i18n._
68 _ = i18n._
77
69
78 bindunixsocket = platform.bindunixsocket
70 bindunixsocket = platform.bindunixsocket
79 cachestat = platform.cachestat
71 cachestat = platform.cachestat
80 checkexec = platform.checkexec
72 checkexec = platform.checkexec
81 checklink = platform.checklink
73 checklink = platform.checklink
82 copymode = platform.copymode
74 copymode = platform.copymode
83 executablepath = platform.executablepath
75 executablepath = platform.executablepath
84 expandglobs = platform.expandglobs
76 expandglobs = platform.expandglobs
85 explainexit = platform.explainexit
77 explainexit = platform.explainexit
86 findexe = platform.findexe
78 findexe = platform.findexe
87 gethgcmd = platform.gethgcmd
79 gethgcmd = platform.gethgcmd
88 getuser = platform.getuser
80 getuser = platform.getuser
89 getpid = os.getpid
81 getpid = os.getpid
90 groupmembers = platform.groupmembers
82 groupmembers = platform.groupmembers
91 groupname = platform.groupname
83 groupname = platform.groupname
92 hidewindow = platform.hidewindow
84 hidewindow = platform.hidewindow
93 isexec = platform.isexec
85 isexec = platform.isexec
94 isowner = platform.isowner
86 isowner = platform.isowner
95 localpath = platform.localpath
87 localpath = platform.localpath
96 lookupreg = platform.lookupreg
88 lookupreg = platform.lookupreg
97 makedir = platform.makedir
89 makedir = platform.makedir
98 nlinks = platform.nlinks
90 nlinks = platform.nlinks
99 normpath = platform.normpath
91 normpath = platform.normpath
100 normcase = platform.normcase
92 normcase = platform.normcase
101 normcasespec = platform.normcasespec
93 normcasespec = platform.normcasespec
102 normcasefallback = platform.normcasefallback
94 normcasefallback = platform.normcasefallback
103 openhardlinks = platform.openhardlinks
95 openhardlinks = platform.openhardlinks
104 oslink = platform.oslink
96 oslink = platform.oslink
105 parsepatchoutput = platform.parsepatchoutput
97 parsepatchoutput = platform.parsepatchoutput
106 pconvert = platform.pconvert
98 pconvert = platform.pconvert
107 poll = platform.poll
99 poll = platform.poll
108 popen = platform.popen
100 popen = platform.popen
109 posixfile = platform.posixfile
101 posixfile = platform.posixfile
110 quotecommand = platform.quotecommand
102 quotecommand = platform.quotecommand
111 readpipe = platform.readpipe
103 readpipe = platform.readpipe
112 rename = platform.rename
104 rename = platform.rename
113 removedirs = platform.removedirs
105 removedirs = platform.removedirs
114 samedevice = platform.samedevice
106 samedevice = platform.samedevice
115 samefile = platform.samefile
107 samefile = platform.samefile
116 samestat = platform.samestat
108 samestat = platform.samestat
117 setbinary = platform.setbinary
109 setbinary = platform.setbinary
118 setflags = platform.setflags
110 setflags = platform.setflags
119 setsignalhandler = platform.setsignalhandler
111 setsignalhandler = platform.setsignalhandler
120 shellquote = platform.shellquote
112 shellquote = platform.shellquote
121 spawndetached = platform.spawndetached
113 spawndetached = platform.spawndetached
122 split = platform.split
114 split = platform.split
123 sshargs = platform.sshargs
115 sshargs = platform.sshargs
124 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
116 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
125 statisexec = platform.statisexec
117 statisexec = platform.statisexec
126 statislink = platform.statislink
118 statislink = platform.statislink
127 testpid = platform.testpid
119 testpid = platform.testpid
128 umask = platform.umask
120 umask = platform.umask
129 unlink = platform.unlink
121 unlink = platform.unlink
130 unlinkpath = platform.unlinkpath
122 unlinkpath = platform.unlinkpath
131 username = platform.username
123 username = platform.username
132
124
133 # Python compatibility
125 # Python compatibility
134
126
135 _notset = object()
127 _notset = object()
136
128
137 # disable Python's problematic floating point timestamps (issue4836)
129 # disable Python's problematic floating point timestamps (issue4836)
138 # (Python hypocritically says you shouldn't change this behavior in
130 # (Python hypocritically says you shouldn't change this behavior in
139 # libraries, and sure enough Mercurial is not a library.)
131 # libraries, and sure enough Mercurial is not a library.)
140 os.stat_float_times(False)
132 os.stat_float_times(False)
141
133
142 def safehasattr(thing, attr):
134 def safehasattr(thing, attr):
143 return getattr(thing, attr, _notset) is not _notset
135 return getattr(thing, attr, _notset) is not _notset
144
136
145 DIGESTS = {
137 DIGESTS = {
146 'md5': hashlib.md5,
138 'md5': hashlib.md5,
147 'sha1': hashlib.sha1,
139 'sha1': hashlib.sha1,
148 'sha512': hashlib.sha512,
140 'sha512': hashlib.sha512,
149 }
141 }
150 # List of digest types from strongest to weakest
142 # List of digest types from strongest to weakest
151 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
143 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
152
144
153 for k in DIGESTS_BY_STRENGTH:
145 for k in DIGESTS_BY_STRENGTH:
154 assert k in DIGESTS
146 assert k in DIGESTS
155
147
156 class digester(object):
148 class digester(object):
157 """helper to compute digests.
149 """helper to compute digests.
158
150
159 This helper can be used to compute one or more digests given their name.
151 This helper can be used to compute one or more digests given their name.
160
152
161 >>> d = digester(['md5', 'sha1'])
153 >>> d = digester(['md5', 'sha1'])
162 >>> d.update('foo')
154 >>> d.update('foo')
163 >>> [k for k in sorted(d)]
155 >>> [k for k in sorted(d)]
164 ['md5', 'sha1']
156 ['md5', 'sha1']
165 >>> d['md5']
157 >>> d['md5']
166 'acbd18db4cc2f85cedef654fccc4a4d8'
158 'acbd18db4cc2f85cedef654fccc4a4d8'
167 >>> d['sha1']
159 >>> d['sha1']
168 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
160 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
169 >>> digester.preferred(['md5', 'sha1'])
161 >>> digester.preferred(['md5', 'sha1'])
170 'sha1'
162 'sha1'
171 """
163 """
172
164
173 def __init__(self, digests, s=''):
165 def __init__(self, digests, s=''):
174 self._hashes = {}
166 self._hashes = {}
175 for k in digests:
167 for k in digests:
176 if k not in DIGESTS:
168 if k not in DIGESTS:
177 raise Abort(_('unknown digest type: %s') % k)
169 raise Abort(_('unknown digest type: %s') % k)
178 self._hashes[k] = DIGESTS[k]()
170 self._hashes[k] = DIGESTS[k]()
179 if s:
171 if s:
180 self.update(s)
172 self.update(s)
181
173
182 def update(self, data):
174 def update(self, data):
183 for h in self._hashes.values():
175 for h in self._hashes.values():
184 h.update(data)
176 h.update(data)
185
177
186 def __getitem__(self, key):
178 def __getitem__(self, key):
187 if key not in DIGESTS:
179 if key not in DIGESTS:
188 raise Abort(_('unknown digest type: %s') % k)
180 raise Abort(_('unknown digest type: %s') % k)
189 return self._hashes[key].hexdigest()
181 return self._hashes[key].hexdigest()
190
182
191 def __iter__(self):
183 def __iter__(self):
192 return iter(self._hashes)
184 return iter(self._hashes)
193
185
194 @staticmethod
186 @staticmethod
195 def preferred(supported):
187 def preferred(supported):
196 """returns the strongest digest type in both supported and DIGESTS."""
188 """returns the strongest digest type in both supported and DIGESTS."""
197
189
198 for k in DIGESTS_BY_STRENGTH:
190 for k in DIGESTS_BY_STRENGTH:
199 if k in supported:
191 if k in supported:
200 return k
192 return k
201 return None
193 return None
202
194
203 class digestchecker(object):
195 class digestchecker(object):
204 """file handle wrapper that additionally checks content against a given
196 """file handle wrapper that additionally checks content against a given
205 size and digests.
197 size and digests.
206
198
207 d = digestchecker(fh, size, {'md5': '...'})
199 d = digestchecker(fh, size, {'md5': '...'})
208
200
209 When multiple digests are given, all of them are validated.
201 When multiple digests are given, all of them are validated.
210 """
202 """
211
203
212 def __init__(self, fh, size, digests):
204 def __init__(self, fh, size, digests):
213 self._fh = fh
205 self._fh = fh
214 self._size = size
206 self._size = size
215 self._got = 0
207 self._got = 0
216 self._digests = dict(digests)
208 self._digests = dict(digests)
217 self._digester = digester(self._digests.keys())
209 self._digester = digester(self._digests.keys())
218
210
219 def read(self, length=-1):
211 def read(self, length=-1):
220 content = self._fh.read(length)
212 content = self._fh.read(length)
221 self._digester.update(content)
213 self._digester.update(content)
222 self._got += len(content)
214 self._got += len(content)
223 return content
215 return content
224
216
225 def validate(self):
217 def validate(self):
226 if self._size != self._got:
218 if self._size != self._got:
227 raise Abort(_('size mismatch: expected %d, got %d') %
219 raise Abort(_('size mismatch: expected %d, got %d') %
228 (self._size, self._got))
220 (self._size, self._got))
229 for k, v in self._digests.items():
221 for k, v in self._digests.items():
230 if v != self._digester[k]:
222 if v != self._digester[k]:
231 # i18n: first parameter is a digest name
223 # i18n: first parameter is a digest name
232 raise Abort(_('%s mismatch: expected %s, got %s') %
224 raise Abort(_('%s mismatch: expected %s, got %s') %
233 (k, v, self._digester[k]))
225 (k, v, self._digester[k]))
234
226
235 try:
227 try:
236 buffer = buffer
228 buffer = buffer
237 except NameError:
229 except NameError:
238 if not pycompat.ispy3:
230 if not pycompat.ispy3:
239 def buffer(sliceable, offset=0):
231 def buffer(sliceable, offset=0):
240 return sliceable[offset:]
232 return sliceable[offset:]
241 else:
233 else:
242 def buffer(sliceable, offset=0):
234 def buffer(sliceable, offset=0):
243 return memoryview(sliceable)[offset:]
235 return memoryview(sliceable)[offset:]
244
236
245 closefds = os.name == 'posix'
237 closefds = os.name == 'posix'
246
238
247 _chunksize = 4096
239 _chunksize = 4096
248
240
249 class bufferedinputpipe(object):
241 class bufferedinputpipe(object):
250 """a manually buffered input pipe
242 """a manually buffered input pipe
251
243
252 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
253 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
254 that data are ready to read if they are already buffered.
246 that data are ready to read if they are already buffered.
255
247
256 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
257 (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
258 empty from the output (allowing collaboration of the buffer with polling).
250 empty from the output (allowing collaboration of the buffer with polling).
259
251
260 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'
261 module from the python stdlib.
253 module from the python stdlib.
262 """
254 """
263
255
264 def __init__(self, input):
256 def __init__(self, input):
265 self._input = input
257 self._input = input
266 self._buffer = []
258 self._buffer = []
267 self._eof = False
259 self._eof = False
268 self._lenbuf = 0
260 self._lenbuf = 0
269
261
270 @property
262 @property
271 def hasbuffer(self):
263 def hasbuffer(self):
272 """True is any data is currently buffered
264 """True is any data is currently buffered
273
265
274 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
275 already data then no polling should be set in place."""
267 already data then no polling should be set in place."""
276 return bool(self._buffer)
268 return bool(self._buffer)
277
269
278 @property
270 @property
279 def closed(self):
271 def closed(self):
280 return self._input.closed
272 return self._input.closed
281
273
282 def fileno(self):
274 def fileno(self):
283 return self._input.fileno()
275 return self._input.fileno()
284
276
285 def close(self):
277 def close(self):
286 return self._input.close()
278 return self._input.close()
287
279
288 def read(self, size):
280 def read(self, size):
289 while (not self._eof) and (self._lenbuf < size):
281 while (not self._eof) and (self._lenbuf < size):
290 self._fillbuffer()
282 self._fillbuffer()
291 return self._frombuffer(size)
283 return self._frombuffer(size)
292
284
293 def readline(self, *args, **kwargs):
285 def readline(self, *args, **kwargs):
294 if 1 < len(self._buffer):
286 if 1 < len(self._buffer):
295 # 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
296 # _frombuffer call that collapse it.
288 # _frombuffer call that collapse it.
297 self._buffer = [''.join(self._buffer)]
289 self._buffer = [''.join(self._buffer)]
298 self._lenbuf = len(self._buffer[0])
290 self._lenbuf = len(self._buffer[0])
299 lfi = -1
291 lfi = -1
300 if self._buffer:
292 if self._buffer:
301 lfi = self._buffer[-1].find('\n')
293 lfi = self._buffer[-1].find('\n')
302 while (not self._eof) and lfi < 0:
294 while (not self._eof) and lfi < 0:
303 self._fillbuffer()
295 self._fillbuffer()
304 if self._buffer:
296 if self._buffer:
305 lfi = self._buffer[-1].find('\n')
297 lfi = self._buffer[-1].find('\n')
306 size = lfi + 1
298 size = lfi + 1
307 if lfi < 0: # end of file
299 if lfi < 0: # end of file
308 size = self._lenbuf
300 size = self._lenbuf
309 elif 1 < len(self._buffer):
301 elif 1 < len(self._buffer):
310 # we need to take previous chunks into account
302 # we need to take previous chunks into account
311 size += self._lenbuf - len(self._buffer[-1])
303 size += self._lenbuf - len(self._buffer[-1])
312 return self._frombuffer(size)
304 return self._frombuffer(size)
313
305
314 def _frombuffer(self, size):
306 def _frombuffer(self, size):
315 """return at most 'size' data from the buffer
307 """return at most 'size' data from the buffer
316
308
317 The data are removed from the buffer."""
309 The data are removed from the buffer."""
318 if size == 0 or not self._buffer:
310 if size == 0 or not self._buffer:
319 return ''
311 return ''
320 buf = self._buffer[0]
312 buf = self._buffer[0]
321 if 1 < len(self._buffer):
313 if 1 < len(self._buffer):
322 buf = ''.join(self._buffer)
314 buf = ''.join(self._buffer)
323
315
324 data = buf[:size]
316 data = buf[:size]
325 buf = buf[len(data):]
317 buf = buf[len(data):]
326 if buf:
318 if buf:
327 self._buffer = [buf]
319 self._buffer = [buf]
328 self._lenbuf = len(buf)
320 self._lenbuf = len(buf)
329 else:
321 else:
330 self._buffer = []
322 self._buffer = []
331 self._lenbuf = 0
323 self._lenbuf = 0
332 return data
324 return data
333
325
334 def _fillbuffer(self):
326 def _fillbuffer(self):
335 """read data to the buffer"""
327 """read data to the buffer"""
336 data = os.read(self._input.fileno(), _chunksize)
328 data = os.read(self._input.fileno(), _chunksize)
337 if not data:
329 if not data:
338 self._eof = True
330 self._eof = True
339 else:
331 else:
340 self._lenbuf += len(data)
332 self._lenbuf += len(data)
341 self._buffer.append(data)
333 self._buffer.append(data)
342
334
343 def popen2(cmd, env=None, newlines=False):
335 def popen2(cmd, env=None, newlines=False):
344 # Setting bufsize to -1 lets the system decide the buffer size.
336 # Setting bufsize to -1 lets the system decide the buffer size.
345 # The default for bufsize is 0, meaning unbuffered. This leads to
337 # The default for bufsize is 0, meaning unbuffered. This leads to
346 # poor performance on Mac OS X: http://bugs.python.org/issue4194
338 # poor performance on Mac OS X: http://bugs.python.org/issue4194
347 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
339 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
348 close_fds=closefds,
340 close_fds=closefds,
349 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
341 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
350 universal_newlines=newlines,
342 universal_newlines=newlines,
351 env=env)
343 env=env)
352 return p.stdin, p.stdout
344 return p.stdin, p.stdout
353
345
354 def popen3(cmd, env=None, newlines=False):
346 def popen3(cmd, env=None, newlines=False):
355 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
347 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
356 return stdin, stdout, stderr
348 return stdin, stdout, stderr
357
349
358 def popen4(cmd, env=None, newlines=False, bufsize=-1):
350 def popen4(cmd, env=None, newlines=False, bufsize=-1):
359 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
351 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
360 close_fds=closefds,
352 close_fds=closefds,
361 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
353 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
362 stderr=subprocess.PIPE,
354 stderr=subprocess.PIPE,
363 universal_newlines=newlines,
355 universal_newlines=newlines,
364 env=env)
356 env=env)
365 return p.stdin, p.stdout, p.stderr, p
357 return p.stdin, p.stdout, p.stderr, p
366
358
367 def version():
359 def version():
368 """Return version information if available."""
360 """Return version information if available."""
369 try:
361 try:
370 from . import __version__
362 from . import __version__
371 return __version__.version
363 return __version__.version
372 except ImportError:
364 except ImportError:
373 return 'unknown'
365 return 'unknown'
374
366
375 def versiontuple(v=None, n=4):
367 def versiontuple(v=None, n=4):
376 """Parses a Mercurial version string into an N-tuple.
368 """Parses a Mercurial version string into an N-tuple.
377
369
378 The version string to be parsed is specified with the ``v`` argument.
370 The version string to be parsed is specified with the ``v`` argument.
379 If it isn't defined, the current Mercurial version string will be parsed.
371 If it isn't defined, the current Mercurial version string will be parsed.
380
372
381 ``n`` can be 2, 3, or 4. Here is how some version strings map to
373 ``n`` can be 2, 3, or 4. Here is how some version strings map to
382 returned values:
374 returned values:
383
375
384 >>> v = '3.6.1+190-df9b73d2d444'
376 >>> v = '3.6.1+190-df9b73d2d444'
385 >>> versiontuple(v, 2)
377 >>> versiontuple(v, 2)
386 (3, 6)
378 (3, 6)
387 >>> versiontuple(v, 3)
379 >>> versiontuple(v, 3)
388 (3, 6, 1)
380 (3, 6, 1)
389 >>> versiontuple(v, 4)
381 >>> versiontuple(v, 4)
390 (3, 6, 1, '190-df9b73d2d444')
382 (3, 6, 1, '190-df9b73d2d444')
391
383
392 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
384 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
393 (3, 6, 1, '190-df9b73d2d444+20151118')
385 (3, 6, 1, '190-df9b73d2d444+20151118')
394
386
395 >>> v = '3.6'
387 >>> v = '3.6'
396 >>> versiontuple(v, 2)
388 >>> versiontuple(v, 2)
397 (3, 6)
389 (3, 6)
398 >>> versiontuple(v, 3)
390 >>> versiontuple(v, 3)
399 (3, 6, None)
391 (3, 6, None)
400 >>> versiontuple(v, 4)
392 >>> versiontuple(v, 4)
401 (3, 6, None, None)
393 (3, 6, None, None)
402
394
403 >>> v = '3.9-rc'
395 >>> v = '3.9-rc'
404 >>> versiontuple(v, 2)
396 >>> versiontuple(v, 2)
405 (3, 9)
397 (3, 9)
406 >>> versiontuple(v, 3)
398 >>> versiontuple(v, 3)
407 (3, 9, None)
399 (3, 9, None)
408 >>> versiontuple(v, 4)
400 >>> versiontuple(v, 4)
409 (3, 9, None, 'rc')
401 (3, 9, None, 'rc')
410
402
411 >>> v = '3.9-rc+2-02a8fea4289b'
403 >>> v = '3.9-rc+2-02a8fea4289b'
412 >>> versiontuple(v, 2)
404 >>> versiontuple(v, 2)
413 (3, 9)
405 (3, 9)
414 >>> versiontuple(v, 3)
406 >>> versiontuple(v, 3)
415 (3, 9, None)
407 (3, 9, None)
416 >>> versiontuple(v, 4)
408 >>> versiontuple(v, 4)
417 (3, 9, None, 'rc+2-02a8fea4289b')
409 (3, 9, None, 'rc+2-02a8fea4289b')
418 """
410 """
419 if not v:
411 if not v:
420 v = version()
412 v = version()
421 parts = remod.split('[\+-]', v, 1)
413 parts = remod.split('[\+-]', v, 1)
422 if len(parts) == 1:
414 if len(parts) == 1:
423 vparts, extra = parts[0], None
415 vparts, extra = parts[0], None
424 else:
416 else:
425 vparts, extra = parts
417 vparts, extra = parts
426
418
427 vints = []
419 vints = []
428 for i in vparts.split('.'):
420 for i in vparts.split('.'):
429 try:
421 try:
430 vints.append(int(i))
422 vints.append(int(i))
431 except ValueError:
423 except ValueError:
432 break
424 break
433 # (3, 6) -> (3, 6, None)
425 # (3, 6) -> (3, 6, None)
434 while len(vints) < 3:
426 while len(vints) < 3:
435 vints.append(None)
427 vints.append(None)
436
428
437 if n == 2:
429 if n == 2:
438 return (vints[0], vints[1])
430 return (vints[0], vints[1])
439 if n == 3:
431 if n == 3:
440 return (vints[0], vints[1], vints[2])
432 return (vints[0], vints[1], vints[2])
441 if n == 4:
433 if n == 4:
442 return (vints[0], vints[1], vints[2], extra)
434 return (vints[0], vints[1], vints[2], extra)
443
435
444 # used by parsedate
436 # used by parsedate
445 defaultdateformats = (
437 defaultdateformats = (
446 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
438 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
447 '%Y-%m-%dT%H:%M', # without seconds
439 '%Y-%m-%dT%H:%M', # without seconds
448 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
440 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
449 '%Y-%m-%dT%H%M', # without seconds
441 '%Y-%m-%dT%H%M', # without seconds
450 '%Y-%m-%d %H:%M:%S', # our common legal variant
442 '%Y-%m-%d %H:%M:%S', # our common legal variant
451 '%Y-%m-%d %H:%M', # without seconds
443 '%Y-%m-%d %H:%M', # without seconds
452 '%Y-%m-%d %H%M%S', # without :
444 '%Y-%m-%d %H%M%S', # without :
453 '%Y-%m-%d %H%M', # without seconds
445 '%Y-%m-%d %H%M', # without seconds
454 '%Y-%m-%d %I:%M:%S%p',
446 '%Y-%m-%d %I:%M:%S%p',
455 '%Y-%m-%d %H:%M',
447 '%Y-%m-%d %H:%M',
456 '%Y-%m-%d %I:%M%p',
448 '%Y-%m-%d %I:%M%p',
457 '%Y-%m-%d',
449 '%Y-%m-%d',
458 '%m-%d',
450 '%m-%d',
459 '%m/%d',
451 '%m/%d',
460 '%m/%d/%y',
452 '%m/%d/%y',
461 '%m/%d/%Y',
453 '%m/%d/%Y',
462 '%a %b %d %H:%M:%S %Y',
454 '%a %b %d %H:%M:%S %Y',
463 '%a %b %d %I:%M:%S%p %Y',
455 '%a %b %d %I:%M:%S%p %Y',
464 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
456 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
465 '%b %d %H:%M:%S %Y',
457 '%b %d %H:%M:%S %Y',
466 '%b %d %I:%M:%S%p %Y',
458 '%b %d %I:%M:%S%p %Y',
467 '%b %d %H:%M:%S',
459 '%b %d %H:%M:%S',
468 '%b %d %I:%M:%S%p',
460 '%b %d %I:%M:%S%p',
469 '%b %d %H:%M',
461 '%b %d %H:%M',
470 '%b %d %I:%M%p',
462 '%b %d %I:%M%p',
471 '%b %d %Y',
463 '%b %d %Y',
472 '%b %d',
464 '%b %d',
473 '%H:%M:%S',
465 '%H:%M:%S',
474 '%I:%M:%S%p',
466 '%I:%M:%S%p',
475 '%H:%M',
467 '%H:%M',
476 '%I:%M%p',
468 '%I:%M%p',
477 )
469 )
478
470
479 extendeddateformats = defaultdateformats + (
471 extendeddateformats = defaultdateformats + (
480 "%Y",
472 "%Y",
481 "%Y-%m",
473 "%Y-%m",
482 "%b",
474 "%b",
483 "%b %Y",
475 "%b %Y",
484 )
476 )
485
477
486 def cachefunc(func):
478 def cachefunc(func):
487 '''cache the result of function calls'''
479 '''cache the result of function calls'''
488 # XXX doesn't handle keywords args
480 # XXX doesn't handle keywords args
489 if func.__code__.co_argcount == 0:
481 if func.__code__.co_argcount == 0:
490 cache = []
482 cache = []
491 def f():
483 def f():
492 if len(cache) == 0:
484 if len(cache) == 0:
493 cache.append(func())
485 cache.append(func())
494 return cache[0]
486 return cache[0]
495 return f
487 return f
496 cache = {}
488 cache = {}
497 if func.__code__.co_argcount == 1:
489 if func.__code__.co_argcount == 1:
498 # we gain a small amount of time because
490 # we gain a small amount of time because
499 # we don't need to pack/unpack the list
491 # we don't need to pack/unpack the list
500 def f(arg):
492 def f(arg):
501 if arg not in cache:
493 if arg not in cache:
502 cache[arg] = func(arg)
494 cache[arg] = func(arg)
503 return cache[arg]
495 return cache[arg]
504 else:
496 else:
505 def f(*args):
497 def f(*args):
506 if args not in cache:
498 if args not in cache:
507 cache[args] = func(*args)
499 cache[args] = func(*args)
508 return cache[args]
500 return cache[args]
509
501
510 return f
502 return f
511
503
512 class sortdict(dict):
504 class sortdict(dict):
513 '''a simple sorted dictionary'''
505 '''a simple sorted dictionary'''
514 def __init__(self, data=None):
506 def __init__(self, data=None):
515 self._list = []
507 self._list = []
516 if data:
508 if data:
517 self.update(data)
509 self.update(data)
518 def copy(self):
510 def copy(self):
519 return sortdict(self)
511 return sortdict(self)
520 def __setitem__(self, key, val):
512 def __setitem__(self, key, val):
521 if key in self:
513 if key in self:
522 self._list.remove(key)
514 self._list.remove(key)
523 self._list.append(key)
515 self._list.append(key)
524 dict.__setitem__(self, key, val)
516 dict.__setitem__(self, key, val)
525 def __iter__(self):
517 def __iter__(self):
526 return self._list.__iter__()
518 return self._list.__iter__()
527 def update(self, src):
519 def update(self, src):
528 if isinstance(src, dict):
520 if isinstance(src, dict):
529 src = src.iteritems()
521 src = src.iteritems()
530 for k, v in src:
522 for k, v in src:
531 self[k] = v
523 self[k] = v
532 def clear(self):
524 def clear(self):
533 dict.clear(self)
525 dict.clear(self)
534 self._list = []
526 self._list = []
535 def items(self):
527 def items(self):
536 return [(k, self[k]) for k in self._list]
528 return [(k, self[k]) for k in self._list]
537 def __delitem__(self, key):
529 def __delitem__(self, key):
538 dict.__delitem__(self, key)
530 dict.__delitem__(self, key)
539 self._list.remove(key)
531 self._list.remove(key)
540 def pop(self, key, *args, **kwargs):
532 def pop(self, key, *args, **kwargs):
541 dict.pop(self, key, *args, **kwargs)
533 dict.pop(self, key, *args, **kwargs)
542 try:
534 try:
543 self._list.remove(key)
535 self._list.remove(key)
544 except ValueError:
536 except ValueError:
545 pass
537 pass
546 def keys(self):
538 def keys(self):
547 return self._list
539 return self._list
548 def iterkeys(self):
540 def iterkeys(self):
549 return self._list.__iter__()
541 return self._list.__iter__()
550 def iteritems(self):
542 def iteritems(self):
551 for k in self._list:
543 for k in self._list:
552 yield k, self[k]
544 yield k, self[k]
553 def insert(self, index, key, val):
545 def insert(self, index, key, val):
554 self._list.insert(index, key)
546 self._list.insert(index, key)
555 dict.__setitem__(self, key, val)
547 dict.__setitem__(self, key, val)
556 def __repr__(self):
548 def __repr__(self):
557 if not self:
549 if not self:
558 return '%s()' % self.__class__.__name__
550 return '%s()' % self.__class__.__name__
559 return '%s(%r)' % (self.__class__.__name__, self.items())
551 return '%s(%r)' % (self.__class__.__name__, self.items())
560
552
561 class _lrucachenode(object):
553 class _lrucachenode(object):
562 """A node in a doubly linked list.
554 """A node in a doubly linked list.
563
555
564 Holds a reference to nodes on either side as well as a key-value
556 Holds a reference to nodes on either side as well as a key-value
565 pair for the dictionary entry.
557 pair for the dictionary entry.
566 """
558 """
567 __slots__ = (u'next', u'prev', u'key', u'value')
559 __slots__ = (u'next', u'prev', u'key', u'value')
568
560
569 def __init__(self):
561 def __init__(self):
570 self.next = None
562 self.next = None
571 self.prev = None
563 self.prev = None
572
564
573 self.key = _notset
565 self.key = _notset
574 self.value = None
566 self.value = None
575
567
576 def markempty(self):
568 def markempty(self):
577 """Mark the node as emptied."""
569 """Mark the node as emptied."""
578 self.key = _notset
570 self.key = _notset
579
571
580 class lrucachedict(object):
572 class lrucachedict(object):
581 """Dict that caches most recent accesses and sets.
573 """Dict that caches most recent accesses and sets.
582
574
583 The dict consists of an actual backing dict - indexed by original
575 The dict consists of an actual backing dict - indexed by original
584 key - and a doubly linked circular list defining the order of entries in
576 key - and a doubly linked circular list defining the order of entries in
585 the cache.
577 the cache.
586
578
587 The head node is the newest entry in the cache. If the cache is full,
579 The head node is the newest entry in the cache. If the cache is full,
588 we recycle head.prev and make it the new head. Cache accesses result in
580 we recycle head.prev and make it the new head. Cache accesses result in
589 the node being moved to before the existing head and being marked as the
581 the node being moved to before the existing head and being marked as the
590 new head node.
582 new head node.
591 """
583 """
592 def __init__(self, max):
584 def __init__(self, max):
593 self._cache = {}
585 self._cache = {}
594
586
595 self._head = head = _lrucachenode()
587 self._head = head = _lrucachenode()
596 head.prev = head
588 head.prev = head
597 head.next = head
589 head.next = head
598 self._size = 1
590 self._size = 1
599 self._capacity = max
591 self._capacity = max
600
592
601 def __len__(self):
593 def __len__(self):
602 return len(self._cache)
594 return len(self._cache)
603
595
604 def __contains__(self, k):
596 def __contains__(self, k):
605 return k in self._cache
597 return k in self._cache
606
598
607 def __iter__(self):
599 def __iter__(self):
608 # We don't have to iterate in cache order, but why not.
600 # We don't have to iterate in cache order, but why not.
609 n = self._head
601 n = self._head
610 for i in range(len(self._cache)):
602 for i in range(len(self._cache)):
611 yield n.key
603 yield n.key
612 n = n.next
604 n = n.next
613
605
614 def __getitem__(self, k):
606 def __getitem__(self, k):
615 node = self._cache[k]
607 node = self._cache[k]
616 self._movetohead(node)
608 self._movetohead(node)
617 return node.value
609 return node.value
618
610
619 def __setitem__(self, k, v):
611 def __setitem__(self, k, v):
620 node = self._cache.get(k)
612 node = self._cache.get(k)
621 # Replace existing value and mark as newest.
613 # Replace existing value and mark as newest.
622 if node is not None:
614 if node is not None:
623 node.value = v
615 node.value = v
624 self._movetohead(node)
616 self._movetohead(node)
625 return
617 return
626
618
627 if self._size < self._capacity:
619 if self._size < self._capacity:
628 node = self._addcapacity()
620 node = self._addcapacity()
629 else:
621 else:
630 # Grab the last/oldest item.
622 # Grab the last/oldest item.
631 node = self._head.prev
623 node = self._head.prev
632
624
633 # At capacity. Kill the old entry.
625 # At capacity. Kill the old entry.
634 if node.key is not _notset:
626 if node.key is not _notset:
635 del self._cache[node.key]
627 del self._cache[node.key]
636
628
637 node.key = k
629 node.key = k
638 node.value = v
630 node.value = v
639 self._cache[k] = node
631 self._cache[k] = node
640 # And mark it as newest entry. No need to adjust order since it
632 # And mark it as newest entry. No need to adjust order since it
641 # is already self._head.prev.
633 # is already self._head.prev.
642 self._head = node
634 self._head = node
643
635
644 def __delitem__(self, k):
636 def __delitem__(self, k):
645 node = self._cache.pop(k)
637 node = self._cache.pop(k)
646 node.markempty()
638 node.markempty()
647
639
648 # Temporarily mark as newest item before re-adjusting head to make
640 # Temporarily mark as newest item before re-adjusting head to make
649 # this node the oldest item.
641 # this node the oldest item.
650 self._movetohead(node)
642 self._movetohead(node)
651 self._head = node.next
643 self._head = node.next
652
644
653 # Additional dict methods.
645 # Additional dict methods.
654
646
655 def get(self, k, default=None):
647 def get(self, k, default=None):
656 try:
648 try:
657 return self._cache[k].value
649 return self._cache[k].value
658 except KeyError:
650 except KeyError:
659 return default
651 return default
660
652
661 def clear(self):
653 def clear(self):
662 n = self._head
654 n = self._head
663 while n.key is not _notset:
655 while n.key is not _notset:
664 n.markempty()
656 n.markempty()
665 n = n.next
657 n = n.next
666
658
667 self._cache.clear()
659 self._cache.clear()
668
660
669 def copy(self):
661 def copy(self):
670 result = lrucachedict(self._capacity)
662 result = lrucachedict(self._capacity)
671 n = self._head.prev
663 n = self._head.prev
672 # Iterate in oldest-to-newest order, so the copy has the right ordering
664 # Iterate in oldest-to-newest order, so the copy has the right ordering
673 for i in range(len(self._cache)):
665 for i in range(len(self._cache)):
674 result[n.key] = n.value
666 result[n.key] = n.value
675 n = n.prev
667 n = n.prev
676 return result
668 return result
677
669
678 def _movetohead(self, node):
670 def _movetohead(self, node):
679 """Mark a node as the newest, making it the new head.
671 """Mark a node as the newest, making it the new head.
680
672
681 When a node is accessed, it becomes the freshest entry in the LRU
673 When a node is accessed, it becomes the freshest entry in the LRU
682 list, which is denoted by self._head.
674 list, which is denoted by self._head.
683
675
684 Visually, let's make ``N`` the new head node (* denotes head):
676 Visually, let's make ``N`` the new head node (* denotes head):
685
677
686 previous/oldest <-> head <-> next/next newest
678 previous/oldest <-> head <-> next/next newest
687
679
688 ----<->--- A* ---<->-----
680 ----<->--- A* ---<->-----
689 | |
681 | |
690 E <-> D <-> N <-> C <-> B
682 E <-> D <-> N <-> C <-> B
691
683
692 To:
684 To:
693
685
694 ----<->--- N* ---<->-----
686 ----<->--- N* ---<->-----
695 | |
687 | |
696 E <-> D <-> C <-> B <-> A
688 E <-> D <-> C <-> B <-> A
697
689
698 This requires the following moves:
690 This requires the following moves:
699
691
700 C.next = D (node.prev.next = node.next)
692 C.next = D (node.prev.next = node.next)
701 D.prev = C (node.next.prev = node.prev)
693 D.prev = C (node.next.prev = node.prev)
702 E.next = N (head.prev.next = node)
694 E.next = N (head.prev.next = node)
703 N.prev = E (node.prev = head.prev)
695 N.prev = E (node.prev = head.prev)
704 N.next = A (node.next = head)
696 N.next = A (node.next = head)
705 A.prev = N (head.prev = node)
697 A.prev = N (head.prev = node)
706 """
698 """
707 head = self._head
699 head = self._head
708 # C.next = D
700 # C.next = D
709 node.prev.next = node.next
701 node.prev.next = node.next
710 # D.prev = C
702 # D.prev = C
711 node.next.prev = node.prev
703 node.next.prev = node.prev
712 # N.prev = E
704 # N.prev = E
713 node.prev = head.prev
705 node.prev = head.prev
714 # N.next = A
706 # N.next = A
715 # It is tempting to do just "head" here, however if node is
707 # It is tempting to do just "head" here, however if node is
716 # adjacent to head, this will do bad things.
708 # adjacent to head, this will do bad things.
717 node.next = head.prev.next
709 node.next = head.prev.next
718 # E.next = N
710 # E.next = N
719 node.next.prev = node
711 node.next.prev = node
720 # A.prev = N
712 # A.prev = N
721 node.prev.next = node
713 node.prev.next = node
722
714
723 self._head = node
715 self._head = node
724
716
725 def _addcapacity(self):
717 def _addcapacity(self):
726 """Add a node to the circular linked list.
718 """Add a node to the circular linked list.
727
719
728 The new node is inserted before the head node.
720 The new node is inserted before the head node.
729 """
721 """
730 head = self._head
722 head = self._head
731 node = _lrucachenode()
723 node = _lrucachenode()
732 head.prev.next = node
724 head.prev.next = node
733 node.prev = head.prev
725 node.prev = head.prev
734 node.next = head
726 node.next = head
735 head.prev = node
727 head.prev = node
736 self._size += 1
728 self._size += 1
737 return node
729 return node
738
730
739 def lrucachefunc(func):
731 def lrucachefunc(func):
740 '''cache most recent results of function calls'''
732 '''cache most recent results of function calls'''
741 cache = {}
733 cache = {}
742 order = collections.deque()
734 order = collections.deque()
743 if func.__code__.co_argcount == 1:
735 if func.__code__.co_argcount == 1:
744 def f(arg):
736 def f(arg):
745 if arg not in cache:
737 if arg not in cache:
746 if len(cache) > 20:
738 if len(cache) > 20:
747 del cache[order.popleft()]
739 del cache[order.popleft()]
748 cache[arg] = func(arg)
740 cache[arg] = func(arg)
749 else:
741 else:
750 order.remove(arg)
742 order.remove(arg)
751 order.append(arg)
743 order.append(arg)
752 return cache[arg]
744 return cache[arg]
753 else:
745 else:
754 def f(*args):
746 def f(*args):
755 if args not in cache:
747 if args not in cache:
756 if len(cache) > 20:
748 if len(cache) > 20:
757 del cache[order.popleft()]
749 del cache[order.popleft()]
758 cache[args] = func(*args)
750 cache[args] = func(*args)
759 else:
751 else:
760 order.remove(args)
752 order.remove(args)
761 order.append(args)
753 order.append(args)
762 return cache[args]
754 return cache[args]
763
755
764 return f
756 return f
765
757
766 class propertycache(object):
758 class propertycache(object):
767 def __init__(self, func):
759 def __init__(self, func):
768 self.func = func
760 self.func = func
769 self.name = func.__name__
761 self.name = func.__name__
770 def __get__(self, obj, type=None):
762 def __get__(self, obj, type=None):
771 result = self.func(obj)
763 result = self.func(obj)
772 self.cachevalue(obj, result)
764 self.cachevalue(obj, result)
773 return result
765 return result
774
766
775 def cachevalue(self, obj, value):
767 def cachevalue(self, obj, value):
776 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
768 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
777 obj.__dict__[self.name] = value
769 obj.__dict__[self.name] = value
778
770
779 def pipefilter(s, cmd):
771 def pipefilter(s, cmd):
780 '''filter string S through command CMD, returning its output'''
772 '''filter string S through command CMD, returning its output'''
781 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
773 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
782 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
774 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
783 pout, perr = p.communicate(s)
775 pout, perr = p.communicate(s)
784 return pout
776 return pout
785
777
786 def tempfilter(s, cmd):
778 def tempfilter(s, cmd):
787 '''filter string S through a pair of temporary files with CMD.
779 '''filter string S through a pair of temporary files with CMD.
788 CMD is used as a template to create the real command to be run,
780 CMD is used as a template to create the real command to be run,
789 with the strings INFILE and OUTFILE replaced by the real names of
781 with the strings INFILE and OUTFILE replaced by the real names of
790 the temporary files generated.'''
782 the temporary files generated.'''
791 inname, outname = None, None
783 inname, outname = None, None
792 try:
784 try:
793 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
785 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
794 fp = os.fdopen(infd, 'wb')
786 fp = os.fdopen(infd, 'wb')
795 fp.write(s)
787 fp.write(s)
796 fp.close()
788 fp.close()
797 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
789 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
798 os.close(outfd)
790 os.close(outfd)
799 cmd = cmd.replace('INFILE', inname)
791 cmd = cmd.replace('INFILE', inname)
800 cmd = cmd.replace('OUTFILE', outname)
792 cmd = cmd.replace('OUTFILE', outname)
801 code = os.system(cmd)
793 code = os.system(cmd)
802 if sys.platform == 'OpenVMS' and code & 1:
794 if sys.platform == 'OpenVMS' and code & 1:
803 code = 0
795 code = 0
804 if code:
796 if code:
805 raise Abort(_("command '%s' failed: %s") %
797 raise Abort(_("command '%s' failed: %s") %
806 (cmd, explainexit(code)))
798 (cmd, explainexit(code)))
807 return readfile(outname)
799 return readfile(outname)
808 finally:
800 finally:
809 try:
801 try:
810 if inname:
802 if inname:
811 os.unlink(inname)
803 os.unlink(inname)
812 except OSError:
804 except OSError:
813 pass
805 pass
814 try:
806 try:
815 if outname:
807 if outname:
816 os.unlink(outname)
808 os.unlink(outname)
817 except OSError:
809 except OSError:
818 pass
810 pass
819
811
820 filtertable = {
812 filtertable = {
821 'tempfile:': tempfilter,
813 'tempfile:': tempfilter,
822 'pipe:': pipefilter,
814 'pipe:': pipefilter,
823 }
815 }
824
816
825 def filter(s, cmd):
817 def filter(s, cmd):
826 "filter a string through a command that transforms its input to its output"
818 "filter a string through a command that transforms its input to its output"
827 for name, fn in filtertable.iteritems():
819 for name, fn in filtertable.iteritems():
828 if cmd.startswith(name):
820 if cmd.startswith(name):
829 return fn(s, cmd[len(name):].lstrip())
821 return fn(s, cmd[len(name):].lstrip())
830 return pipefilter(s, cmd)
822 return pipefilter(s, cmd)
831
823
832 def binary(s):
824 def binary(s):
833 """return true if a string is binary data"""
825 """return true if a string is binary data"""
834 return bool(s and '\0' in s)
826 return bool(s and '\0' in s)
835
827
836 def increasingchunks(source, min=1024, max=65536):
828 def increasingchunks(source, min=1024, max=65536):
837 '''return no less than min bytes per chunk while data remains,
829 '''return no less than min bytes per chunk while data remains,
838 doubling min after each chunk until it reaches max'''
830 doubling min after each chunk until it reaches max'''
839 def log2(x):
831 def log2(x):
840 if not x:
832 if not x:
841 return 0
833 return 0
842 i = 0
834 i = 0
843 while x:
835 while x:
844 x >>= 1
836 x >>= 1
845 i += 1
837 i += 1
846 return i - 1
838 return i - 1
847
839
848 buf = []
840 buf = []
849 blen = 0
841 blen = 0
850 for chunk in source:
842 for chunk in source:
851 buf.append(chunk)
843 buf.append(chunk)
852 blen += len(chunk)
844 blen += len(chunk)
853 if blen >= min:
845 if blen >= min:
854 if min < max:
846 if min < max:
855 min = min << 1
847 min = min << 1
856 nmin = 1 << log2(blen)
848 nmin = 1 << log2(blen)
857 if nmin > min:
849 if nmin > min:
858 min = nmin
850 min = nmin
859 if min > max:
851 if min > max:
860 min = max
852 min = max
861 yield ''.join(buf)
853 yield ''.join(buf)
862 blen = 0
854 blen = 0
863 buf = []
855 buf = []
864 if buf:
856 if buf:
865 yield ''.join(buf)
857 yield ''.join(buf)
866
858
867 Abort = error.Abort
859 Abort = error.Abort
868
860
869 def always(fn):
861 def always(fn):
870 return True
862 return True
871
863
872 def never(fn):
864 def never(fn):
873 return False
865 return False
874
866
875 def nogc(func):
867 def nogc(func):
876 """disable garbage collector
868 """disable garbage collector
877
869
878 Python's garbage collector triggers a GC each time a certain number of
870 Python's garbage collector triggers a GC each time a certain number of
879 container objects (the number being defined by gc.get_threshold()) are
871 container objects (the number being defined by gc.get_threshold()) are
880 allocated even when marked not to be tracked by the collector. Tracking has
872 allocated even when marked not to be tracked by the collector. Tracking has
881 no effect on when GCs are triggered, only on what objects the GC looks
873 no effect on when GCs are triggered, only on what objects the GC looks
882 into. As a workaround, disable GC while building complex (huge)
874 into. As a workaround, disable GC while building complex (huge)
883 containers.
875 containers.
884
876
885 This garbage collector issue have been fixed in 2.7.
877 This garbage collector issue have been fixed in 2.7.
886 """
878 """
887 if sys.version_info >= (2, 7):
879 if sys.version_info >= (2, 7):
888 return func
880 return func
889 def wrapper(*args, **kwargs):
881 def wrapper(*args, **kwargs):
890 gcenabled = gc.isenabled()
882 gcenabled = gc.isenabled()
891 gc.disable()
883 gc.disable()
892 try:
884 try:
893 return func(*args, **kwargs)
885 return func(*args, **kwargs)
894 finally:
886 finally:
895 if gcenabled:
887 if gcenabled:
896 gc.enable()
888 gc.enable()
897 return wrapper
889 return wrapper
898
890
899 def pathto(root, n1, n2):
891 def pathto(root, n1, n2):
900 '''return the relative path from one place to another.
892 '''return the relative path from one place to another.
901 root should use os.sep to separate directories
893 root should use os.sep to separate directories
902 n1 should use os.sep to separate directories
894 n1 should use os.sep to separate directories
903 n2 should use "/" to separate directories
895 n2 should use "/" to separate directories
904 returns an os.sep-separated path.
896 returns an os.sep-separated path.
905
897
906 If n1 is a relative path, it's assumed it's
898 If n1 is a relative path, it's assumed it's
907 relative to root.
899 relative to root.
908 n2 should always be relative to root.
900 n2 should always be relative to root.
909 '''
901 '''
910 if not n1:
902 if not n1:
911 return localpath(n2)
903 return localpath(n2)
912 if os.path.isabs(n1):
904 if os.path.isabs(n1):
913 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
905 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
914 return os.path.join(root, localpath(n2))
906 return os.path.join(root, localpath(n2))
915 n2 = '/'.join((pconvert(root), n2))
907 n2 = '/'.join((pconvert(root), n2))
916 a, b = splitpath(n1), n2.split('/')
908 a, b = splitpath(n1), n2.split('/')
917 a.reverse()
909 a.reverse()
918 b.reverse()
910 b.reverse()
919 while a and b and a[-1] == b[-1]:
911 while a and b and a[-1] == b[-1]:
920 a.pop()
912 a.pop()
921 b.pop()
913 b.pop()
922 b.reverse()
914 b.reverse()
923 return os.sep.join((['..'] * len(a)) + b) or '.'
915 return os.sep.join((['..'] * len(a)) + b) or '.'
924
916
925 def mainfrozen():
917 def mainfrozen():
926 """return True if we are a frozen executable.
918 """return True if we are a frozen executable.
927
919
928 The code supports py2exe (most common, Windows only) and tools/freeze
920 The code supports py2exe (most common, Windows only) and tools/freeze
929 (portable, not much used).
921 (portable, not much used).
930 """
922 """
931 return (safehasattr(sys, "frozen") or # new py2exe
923 return (safehasattr(sys, "frozen") or # new py2exe
932 safehasattr(sys, "importers") or # old py2exe
924 safehasattr(sys, "importers") or # old py2exe
933 imp.is_frozen(u"__main__")) # tools/freeze
925 imp.is_frozen(u"__main__")) # tools/freeze
934
926
935 # the location of data files matching the source code
927 # the location of data files matching the source code
936 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
928 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
937 # executable version (py2exe) doesn't support __file__
929 # executable version (py2exe) doesn't support __file__
938 datapath = os.path.dirname(sys.executable)
930 datapath = os.path.dirname(sys.executable)
939 else:
931 else:
940 datapath = os.path.dirname(__file__)
932 datapath = os.path.dirname(__file__)
941
933
942 if not isinstance(datapath, bytes):
934 if not isinstance(datapath, bytes):
943 datapath = pycompat.fsencode(datapath)
935 datapath = pycompat.fsencode(datapath)
944
936
945 i18n.setdatapath(datapath)
937 i18n.setdatapath(datapath)
946
938
947 _hgexecutable = None
939 _hgexecutable = None
948
940
949 def hgexecutable():
941 def hgexecutable():
950 """return location of the 'hg' executable.
942 """return location of the 'hg' executable.
951
943
952 Defaults to $HG or 'hg' in the search path.
944 Defaults to $HG or 'hg' in the search path.
953 """
945 """
954 if _hgexecutable is None:
946 if _hgexecutable is None:
955 hg = os.environ.get('HG')
947 hg = os.environ.get('HG')
956 mainmod = sys.modules['__main__']
948 mainmod = sys.modules['__main__']
957 if hg:
949 if hg:
958 _sethgexecutable(hg)
950 _sethgexecutable(hg)
959 elif mainfrozen():
951 elif mainfrozen():
960 if getattr(sys, 'frozen', None) == 'macosx_app':
952 if getattr(sys, 'frozen', None) == 'macosx_app':
961 # Env variable set by py2app
953 # Env variable set by py2app
962 _sethgexecutable(os.environ['EXECUTABLEPATH'])
954 _sethgexecutable(os.environ['EXECUTABLEPATH'])
963 else:
955 else:
964 _sethgexecutable(sys.executable)
956 _sethgexecutable(sys.executable)
965 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
957 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
966 _sethgexecutable(mainmod.__file__)
958 _sethgexecutable(mainmod.__file__)
967 else:
959 else:
968 exe = findexe('hg') or os.path.basename(sys.argv[0])
960 exe = findexe('hg') or os.path.basename(sys.argv[0])
969 _sethgexecutable(exe)
961 _sethgexecutable(exe)
970 return _hgexecutable
962 return _hgexecutable
971
963
972 def _sethgexecutable(path):
964 def _sethgexecutable(path):
973 """set location of the 'hg' executable"""
965 """set location of the 'hg' executable"""
974 global _hgexecutable
966 global _hgexecutable
975 _hgexecutable = path
967 _hgexecutable = path
976
968
977 def _isstdout(f):
969 def _isstdout(f):
978 fileno = getattr(f, 'fileno', None)
970 fileno = getattr(f, 'fileno', None)
979 return fileno and fileno() == sys.__stdout__.fileno()
971 return fileno and fileno() == sys.__stdout__.fileno()
980
972
981 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
973 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
982 '''enhanced shell command execution.
974 '''enhanced shell command execution.
983 run with environment maybe modified, maybe in different dir.
975 run with environment maybe modified, maybe in different dir.
984
976
985 if command fails and onerr is None, return status, else raise onerr
977 if command fails and onerr is None, return status, else raise onerr
986 object as exception.
978 object as exception.
987
979
988 if out is specified, it is assumed to be a file-like object that has a
980 if out is specified, it is assumed to be a file-like object that has a
989 write() method. stdout and stderr will be redirected to out.'''
981 write() method. stdout and stderr will be redirected to out.'''
990 if environ is None:
982 if environ is None:
991 environ = {}
983 environ = {}
992 try:
984 try:
993 sys.stdout.flush()
985 sys.stdout.flush()
994 except Exception:
986 except Exception:
995 pass
987 pass
996 def py2shell(val):
988 def py2shell(val):
997 'convert python object into string that is useful to shell'
989 'convert python object into string that is useful to shell'
998 if val is None or val is False:
990 if val is None or val is False:
999 return '0'
991 return '0'
1000 if val is True:
992 if val is True:
1001 return '1'
993 return '1'
1002 return str(val)
994 return str(val)
1003 origcmd = cmd
995 origcmd = cmd
1004 cmd = quotecommand(cmd)
996 cmd = quotecommand(cmd)
1005 if sys.platform == 'plan9' and (sys.version_info[0] == 2
997 if sys.platform == 'plan9' and (sys.version_info[0] == 2
1006 and sys.version_info[1] < 7):
998 and sys.version_info[1] < 7):
1007 # subprocess kludge to work around issues in half-baked Python
999 # subprocess kludge to work around issues in half-baked Python
1008 # ports, notably bichued/python:
1000 # ports, notably bichued/python:
1009 if not cwd is None:
1001 if not cwd is None:
1010 os.chdir(cwd)
1002 os.chdir(cwd)
1011 rc = os.system(cmd)
1003 rc = os.system(cmd)
1012 else:
1004 else:
1013 env = dict(os.environ)
1005 env = dict(os.environ)
1014 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1006 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1015 env['HG'] = hgexecutable()
1007 env['HG'] = hgexecutable()
1016 if out is None or _isstdout(out):
1008 if out is None or _isstdout(out):
1017 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1009 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1018 env=env, cwd=cwd)
1010 env=env, cwd=cwd)
1019 else:
1011 else:
1020 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1012 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1021 env=env, cwd=cwd, stdout=subprocess.PIPE,
1013 env=env, cwd=cwd, stdout=subprocess.PIPE,
1022 stderr=subprocess.STDOUT)
1014 stderr=subprocess.STDOUT)
1023 for line in iter(proc.stdout.readline, ''):
1015 for line in iter(proc.stdout.readline, ''):
1024 out.write(line)
1016 out.write(line)
1025 proc.wait()
1017 proc.wait()
1026 rc = proc.returncode
1018 rc = proc.returncode
1027 if sys.platform == 'OpenVMS' and rc & 1:
1019 if sys.platform == 'OpenVMS' and rc & 1:
1028 rc = 0
1020 rc = 0
1029 if rc and onerr:
1021 if rc and onerr:
1030 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
1022 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
1031 explainexit(rc)[0])
1023 explainexit(rc)[0])
1032 if errprefix:
1024 if errprefix:
1033 errmsg = '%s: %s' % (errprefix, errmsg)
1025 errmsg = '%s: %s' % (errprefix, errmsg)
1034 raise onerr(errmsg)
1026 raise onerr(errmsg)
1035 return rc
1027 return rc
1036
1028
1037 def checksignature(func):
1029 def checksignature(func):
1038 '''wrap a function with code to check for calling errors'''
1030 '''wrap a function with code to check for calling errors'''
1039 def check(*args, **kwargs):
1031 def check(*args, **kwargs):
1040 try:
1032 try:
1041 return func(*args, **kwargs)
1033 return func(*args, **kwargs)
1042 except TypeError:
1034 except TypeError:
1043 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1035 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1044 raise error.SignatureError
1036 raise error.SignatureError
1045 raise
1037 raise
1046
1038
1047 return check
1039 return check
1048
1040
1049 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1041 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1050 '''copy a file, preserving mode and optionally other stat info like
1042 '''copy a file, preserving mode and optionally other stat info like
1051 atime/mtime
1043 atime/mtime
1052
1044
1053 checkambig argument is used with filestat, and is useful only if
1045 checkambig argument is used with filestat, and is useful only if
1054 destination file is guarded by any lock (e.g. repo.lock or
1046 destination file is guarded by any lock (e.g. repo.lock or
1055 repo.wlock).
1047 repo.wlock).
1056
1048
1057 copystat and checkambig should be exclusive.
1049 copystat and checkambig should be exclusive.
1058 '''
1050 '''
1059 assert not (copystat and checkambig)
1051 assert not (copystat and checkambig)
1060 oldstat = None
1052 oldstat = None
1061 if os.path.lexists(dest):
1053 if os.path.lexists(dest):
1062 if checkambig:
1054 if checkambig:
1063 oldstat = checkambig and filestat(dest)
1055 oldstat = checkambig and filestat(dest)
1064 unlink(dest)
1056 unlink(dest)
1065 # hardlinks are problematic on CIFS, quietly ignore this flag
1057 # hardlinks are problematic on CIFS, quietly ignore this flag
1066 # until we find a way to work around it cleanly (issue4546)
1058 # until we find a way to work around it cleanly (issue4546)
1067 if False and hardlink:
1059 if False and hardlink:
1068 try:
1060 try:
1069 oslink(src, dest)
1061 oslink(src, dest)
1070 return
1062 return
1071 except (IOError, OSError):
1063 except (IOError, OSError):
1072 pass # fall back to normal copy
1064 pass # fall back to normal copy
1073 if os.path.islink(src):
1065 if os.path.islink(src):
1074 os.symlink(os.readlink(src), dest)
1066 os.symlink(os.readlink(src), dest)
1075 # copytime is ignored for symlinks, but in general copytime isn't needed
1067 # copytime is ignored for symlinks, but in general copytime isn't needed
1076 # for them anyway
1068 # for them anyway
1077 else:
1069 else:
1078 try:
1070 try:
1079 shutil.copyfile(src, dest)
1071 shutil.copyfile(src, dest)
1080 if copystat:
1072 if copystat:
1081 # copystat also copies mode
1073 # copystat also copies mode
1082 shutil.copystat(src, dest)
1074 shutil.copystat(src, dest)
1083 else:
1075 else:
1084 shutil.copymode(src, dest)
1076 shutil.copymode(src, dest)
1085 if oldstat and oldstat.stat:
1077 if oldstat and oldstat.stat:
1086 newstat = filestat(dest)
1078 newstat = filestat(dest)
1087 if newstat.isambig(oldstat):
1079 if newstat.isambig(oldstat):
1088 # stat of copied file is ambiguous to original one
1080 # stat of copied file is ambiguous to original one
1089 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1081 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1090 os.utime(dest, (advanced, advanced))
1082 os.utime(dest, (advanced, advanced))
1091 except shutil.Error as inst:
1083 except shutil.Error as inst:
1092 raise Abort(str(inst))
1084 raise Abort(str(inst))
1093
1085
1094 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1086 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1095 """Copy a directory tree using hardlinks if possible."""
1087 """Copy a directory tree using hardlinks if possible."""
1096 num = 0
1088 num = 0
1097
1089
1098 if hardlink is None:
1090 if hardlink is None:
1099 hardlink = (os.stat(src).st_dev ==
1091 hardlink = (os.stat(src).st_dev ==
1100 os.stat(os.path.dirname(dst)).st_dev)
1092 os.stat(os.path.dirname(dst)).st_dev)
1101 if hardlink:
1093 if hardlink:
1102 topic = _('linking')
1094 topic = _('linking')
1103 else:
1095 else:
1104 topic = _('copying')
1096 topic = _('copying')
1105
1097
1106 if os.path.isdir(src):
1098 if os.path.isdir(src):
1107 os.mkdir(dst)
1099 os.mkdir(dst)
1108 for name, kind in osutil.listdir(src):
1100 for name, kind in osutil.listdir(src):
1109 srcname = os.path.join(src, name)
1101 srcname = os.path.join(src, name)
1110 dstname = os.path.join(dst, name)
1102 dstname = os.path.join(dst, name)
1111 def nprog(t, pos):
1103 def nprog(t, pos):
1112 if pos is not None:
1104 if pos is not None:
1113 return progress(t, pos + num)
1105 return progress(t, pos + num)
1114 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1106 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1115 num += n
1107 num += n
1116 else:
1108 else:
1117 if hardlink:
1109 if hardlink:
1118 try:
1110 try:
1119 oslink(src, dst)
1111 oslink(src, dst)
1120 except (IOError, OSError):
1112 except (IOError, OSError):
1121 hardlink = False
1113 hardlink = False
1122 shutil.copy(src, dst)
1114 shutil.copy(src, dst)
1123 else:
1115 else:
1124 shutil.copy(src, dst)
1116 shutil.copy(src, dst)
1125 num += 1
1117 num += 1
1126 progress(topic, num)
1118 progress(topic, num)
1127 progress(topic, None)
1119 progress(topic, None)
1128
1120
1129 return hardlink, num
1121 return hardlink, num
1130
1122
1131 _winreservednames = '''con prn aux nul
1123 _winreservednames = '''con prn aux nul
1132 com1 com2 com3 com4 com5 com6 com7 com8 com9
1124 com1 com2 com3 com4 com5 com6 com7 com8 com9
1133 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1125 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1134 _winreservedchars = ':*?"<>|'
1126 _winreservedchars = ':*?"<>|'
1135 def checkwinfilename(path):
1127 def checkwinfilename(path):
1136 r'''Check that the base-relative path is a valid filename on Windows.
1128 r'''Check that the base-relative path is a valid filename on Windows.
1137 Returns None if the path is ok, or a UI string describing the problem.
1129 Returns None if the path is ok, or a UI string describing the problem.
1138
1130
1139 >>> checkwinfilename("just/a/normal/path")
1131 >>> checkwinfilename("just/a/normal/path")
1140 >>> checkwinfilename("foo/bar/con.xml")
1132 >>> checkwinfilename("foo/bar/con.xml")
1141 "filename contains 'con', which is reserved on Windows"
1133 "filename contains 'con', which is reserved on Windows"
1142 >>> checkwinfilename("foo/con.xml/bar")
1134 >>> checkwinfilename("foo/con.xml/bar")
1143 "filename contains 'con', which is reserved on Windows"
1135 "filename contains 'con', which is reserved on Windows"
1144 >>> checkwinfilename("foo/bar/xml.con")
1136 >>> checkwinfilename("foo/bar/xml.con")
1145 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1137 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1146 "filename contains 'AUX', which is reserved on Windows"
1138 "filename contains 'AUX', which is reserved on Windows"
1147 >>> checkwinfilename("foo/bar/bla:.txt")
1139 >>> checkwinfilename("foo/bar/bla:.txt")
1148 "filename contains ':', which is reserved on Windows"
1140 "filename contains ':', which is reserved on Windows"
1149 >>> checkwinfilename("foo/bar/b\07la.txt")
1141 >>> checkwinfilename("foo/bar/b\07la.txt")
1150 "filename contains '\\x07', which is invalid on Windows"
1142 "filename contains '\\x07', which is invalid on Windows"
1151 >>> checkwinfilename("foo/bar/bla ")
1143 >>> checkwinfilename("foo/bar/bla ")
1152 "filename ends with ' ', which is not allowed on Windows"
1144 "filename ends with ' ', which is not allowed on Windows"
1153 >>> checkwinfilename("../bar")
1145 >>> checkwinfilename("../bar")
1154 >>> checkwinfilename("foo\\")
1146 >>> checkwinfilename("foo\\")
1155 "filename ends with '\\', which is invalid on Windows"
1147 "filename ends with '\\', which is invalid on Windows"
1156 >>> checkwinfilename("foo\\/bar")
1148 >>> checkwinfilename("foo\\/bar")
1157 "directory name ends with '\\', which is invalid on Windows"
1149 "directory name ends with '\\', which is invalid on Windows"
1158 '''
1150 '''
1159 if path.endswith('\\'):
1151 if path.endswith('\\'):
1160 return _("filename ends with '\\', which is invalid on Windows")
1152 return _("filename ends with '\\', which is invalid on Windows")
1161 if '\\/' in path:
1153 if '\\/' in path:
1162 return _("directory name ends with '\\', which is invalid on Windows")
1154 return _("directory name ends with '\\', which is invalid on Windows")
1163 for n in path.replace('\\', '/').split('/'):
1155 for n in path.replace('\\', '/').split('/'):
1164 if not n:
1156 if not n:
1165 continue
1157 continue
1166 for c in n:
1158 for c in n:
1167 if c in _winreservedchars:
1159 if c in _winreservedchars:
1168 return _("filename contains '%s', which is reserved "
1160 return _("filename contains '%s', which is reserved "
1169 "on Windows") % c
1161 "on Windows") % c
1170 if ord(c) <= 31:
1162 if ord(c) <= 31:
1171 return _("filename contains %r, which is invalid "
1163 return _("filename contains %r, which is invalid "
1172 "on Windows") % c
1164 "on Windows") % c
1173 base = n.split('.')[0]
1165 base = n.split('.')[0]
1174 if base and base.lower() in _winreservednames:
1166 if base and base.lower() in _winreservednames:
1175 return _("filename contains '%s', which is reserved "
1167 return _("filename contains '%s', which is reserved "
1176 "on Windows") % base
1168 "on Windows") % base
1177 t = n[-1]
1169 t = n[-1]
1178 if t in '. ' and n not in '..':
1170 if t in '. ' and n not in '..':
1179 return _("filename ends with '%s', which is not allowed "
1171 return _("filename ends with '%s', which is not allowed "
1180 "on Windows") % t
1172 "on Windows") % t
1181
1173
1182 if os.name == 'nt':
1174 if os.name == 'nt':
1183 checkosfilename = checkwinfilename
1175 checkosfilename = checkwinfilename
1184 else:
1176 else:
1185 checkosfilename = platform.checkosfilename
1177 checkosfilename = platform.checkosfilename
1186
1178
1187 def makelock(info, pathname):
1179 def makelock(info, pathname):
1188 try:
1180 try:
1189 return os.symlink(info, pathname)
1181 return os.symlink(info, pathname)
1190 except OSError as why:
1182 except OSError as why:
1191 if why.errno == errno.EEXIST:
1183 if why.errno == errno.EEXIST:
1192 raise
1184 raise
1193 except AttributeError: # no symlink in os
1185 except AttributeError: # no symlink in os
1194 pass
1186 pass
1195
1187
1196 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1188 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1197 os.write(ld, info)
1189 os.write(ld, info)
1198 os.close(ld)
1190 os.close(ld)
1199
1191
1200 def readlock(pathname):
1192 def readlock(pathname):
1201 try:
1193 try:
1202 return os.readlink(pathname)
1194 return os.readlink(pathname)
1203 except OSError as why:
1195 except OSError as why:
1204 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1196 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1205 raise
1197 raise
1206 except AttributeError: # no symlink in os
1198 except AttributeError: # no symlink in os
1207 pass
1199 pass
1208 fp = posixfile(pathname)
1200 fp = posixfile(pathname)
1209 r = fp.read()
1201 r = fp.read()
1210 fp.close()
1202 fp.close()
1211 return r
1203 return r
1212
1204
1213 def fstat(fp):
1205 def fstat(fp):
1214 '''stat file object that may not have fileno method.'''
1206 '''stat file object that may not have fileno method.'''
1215 try:
1207 try:
1216 return os.fstat(fp.fileno())
1208 return os.fstat(fp.fileno())
1217 except AttributeError:
1209 except AttributeError:
1218 return os.stat(fp.name)
1210 return os.stat(fp.name)
1219
1211
1220 # File system features
1212 # File system features
1221
1213
1222 def fscasesensitive(path):
1214 def fscasesensitive(path):
1223 """
1215 """
1224 Return true if the given path is on a case-sensitive filesystem
1216 Return true if the given path is on a case-sensitive filesystem
1225
1217
1226 Requires a path (like /foo/.hg) ending with a foldable final
1218 Requires a path (like /foo/.hg) ending with a foldable final
1227 directory component.
1219 directory component.
1228 """
1220 """
1229 s1 = os.lstat(path)
1221 s1 = os.lstat(path)
1230 d, b = os.path.split(path)
1222 d, b = os.path.split(path)
1231 b2 = b.upper()
1223 b2 = b.upper()
1232 if b == b2:
1224 if b == b2:
1233 b2 = b.lower()
1225 b2 = b.lower()
1234 if b == b2:
1226 if b == b2:
1235 return True # no evidence against case sensitivity
1227 return True # no evidence against case sensitivity
1236 p2 = os.path.join(d, b2)
1228 p2 = os.path.join(d, b2)
1237 try:
1229 try:
1238 s2 = os.lstat(p2)
1230 s2 = os.lstat(p2)
1239 if s2 == s1:
1231 if s2 == s1:
1240 return False
1232 return False
1241 return True
1233 return True
1242 except OSError:
1234 except OSError:
1243 return True
1235 return True
1244
1236
1245 try:
1237 try:
1246 import re2
1238 import re2
1247 _re2 = None
1239 _re2 = None
1248 except ImportError:
1240 except ImportError:
1249 _re2 = False
1241 _re2 = False
1250
1242
1251 class _re(object):
1243 class _re(object):
1252 def _checkre2(self):
1244 def _checkre2(self):
1253 global _re2
1245 global _re2
1254 try:
1246 try:
1255 # check if match works, see issue3964
1247 # check if match works, see issue3964
1256 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1248 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1257 except ImportError:
1249 except ImportError:
1258 _re2 = False
1250 _re2 = False
1259
1251
1260 def compile(self, pat, flags=0):
1252 def compile(self, pat, flags=0):
1261 '''Compile a regular expression, using re2 if possible
1253 '''Compile a regular expression, using re2 if possible
1262
1254
1263 For best performance, use only re2-compatible regexp features. The
1255 For best performance, use only re2-compatible regexp features. The
1264 only flags from the re module that are re2-compatible are
1256 only flags from the re module that are re2-compatible are
1265 IGNORECASE and MULTILINE.'''
1257 IGNORECASE and MULTILINE.'''
1266 if _re2 is None:
1258 if _re2 is None:
1267 self._checkre2()
1259 self._checkre2()
1268 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1260 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1269 if flags & remod.IGNORECASE:
1261 if flags & remod.IGNORECASE:
1270 pat = '(?i)' + pat
1262 pat = '(?i)' + pat
1271 if flags & remod.MULTILINE:
1263 if flags & remod.MULTILINE:
1272 pat = '(?m)' + pat
1264 pat = '(?m)' + pat
1273 try:
1265 try:
1274 return re2.compile(pat)
1266 return re2.compile(pat)
1275 except re2.error:
1267 except re2.error:
1276 pass
1268 pass
1277 return remod.compile(pat, flags)
1269 return remod.compile(pat, flags)
1278
1270
1279 @propertycache
1271 @propertycache
1280 def escape(self):
1272 def escape(self):
1281 '''Return the version of escape corresponding to self.compile.
1273 '''Return the version of escape corresponding to self.compile.
1282
1274
1283 This is imperfect because whether re2 or re is used for a particular
1275 This is imperfect because whether re2 or re is used for a particular
1284 function depends on the flags, etc, but it's the best we can do.
1276 function depends on the flags, etc, but it's the best we can do.
1285 '''
1277 '''
1286 global _re2
1278 global _re2
1287 if _re2 is None:
1279 if _re2 is None:
1288 self._checkre2()
1280 self._checkre2()
1289 if _re2:
1281 if _re2:
1290 return re2.escape
1282 return re2.escape
1291 else:
1283 else:
1292 return remod.escape
1284 return remod.escape
1293
1285
1294 re = _re()
1286 re = _re()
1295
1287
1296 _fspathcache = {}
1288 _fspathcache = {}
1297 def fspath(name, root):
1289 def fspath(name, root):
1298 '''Get name in the case stored in the filesystem
1290 '''Get name in the case stored in the filesystem
1299
1291
1300 The name should be relative to root, and be normcase-ed for efficiency.
1292 The name should be relative to root, and be normcase-ed for efficiency.
1301
1293
1302 Note that this function is unnecessary, and should not be
1294 Note that this function is unnecessary, and should not be
1303 called, for case-sensitive filesystems (simply because it's expensive).
1295 called, for case-sensitive filesystems (simply because it's expensive).
1304
1296
1305 The root should be normcase-ed, too.
1297 The root should be normcase-ed, too.
1306 '''
1298 '''
1307 def _makefspathcacheentry(dir):
1299 def _makefspathcacheentry(dir):
1308 return dict((normcase(n), n) for n in os.listdir(dir))
1300 return dict((normcase(n), n) for n in os.listdir(dir))
1309
1301
1310 seps = os.sep
1302 seps = os.sep
1311 if os.altsep:
1303 if os.altsep:
1312 seps = seps + os.altsep
1304 seps = seps + os.altsep
1313 # Protect backslashes. This gets silly very quickly.
1305 # Protect backslashes. This gets silly very quickly.
1314 seps.replace('\\','\\\\')
1306 seps.replace('\\','\\\\')
1315 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1307 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1316 dir = os.path.normpath(root)
1308 dir = os.path.normpath(root)
1317 result = []
1309 result = []
1318 for part, sep in pattern.findall(name):
1310 for part, sep in pattern.findall(name):
1319 if sep:
1311 if sep:
1320 result.append(sep)
1312 result.append(sep)
1321 continue
1313 continue
1322
1314
1323 if dir not in _fspathcache:
1315 if dir not in _fspathcache:
1324 _fspathcache[dir] = _makefspathcacheentry(dir)
1316 _fspathcache[dir] = _makefspathcacheentry(dir)
1325 contents = _fspathcache[dir]
1317 contents = _fspathcache[dir]
1326
1318
1327 found = contents.get(part)
1319 found = contents.get(part)
1328 if not found:
1320 if not found:
1329 # retry "once per directory" per "dirstate.walk" which
1321 # retry "once per directory" per "dirstate.walk" which
1330 # may take place for each patches of "hg qpush", for example
1322 # may take place for each patches of "hg qpush", for example
1331 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1323 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1332 found = contents.get(part)
1324 found = contents.get(part)
1333
1325
1334 result.append(found or part)
1326 result.append(found or part)
1335 dir = os.path.join(dir, part)
1327 dir = os.path.join(dir, part)
1336
1328
1337 return ''.join(result)
1329 return ''.join(result)
1338
1330
1339 def checknlink(testfile):
1331 def checknlink(testfile):
1340 '''check whether hardlink count reporting works properly'''
1332 '''check whether hardlink count reporting works properly'''
1341
1333
1342 # testfile may be open, so we need a separate file for checking to
1334 # testfile may be open, so we need a separate file for checking to
1343 # work around issue2543 (or testfile may get lost on Samba shares)
1335 # work around issue2543 (or testfile may get lost on Samba shares)
1344 f1 = testfile + ".hgtmp1"
1336 f1 = testfile + ".hgtmp1"
1345 if os.path.lexists(f1):
1337 if os.path.lexists(f1):
1346 return False
1338 return False
1347 try:
1339 try:
1348 posixfile(f1, 'w').close()
1340 posixfile(f1, 'w').close()
1349 except IOError:
1341 except IOError:
1350 try:
1342 try:
1351 os.unlink(f1)
1343 os.unlink(f1)
1352 except OSError:
1344 except OSError:
1353 pass
1345 pass
1354 return False
1346 return False
1355
1347
1356 f2 = testfile + ".hgtmp2"
1348 f2 = testfile + ".hgtmp2"
1357 fd = None
1349 fd = None
1358 try:
1350 try:
1359 oslink(f1, f2)
1351 oslink(f1, f2)
1360 # nlinks() may behave differently for files on Windows shares if
1352 # nlinks() may behave differently for files on Windows shares if
1361 # the file is open.
1353 # the file is open.
1362 fd = posixfile(f2)
1354 fd = posixfile(f2)
1363 return nlinks(f2) > 1
1355 return nlinks(f2) > 1
1364 except OSError:
1356 except OSError:
1365 return False
1357 return False
1366 finally:
1358 finally:
1367 if fd is not None:
1359 if fd is not None:
1368 fd.close()
1360 fd.close()
1369 for f in (f1, f2):
1361 for f in (f1, f2):
1370 try:
1362 try:
1371 os.unlink(f)
1363 os.unlink(f)
1372 except OSError:
1364 except OSError:
1373 pass
1365 pass
1374
1366
1375 def endswithsep(path):
1367 def endswithsep(path):
1376 '''Check path ends with os.sep or os.altsep.'''
1368 '''Check path ends with os.sep or os.altsep.'''
1377 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1369 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1378
1370
1379 def splitpath(path):
1371 def splitpath(path):
1380 '''Split path by os.sep.
1372 '''Split path by os.sep.
1381 Note that this function does not use os.altsep because this is
1373 Note that this function does not use os.altsep because this is
1382 an alternative of simple "xxx.split(os.sep)".
1374 an alternative of simple "xxx.split(os.sep)".
1383 It is recommended to use os.path.normpath() before using this
1375 It is recommended to use os.path.normpath() before using this
1384 function if need.'''
1376 function if need.'''
1385 return path.split(os.sep)
1377 return path.split(os.sep)
1386
1378
1387 def gui():
1379 def gui():
1388 '''Are we running in a GUI?'''
1380 '''Are we running in a GUI?'''
1389 if sys.platform == 'darwin':
1381 if sys.platform == 'darwin':
1390 if 'SSH_CONNECTION' in os.environ:
1382 if 'SSH_CONNECTION' in os.environ:
1391 # handle SSH access to a box where the user is logged in
1383 # handle SSH access to a box where the user is logged in
1392 return False
1384 return False
1393 elif getattr(osutil, 'isgui', None):
1385 elif getattr(osutil, 'isgui', None):
1394 # check if a CoreGraphics session is available
1386 # check if a CoreGraphics session is available
1395 return osutil.isgui()
1387 return osutil.isgui()
1396 else:
1388 else:
1397 # pure build; use a safe default
1389 # pure build; use a safe default
1398 return True
1390 return True
1399 else:
1391 else:
1400 return os.name == "nt" or os.environ.get("DISPLAY")
1392 return os.name == "nt" or os.environ.get("DISPLAY")
1401
1393
1402 def mktempcopy(name, emptyok=False, createmode=None):
1394 def mktempcopy(name, emptyok=False, createmode=None):
1403 """Create a temporary file with the same contents from name
1395 """Create a temporary file with the same contents from name
1404
1396
1405 The permission bits are copied from the original file.
1397 The permission bits are copied from the original file.
1406
1398
1407 If the temporary file is going to be truncated immediately, you
1399 If the temporary file is going to be truncated immediately, you
1408 can use emptyok=True as an optimization.
1400 can use emptyok=True as an optimization.
1409
1401
1410 Returns the name of the temporary file.
1402 Returns the name of the temporary file.
1411 """
1403 """
1412 d, fn = os.path.split(name)
1404 d, fn = os.path.split(name)
1413 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1405 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1414 os.close(fd)
1406 os.close(fd)
1415 # Temporary files are created with mode 0600, which is usually not
1407 # Temporary files are created with mode 0600, which is usually not
1416 # what we want. If the original file already exists, just copy
1408 # what we want. If the original file already exists, just copy
1417 # its mode. Otherwise, manually obey umask.
1409 # its mode. Otherwise, manually obey umask.
1418 copymode(name, temp, createmode)
1410 copymode(name, temp, createmode)
1419 if emptyok:
1411 if emptyok:
1420 return temp
1412 return temp
1421 try:
1413 try:
1422 try:
1414 try:
1423 ifp = posixfile(name, "rb")
1415 ifp = posixfile(name, "rb")
1424 except IOError as inst:
1416 except IOError as inst:
1425 if inst.errno == errno.ENOENT:
1417 if inst.errno == errno.ENOENT:
1426 return temp
1418 return temp
1427 if not getattr(inst, 'filename', None):
1419 if not getattr(inst, 'filename', None):
1428 inst.filename = name
1420 inst.filename = name
1429 raise
1421 raise
1430 ofp = posixfile(temp, "wb")
1422 ofp = posixfile(temp, "wb")
1431 for chunk in filechunkiter(ifp):
1423 for chunk in filechunkiter(ifp):
1432 ofp.write(chunk)
1424 ofp.write(chunk)
1433 ifp.close()
1425 ifp.close()
1434 ofp.close()
1426 ofp.close()
1435 except: # re-raises
1427 except: # re-raises
1436 try: os.unlink(temp)
1428 try: os.unlink(temp)
1437 except OSError: pass
1429 except OSError: pass
1438 raise
1430 raise
1439 return temp
1431 return temp
1440
1432
1441 class filestat(object):
1433 class filestat(object):
1442 """help to exactly detect change of a file
1434 """help to exactly detect change of a file
1443
1435
1444 'stat' attribute is result of 'os.stat()' if specified 'path'
1436 'stat' attribute is result of 'os.stat()' if specified 'path'
1445 exists. Otherwise, it is None. This can avoid preparative
1437 exists. Otherwise, it is None. This can avoid preparative
1446 'exists()' examination on client side of this class.
1438 'exists()' examination on client side of this class.
1447 """
1439 """
1448 def __init__(self, path):
1440 def __init__(self, path):
1449 try:
1441 try:
1450 self.stat = os.stat(path)
1442 self.stat = os.stat(path)
1451 except OSError as err:
1443 except OSError as err:
1452 if err.errno != errno.ENOENT:
1444 if err.errno != errno.ENOENT:
1453 raise
1445 raise
1454 self.stat = None
1446 self.stat = None
1455
1447
1456 __hash__ = object.__hash__
1448 __hash__ = object.__hash__
1457
1449
1458 def __eq__(self, old):
1450 def __eq__(self, old):
1459 try:
1451 try:
1460 # if ambiguity between stat of new and old file is
1452 # if ambiguity between stat of new and old file is
1461 # avoided, comparison of size, ctime and mtime is enough
1453 # avoided, comparison of size, ctime and mtime is enough
1462 # to exactly detect change of a file regardless of platform
1454 # to exactly detect change of a file regardless of platform
1463 return (self.stat.st_size == old.stat.st_size and
1455 return (self.stat.st_size == old.stat.st_size and
1464 self.stat.st_ctime == old.stat.st_ctime and
1456 self.stat.st_ctime == old.stat.st_ctime and
1465 self.stat.st_mtime == old.stat.st_mtime)
1457 self.stat.st_mtime == old.stat.st_mtime)
1466 except AttributeError:
1458 except AttributeError:
1467 return False
1459 return False
1468
1460
1469 def isambig(self, old):
1461 def isambig(self, old):
1470 """Examine whether new (= self) stat is ambiguous against old one
1462 """Examine whether new (= self) stat is ambiguous against old one
1471
1463
1472 "S[N]" below means stat of a file at N-th change:
1464 "S[N]" below means stat of a file at N-th change:
1473
1465
1474 - S[n-1].ctime < S[n].ctime: can detect change of a file
1466 - S[n-1].ctime < S[n].ctime: can detect change of a file
1475 - S[n-1].ctime == S[n].ctime
1467 - S[n-1].ctime == S[n].ctime
1476 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1468 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1477 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1469 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1478 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1470 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1479 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1471 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1480
1472
1481 Case (*2) above means that a file was changed twice or more at
1473 Case (*2) above means that a file was changed twice or more at
1482 same time in sec (= S[n-1].ctime), and comparison of timestamp
1474 same time in sec (= S[n-1].ctime), and comparison of timestamp
1483 is ambiguous.
1475 is ambiguous.
1484
1476
1485 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1477 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1486 timestamp is ambiguous".
1478 timestamp is ambiguous".
1487
1479
1488 But advancing mtime only in case (*2) doesn't work as
1480 But advancing mtime only in case (*2) doesn't work as
1489 expected, because naturally advanced S[n].mtime in case (*1)
1481 expected, because naturally advanced S[n].mtime in case (*1)
1490 might be equal to manually advanced S[n-1 or earlier].mtime.
1482 might be equal to manually advanced S[n-1 or earlier].mtime.
1491
1483
1492 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1484 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1493 treated as ambiguous regardless of mtime, to avoid overlooking
1485 treated as ambiguous regardless of mtime, to avoid overlooking
1494 by confliction between such mtime.
1486 by confliction between such mtime.
1495
1487
1496 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1488 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1497 S[n].mtime", even if size of a file isn't changed.
1489 S[n].mtime", even if size of a file isn't changed.
1498 """
1490 """
1499 try:
1491 try:
1500 return (self.stat.st_ctime == old.stat.st_ctime)
1492 return (self.stat.st_ctime == old.stat.st_ctime)
1501 except AttributeError:
1493 except AttributeError:
1502 return False
1494 return False
1503
1495
1504 def avoidambig(self, path, old):
1496 def avoidambig(self, path, old):
1505 """Change file stat of specified path to avoid ambiguity
1497 """Change file stat of specified path to avoid ambiguity
1506
1498
1507 'old' should be previous filestat of 'path'.
1499 'old' should be previous filestat of 'path'.
1508
1500
1509 This skips avoiding ambiguity, if a process doesn't have
1501 This skips avoiding ambiguity, if a process doesn't have
1510 appropriate privileges for 'path'.
1502 appropriate privileges for 'path'.
1511 """
1503 """
1512 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1504 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1513 try:
1505 try:
1514 os.utime(path, (advanced, advanced))
1506 os.utime(path, (advanced, advanced))
1515 except OSError as inst:
1507 except OSError as inst:
1516 if inst.errno == errno.EPERM:
1508 if inst.errno == errno.EPERM:
1517 # utime() on the file created by another user causes EPERM,
1509 # utime() on the file created by another user causes EPERM,
1518 # if a process doesn't have appropriate privileges
1510 # if a process doesn't have appropriate privileges
1519 return
1511 return
1520 raise
1512 raise
1521
1513
1522 def __ne__(self, other):
1514 def __ne__(self, other):
1523 return not self == other
1515 return not self == other
1524
1516
1525 class atomictempfile(object):
1517 class atomictempfile(object):
1526 '''writable file object that atomically updates a file
1518 '''writable file object that atomically updates a file
1527
1519
1528 All writes will go to a temporary copy of the original file. Call
1520 All writes will go to a temporary copy of the original file. Call
1529 close() when you are done writing, and atomictempfile will rename
1521 close() when you are done writing, and atomictempfile will rename
1530 the temporary copy to the original name, making the changes
1522 the temporary copy to the original name, making the changes
1531 visible. If the object is destroyed without being closed, all your
1523 visible. If the object is destroyed without being closed, all your
1532 writes are discarded.
1524 writes are discarded.
1533
1525
1534 checkambig argument of constructor is used with filestat, and is
1526 checkambig argument of constructor is used with filestat, and is
1535 useful only if target file is guarded by any lock (e.g. repo.lock
1527 useful only if target file is guarded by any lock (e.g. repo.lock
1536 or repo.wlock).
1528 or repo.wlock).
1537 '''
1529 '''
1538 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1530 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1539 self.__name = name # permanent name
1531 self.__name = name # permanent name
1540 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1532 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1541 createmode=createmode)
1533 createmode=createmode)
1542 self._fp = posixfile(self._tempname, mode)
1534 self._fp = posixfile(self._tempname, mode)
1543 self._checkambig = checkambig
1535 self._checkambig = checkambig
1544
1536
1545 # delegated methods
1537 # delegated methods
1546 self.read = self._fp.read
1538 self.read = self._fp.read
1547 self.write = self._fp.write
1539 self.write = self._fp.write
1548 self.seek = self._fp.seek
1540 self.seek = self._fp.seek
1549 self.tell = self._fp.tell
1541 self.tell = self._fp.tell
1550 self.fileno = self._fp.fileno
1542 self.fileno = self._fp.fileno
1551
1543
1552 def close(self):
1544 def close(self):
1553 if not self._fp.closed:
1545 if not self._fp.closed:
1554 self._fp.close()
1546 self._fp.close()
1555 filename = localpath(self.__name)
1547 filename = localpath(self.__name)
1556 oldstat = self._checkambig and filestat(filename)
1548 oldstat = self._checkambig and filestat(filename)
1557 if oldstat and oldstat.stat:
1549 if oldstat and oldstat.stat:
1558 rename(self._tempname, filename)
1550 rename(self._tempname, filename)
1559 newstat = filestat(filename)
1551 newstat = filestat(filename)
1560 if newstat.isambig(oldstat):
1552 if newstat.isambig(oldstat):
1561 # stat of changed file is ambiguous to original one
1553 # stat of changed file is ambiguous to original one
1562 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1554 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1563 os.utime(filename, (advanced, advanced))
1555 os.utime(filename, (advanced, advanced))
1564 else:
1556 else:
1565 rename(self._tempname, filename)
1557 rename(self._tempname, filename)
1566
1558
1567 def discard(self):
1559 def discard(self):
1568 if not self._fp.closed:
1560 if not self._fp.closed:
1569 try:
1561 try:
1570 os.unlink(self._tempname)
1562 os.unlink(self._tempname)
1571 except OSError:
1563 except OSError:
1572 pass
1564 pass
1573 self._fp.close()
1565 self._fp.close()
1574
1566
1575 def __del__(self):
1567 def __del__(self):
1576 if safehasattr(self, '_fp'): # constructor actually did something
1568 if safehasattr(self, '_fp'): # constructor actually did something
1577 self.discard()
1569 self.discard()
1578
1570
1579 def __enter__(self):
1571 def __enter__(self):
1580 return self
1572 return self
1581
1573
1582 def __exit__(self, exctype, excvalue, traceback):
1574 def __exit__(self, exctype, excvalue, traceback):
1583 if exctype is not None:
1575 if exctype is not None:
1584 self.discard()
1576 self.discard()
1585 else:
1577 else:
1586 self.close()
1578 self.close()
1587
1579
1588 def makedirs(name, mode=None, notindexed=False):
1580 def makedirs(name, mode=None, notindexed=False):
1589 """recursive directory creation with parent mode inheritance
1581 """recursive directory creation with parent mode inheritance
1590
1582
1591 Newly created directories are marked as "not to be indexed by
1583 Newly created directories are marked as "not to be indexed by
1592 the content indexing service", if ``notindexed`` is specified
1584 the content indexing service", if ``notindexed`` is specified
1593 for "write" mode access.
1585 for "write" mode access.
1594 """
1586 """
1595 try:
1587 try:
1596 makedir(name, notindexed)
1588 makedir(name, notindexed)
1597 except OSError as err:
1589 except OSError as err:
1598 if err.errno == errno.EEXIST:
1590 if err.errno == errno.EEXIST:
1599 return
1591 return
1600 if err.errno != errno.ENOENT or not name:
1592 if err.errno != errno.ENOENT or not name:
1601 raise
1593 raise
1602 parent = os.path.dirname(os.path.abspath(name))
1594 parent = os.path.dirname(os.path.abspath(name))
1603 if parent == name:
1595 if parent == name:
1604 raise
1596 raise
1605 makedirs(parent, mode, notindexed)
1597 makedirs(parent, mode, notindexed)
1606 try:
1598 try:
1607 makedir(name, notindexed)
1599 makedir(name, notindexed)
1608 except OSError as err:
1600 except OSError as err:
1609 # Catch EEXIST to handle races
1601 # Catch EEXIST to handle races
1610 if err.errno == errno.EEXIST:
1602 if err.errno == errno.EEXIST:
1611 return
1603 return
1612 raise
1604 raise
1613 if mode is not None:
1605 if mode is not None:
1614 os.chmod(name, mode)
1606 os.chmod(name, mode)
1615
1607
1616 def readfile(path):
1608 def readfile(path):
1617 with open(path, 'rb') as fp:
1609 with open(path, 'rb') as fp:
1618 return fp.read()
1610 return fp.read()
1619
1611
1620 def writefile(path, text):
1612 def writefile(path, text):
1621 with open(path, 'wb') as fp:
1613 with open(path, 'wb') as fp:
1622 fp.write(text)
1614 fp.write(text)
1623
1615
1624 def appendfile(path, text):
1616 def appendfile(path, text):
1625 with open(path, 'ab') as fp:
1617 with open(path, 'ab') as fp:
1626 fp.write(text)
1618 fp.write(text)
1627
1619
1628 class chunkbuffer(object):
1620 class chunkbuffer(object):
1629 """Allow arbitrary sized chunks of data to be efficiently read from an
1621 """Allow arbitrary sized chunks of data to be efficiently read from an
1630 iterator over chunks of arbitrary size."""
1622 iterator over chunks of arbitrary size."""
1631
1623
1632 def __init__(self, in_iter):
1624 def __init__(self, in_iter):
1633 """in_iter is the iterator that's iterating over the input chunks.
1625 """in_iter is the iterator that's iterating over the input chunks.
1634 targetsize is how big a buffer to try to maintain."""
1626 targetsize is how big a buffer to try to maintain."""
1635 def splitbig(chunks):
1627 def splitbig(chunks):
1636 for chunk in chunks:
1628 for chunk in chunks:
1637 if len(chunk) > 2**20:
1629 if len(chunk) > 2**20:
1638 pos = 0
1630 pos = 0
1639 while pos < len(chunk):
1631 while pos < len(chunk):
1640 end = pos + 2 ** 18
1632 end = pos + 2 ** 18
1641 yield chunk[pos:end]
1633 yield chunk[pos:end]
1642 pos = end
1634 pos = end
1643 else:
1635 else:
1644 yield chunk
1636 yield chunk
1645 self.iter = splitbig(in_iter)
1637 self.iter = splitbig(in_iter)
1646 self._queue = collections.deque()
1638 self._queue = collections.deque()
1647 self._chunkoffset = 0
1639 self._chunkoffset = 0
1648
1640
1649 def read(self, l=None):
1641 def read(self, l=None):
1650 """Read L bytes of data from the iterator of chunks of data.
1642 """Read L bytes of data from the iterator of chunks of data.
1651 Returns less than L bytes if the iterator runs dry.
1643 Returns less than L bytes if the iterator runs dry.
1652
1644
1653 If size parameter is omitted, read everything"""
1645 If size parameter is omitted, read everything"""
1654 if l is None:
1646 if l is None:
1655 return ''.join(self.iter)
1647 return ''.join(self.iter)
1656
1648
1657 left = l
1649 left = l
1658 buf = []
1650 buf = []
1659 queue = self._queue
1651 queue = self._queue
1660 while left > 0:
1652 while left > 0:
1661 # refill the queue
1653 # refill the queue
1662 if not queue:
1654 if not queue:
1663 target = 2**18
1655 target = 2**18
1664 for chunk in self.iter:
1656 for chunk in self.iter:
1665 queue.append(chunk)
1657 queue.append(chunk)
1666 target -= len(chunk)
1658 target -= len(chunk)
1667 if target <= 0:
1659 if target <= 0:
1668 break
1660 break
1669 if not queue:
1661 if not queue:
1670 break
1662 break
1671
1663
1672 # The easy way to do this would be to queue.popleft(), modify the
1664 # The easy way to do this would be to queue.popleft(), modify the
1673 # chunk (if necessary), then queue.appendleft(). However, for cases
1665 # chunk (if necessary), then queue.appendleft(). However, for cases
1674 # where we read partial chunk content, this incurs 2 dequeue
1666 # where we read partial chunk content, this incurs 2 dequeue
1675 # mutations and creates a new str for the remaining chunk in the
1667 # mutations and creates a new str for the remaining chunk in the
1676 # queue. Our code below avoids this overhead.
1668 # queue. Our code below avoids this overhead.
1677
1669
1678 chunk = queue[0]
1670 chunk = queue[0]
1679 chunkl = len(chunk)
1671 chunkl = len(chunk)
1680 offset = self._chunkoffset
1672 offset = self._chunkoffset
1681
1673
1682 # Use full chunk.
1674 # Use full chunk.
1683 if offset == 0 and left >= chunkl:
1675 if offset == 0 and left >= chunkl:
1684 left -= chunkl
1676 left -= chunkl
1685 queue.popleft()
1677 queue.popleft()
1686 buf.append(chunk)
1678 buf.append(chunk)
1687 # self._chunkoffset remains at 0.
1679 # self._chunkoffset remains at 0.
1688 continue
1680 continue
1689
1681
1690 chunkremaining = chunkl - offset
1682 chunkremaining = chunkl - offset
1691
1683
1692 # Use all of unconsumed part of chunk.
1684 # Use all of unconsumed part of chunk.
1693 if left >= chunkremaining:
1685 if left >= chunkremaining:
1694 left -= chunkremaining
1686 left -= chunkremaining
1695 queue.popleft()
1687 queue.popleft()
1696 # offset == 0 is enabled by block above, so this won't merely
1688 # offset == 0 is enabled by block above, so this won't merely
1697 # copy via ``chunk[0:]``.
1689 # copy via ``chunk[0:]``.
1698 buf.append(chunk[offset:])
1690 buf.append(chunk[offset:])
1699 self._chunkoffset = 0
1691 self._chunkoffset = 0
1700
1692
1701 # Partial chunk needed.
1693 # Partial chunk needed.
1702 else:
1694 else:
1703 buf.append(chunk[offset:offset + left])
1695 buf.append(chunk[offset:offset + left])
1704 self._chunkoffset += left
1696 self._chunkoffset += left
1705 left -= chunkremaining
1697 left -= chunkremaining
1706
1698
1707 return ''.join(buf)
1699 return ''.join(buf)
1708
1700
1709 def filechunkiter(f, size=131072, limit=None):
1701 def filechunkiter(f, size=131072, limit=None):
1710 """Create a generator that produces the data in the file size
1702 """Create a generator that produces the data in the file size
1711 (default 131072) bytes at a time, up to optional limit (default is
1703 (default 131072) bytes at a time, up to optional limit (default is
1712 to read all data). Chunks may be less than size bytes if the
1704 to read all data). Chunks may be less than size bytes if the
1713 chunk is the last chunk in the file, or the file is a socket or
1705 chunk is the last chunk in the file, or the file is a socket or
1714 some other type of file that sometimes reads less data than is
1706 some other type of file that sometimes reads less data than is
1715 requested."""
1707 requested."""
1716 assert size >= 0
1708 assert size >= 0
1717 assert limit is None or limit >= 0
1709 assert limit is None or limit >= 0
1718 while True:
1710 while True:
1719 if limit is None:
1711 if limit is None:
1720 nbytes = size
1712 nbytes = size
1721 else:
1713 else:
1722 nbytes = min(limit, size)
1714 nbytes = min(limit, size)
1723 s = nbytes and f.read(nbytes)
1715 s = nbytes and f.read(nbytes)
1724 if not s:
1716 if not s:
1725 break
1717 break
1726 if limit:
1718 if limit:
1727 limit -= len(s)
1719 limit -= len(s)
1728 yield s
1720 yield s
1729
1721
1730 def makedate(timestamp=None):
1722 def makedate(timestamp=None):
1731 '''Return a unix timestamp (or the current time) as a (unixtime,
1723 '''Return a unix timestamp (or the current time) as a (unixtime,
1732 offset) tuple based off the local timezone.'''
1724 offset) tuple based off the local timezone.'''
1733 if timestamp is None:
1725 if timestamp is None:
1734 timestamp = time.time()
1726 timestamp = time.time()
1735 if timestamp < 0:
1727 if timestamp < 0:
1736 hint = _("check your clock")
1728 hint = _("check your clock")
1737 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1729 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1738 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1730 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1739 datetime.datetime.fromtimestamp(timestamp))
1731 datetime.datetime.fromtimestamp(timestamp))
1740 tz = delta.days * 86400 + delta.seconds
1732 tz = delta.days * 86400 + delta.seconds
1741 return timestamp, tz
1733 return timestamp, tz
1742
1734
1743 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1735 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1744 """represent a (unixtime, offset) tuple as a localized time.
1736 """represent a (unixtime, offset) tuple as a localized time.
1745 unixtime is seconds since the epoch, and offset is the time zone's
1737 unixtime is seconds since the epoch, and offset is the time zone's
1746 number of seconds away from UTC.
1738 number of seconds away from UTC.
1747
1739
1748 >>> datestr((0, 0))
1740 >>> datestr((0, 0))
1749 'Thu Jan 01 00:00:00 1970 +0000'
1741 'Thu Jan 01 00:00:00 1970 +0000'
1750 >>> datestr((42, 0))
1742 >>> datestr((42, 0))
1751 'Thu Jan 01 00:00:42 1970 +0000'
1743 'Thu Jan 01 00:00:42 1970 +0000'
1752 >>> datestr((-42, 0))
1744 >>> datestr((-42, 0))
1753 'Wed Dec 31 23:59:18 1969 +0000'
1745 'Wed Dec 31 23:59:18 1969 +0000'
1754 >>> datestr((0x7fffffff, 0))
1746 >>> datestr((0x7fffffff, 0))
1755 'Tue Jan 19 03:14:07 2038 +0000'
1747 'Tue Jan 19 03:14:07 2038 +0000'
1756 >>> datestr((-0x80000000, 0))
1748 >>> datestr((-0x80000000, 0))
1757 'Fri Dec 13 20:45:52 1901 +0000'
1749 'Fri Dec 13 20:45:52 1901 +0000'
1758 """
1750 """
1759 t, tz = date or makedate()
1751 t, tz = date or makedate()
1760 if "%1" in format or "%2" in format or "%z" in format:
1752 if "%1" in format or "%2" in format or "%z" in format:
1761 sign = (tz > 0) and "-" or "+"
1753 sign = (tz > 0) and "-" or "+"
1762 minutes = abs(tz) // 60
1754 minutes = abs(tz) // 60
1763 q, r = divmod(minutes, 60)
1755 q, r = divmod(minutes, 60)
1764 format = format.replace("%z", "%1%2")
1756 format = format.replace("%z", "%1%2")
1765 format = format.replace("%1", "%c%02d" % (sign, q))
1757 format = format.replace("%1", "%c%02d" % (sign, q))
1766 format = format.replace("%2", "%02d" % r)
1758 format = format.replace("%2", "%02d" % r)
1767 d = t - tz
1759 d = t - tz
1768 if d > 0x7fffffff:
1760 if d > 0x7fffffff:
1769 d = 0x7fffffff
1761 d = 0x7fffffff
1770 elif d < -0x80000000:
1762 elif d < -0x80000000:
1771 d = -0x80000000
1763 d = -0x80000000
1772 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1764 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1773 # because they use the gmtime() system call which is buggy on Windows
1765 # because they use the gmtime() system call which is buggy on Windows
1774 # for negative values.
1766 # for negative values.
1775 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1767 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1776 s = t.strftime(format)
1768 s = t.strftime(format)
1777 return s
1769 return s
1778
1770
1779 def shortdate(date=None):
1771 def shortdate(date=None):
1780 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1772 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1781 return datestr(date, format='%Y-%m-%d')
1773 return datestr(date, format='%Y-%m-%d')
1782
1774
1783 def parsetimezone(s):
1775 def parsetimezone(s):
1784 """find a trailing timezone, if any, in string, and return a
1776 """find a trailing timezone, if any, in string, and return a
1785 (offset, remainder) pair"""
1777 (offset, remainder) pair"""
1786
1778
1787 if s.endswith("GMT") or s.endswith("UTC"):
1779 if s.endswith("GMT") or s.endswith("UTC"):
1788 return 0, s[:-3].rstrip()
1780 return 0, s[:-3].rstrip()
1789
1781
1790 # Unix-style timezones [+-]hhmm
1782 # Unix-style timezones [+-]hhmm
1791 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1783 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1792 sign = (s[-5] == "+") and 1 or -1
1784 sign = (s[-5] == "+") and 1 or -1
1793 hours = int(s[-4:-2])
1785 hours = int(s[-4:-2])
1794 minutes = int(s[-2:])
1786 minutes = int(s[-2:])
1795 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1787 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1796
1788
1797 # ISO8601 trailing Z
1789 # ISO8601 trailing Z
1798 if s.endswith("Z") and s[-2:-1].isdigit():
1790 if s.endswith("Z") and s[-2:-1].isdigit():
1799 return 0, s[:-1]
1791 return 0, s[:-1]
1800
1792
1801 # ISO8601-style [+-]hh:mm
1793 # ISO8601-style [+-]hh:mm
1802 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1794 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1803 s[-5:-3].isdigit() and s[-2:].isdigit()):
1795 s[-5:-3].isdigit() and s[-2:].isdigit()):
1804 sign = (s[-6] == "+") and 1 or -1
1796 sign = (s[-6] == "+") and 1 or -1
1805 hours = int(s[-5:-3])
1797 hours = int(s[-5:-3])
1806 minutes = int(s[-2:])
1798 minutes = int(s[-2:])
1807 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1799 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1808
1800
1809 return None, s
1801 return None, s
1810
1802
1811 def strdate(string, format, defaults=[]):
1803 def strdate(string, format, defaults=[]):
1812 """parse a localized time string and return a (unixtime, offset) tuple.
1804 """parse a localized time string and return a (unixtime, offset) tuple.
1813 if the string cannot be parsed, ValueError is raised."""
1805 if the string cannot be parsed, ValueError is raised."""
1814 # NOTE: unixtime = localunixtime + offset
1806 # NOTE: unixtime = localunixtime + offset
1815 offset, date = parsetimezone(string)
1807 offset, date = parsetimezone(string)
1816
1808
1817 # add missing elements from defaults
1809 # add missing elements from defaults
1818 usenow = False # default to using biased defaults
1810 usenow = False # default to using biased defaults
1819 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1811 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1820 found = [True for p in part if ("%"+p) in format]
1812 found = [True for p in part if ("%"+p) in format]
1821 if not found:
1813 if not found:
1822 date += "@" + defaults[part][usenow]
1814 date += "@" + defaults[part][usenow]
1823 format += "@%" + part[0]
1815 format += "@%" + part[0]
1824 else:
1816 else:
1825 # We've found a specific time element, less specific time
1817 # We've found a specific time element, less specific time
1826 # elements are relative to today
1818 # elements are relative to today
1827 usenow = True
1819 usenow = True
1828
1820
1829 timetuple = time.strptime(date, format)
1821 timetuple = time.strptime(date, format)
1830 localunixtime = int(calendar.timegm(timetuple))
1822 localunixtime = int(calendar.timegm(timetuple))
1831 if offset is None:
1823 if offset is None:
1832 # local timezone
1824 # local timezone
1833 unixtime = int(time.mktime(timetuple))
1825 unixtime = int(time.mktime(timetuple))
1834 offset = unixtime - localunixtime
1826 offset = unixtime - localunixtime
1835 else:
1827 else:
1836 unixtime = localunixtime + offset
1828 unixtime = localunixtime + offset
1837 return unixtime, offset
1829 return unixtime, offset
1838
1830
1839 def parsedate(date, formats=None, bias=None):
1831 def parsedate(date, formats=None, bias=None):
1840 """parse a localized date/time and return a (unixtime, offset) tuple.
1832 """parse a localized date/time and return a (unixtime, offset) tuple.
1841
1833
1842 The date may be a "unixtime offset" string or in one of the specified
1834 The date may be a "unixtime offset" string or in one of the specified
1843 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1835 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1844
1836
1845 >>> parsedate(' today ') == parsedate(\
1837 >>> parsedate(' today ') == parsedate(\
1846 datetime.date.today().strftime('%b %d'))
1838 datetime.date.today().strftime('%b %d'))
1847 True
1839 True
1848 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1840 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1849 datetime.timedelta(days=1)\
1841 datetime.timedelta(days=1)\
1850 ).strftime('%b %d'))
1842 ).strftime('%b %d'))
1851 True
1843 True
1852 >>> now, tz = makedate()
1844 >>> now, tz = makedate()
1853 >>> strnow, strtz = parsedate('now')
1845 >>> strnow, strtz = parsedate('now')
1854 >>> (strnow - now) < 1
1846 >>> (strnow - now) < 1
1855 True
1847 True
1856 >>> tz == strtz
1848 >>> tz == strtz
1857 True
1849 True
1858 """
1850 """
1859 if bias is None:
1851 if bias is None:
1860 bias = {}
1852 bias = {}
1861 if not date:
1853 if not date:
1862 return 0, 0
1854 return 0, 0
1863 if isinstance(date, tuple) and len(date) == 2:
1855 if isinstance(date, tuple) and len(date) == 2:
1864 return date
1856 return date
1865 if not formats:
1857 if not formats:
1866 formats = defaultdateformats
1858 formats = defaultdateformats
1867 date = date.strip()
1859 date = date.strip()
1868
1860
1869 if date == 'now' or date == _('now'):
1861 if date == 'now' or date == _('now'):
1870 return makedate()
1862 return makedate()
1871 if date == 'today' or date == _('today'):
1863 if date == 'today' or date == _('today'):
1872 date = datetime.date.today().strftime('%b %d')
1864 date = datetime.date.today().strftime('%b %d')
1873 elif date == 'yesterday' or date == _('yesterday'):
1865 elif date == 'yesterday' or date == _('yesterday'):
1874 date = (datetime.date.today() -
1866 date = (datetime.date.today() -
1875 datetime.timedelta(days=1)).strftime('%b %d')
1867 datetime.timedelta(days=1)).strftime('%b %d')
1876
1868
1877 try:
1869 try:
1878 when, offset = map(int, date.split(' '))
1870 when, offset = map(int, date.split(' '))
1879 except ValueError:
1871 except ValueError:
1880 # fill out defaults
1872 # fill out defaults
1881 now = makedate()
1873 now = makedate()
1882 defaults = {}
1874 defaults = {}
1883 for part in ("d", "mb", "yY", "HI", "M", "S"):
1875 for part in ("d", "mb", "yY", "HI", "M", "S"):
1884 # this piece is for rounding the specific end of unknowns
1876 # this piece is for rounding the specific end of unknowns
1885 b = bias.get(part)
1877 b = bias.get(part)
1886 if b is None:
1878 if b is None:
1887 if part[0] in "HMS":
1879 if part[0] in "HMS":
1888 b = "00"
1880 b = "00"
1889 else:
1881 else:
1890 b = "0"
1882 b = "0"
1891
1883
1892 # this piece is for matching the generic end to today's date
1884 # this piece is for matching the generic end to today's date
1893 n = datestr(now, "%" + part[0])
1885 n = datestr(now, "%" + part[0])
1894
1886
1895 defaults[part] = (b, n)
1887 defaults[part] = (b, n)
1896
1888
1897 for format in formats:
1889 for format in formats:
1898 try:
1890 try:
1899 when, offset = strdate(date, format, defaults)
1891 when, offset = strdate(date, format, defaults)
1900 except (ValueError, OverflowError):
1892 except (ValueError, OverflowError):
1901 pass
1893 pass
1902 else:
1894 else:
1903 break
1895 break
1904 else:
1896 else:
1905 raise Abort(_('invalid date: %r') % date)
1897 raise Abort(_('invalid date: %r') % date)
1906 # validate explicit (probably user-specified) date and
1898 # validate explicit (probably user-specified) date and
1907 # time zone offset. values must fit in signed 32 bits for
1899 # time zone offset. values must fit in signed 32 bits for
1908 # current 32-bit linux runtimes. timezones go from UTC-12
1900 # current 32-bit linux runtimes. timezones go from UTC-12
1909 # to UTC+14
1901 # to UTC+14
1910 if when < -0x80000000 or when > 0x7fffffff:
1902 if when < -0x80000000 or when > 0x7fffffff:
1911 raise Abort(_('date exceeds 32 bits: %d') % when)
1903 raise Abort(_('date exceeds 32 bits: %d') % when)
1912 if offset < -50400 or offset > 43200:
1904 if offset < -50400 or offset > 43200:
1913 raise Abort(_('impossible time zone offset: %d') % offset)
1905 raise Abort(_('impossible time zone offset: %d') % offset)
1914 return when, offset
1906 return when, offset
1915
1907
1916 def matchdate(date):
1908 def matchdate(date):
1917 """Return a function that matches a given date match specifier
1909 """Return a function that matches a given date match specifier
1918
1910
1919 Formats include:
1911 Formats include:
1920
1912
1921 '{date}' match a given date to the accuracy provided
1913 '{date}' match a given date to the accuracy provided
1922
1914
1923 '<{date}' on or before a given date
1915 '<{date}' on or before a given date
1924
1916
1925 '>{date}' on or after a given date
1917 '>{date}' on or after a given date
1926
1918
1927 >>> p1 = parsedate("10:29:59")
1919 >>> p1 = parsedate("10:29:59")
1928 >>> p2 = parsedate("10:30:00")
1920 >>> p2 = parsedate("10:30:00")
1929 >>> p3 = parsedate("10:30:59")
1921 >>> p3 = parsedate("10:30:59")
1930 >>> p4 = parsedate("10:31:00")
1922 >>> p4 = parsedate("10:31:00")
1931 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1923 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1932 >>> f = matchdate("10:30")
1924 >>> f = matchdate("10:30")
1933 >>> f(p1[0])
1925 >>> f(p1[0])
1934 False
1926 False
1935 >>> f(p2[0])
1927 >>> f(p2[0])
1936 True
1928 True
1937 >>> f(p3[0])
1929 >>> f(p3[0])
1938 True
1930 True
1939 >>> f(p4[0])
1931 >>> f(p4[0])
1940 False
1932 False
1941 >>> f(p5[0])
1933 >>> f(p5[0])
1942 False
1934 False
1943 """
1935 """
1944
1936
1945 def lower(date):
1937 def lower(date):
1946 d = {'mb': "1", 'd': "1"}
1938 d = {'mb': "1", 'd': "1"}
1947 return parsedate(date, extendeddateformats, d)[0]
1939 return parsedate(date, extendeddateformats, d)[0]
1948
1940
1949 def upper(date):
1941 def upper(date):
1950 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1942 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1951 for days in ("31", "30", "29"):
1943 for days in ("31", "30", "29"):
1952 try:
1944 try:
1953 d["d"] = days
1945 d["d"] = days
1954 return parsedate(date, extendeddateformats, d)[0]
1946 return parsedate(date, extendeddateformats, d)[0]
1955 except Abort:
1947 except Abort:
1956 pass
1948 pass
1957 d["d"] = "28"
1949 d["d"] = "28"
1958 return parsedate(date, extendeddateformats, d)[0]
1950 return parsedate(date, extendeddateformats, d)[0]
1959
1951
1960 date = date.strip()
1952 date = date.strip()
1961
1953
1962 if not date:
1954 if not date:
1963 raise Abort(_("dates cannot consist entirely of whitespace"))
1955 raise Abort(_("dates cannot consist entirely of whitespace"))
1964 elif date[0] == "<":
1956 elif date[0] == "<":
1965 if not date[1:]:
1957 if not date[1:]:
1966 raise Abort(_("invalid day spec, use '<DATE'"))
1958 raise Abort(_("invalid day spec, use '<DATE'"))
1967 when = upper(date[1:])
1959 when = upper(date[1:])
1968 return lambda x: x <= when
1960 return lambda x: x <= when
1969 elif date[0] == ">":
1961 elif date[0] == ">":
1970 if not date[1:]:
1962 if not date[1:]:
1971 raise Abort(_("invalid day spec, use '>DATE'"))
1963 raise Abort(_("invalid day spec, use '>DATE'"))
1972 when = lower(date[1:])
1964 when = lower(date[1:])
1973 return lambda x: x >= when
1965 return lambda x: x >= when
1974 elif date[0] == "-":
1966 elif date[0] == "-":
1975 try:
1967 try:
1976 days = int(date[1:])
1968 days = int(date[1:])
1977 except ValueError:
1969 except ValueError:
1978 raise Abort(_("invalid day spec: %s") % date[1:])
1970 raise Abort(_("invalid day spec: %s") % date[1:])
1979 if days < 0:
1971 if days < 0:
1980 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1972 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1981 % date[1:])
1973 % date[1:])
1982 when = makedate()[0] - days * 3600 * 24
1974 when = makedate()[0] - days * 3600 * 24
1983 return lambda x: x >= when
1975 return lambda x: x >= when
1984 elif " to " in date:
1976 elif " to " in date:
1985 a, b = date.split(" to ")
1977 a, b = date.split(" to ")
1986 start, stop = lower(a), upper(b)
1978 start, stop = lower(a), upper(b)
1987 return lambda x: x >= start and x <= stop
1979 return lambda x: x >= start and x <= stop
1988 else:
1980 else:
1989 start, stop = lower(date), upper(date)
1981 start, stop = lower(date), upper(date)
1990 return lambda x: x >= start and x <= stop
1982 return lambda x: x >= start and x <= stop
1991
1983
1992 def stringmatcher(pattern):
1984 def stringmatcher(pattern):
1993 """
1985 """
1994 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1986 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1995 returns the matcher name, pattern, and matcher function.
1987 returns the matcher name, pattern, and matcher function.
1996 missing or unknown prefixes are treated as literal matches.
1988 missing or unknown prefixes are treated as literal matches.
1997
1989
1998 helper for tests:
1990 helper for tests:
1999 >>> def test(pattern, *tests):
1991 >>> def test(pattern, *tests):
2000 ... kind, pattern, matcher = stringmatcher(pattern)
1992 ... kind, pattern, matcher = stringmatcher(pattern)
2001 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1993 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2002
1994
2003 exact matching (no prefix):
1995 exact matching (no prefix):
2004 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1996 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2005 ('literal', 'abcdefg', [False, False, True])
1997 ('literal', 'abcdefg', [False, False, True])
2006
1998
2007 regex matching ('re:' prefix)
1999 regex matching ('re:' prefix)
2008 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2000 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2009 ('re', 'a.+b', [False, False, True])
2001 ('re', 'a.+b', [False, False, True])
2010
2002
2011 force exact matches ('literal:' prefix)
2003 force exact matches ('literal:' prefix)
2012 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2004 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2013 ('literal', 're:foobar', [False, True])
2005 ('literal', 're:foobar', [False, True])
2014
2006
2015 unknown prefixes are ignored and treated as literals
2007 unknown prefixes are ignored and treated as literals
2016 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2008 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2017 ('literal', 'foo:bar', [False, False, True])
2009 ('literal', 'foo:bar', [False, False, True])
2018 """
2010 """
2019 if pattern.startswith('re:'):
2011 if pattern.startswith('re:'):
2020 pattern = pattern[3:]
2012 pattern = pattern[3:]
2021 try:
2013 try:
2022 regex = remod.compile(pattern)
2014 regex = remod.compile(pattern)
2023 except remod.error as e:
2015 except remod.error as e:
2024 raise error.ParseError(_('invalid regular expression: %s')
2016 raise error.ParseError(_('invalid regular expression: %s')
2025 % e)
2017 % e)
2026 return 're', pattern, regex.search
2018 return 're', pattern, regex.search
2027 elif pattern.startswith('literal:'):
2019 elif pattern.startswith('literal:'):
2028 pattern = pattern[8:]
2020 pattern = pattern[8:]
2029 return 'literal', pattern, pattern.__eq__
2021 return 'literal', pattern, pattern.__eq__
2030
2022
2031 def shortuser(user):
2023 def shortuser(user):
2032 """Return a short representation of a user name or email address."""
2024 """Return a short representation of a user name or email address."""
2033 f = user.find('@')
2025 f = user.find('@')
2034 if f >= 0:
2026 if f >= 0:
2035 user = user[:f]
2027 user = user[:f]
2036 f = user.find('<')
2028 f = user.find('<')
2037 if f >= 0:
2029 if f >= 0:
2038 user = user[f + 1:]
2030 user = user[f + 1:]
2039 f = user.find(' ')
2031 f = user.find(' ')
2040 if f >= 0:
2032 if f >= 0:
2041 user = user[:f]
2033 user = user[:f]
2042 f = user.find('.')
2034 f = user.find('.')
2043 if f >= 0:
2035 if f >= 0:
2044 user = user[:f]
2036 user = user[:f]
2045 return user
2037 return user
2046
2038
2047 def emailuser(user):
2039 def emailuser(user):
2048 """Return the user portion of an email address."""
2040 """Return the user portion of an email address."""
2049 f = user.find('@')
2041 f = user.find('@')
2050 if f >= 0:
2042 if f >= 0:
2051 user = user[:f]
2043 user = user[:f]
2052 f = user.find('<')
2044 f = user.find('<')
2053 if f >= 0:
2045 if f >= 0:
2054 user = user[f + 1:]
2046 user = user[f + 1:]
2055 return user
2047 return user
2056
2048
2057 def email(author):
2049 def email(author):
2058 '''get email of author.'''
2050 '''get email of author.'''
2059 r = author.find('>')
2051 r = author.find('>')
2060 if r == -1:
2052 if r == -1:
2061 r = None
2053 r = None
2062 return author[author.find('<') + 1:r]
2054 return author[author.find('<') + 1:r]
2063
2055
2064 def ellipsis(text, maxlength=400):
2056 def ellipsis(text, maxlength=400):
2065 """Trim string to at most maxlength (default: 400) columns in display."""
2057 """Trim string to at most maxlength (default: 400) columns in display."""
2066 return encoding.trim(text, maxlength, ellipsis='...')
2058 return encoding.trim(text, maxlength, ellipsis='...')
2067
2059
2068 def unitcountfn(*unittable):
2060 def unitcountfn(*unittable):
2069 '''return a function that renders a readable count of some quantity'''
2061 '''return a function that renders a readable count of some quantity'''
2070
2062
2071 def go(count):
2063 def go(count):
2072 for multiplier, divisor, format in unittable:
2064 for multiplier, divisor, format in unittable:
2073 if count >= divisor * multiplier:
2065 if count >= divisor * multiplier:
2074 return format % (count / float(divisor))
2066 return format % (count / float(divisor))
2075 return unittable[-1][2] % count
2067 return unittable[-1][2] % count
2076
2068
2077 return go
2069 return go
2078
2070
2079 bytecount = unitcountfn(
2071 bytecount = unitcountfn(
2080 (100, 1 << 30, _('%.0f GB')),
2072 (100, 1 << 30, _('%.0f GB')),
2081 (10, 1 << 30, _('%.1f GB')),
2073 (10, 1 << 30, _('%.1f GB')),
2082 (1, 1 << 30, _('%.2f GB')),
2074 (1, 1 << 30, _('%.2f GB')),
2083 (100, 1 << 20, _('%.0f MB')),
2075 (100, 1 << 20, _('%.0f MB')),
2084 (10, 1 << 20, _('%.1f MB')),
2076 (10, 1 << 20, _('%.1f MB')),
2085 (1, 1 << 20, _('%.2f MB')),
2077 (1, 1 << 20, _('%.2f MB')),
2086 (100, 1 << 10, _('%.0f KB')),
2078 (100, 1 << 10, _('%.0f KB')),
2087 (10, 1 << 10, _('%.1f KB')),
2079 (10, 1 << 10, _('%.1f KB')),
2088 (1, 1 << 10, _('%.2f KB')),
2080 (1, 1 << 10, _('%.2f KB')),
2089 (1, 1, _('%.0f bytes')),
2081 (1, 1, _('%.0f bytes')),
2090 )
2082 )
2091
2083
2092 def uirepr(s):
2084 def uirepr(s):
2093 # Avoid double backslash in Windows path repr()
2085 # Avoid double backslash in Windows path repr()
2094 return repr(s).replace('\\\\', '\\')
2086 return repr(s).replace('\\\\', '\\')
2095
2087
2096 # delay import of textwrap
2088 # delay import of textwrap
2097 def MBTextWrapper(**kwargs):
2089 def MBTextWrapper(**kwargs):
2098 class tw(textwrap.TextWrapper):
2090 class tw(textwrap.TextWrapper):
2099 """
2091 """
2100 Extend TextWrapper for width-awareness.
2092 Extend TextWrapper for width-awareness.
2101
2093
2102 Neither number of 'bytes' in any encoding nor 'characters' is
2094 Neither number of 'bytes' in any encoding nor 'characters' is
2103 appropriate to calculate terminal columns for specified string.
2095 appropriate to calculate terminal columns for specified string.
2104
2096
2105 Original TextWrapper implementation uses built-in 'len()' directly,
2097 Original TextWrapper implementation uses built-in 'len()' directly,
2106 so overriding is needed to use width information of each characters.
2098 so overriding is needed to use width information of each characters.
2107
2099
2108 In addition, characters classified into 'ambiguous' width are
2100 In addition, characters classified into 'ambiguous' width are
2109 treated as wide in East Asian area, but as narrow in other.
2101 treated as wide in East Asian area, but as narrow in other.
2110
2102
2111 This requires use decision to determine width of such characters.
2103 This requires use decision to determine width of such characters.
2112 """
2104 """
2113 def _cutdown(self, ucstr, space_left):
2105 def _cutdown(self, ucstr, space_left):
2114 l = 0
2106 l = 0
2115 colwidth = encoding.ucolwidth
2107 colwidth = encoding.ucolwidth
2116 for i in xrange(len(ucstr)):
2108 for i in xrange(len(ucstr)):
2117 l += colwidth(ucstr[i])
2109 l += colwidth(ucstr[i])
2118 if space_left < l:
2110 if space_left < l:
2119 return (ucstr[:i], ucstr[i:])
2111 return (ucstr[:i], ucstr[i:])
2120 return ucstr, ''
2112 return ucstr, ''
2121
2113
2122 # overriding of base class
2114 # overriding of base class
2123 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2115 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2124 space_left = max(width - cur_len, 1)
2116 space_left = max(width - cur_len, 1)
2125
2117
2126 if self.break_long_words:
2118 if self.break_long_words:
2127 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2119 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2128 cur_line.append(cut)
2120 cur_line.append(cut)
2129 reversed_chunks[-1] = res
2121 reversed_chunks[-1] = res
2130 elif not cur_line:
2122 elif not cur_line:
2131 cur_line.append(reversed_chunks.pop())
2123 cur_line.append(reversed_chunks.pop())
2132
2124
2133 # this overriding code is imported from TextWrapper of Python 2.6
2125 # this overriding code is imported from TextWrapper of Python 2.6
2134 # to calculate columns of string by 'encoding.ucolwidth()'
2126 # to calculate columns of string by 'encoding.ucolwidth()'
2135 def _wrap_chunks(self, chunks):
2127 def _wrap_chunks(self, chunks):
2136 colwidth = encoding.ucolwidth
2128 colwidth = encoding.ucolwidth
2137
2129
2138 lines = []
2130 lines = []
2139 if self.width <= 0:
2131 if self.width <= 0:
2140 raise ValueError("invalid width %r (must be > 0)" % self.width)
2132 raise ValueError("invalid width %r (must be > 0)" % self.width)
2141
2133
2142 # Arrange in reverse order so items can be efficiently popped
2134 # Arrange in reverse order so items can be efficiently popped
2143 # from a stack of chucks.
2135 # from a stack of chucks.
2144 chunks.reverse()
2136 chunks.reverse()
2145
2137
2146 while chunks:
2138 while chunks:
2147
2139
2148 # Start the list of chunks that will make up the current line.
2140 # Start the list of chunks that will make up the current line.
2149 # cur_len is just the length of all the chunks in cur_line.
2141 # cur_len is just the length of all the chunks in cur_line.
2150 cur_line = []
2142 cur_line = []
2151 cur_len = 0
2143 cur_len = 0
2152
2144
2153 # Figure out which static string will prefix this line.
2145 # Figure out which static string will prefix this line.
2154 if lines:
2146 if lines:
2155 indent = self.subsequent_indent
2147 indent = self.subsequent_indent
2156 else:
2148 else:
2157 indent = self.initial_indent
2149 indent = self.initial_indent
2158
2150
2159 # Maximum width for this line.
2151 # Maximum width for this line.
2160 width = self.width - len(indent)
2152 width = self.width - len(indent)
2161
2153
2162 # First chunk on line is whitespace -- drop it, unless this
2154 # First chunk on line is whitespace -- drop it, unless this
2163 # is the very beginning of the text (i.e. no lines started yet).
2155 # is the very beginning of the text (i.e. no lines started yet).
2164 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2156 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2165 del chunks[-1]
2157 del chunks[-1]
2166
2158
2167 while chunks:
2159 while chunks:
2168 l = colwidth(chunks[-1])
2160 l = colwidth(chunks[-1])
2169
2161
2170 # Can at least squeeze this chunk onto the current line.
2162 # Can at least squeeze this chunk onto the current line.
2171 if cur_len + l <= width:
2163 if cur_len + l <= width:
2172 cur_line.append(chunks.pop())
2164 cur_line.append(chunks.pop())
2173 cur_len += l
2165 cur_len += l
2174
2166
2175 # Nope, this line is full.
2167 # Nope, this line is full.
2176 else:
2168 else:
2177 break
2169 break
2178
2170
2179 # The current line is full, and the next chunk is too big to
2171 # The current line is full, and the next chunk is too big to
2180 # fit on *any* line (not just this one).
2172 # fit on *any* line (not just this one).
2181 if chunks and colwidth(chunks[-1]) > width:
2173 if chunks and colwidth(chunks[-1]) > width:
2182 self._handle_long_word(chunks, cur_line, cur_len, width)
2174 self._handle_long_word(chunks, cur_line, cur_len, width)
2183
2175
2184 # If the last chunk on this line is all whitespace, drop it.
2176 # If the last chunk on this line is all whitespace, drop it.
2185 if (self.drop_whitespace and
2177 if (self.drop_whitespace and
2186 cur_line and cur_line[-1].strip() == ''):
2178 cur_line and cur_line[-1].strip() == ''):
2187 del cur_line[-1]
2179 del cur_line[-1]
2188
2180
2189 # Convert current line back to a string and store it in list
2181 # Convert current line back to a string and store it in list
2190 # of all lines (return value).
2182 # of all lines (return value).
2191 if cur_line:
2183 if cur_line:
2192 lines.append(indent + ''.join(cur_line))
2184 lines.append(indent + ''.join(cur_line))
2193
2185
2194 return lines
2186 return lines
2195
2187
2196 global MBTextWrapper
2188 global MBTextWrapper
2197 MBTextWrapper = tw
2189 MBTextWrapper = tw
2198 return tw(**kwargs)
2190 return tw(**kwargs)
2199
2191
2200 def wrap(line, width, initindent='', hangindent=''):
2192 def wrap(line, width, initindent='', hangindent=''):
2201 maxindent = max(len(hangindent), len(initindent))
2193 maxindent = max(len(hangindent), len(initindent))
2202 if width <= maxindent:
2194 if width <= maxindent:
2203 # adjust for weird terminal size
2195 # adjust for weird terminal size
2204 width = max(78, maxindent + 1)
2196 width = max(78, maxindent + 1)
2205 line = line.decode(encoding.encoding, encoding.encodingmode)
2197 line = line.decode(encoding.encoding, encoding.encodingmode)
2206 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
2198 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
2207 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
2199 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
2208 wrapper = MBTextWrapper(width=width,
2200 wrapper = MBTextWrapper(width=width,
2209 initial_indent=initindent,
2201 initial_indent=initindent,
2210 subsequent_indent=hangindent)
2202 subsequent_indent=hangindent)
2211 return wrapper.fill(line).encode(encoding.encoding)
2203 return wrapper.fill(line).encode(encoding.encoding)
2212
2204
2213 if (pyplatform.python_implementation() == 'CPython' and
2205 if (pyplatform.python_implementation() == 'CPython' and
2214 sys.version_info < (3, 0)):
2206 sys.version_info < (3, 0)):
2215 # There is an issue in CPython that some IO methods do not handle EINTR
2207 # There is an issue in CPython that some IO methods do not handle EINTR
2216 # correctly. The following table shows what CPython version (and functions)
2208 # correctly. The following table shows what CPython version (and functions)
2217 # are affected (buggy: has the EINTR bug, okay: otherwise):
2209 # are affected (buggy: has the EINTR bug, okay: otherwise):
2218 #
2210 #
2219 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2211 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2220 # --------------------------------------------------
2212 # --------------------------------------------------
2221 # fp.__iter__ | buggy | buggy | okay
2213 # fp.__iter__ | buggy | buggy | okay
2222 # fp.read* | buggy | okay [1] | okay
2214 # fp.read* | buggy | okay [1] | okay
2223 #
2215 #
2224 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2216 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2225 #
2217 #
2226 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2218 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2227 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2219 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2228 #
2220 #
2229 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2221 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2230 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2222 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2231 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2223 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2232 # fp.__iter__ but not other fp.read* methods.
2224 # fp.__iter__ but not other fp.read* methods.
2233 #
2225 #
2234 # On modern systems like Linux, the "read" syscall cannot be interrupted
2226 # On modern systems like Linux, the "read" syscall cannot be interrupted
2235 # when reading "fast" files like on-disk files. So the EINTR issue only
2227 # when reading "fast" files like on-disk files. So the EINTR issue only
2236 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2228 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2237 # files approximately as "fast" files and use the fast (unsafe) code path,
2229 # files approximately as "fast" files and use the fast (unsafe) code path,
2238 # to minimize the performance impact.
2230 # to minimize the performance impact.
2239 if sys.version_info >= (2, 7, 4):
2231 if sys.version_info >= (2, 7, 4):
2240 # fp.readline deals with EINTR correctly, use it as a workaround.
2232 # fp.readline deals with EINTR correctly, use it as a workaround.
2241 def _safeiterfile(fp):
2233 def _safeiterfile(fp):
2242 return iter(fp.readline, '')
2234 return iter(fp.readline, '')
2243 else:
2235 else:
2244 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2236 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2245 # note: this may block longer than necessary because of bufsize.
2237 # note: this may block longer than necessary because of bufsize.
2246 def _safeiterfile(fp, bufsize=4096):
2238 def _safeiterfile(fp, bufsize=4096):
2247 fd = fp.fileno()
2239 fd = fp.fileno()
2248 line = ''
2240 line = ''
2249 while True:
2241 while True:
2250 try:
2242 try:
2251 buf = os.read(fd, bufsize)
2243 buf = os.read(fd, bufsize)
2252 except OSError as ex:
2244 except OSError as ex:
2253 # os.read only raises EINTR before any data is read
2245 # os.read only raises EINTR before any data is read
2254 if ex.errno == errno.EINTR:
2246 if ex.errno == errno.EINTR:
2255 continue
2247 continue
2256 else:
2248 else:
2257 raise
2249 raise
2258 line += buf
2250 line += buf
2259 if '\n' in buf:
2251 if '\n' in buf:
2260 splitted = line.splitlines(True)
2252 splitted = line.splitlines(True)
2261 line = ''
2253 line = ''
2262 for l in splitted:
2254 for l in splitted:
2263 if l[-1] == '\n':
2255 if l[-1] == '\n':
2264 yield l
2256 yield l
2265 else:
2257 else:
2266 line = l
2258 line = l
2267 if not buf:
2259 if not buf:
2268 break
2260 break
2269 if line:
2261 if line:
2270 yield line
2262 yield line
2271
2263
2272 def iterfile(fp):
2264 def iterfile(fp):
2273 fastpath = True
2265 fastpath = True
2274 if type(fp) is file:
2266 if type(fp) is file:
2275 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2267 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2276 if fastpath:
2268 if fastpath:
2277 return fp
2269 return fp
2278 else:
2270 else:
2279 return _safeiterfile(fp)
2271 return _safeiterfile(fp)
2280 else:
2272 else:
2281 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2273 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2282 def iterfile(fp):
2274 def iterfile(fp):
2283 return fp
2275 return fp
2284
2276
2285 def iterlines(iterator):
2277 def iterlines(iterator):
2286 for chunk in iterator:
2278 for chunk in iterator:
2287 for line in chunk.splitlines():
2279 for line in chunk.splitlines():
2288 yield line
2280 yield line
2289
2281
2290 def expandpath(path):
2282 def expandpath(path):
2291 return os.path.expanduser(os.path.expandvars(path))
2283 return os.path.expanduser(os.path.expandvars(path))
2292
2284
2293 def hgcmd():
2285 def hgcmd():
2294 """Return the command used to execute current hg
2286 """Return the command used to execute current hg
2295
2287
2296 This is different from hgexecutable() because on Windows we want
2288 This is different from hgexecutable() because on Windows we want
2297 to avoid things opening new shell windows like batch files, so we
2289 to avoid things opening new shell windows like batch files, so we
2298 get either the python call or current executable.
2290 get either the python call or current executable.
2299 """
2291 """
2300 if mainfrozen():
2292 if mainfrozen():
2301 if getattr(sys, 'frozen', None) == 'macosx_app':
2293 if getattr(sys, 'frozen', None) == 'macosx_app':
2302 # Env variable set by py2app
2294 # Env variable set by py2app
2303 return [os.environ['EXECUTABLEPATH']]
2295 return [os.environ['EXECUTABLEPATH']]
2304 else:
2296 else:
2305 return [sys.executable]
2297 return [sys.executable]
2306 return gethgcmd()
2298 return gethgcmd()
2307
2299
2308 def rundetached(args, condfn):
2300 def rundetached(args, condfn):
2309 """Execute the argument list in a detached process.
2301 """Execute the argument list in a detached process.
2310
2302
2311 condfn is a callable which is called repeatedly and should return
2303 condfn is a callable which is called repeatedly and should return
2312 True once the child process is known to have started successfully.
2304 True once the child process is known to have started successfully.
2313 At this point, the child process PID is returned. If the child
2305 At this point, the child process PID is returned. If the child
2314 process fails to start or finishes before condfn() evaluates to
2306 process fails to start or finishes before condfn() evaluates to
2315 True, return -1.
2307 True, return -1.
2316 """
2308 """
2317 # Windows case is easier because the child process is either
2309 # Windows case is easier because the child process is either
2318 # successfully starting and validating the condition or exiting
2310 # successfully starting and validating the condition or exiting
2319 # on failure. We just poll on its PID. On Unix, if the child
2311 # on failure. We just poll on its PID. On Unix, if the child
2320 # process fails to start, it will be left in a zombie state until
2312 # process fails to start, it will be left in a zombie state until
2321 # the parent wait on it, which we cannot do since we expect a long
2313 # the parent wait on it, which we cannot do since we expect a long
2322 # running process on success. Instead we listen for SIGCHLD telling
2314 # running process on success. Instead we listen for SIGCHLD telling
2323 # us our child process terminated.
2315 # us our child process terminated.
2324 terminated = set()
2316 terminated = set()
2325 def handler(signum, frame):
2317 def handler(signum, frame):
2326 terminated.add(os.wait())
2318 terminated.add(os.wait())
2327 prevhandler = None
2319 prevhandler = None
2328 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2320 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2329 if SIGCHLD is not None:
2321 if SIGCHLD is not None:
2330 prevhandler = signal.signal(SIGCHLD, handler)
2322 prevhandler = signal.signal(SIGCHLD, handler)
2331 try:
2323 try:
2332 pid = spawndetached(args)
2324 pid = spawndetached(args)
2333 while not condfn():
2325 while not condfn():
2334 if ((pid in terminated or not testpid(pid))
2326 if ((pid in terminated or not testpid(pid))
2335 and not condfn()):
2327 and not condfn()):
2336 return -1
2328 return -1
2337 time.sleep(0.1)
2329 time.sleep(0.1)
2338 return pid
2330 return pid
2339 finally:
2331 finally:
2340 if prevhandler is not None:
2332 if prevhandler is not None:
2341 signal.signal(signal.SIGCHLD, prevhandler)
2333 signal.signal(signal.SIGCHLD, prevhandler)
2342
2334
2343 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2335 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2344 """Return the result of interpolating items in the mapping into string s.
2336 """Return the result of interpolating items in the mapping into string s.
2345
2337
2346 prefix is a single character string, or a two character string with
2338 prefix is a single character string, or a two character string with
2347 a backslash as the first character if the prefix needs to be escaped in
2339 a backslash as the first character if the prefix needs to be escaped in
2348 a regular expression.
2340 a regular expression.
2349
2341
2350 fn is an optional function that will be applied to the replacement text
2342 fn is an optional function that will be applied to the replacement text
2351 just before replacement.
2343 just before replacement.
2352
2344
2353 escape_prefix is an optional flag that allows using doubled prefix for
2345 escape_prefix is an optional flag that allows using doubled prefix for
2354 its escaping.
2346 its escaping.
2355 """
2347 """
2356 fn = fn or (lambda s: s)
2348 fn = fn or (lambda s: s)
2357 patterns = '|'.join(mapping.keys())
2349 patterns = '|'.join(mapping.keys())
2358 if escape_prefix:
2350 if escape_prefix:
2359 patterns += '|' + prefix
2351 patterns += '|' + prefix
2360 if len(prefix) > 1:
2352 if len(prefix) > 1:
2361 prefix_char = prefix[1:]
2353 prefix_char = prefix[1:]
2362 else:
2354 else:
2363 prefix_char = prefix
2355 prefix_char = prefix
2364 mapping[prefix_char] = prefix_char
2356 mapping[prefix_char] = prefix_char
2365 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2357 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2366 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2358 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2367
2359
2368 def getport(port):
2360 def getport(port):
2369 """Return the port for a given network service.
2361 """Return the port for a given network service.
2370
2362
2371 If port is an integer, it's returned as is. If it's a string, it's
2363 If port is an integer, it's returned as is. If it's a string, it's
2372 looked up using socket.getservbyname(). If there's no matching
2364 looked up using socket.getservbyname(). If there's no matching
2373 service, error.Abort is raised.
2365 service, error.Abort is raised.
2374 """
2366 """
2375 try:
2367 try:
2376 return int(port)
2368 return int(port)
2377 except ValueError:
2369 except ValueError:
2378 pass
2370 pass
2379
2371
2380 try:
2372 try:
2381 return socket.getservbyname(port)
2373 return socket.getservbyname(port)
2382 except socket.error:
2374 except socket.error:
2383 raise Abort(_("no port number associated with service '%s'") % port)
2375 raise Abort(_("no port number associated with service '%s'") % port)
2384
2376
2385 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2377 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2386 '0': False, 'no': False, 'false': False, 'off': False,
2378 '0': False, 'no': False, 'false': False, 'off': False,
2387 'never': False}
2379 'never': False}
2388
2380
2389 def parsebool(s):
2381 def parsebool(s):
2390 """Parse s into a boolean.
2382 """Parse s into a boolean.
2391
2383
2392 If s is not a valid boolean, returns None.
2384 If s is not a valid boolean, returns None.
2393 """
2385 """
2394 return _booleans.get(s.lower(), None)
2386 return _booleans.get(s.lower(), None)
2395
2387
2396 _hextochr = dict((a + b, chr(int(a + b, 16)))
2388 _hextochr = dict((a + b, chr(int(a + b, 16)))
2397 for a in string.hexdigits for b in string.hexdigits)
2389 for a in string.hexdigits for b in string.hexdigits)
2398
2390
2399 class url(object):
2391 class url(object):
2400 r"""Reliable URL parser.
2392 r"""Reliable URL parser.
2401
2393
2402 This parses URLs and provides attributes for the following
2394 This parses URLs and provides attributes for the following
2403 components:
2395 components:
2404
2396
2405 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2397 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2406
2398
2407 Missing components are set to None. The only exception is
2399 Missing components are set to None. The only exception is
2408 fragment, which is set to '' if present but empty.
2400 fragment, which is set to '' if present but empty.
2409
2401
2410 If parsefragment is False, fragment is included in query. If
2402 If parsefragment is False, fragment is included in query. If
2411 parsequery is False, query is included in path. If both are
2403 parsequery is False, query is included in path. If both are
2412 False, both fragment and query are included in path.
2404 False, both fragment and query are included in path.
2413
2405
2414 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2406 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2415
2407
2416 Note that for backward compatibility reasons, bundle URLs do not
2408 Note that for backward compatibility reasons, bundle URLs do not
2417 take host names. That means 'bundle://../' has a path of '../'.
2409 take host names. That means 'bundle://../' has a path of '../'.
2418
2410
2419 Examples:
2411 Examples:
2420
2412
2421 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2413 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2422 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2414 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2423 >>> url('ssh://[::1]:2200//home/joe/repo')
2415 >>> url('ssh://[::1]:2200//home/joe/repo')
2424 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2416 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2425 >>> url('file:///home/joe/repo')
2417 >>> url('file:///home/joe/repo')
2426 <url scheme: 'file', path: '/home/joe/repo'>
2418 <url scheme: 'file', path: '/home/joe/repo'>
2427 >>> url('file:///c:/temp/foo/')
2419 >>> url('file:///c:/temp/foo/')
2428 <url scheme: 'file', path: 'c:/temp/foo/'>
2420 <url scheme: 'file', path: 'c:/temp/foo/'>
2429 >>> url('bundle:foo')
2421 >>> url('bundle:foo')
2430 <url scheme: 'bundle', path: 'foo'>
2422 <url scheme: 'bundle', path: 'foo'>
2431 >>> url('bundle://../foo')
2423 >>> url('bundle://../foo')
2432 <url scheme: 'bundle', path: '../foo'>
2424 <url scheme: 'bundle', path: '../foo'>
2433 >>> url(r'c:\foo\bar')
2425 >>> url(r'c:\foo\bar')
2434 <url path: 'c:\\foo\\bar'>
2426 <url path: 'c:\\foo\\bar'>
2435 >>> url(r'\\blah\blah\blah')
2427 >>> url(r'\\blah\blah\blah')
2436 <url path: '\\\\blah\\blah\\blah'>
2428 <url path: '\\\\blah\\blah\\blah'>
2437 >>> url(r'\\blah\blah\blah#baz')
2429 >>> url(r'\\blah\blah\blah#baz')
2438 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2430 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2439 >>> url(r'file:///C:\users\me')
2431 >>> url(r'file:///C:\users\me')
2440 <url scheme: 'file', path: 'C:\\users\\me'>
2432 <url scheme: 'file', path: 'C:\\users\\me'>
2441
2433
2442 Authentication credentials:
2434 Authentication credentials:
2443
2435
2444 >>> url('ssh://joe:xyz@x/repo')
2436 >>> url('ssh://joe:xyz@x/repo')
2445 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2437 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2446 >>> url('ssh://joe@x/repo')
2438 >>> url('ssh://joe@x/repo')
2447 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2439 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2448
2440
2449 Query strings and fragments:
2441 Query strings and fragments:
2450
2442
2451 >>> url('http://host/a?b#c')
2443 >>> url('http://host/a?b#c')
2452 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2444 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2453 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2445 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2454 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2446 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2455
2447
2456 Empty path:
2448 Empty path:
2457
2449
2458 >>> url('')
2450 >>> url('')
2459 <url path: ''>
2451 <url path: ''>
2460 >>> url('#a')
2452 >>> url('#a')
2461 <url path: '', fragment: 'a'>
2453 <url path: '', fragment: 'a'>
2462 >>> url('http://host/')
2454 >>> url('http://host/')
2463 <url scheme: 'http', host: 'host', path: ''>
2455 <url scheme: 'http', host: 'host', path: ''>
2464 >>> url('http://host/#a')
2456 >>> url('http://host/#a')
2465 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2457 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2466
2458
2467 Only scheme:
2459 Only scheme:
2468
2460
2469 >>> url('http:')
2461 >>> url('http:')
2470 <url scheme: 'http'>
2462 <url scheme: 'http'>
2471 """
2463 """
2472
2464
2473 _safechars = "!~*'()+"
2465 _safechars = "!~*'()+"
2474 _safepchars = "/!~*'()+:\\"
2466 _safepchars = "/!~*'()+:\\"
2475 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2467 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2476
2468
2477 def __init__(self, path, parsequery=True, parsefragment=True):
2469 def __init__(self, path, parsequery=True, parsefragment=True):
2478 # We slowly chomp away at path until we have only the path left
2470 # We slowly chomp away at path until we have only the path left
2479 self.scheme = self.user = self.passwd = self.host = None
2471 self.scheme = self.user = self.passwd = self.host = None
2480 self.port = self.path = self.query = self.fragment = None
2472 self.port = self.path = self.query = self.fragment = None
2481 self._localpath = True
2473 self._localpath = True
2482 self._hostport = ''
2474 self._hostport = ''
2483 self._origpath = path
2475 self._origpath = path
2484
2476
2485 if parsefragment and '#' in path:
2477 if parsefragment and '#' in path:
2486 path, self.fragment = path.split('#', 1)
2478 path, self.fragment = path.split('#', 1)
2487
2479
2488 # special case for Windows drive letters and UNC paths
2480 # special case for Windows drive letters and UNC paths
2489 if hasdriveletter(path) or path.startswith('\\\\'):
2481 if hasdriveletter(path) or path.startswith('\\\\'):
2490 self.path = path
2482 self.path = path
2491 return
2483 return
2492
2484
2493 # For compatibility reasons, we can't handle bundle paths as
2485 # For compatibility reasons, we can't handle bundle paths as
2494 # normal URLS
2486 # normal URLS
2495 if path.startswith('bundle:'):
2487 if path.startswith('bundle:'):
2496 self.scheme = 'bundle'
2488 self.scheme = 'bundle'
2497 path = path[7:]
2489 path = path[7:]
2498 if path.startswith('//'):
2490 if path.startswith('//'):
2499 path = path[2:]
2491 path = path[2:]
2500 self.path = path
2492 self.path = path
2501 return
2493 return
2502
2494
2503 if self._matchscheme(path):
2495 if self._matchscheme(path):
2504 parts = path.split(':', 1)
2496 parts = path.split(':', 1)
2505 if parts[0]:
2497 if parts[0]:
2506 self.scheme, path = parts
2498 self.scheme, path = parts
2507 self._localpath = False
2499 self._localpath = False
2508
2500
2509 if not path:
2501 if not path:
2510 path = None
2502 path = None
2511 if self._localpath:
2503 if self._localpath:
2512 self.path = ''
2504 self.path = ''
2513 return
2505 return
2514 else:
2506 else:
2515 if self._localpath:
2507 if self._localpath:
2516 self.path = path
2508 self.path = path
2517 return
2509 return
2518
2510
2519 if parsequery and '?' in path:
2511 if parsequery and '?' in path:
2520 path, self.query = path.split('?', 1)
2512 path, self.query = path.split('?', 1)
2521 if not path:
2513 if not path:
2522 path = None
2514 path = None
2523 if not self.query:
2515 if not self.query:
2524 self.query = None
2516 self.query = None
2525
2517
2526 # // is required to specify a host/authority
2518 # // is required to specify a host/authority
2527 if path and path.startswith('//'):
2519 if path and path.startswith('//'):
2528 parts = path[2:].split('/', 1)
2520 parts = path[2:].split('/', 1)
2529 if len(parts) > 1:
2521 if len(parts) > 1:
2530 self.host, path = parts
2522 self.host, path = parts
2531 else:
2523 else:
2532 self.host = parts[0]
2524 self.host = parts[0]
2533 path = None
2525 path = None
2534 if not self.host:
2526 if not self.host:
2535 self.host = None
2527 self.host = None
2536 # path of file:///d is /d
2528 # path of file:///d is /d
2537 # path of file:///d:/ is d:/, not /d:/
2529 # path of file:///d:/ is d:/, not /d:/
2538 if path and not hasdriveletter(path):
2530 if path and not hasdriveletter(path):
2539 path = '/' + path
2531 path = '/' + path
2540
2532
2541 if self.host and '@' in self.host:
2533 if self.host and '@' in self.host:
2542 self.user, self.host = self.host.rsplit('@', 1)
2534 self.user, self.host = self.host.rsplit('@', 1)
2543 if ':' in self.user:
2535 if ':' in self.user:
2544 self.user, self.passwd = self.user.split(':', 1)
2536 self.user, self.passwd = self.user.split(':', 1)
2545 if not self.host:
2537 if not self.host:
2546 self.host = None
2538 self.host = None
2547
2539
2548 # Don't split on colons in IPv6 addresses without ports
2540 # Don't split on colons in IPv6 addresses without ports
2549 if (self.host and ':' in self.host and
2541 if (self.host and ':' in self.host and
2550 not (self.host.startswith('[') and self.host.endswith(']'))):
2542 not (self.host.startswith('[') and self.host.endswith(']'))):
2551 self._hostport = self.host
2543 self._hostport = self.host
2552 self.host, self.port = self.host.rsplit(':', 1)
2544 self.host, self.port = self.host.rsplit(':', 1)
2553 if not self.host:
2545 if not self.host:
2554 self.host = None
2546 self.host = None
2555
2547
2556 if (self.host and self.scheme == 'file' and
2548 if (self.host and self.scheme == 'file' and
2557 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2549 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2558 raise Abort(_('file:// URLs can only refer to localhost'))
2550 raise Abort(_('file:// URLs can only refer to localhost'))
2559
2551
2560 self.path = path
2552 self.path = path
2561
2553
2562 # leave the query string escaped
2554 # leave the query string escaped
2563 for a in ('user', 'passwd', 'host', 'port',
2555 for a in ('user', 'passwd', 'host', 'port',
2564 'path', 'fragment'):
2556 'path', 'fragment'):
2565 v = getattr(self, a)
2557 v = getattr(self, a)
2566 if v is not None:
2558 if v is not None:
2567 setattr(self, a, pycompat.urlunquote(v))
2559 setattr(self, a, pycompat.urlunquote(v))
2568
2560
2569 def __repr__(self):
2561 def __repr__(self):
2570 attrs = []
2562 attrs = []
2571 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2563 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2572 'query', 'fragment'):
2564 'query', 'fragment'):
2573 v = getattr(self, a)
2565 v = getattr(self, a)
2574 if v is not None:
2566 if v is not None:
2575 attrs.append('%s: %r' % (a, v))
2567 attrs.append('%s: %r' % (a, v))
2576 return '<url %s>' % ', '.join(attrs)
2568 return '<url %s>' % ', '.join(attrs)
2577
2569
2578 def __str__(self):
2570 def __str__(self):
2579 r"""Join the URL's components back into a URL string.
2571 r"""Join the URL's components back into a URL string.
2580
2572
2581 Examples:
2573 Examples:
2582
2574
2583 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2575 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2584 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2576 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2585 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2577 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2586 'http://user:pw@host:80/?foo=bar&baz=42'
2578 'http://user:pw@host:80/?foo=bar&baz=42'
2587 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2579 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2588 'http://user:pw@host:80/?foo=bar%3dbaz'
2580 'http://user:pw@host:80/?foo=bar%3dbaz'
2589 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2581 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2590 'ssh://user:pw@[::1]:2200//home/joe#'
2582 'ssh://user:pw@[::1]:2200//home/joe#'
2591 >>> str(url('http://localhost:80//'))
2583 >>> str(url('http://localhost:80//'))
2592 'http://localhost:80//'
2584 'http://localhost:80//'
2593 >>> str(url('http://localhost:80/'))
2585 >>> str(url('http://localhost:80/'))
2594 'http://localhost:80/'
2586 'http://localhost:80/'
2595 >>> str(url('http://localhost:80'))
2587 >>> str(url('http://localhost:80'))
2596 'http://localhost:80/'
2588 'http://localhost:80/'
2597 >>> str(url('bundle:foo'))
2589 >>> str(url('bundle:foo'))
2598 'bundle:foo'
2590 'bundle:foo'
2599 >>> str(url('bundle://../foo'))
2591 >>> str(url('bundle://../foo'))
2600 'bundle:../foo'
2592 'bundle:../foo'
2601 >>> str(url('path'))
2593 >>> str(url('path'))
2602 'path'
2594 'path'
2603 >>> str(url('file:///tmp/foo/bar'))
2595 >>> str(url('file:///tmp/foo/bar'))
2604 'file:///tmp/foo/bar'
2596 'file:///tmp/foo/bar'
2605 >>> str(url('file:///c:/tmp/foo/bar'))
2597 >>> str(url('file:///c:/tmp/foo/bar'))
2606 'file:///c:/tmp/foo/bar'
2598 'file:///c:/tmp/foo/bar'
2607 >>> print url(r'bundle:foo\bar')
2599 >>> print url(r'bundle:foo\bar')
2608 bundle:foo\bar
2600 bundle:foo\bar
2609 >>> print url(r'file:///D:\data\hg')
2601 >>> print url(r'file:///D:\data\hg')
2610 file:///D:\data\hg
2602 file:///D:\data\hg
2611 """
2603 """
2612 if self._localpath:
2604 if self._localpath:
2613 s = self.path
2605 s = self.path
2614 if self.scheme == 'bundle':
2606 if self.scheme == 'bundle':
2615 s = 'bundle:' + s
2607 s = 'bundle:' + s
2616 if self.fragment:
2608 if self.fragment:
2617 s += '#' + self.fragment
2609 s += '#' + self.fragment
2618 return s
2610 return s
2619
2611
2620 s = self.scheme + ':'
2612 s = self.scheme + ':'
2621 if self.user or self.passwd or self.host:
2613 if self.user or self.passwd or self.host:
2622 s += '//'
2614 s += '//'
2623 elif self.scheme and (not self.path or self.path.startswith('/')
2615 elif self.scheme and (not self.path or self.path.startswith('/')
2624 or hasdriveletter(self.path)):
2616 or hasdriveletter(self.path)):
2625 s += '//'
2617 s += '//'
2626 if hasdriveletter(self.path):
2618 if hasdriveletter(self.path):
2627 s += '/'
2619 s += '/'
2628 if self.user:
2620 if self.user:
2629 s += urlreq.quote(self.user, safe=self._safechars)
2621 s += urlreq.quote(self.user, safe=self._safechars)
2630 if self.passwd:
2622 if self.passwd:
2631 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2623 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2632 if self.user or self.passwd:
2624 if self.user or self.passwd:
2633 s += '@'
2625 s += '@'
2634 if self.host:
2626 if self.host:
2635 if not (self.host.startswith('[') and self.host.endswith(']')):
2627 if not (self.host.startswith('[') and self.host.endswith(']')):
2636 s += urlreq.quote(self.host)
2628 s += urlreq.quote(self.host)
2637 else:
2629 else:
2638 s += self.host
2630 s += self.host
2639 if self.port:
2631 if self.port:
2640 s += ':' + urlreq.quote(self.port)
2632 s += ':' + urlreq.quote(self.port)
2641 if self.host:
2633 if self.host:
2642 s += '/'
2634 s += '/'
2643 if self.path:
2635 if self.path:
2644 # TODO: similar to the query string, we should not unescape the
2636 # TODO: similar to the query string, we should not unescape the
2645 # path when we store it, the path might contain '%2f' = '/',
2637 # path when we store it, the path might contain '%2f' = '/',
2646 # which we should *not* escape.
2638 # which we should *not* escape.
2647 s += urlreq.quote(self.path, safe=self._safepchars)
2639 s += urlreq.quote(self.path, safe=self._safepchars)
2648 if self.query:
2640 if self.query:
2649 # we store the query in escaped form.
2641 # we store the query in escaped form.
2650 s += '?' + self.query
2642 s += '?' + self.query
2651 if self.fragment is not None:
2643 if self.fragment is not None:
2652 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2644 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2653 return s
2645 return s
2654
2646
2655 def authinfo(self):
2647 def authinfo(self):
2656 user, passwd = self.user, self.passwd
2648 user, passwd = self.user, self.passwd
2657 try:
2649 try:
2658 self.user, self.passwd = None, None
2650 self.user, self.passwd = None, None
2659 s = str(self)
2651 s = str(self)
2660 finally:
2652 finally:
2661 self.user, self.passwd = user, passwd
2653 self.user, self.passwd = user, passwd
2662 if not self.user:
2654 if not self.user:
2663 return (s, None)
2655 return (s, None)
2664 # authinfo[1] is passed to urllib2 password manager, and its
2656 # authinfo[1] is passed to urllib2 password manager, and its
2665 # URIs must not contain credentials. The host is passed in the
2657 # URIs must not contain credentials. The host is passed in the
2666 # URIs list because Python < 2.4.3 uses only that to search for
2658 # URIs list because Python < 2.4.3 uses only that to search for
2667 # a password.
2659 # a password.
2668 return (s, (None, (s, self.host),
2660 return (s, (None, (s, self.host),
2669 self.user, self.passwd or ''))
2661 self.user, self.passwd or ''))
2670
2662
2671 def isabs(self):
2663 def isabs(self):
2672 if self.scheme and self.scheme != 'file':
2664 if self.scheme and self.scheme != 'file':
2673 return True # remote URL
2665 return True # remote URL
2674 if hasdriveletter(self.path):
2666 if hasdriveletter(self.path):
2675 return True # absolute for our purposes - can't be joined()
2667 return True # absolute for our purposes - can't be joined()
2676 if self.path.startswith(r'\\'):
2668 if self.path.startswith(r'\\'):
2677 return True # Windows UNC path
2669 return True # Windows UNC path
2678 if self.path.startswith('/'):
2670 if self.path.startswith('/'):
2679 return True # POSIX-style
2671 return True # POSIX-style
2680 return False
2672 return False
2681
2673
2682 def localpath(self):
2674 def localpath(self):
2683 if self.scheme == 'file' or self.scheme == 'bundle':
2675 if self.scheme == 'file' or self.scheme == 'bundle':
2684 path = self.path or '/'
2676 path = self.path or '/'
2685 # For Windows, we need to promote hosts containing drive
2677 # For Windows, we need to promote hosts containing drive
2686 # letters to paths with drive letters.
2678 # letters to paths with drive letters.
2687 if hasdriveletter(self._hostport):
2679 if hasdriveletter(self._hostport):
2688 path = self._hostport + '/' + self.path
2680 path = self._hostport + '/' + self.path
2689 elif (self.host is not None and self.path
2681 elif (self.host is not None and self.path
2690 and not hasdriveletter(path)):
2682 and not hasdriveletter(path)):
2691 path = '/' + path
2683 path = '/' + path
2692 return path
2684 return path
2693 return self._origpath
2685 return self._origpath
2694
2686
2695 def islocal(self):
2687 def islocal(self):
2696 '''whether localpath will return something that posixfile can open'''
2688 '''whether localpath will return something that posixfile can open'''
2697 return (not self.scheme or self.scheme == 'file'
2689 return (not self.scheme or self.scheme == 'file'
2698 or self.scheme == 'bundle')
2690 or self.scheme == 'bundle')
2699
2691
2700 def hasscheme(path):
2692 def hasscheme(path):
2701 return bool(url(path).scheme)
2693 return bool(url(path).scheme)
2702
2694
2703 def hasdriveletter(path):
2695 def hasdriveletter(path):
2704 return path and path[1:2] == ':' and path[0:1].isalpha()
2696 return path and path[1:2] == ':' and path[0:1].isalpha()
2705
2697
2706 def urllocalpath(path):
2698 def urllocalpath(path):
2707 return url(path, parsequery=False, parsefragment=False).localpath()
2699 return url(path, parsequery=False, parsefragment=False).localpath()
2708
2700
2709 def hidepassword(u):
2701 def hidepassword(u):
2710 '''hide user credential in a url string'''
2702 '''hide user credential in a url string'''
2711 u = url(u)
2703 u = url(u)
2712 if u.passwd:
2704 if u.passwd:
2713 u.passwd = '***'
2705 u.passwd = '***'
2714 return str(u)
2706 return str(u)
2715
2707
2716 def removeauth(u):
2708 def removeauth(u):
2717 '''remove all authentication information from a url string'''
2709 '''remove all authentication information from a url string'''
2718 u = url(u)
2710 u = url(u)
2719 u.user = u.passwd = None
2711 u.user = u.passwd = None
2720 return str(u)
2712 return str(u)
2721
2713
2722 def isatty(fp):
2714 def isatty(fp):
2723 try:
2715 try:
2724 return fp.isatty()
2716 return fp.isatty()
2725 except AttributeError:
2717 except AttributeError:
2726 return False
2718 return False
2727
2719
2728 timecount = unitcountfn(
2720 timecount = unitcountfn(
2729 (1, 1e3, _('%.0f s')),
2721 (1, 1e3, _('%.0f s')),
2730 (100, 1, _('%.1f s')),
2722 (100, 1, _('%.1f s')),
2731 (10, 1, _('%.2f s')),
2723 (10, 1, _('%.2f s')),
2732 (1, 1, _('%.3f s')),
2724 (1, 1, _('%.3f s')),
2733 (100, 0.001, _('%.1f ms')),
2725 (100, 0.001, _('%.1f ms')),
2734 (10, 0.001, _('%.2f ms')),
2726 (10, 0.001, _('%.2f ms')),
2735 (1, 0.001, _('%.3f ms')),
2727 (1, 0.001, _('%.3f ms')),
2736 (100, 0.000001, _('%.1f us')),
2728 (100, 0.000001, _('%.1f us')),
2737 (10, 0.000001, _('%.2f us')),
2729 (10, 0.000001, _('%.2f us')),
2738 (1, 0.000001, _('%.3f us')),
2730 (1, 0.000001, _('%.3f us')),
2739 (100, 0.000000001, _('%.1f ns')),
2731 (100, 0.000000001, _('%.1f ns')),
2740 (10, 0.000000001, _('%.2f ns')),
2732 (10, 0.000000001, _('%.2f ns')),
2741 (1, 0.000000001, _('%.3f ns')),
2733 (1, 0.000000001, _('%.3f ns')),
2742 )
2734 )
2743
2735
2744 _timenesting = [0]
2736 _timenesting = [0]
2745
2737
2746 def timed(func):
2738 def timed(func):
2747 '''Report the execution time of a function call to stderr.
2739 '''Report the execution time of a function call to stderr.
2748
2740
2749 During development, use as a decorator when you need to measure
2741 During development, use as a decorator when you need to measure
2750 the cost of a function, e.g. as follows:
2742 the cost of a function, e.g. as follows:
2751
2743
2752 @util.timed
2744 @util.timed
2753 def foo(a, b, c):
2745 def foo(a, b, c):
2754 pass
2746 pass
2755 '''
2747 '''
2756
2748
2757 def wrapper(*args, **kwargs):
2749 def wrapper(*args, **kwargs):
2758 start = time.time()
2750 start = time.time()
2759 indent = 2
2751 indent = 2
2760 _timenesting[0] += indent
2752 _timenesting[0] += indent
2761 try:
2753 try:
2762 return func(*args, **kwargs)
2754 return func(*args, **kwargs)
2763 finally:
2755 finally:
2764 elapsed = time.time() - start
2756 elapsed = time.time() - start
2765 _timenesting[0] -= indent
2757 _timenesting[0] -= indent
2766 sys.stderr.write('%s%s: %s\n' %
2758 sys.stderr.write('%s%s: %s\n' %
2767 (' ' * _timenesting[0], func.__name__,
2759 (' ' * _timenesting[0], func.__name__,
2768 timecount(elapsed)))
2760 timecount(elapsed)))
2769 return wrapper
2761 return wrapper
2770
2762
2771 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2763 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2772 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2764 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2773
2765
2774 def sizetoint(s):
2766 def sizetoint(s):
2775 '''Convert a space specifier to a byte count.
2767 '''Convert a space specifier to a byte count.
2776
2768
2777 >>> sizetoint('30')
2769 >>> sizetoint('30')
2778 30
2770 30
2779 >>> sizetoint('2.2kb')
2771 >>> sizetoint('2.2kb')
2780 2252
2772 2252
2781 >>> sizetoint('6M')
2773 >>> sizetoint('6M')
2782 6291456
2774 6291456
2783 '''
2775 '''
2784 t = s.strip().lower()
2776 t = s.strip().lower()
2785 try:
2777 try:
2786 for k, u in _sizeunits:
2778 for k, u in _sizeunits:
2787 if t.endswith(k):
2779 if t.endswith(k):
2788 return int(float(t[:-len(k)]) * u)
2780 return int(float(t[:-len(k)]) * u)
2789 return int(t)
2781 return int(t)
2790 except ValueError:
2782 except ValueError:
2791 raise error.ParseError(_("couldn't parse size: %s") % s)
2783 raise error.ParseError(_("couldn't parse size: %s") % s)
2792
2784
2793 class hooks(object):
2785 class hooks(object):
2794 '''A collection of hook functions that can be used to extend a
2786 '''A collection of hook functions that can be used to extend a
2795 function's behavior. Hooks are called in lexicographic order,
2787 function's behavior. Hooks are called in lexicographic order,
2796 based on the names of their sources.'''
2788 based on the names of their sources.'''
2797
2789
2798 def __init__(self):
2790 def __init__(self):
2799 self._hooks = []
2791 self._hooks = []
2800
2792
2801 def add(self, source, hook):
2793 def add(self, source, hook):
2802 self._hooks.append((source, hook))
2794 self._hooks.append((source, hook))
2803
2795
2804 def __call__(self, *args):
2796 def __call__(self, *args):
2805 self._hooks.sort(key=lambda x: x[0])
2797 self._hooks.sort(key=lambda x: x[0])
2806 results = []
2798 results = []
2807 for source, hook in self._hooks:
2799 for source, hook in self._hooks:
2808 results.append(hook(*args))
2800 results.append(hook(*args))
2809 return results
2801 return results
2810
2802
2811 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s'):
2803 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s'):
2812 '''Yields lines for a nicely formatted stacktrace.
2804 '''Yields lines for a nicely formatted stacktrace.
2813 Skips the 'skip' last entries.
2805 Skips the 'skip' last entries.
2814 Each file+linenumber is formatted according to fileline.
2806 Each file+linenumber is formatted according to fileline.
2815 Each line is formatted according to line.
2807 Each line is formatted according to line.
2816 If line is None, it yields:
2808 If line is None, it yields:
2817 length of longest filepath+line number,
2809 length of longest filepath+line number,
2818 filepath+linenumber,
2810 filepath+linenumber,
2819 function
2811 function
2820
2812
2821 Not be used in production code but very convenient while developing.
2813 Not be used in production code but very convenient while developing.
2822 '''
2814 '''
2823 entries = [(fileline % (fn, ln), func)
2815 entries = [(fileline % (fn, ln), func)
2824 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2816 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2825 if entries:
2817 if entries:
2826 fnmax = max(len(entry[0]) for entry in entries)
2818 fnmax = max(len(entry[0]) for entry in entries)
2827 for fnln, func in entries:
2819 for fnln, func in entries:
2828 if line is None:
2820 if line is None:
2829 yield (fnmax, fnln, func)
2821 yield (fnmax, fnln, func)
2830 else:
2822 else:
2831 yield line % (fnmax, fnln, func)
2823 yield line % (fnmax, fnln, func)
2832
2824
2833 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2825 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2834 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2826 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2835 Skips the 'skip' last entries. By default it will flush stdout first.
2827 Skips the 'skip' last entries. By default it will flush stdout first.
2836 It can be used everywhere and intentionally does not require an ui object.
2828 It can be used everywhere and intentionally does not require an ui object.
2837 Not be used in production code but very convenient while developing.
2829 Not be used in production code but very convenient while developing.
2838 '''
2830 '''
2839 if otherf:
2831 if otherf:
2840 otherf.flush()
2832 otherf.flush()
2841 f.write('%s at:\n' % msg)
2833 f.write('%s at:\n' % msg)
2842 for line in getstackframes(skip + 1):
2834 for line in getstackframes(skip + 1):
2843 f.write(line)
2835 f.write(line)
2844 f.flush()
2836 f.flush()
2845
2837
2846 class dirs(object):
2838 class dirs(object):
2847 '''a multiset of directory names from a dirstate or manifest'''
2839 '''a multiset of directory names from a dirstate or manifest'''
2848
2840
2849 def __init__(self, map, skip=None):
2841 def __init__(self, map, skip=None):
2850 self._dirs = {}
2842 self._dirs = {}
2851 addpath = self.addpath
2843 addpath = self.addpath
2852 if safehasattr(map, 'iteritems') and skip is not None:
2844 if safehasattr(map, 'iteritems') and skip is not None:
2853 for f, s in map.iteritems():
2845 for f, s in map.iteritems():
2854 if s[0] != skip:
2846 if s[0] != skip:
2855 addpath(f)
2847 addpath(f)
2856 else:
2848 else:
2857 for f in map:
2849 for f in map:
2858 addpath(f)
2850 addpath(f)
2859
2851
2860 def addpath(self, path):
2852 def addpath(self, path):
2861 dirs = self._dirs
2853 dirs = self._dirs
2862 for base in finddirs(path):
2854 for base in finddirs(path):
2863 if base in dirs:
2855 if base in dirs:
2864 dirs[base] += 1
2856 dirs[base] += 1
2865 return
2857 return
2866 dirs[base] = 1
2858 dirs[base] = 1
2867
2859
2868 def delpath(self, path):
2860 def delpath(self, path):
2869 dirs = self._dirs
2861 dirs = self._dirs
2870 for base in finddirs(path):
2862 for base in finddirs(path):
2871 if dirs[base] > 1:
2863 if dirs[base] > 1:
2872 dirs[base] -= 1
2864 dirs[base] -= 1
2873 return
2865 return
2874 del dirs[base]
2866 del dirs[base]
2875
2867
2876 def __iter__(self):
2868 def __iter__(self):
2877 return self._dirs.iterkeys()
2869 return self._dirs.iterkeys()
2878
2870
2879 def __contains__(self, d):
2871 def __contains__(self, d):
2880 return d in self._dirs
2872 return d in self._dirs
2881
2873
2882 if safehasattr(parsers, 'dirs'):
2874 if safehasattr(parsers, 'dirs'):
2883 dirs = parsers.dirs
2875 dirs = parsers.dirs
2884
2876
2885 def finddirs(path):
2877 def finddirs(path):
2886 pos = path.rfind('/')
2878 pos = path.rfind('/')
2887 while pos != -1:
2879 while pos != -1:
2888 yield path[:pos]
2880 yield path[:pos]
2889 pos = path.rfind('/', 0, pos)
2881 pos = path.rfind('/', 0, pos)
2890
2882
2891 class ctxmanager(object):
2883 class ctxmanager(object):
2892 '''A context manager for use in 'with' blocks to allow multiple
2884 '''A context manager for use in 'with' blocks to allow multiple
2893 contexts to be entered at once. This is both safer and more
2885 contexts to be entered at once. This is both safer and more
2894 flexible than contextlib.nested.
2886 flexible than contextlib.nested.
2895
2887
2896 Once Mercurial supports Python 2.7+, this will become mostly
2888 Once Mercurial supports Python 2.7+, this will become mostly
2897 unnecessary.
2889 unnecessary.
2898 '''
2890 '''
2899
2891
2900 def __init__(self, *args):
2892 def __init__(self, *args):
2901 '''Accepts a list of no-argument functions that return context
2893 '''Accepts a list of no-argument functions that return context
2902 managers. These will be invoked at __call__ time.'''
2894 managers. These will be invoked at __call__ time.'''
2903 self._pending = args
2895 self._pending = args
2904 self._atexit = []
2896 self._atexit = []
2905
2897
2906 def __enter__(self):
2898 def __enter__(self):
2907 return self
2899 return self
2908
2900
2909 def enter(self):
2901 def enter(self):
2910 '''Create and enter context managers in the order in which they were
2902 '''Create and enter context managers in the order in which they were
2911 passed to the constructor.'''
2903 passed to the constructor.'''
2912 values = []
2904 values = []
2913 for func in self._pending:
2905 for func in self._pending:
2914 obj = func()
2906 obj = func()
2915 values.append(obj.__enter__())
2907 values.append(obj.__enter__())
2916 self._atexit.append(obj.__exit__)
2908 self._atexit.append(obj.__exit__)
2917 del self._pending
2909 del self._pending
2918 return values
2910 return values
2919
2911
2920 def atexit(self, func, *args, **kwargs):
2912 def atexit(self, func, *args, **kwargs):
2921 '''Add a function to call when this context manager exits. The
2913 '''Add a function to call when this context manager exits. The
2922 ordering of multiple atexit calls is unspecified, save that
2914 ordering of multiple atexit calls is unspecified, save that
2923 they will happen before any __exit__ functions.'''
2915 they will happen before any __exit__ functions.'''
2924 def wrapper(exc_type, exc_val, exc_tb):
2916 def wrapper(exc_type, exc_val, exc_tb):
2925 func(*args, **kwargs)
2917 func(*args, **kwargs)
2926 self._atexit.append(wrapper)
2918 self._atexit.append(wrapper)
2927 return func
2919 return func
2928
2920
2929 def __exit__(self, exc_type, exc_val, exc_tb):
2921 def __exit__(self, exc_type, exc_val, exc_tb):
2930 '''Context managers are exited in the reverse order from which
2922 '''Context managers are exited in the reverse order from which
2931 they were created.'''
2923 they were created.'''
2932 received = exc_type is not None
2924 received = exc_type is not None
2933 suppressed = False
2925 suppressed = False
2934 pending = None
2926 pending = None
2935 self._atexit.reverse()
2927 self._atexit.reverse()
2936 for exitfunc in self._atexit:
2928 for exitfunc in self._atexit:
2937 try:
2929 try:
2938 if exitfunc(exc_type, exc_val, exc_tb):
2930 if exitfunc(exc_type, exc_val, exc_tb):
2939 suppressed = True
2931 suppressed = True
2940 exc_type = None
2932 exc_type = None
2941 exc_val = None
2933 exc_val = None
2942 exc_tb = None
2934 exc_tb = None
2943 except BaseException:
2935 except BaseException:
2944 pending = sys.exc_info()
2936 pending = sys.exc_info()
2945 exc_type, exc_val, exc_tb = pending = sys.exc_info()
2937 exc_type, exc_val, exc_tb = pending = sys.exc_info()
2946 del self._atexit
2938 del self._atexit
2947 if pending:
2939 if pending:
2948 raise exc_val
2940 raise exc_val
2949 return received and suppressed
2941 return received and suppressed
2950
2942
2951 # compression code
2943 # compression code
2952
2944
2953 class compressormanager(object):
2945 class compressormanager(object):
2954 """Holds registrations of various compression engines.
2946 """Holds registrations of various compression engines.
2955
2947
2956 This class essentially abstracts the differences between compression
2948 This class essentially abstracts the differences between compression
2957 engines to allow new compression formats to be added easily, possibly from
2949 engines to allow new compression formats to be added easily, possibly from
2958 extensions.
2950 extensions.
2959
2951
2960 Compressors are registered against the global instance by calling its
2952 Compressors are registered against the global instance by calling its
2961 ``register()`` method.
2953 ``register()`` method.
2962 """
2954 """
2963 def __init__(self):
2955 def __init__(self):
2964 self._engines = {}
2956 self._engines = {}
2965 # Bundle spec human name to engine name.
2957 # Bundle spec human name to engine name.
2966 self._bundlenames = {}
2958 self._bundlenames = {}
2967 # Internal bundle identifier to engine name.
2959 # Internal bundle identifier to engine name.
2968 self._bundletypes = {}
2960 self._bundletypes = {}
2969
2961
2970 def __getitem__(self, key):
2962 def __getitem__(self, key):
2971 return self._engines[key]
2963 return self._engines[key]
2972
2964
2973 def __contains__(self, key):
2965 def __contains__(self, key):
2974 return key in self._engines
2966 return key in self._engines
2975
2967
2976 def __iter__(self):
2968 def __iter__(self):
2977 return iter(self._engines.keys())
2969 return iter(self._engines.keys())
2978
2970
2979 def register(self, engine):
2971 def register(self, engine):
2980 """Register a compression engine with the manager.
2972 """Register a compression engine with the manager.
2981
2973
2982 The argument must be a ``compressionengine`` instance.
2974 The argument must be a ``compressionengine`` instance.
2983 """
2975 """
2984 if not isinstance(engine, compressionengine):
2976 if not isinstance(engine, compressionengine):
2985 raise ValueError(_('argument must be a compressionengine'))
2977 raise ValueError(_('argument must be a compressionengine'))
2986
2978
2987 name = engine.name()
2979 name = engine.name()
2988
2980
2989 if name in self._engines:
2981 if name in self._engines:
2990 raise error.Abort(_('compression engine %s already registered') %
2982 raise error.Abort(_('compression engine %s already registered') %
2991 name)
2983 name)
2992
2984
2993 bundleinfo = engine.bundletype()
2985 bundleinfo = engine.bundletype()
2994 if bundleinfo:
2986 if bundleinfo:
2995 bundlename, bundletype = bundleinfo
2987 bundlename, bundletype = bundleinfo
2996
2988
2997 if bundlename in self._bundlenames:
2989 if bundlename in self._bundlenames:
2998 raise error.Abort(_('bundle name %s already registered') %
2990 raise error.Abort(_('bundle name %s already registered') %
2999 bundlename)
2991 bundlename)
3000 if bundletype in self._bundletypes:
2992 if bundletype in self._bundletypes:
3001 raise error.Abort(_('bundle type %s already registered by %s') %
2993 raise error.Abort(_('bundle type %s already registered by %s') %
3002 (bundletype, self._bundletypes[bundletype]))
2994 (bundletype, self._bundletypes[bundletype]))
3003
2995
3004 # No external facing name declared.
2996 # No external facing name declared.
3005 if bundlename:
2997 if bundlename:
3006 self._bundlenames[bundlename] = name
2998 self._bundlenames[bundlename] = name
3007
2999
3008 self._bundletypes[bundletype] = name
3000 self._bundletypes[bundletype] = name
3009
3001
3010 self._engines[name] = engine
3002 self._engines[name] = engine
3011
3003
3012 @property
3004 @property
3013 def supportedbundlenames(self):
3005 def supportedbundlenames(self):
3014 return set(self._bundlenames.keys())
3006 return set(self._bundlenames.keys())
3015
3007
3016 @property
3008 @property
3017 def supportedbundletypes(self):
3009 def supportedbundletypes(self):
3018 return set(self._bundletypes.keys())
3010 return set(self._bundletypes.keys())
3019
3011
3020 def forbundlename(self, bundlename):
3012 def forbundlename(self, bundlename):
3021 """Obtain a compression engine registered to a bundle name.
3013 """Obtain a compression engine registered to a bundle name.
3022
3014
3023 Will raise KeyError if the bundle type isn't registered.
3015 Will raise KeyError if the bundle type isn't registered.
3024
3016
3025 Will abort if the engine is known but not available.
3017 Will abort if the engine is known but not available.
3026 """
3018 """
3027 engine = self._engines[self._bundlenames[bundlename]]
3019 engine = self._engines[self._bundlenames[bundlename]]
3028 if not engine.available():
3020 if not engine.available():
3029 raise error.Abort(_('compression engine %s could not be loaded') %
3021 raise error.Abort(_('compression engine %s could not be loaded') %
3030 engine.name())
3022 engine.name())
3031 return engine
3023 return engine
3032
3024
3033 def forbundletype(self, bundletype):
3025 def forbundletype(self, bundletype):
3034 """Obtain a compression engine registered to a bundle type.
3026 """Obtain a compression engine registered to a bundle type.
3035
3027
3036 Will raise KeyError if the bundle type isn't registered.
3028 Will raise KeyError if the bundle type isn't registered.
3037
3029
3038 Will abort if the engine is known but not available.
3030 Will abort if the engine is known but not available.
3039 """
3031 """
3040 engine = self._engines[self._bundletypes[bundletype]]
3032 engine = self._engines[self._bundletypes[bundletype]]
3041 if not engine.available():
3033 if not engine.available():
3042 raise error.Abort(_('compression engine %s could not be loaded') %
3034 raise error.Abort(_('compression engine %s could not be loaded') %
3043 engine.name())
3035 engine.name())
3044 return engine
3036 return engine
3045
3037
3046 compengines = compressormanager()
3038 compengines = compressormanager()
3047
3039
3048 class compressionengine(object):
3040 class compressionengine(object):
3049 """Base class for compression engines.
3041 """Base class for compression engines.
3050
3042
3051 Compression engines must implement the interface defined by this class.
3043 Compression engines must implement the interface defined by this class.
3052 """
3044 """
3053 def name(self):
3045 def name(self):
3054 """Returns the name of the compression engine.
3046 """Returns the name of the compression engine.
3055
3047
3056 This is the key the engine is registered under.
3048 This is the key the engine is registered under.
3057
3049
3058 This method must be implemented.
3050 This method must be implemented.
3059 """
3051 """
3060 raise NotImplementedError()
3052 raise NotImplementedError()
3061
3053
3062 def available(self):
3054 def available(self):
3063 """Whether the compression engine is available.
3055 """Whether the compression engine is available.
3064
3056
3065 The intent of this method is to allow optional compression engines
3057 The intent of this method is to allow optional compression engines
3066 that may not be available in all installations (such as engines relying
3058 that may not be available in all installations (such as engines relying
3067 on C extensions that may not be present).
3059 on C extensions that may not be present).
3068 """
3060 """
3069 return True
3061 return True
3070
3062
3071 def bundletype(self):
3063 def bundletype(self):
3072 """Describes bundle identifiers for this engine.
3064 """Describes bundle identifiers for this engine.
3073
3065
3074 If this compression engine isn't supported for bundles, returns None.
3066 If this compression engine isn't supported for bundles, returns None.
3075
3067
3076 If this engine can be used for bundles, returns a 2-tuple of strings of
3068 If this engine can be used for bundles, returns a 2-tuple of strings of
3077 the user-facing "bundle spec" compression name and an internal
3069 the user-facing "bundle spec" compression name and an internal
3078 identifier used to denote the compression format within bundles. To
3070 identifier used to denote the compression format within bundles. To
3079 exclude the name from external usage, set the first element to ``None``.
3071 exclude the name from external usage, set the first element to ``None``.
3080
3072
3081 If bundle compression is supported, the class must also implement
3073 If bundle compression is supported, the class must also implement
3082 ``compressstream`` and `decompressorreader``.
3074 ``compressstream`` and `decompressorreader``.
3083 """
3075 """
3084 return None
3076 return None
3085
3077
3086 def compressstream(self, it, opts=None):
3078 def compressstream(self, it, opts=None):
3087 """Compress an iterator of chunks.
3079 """Compress an iterator of chunks.
3088
3080
3089 The method receives an iterator (ideally a generator) of chunks of
3081 The method receives an iterator (ideally a generator) of chunks of
3090 bytes to be compressed. It returns an iterator (ideally a generator)
3082 bytes to be compressed. It returns an iterator (ideally a generator)
3091 of bytes of chunks representing the compressed output.
3083 of bytes of chunks representing the compressed output.
3092
3084
3093 Optionally accepts an argument defining how to perform compression.
3085 Optionally accepts an argument defining how to perform compression.
3094 Each engine treats this argument differently.
3086 Each engine treats this argument differently.
3095 """
3087 """
3096 raise NotImplementedError()
3088 raise NotImplementedError()
3097
3089
3098 def decompressorreader(self, fh):
3090 def decompressorreader(self, fh):
3099 """Perform decompression on a file object.
3091 """Perform decompression on a file object.
3100
3092
3101 Argument is an object with a ``read(size)`` method that returns
3093 Argument is an object with a ``read(size)`` method that returns
3102 compressed data. Return value is an object with a ``read(size)`` that
3094 compressed data. Return value is an object with a ``read(size)`` that
3103 returns uncompressed data.
3095 returns uncompressed data.
3104 """
3096 """
3105 raise NotImplementedError()
3097 raise NotImplementedError()
3106
3098
3107 class _zlibengine(compressionengine):
3099 class _zlibengine(compressionengine):
3108 def name(self):
3100 def name(self):
3109 return 'zlib'
3101 return 'zlib'
3110
3102
3111 def bundletype(self):
3103 def bundletype(self):
3112 return 'gzip', 'GZ'
3104 return 'gzip', 'GZ'
3113
3105
3114 def compressstream(self, it, opts=None):
3106 def compressstream(self, it, opts=None):
3115 opts = opts or {}
3107 opts = opts or {}
3116
3108
3117 z = zlib.compressobj(opts.get('level', -1))
3109 z = zlib.compressobj(opts.get('level', -1))
3118 for chunk in it:
3110 for chunk in it:
3119 data = z.compress(chunk)
3111 data = z.compress(chunk)
3120 # Not all calls to compress emit data. It is cheaper to inspect
3112 # Not all calls to compress emit data. It is cheaper to inspect
3121 # here than to feed empty chunks through generator.
3113 # here than to feed empty chunks through generator.
3122 if data:
3114 if data:
3123 yield data
3115 yield data
3124
3116
3125 yield z.flush()
3117 yield z.flush()
3126
3118
3127 def decompressorreader(self, fh):
3119 def decompressorreader(self, fh):
3128 def gen():
3120 def gen():
3129 d = zlib.decompressobj()
3121 d = zlib.decompressobj()
3130 for chunk in filechunkiter(fh):
3122 for chunk in filechunkiter(fh):
3131 yield d.decompress(chunk)
3123 yield d.decompress(chunk)
3132
3124
3133 return chunkbuffer(gen())
3125 return chunkbuffer(gen())
3134
3126
3135 compengines.register(_zlibengine())
3127 compengines.register(_zlibengine())
3136
3128
3137 class _bz2engine(compressionengine):
3129 class _bz2engine(compressionengine):
3138 def name(self):
3130 def name(self):
3139 return 'bz2'
3131 return 'bz2'
3140
3132
3141 def bundletype(self):
3133 def bundletype(self):
3142 return 'bzip2', 'BZ'
3134 return 'bzip2', 'BZ'
3143
3135
3144 def compressstream(self, it, opts=None):
3136 def compressstream(self, it, opts=None):
3145 opts = opts or {}
3137 opts = opts or {}
3146 z = bz2.BZ2Compressor(opts.get('level', 9))
3138 z = bz2.BZ2Compressor(opts.get('level', 9))
3147 for chunk in it:
3139 for chunk in it:
3148 data = z.compress(chunk)
3140 data = z.compress(chunk)
3149 if data:
3141 if data:
3150 yield data
3142 yield data
3151
3143
3152 yield z.flush()
3144 yield z.flush()
3153
3145
3154 def decompressorreader(self, fh):
3146 def decompressorreader(self, fh):
3155 def gen():
3147 def gen():
3156 d = bz2.BZ2Decompressor()
3148 d = bz2.BZ2Decompressor()
3157 for chunk in filechunkiter(fh):
3149 for chunk in filechunkiter(fh):
3158 yield d.decompress(chunk)
3150 yield d.decompress(chunk)
3159
3151
3160 return chunkbuffer(gen())
3152 return chunkbuffer(gen())
3161
3153
3162 compengines.register(_bz2engine())
3154 compengines.register(_bz2engine())
3163
3155
3164 class _truncatedbz2engine(compressionengine):
3156 class _truncatedbz2engine(compressionengine):
3165 def name(self):
3157 def name(self):
3166 return 'bz2truncated'
3158 return 'bz2truncated'
3167
3159
3168 def bundletype(self):
3160 def bundletype(self):
3169 return None, '_truncatedBZ'
3161 return None, '_truncatedBZ'
3170
3162
3171 # We don't implement compressstream because it is hackily handled elsewhere.
3163 # We don't implement compressstream because it is hackily handled elsewhere.
3172
3164
3173 def decompressorreader(self, fh):
3165 def decompressorreader(self, fh):
3174 def gen():
3166 def gen():
3175 # The input stream doesn't have the 'BZ' header. So add it back.
3167 # The input stream doesn't have the 'BZ' header. So add it back.
3176 d = bz2.BZ2Decompressor()
3168 d = bz2.BZ2Decompressor()
3177 d.decompress('BZ')
3169 d.decompress('BZ')
3178 for chunk in filechunkiter(fh):
3170 for chunk in filechunkiter(fh):
3179 yield d.decompress(chunk)
3171 yield d.decompress(chunk)
3180
3172
3181 return chunkbuffer(gen())
3173 return chunkbuffer(gen())
3182
3174
3183 compengines.register(_truncatedbz2engine())
3175 compengines.register(_truncatedbz2engine())
3184
3176
3185 class _noopengine(compressionengine):
3177 class _noopengine(compressionengine):
3186 def name(self):
3178 def name(self):
3187 return 'none'
3179 return 'none'
3188
3180
3189 def bundletype(self):
3181 def bundletype(self):
3190 return 'none', 'UN'
3182 return 'none', 'UN'
3191
3183
3192 def compressstream(self, it, opts=None):
3184 def compressstream(self, it, opts=None):
3193 return it
3185 return it
3194
3186
3195 def decompressorreader(self, fh):
3187 def decompressorreader(self, fh):
3196 return fh
3188 return fh
3197
3189
3198 compengines.register(_noopengine())
3190 compengines.register(_noopengine())
3199
3191
3200 class _zstdengine(compressionengine):
3192 class _zstdengine(compressionengine):
3201 def name(self):
3193 def name(self):
3202 return 'zstd'
3194 return 'zstd'
3203
3195
3204 @propertycache
3196 @propertycache
3205 def _module(self):
3197 def _module(self):
3206 # Not all installs have the zstd module available. So defer importing
3198 # Not all installs have the zstd module available. So defer importing
3207 # until first access.
3199 # until first access.
3208 try:
3200 try:
3209 from . import zstd
3201 from . import zstd
3210 # Force delayed import.
3202 # Force delayed import.
3211 zstd.__version__
3203 zstd.__version__
3212 return zstd
3204 return zstd
3213 except ImportError:
3205 except ImportError:
3214 return None
3206 return None
3215
3207
3216 def available(self):
3208 def available(self):
3217 return bool(self._module)
3209 return bool(self._module)
3218
3210
3219 def bundletype(self):
3211 def bundletype(self):
3220 return 'zstd', 'ZS'
3212 return 'zstd', 'ZS'
3221
3213
3222 def compressstream(self, it, opts=None):
3214 def compressstream(self, it, opts=None):
3223 opts = opts or {}
3215 opts = opts or {}
3224 # zstd level 3 is almost always significantly faster than zlib
3216 # zstd level 3 is almost always significantly faster than zlib
3225 # while providing no worse compression. It strikes a good balance
3217 # while providing no worse compression. It strikes a good balance
3226 # between speed and compression.
3218 # between speed and compression.
3227 level = opts.get('level', 3)
3219 level = opts.get('level', 3)
3228
3220
3229 zstd = self._module
3221 zstd = self._module
3230 z = zstd.ZstdCompressor(level=level).compressobj()
3222 z = zstd.ZstdCompressor(level=level).compressobj()
3231 for chunk in it:
3223 for chunk in it:
3232 data = z.compress(chunk)
3224 data = z.compress(chunk)
3233 if data:
3225 if data:
3234 yield data
3226 yield data
3235
3227
3236 yield z.flush()
3228 yield z.flush()
3237
3229
3238 def decompressorreader(self, fh):
3230 def decompressorreader(self, fh):
3239 zstd = self._module
3231 zstd = self._module
3240 dctx = zstd.ZstdDecompressor()
3232 dctx = zstd.ZstdDecompressor()
3241 return chunkbuffer(dctx.read_from(fh))
3233 return chunkbuffer(dctx.read_from(fh))
3242
3234
3243 compengines.register(_zstdengine())
3235 compengines.register(_zstdengine())
3244
3236
3245 # convenient shortcut
3237 # convenient shortcut
3246 dst = debugstacktrace
3238 dst = debugstacktrace
General Comments 0
You need to be logged in to leave comments. Login now