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