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