##// END OF EJS Templates
util: add a comment to suppress a PyCharm warning about a PEP 8 violation...
Matt Harbison -
r52773:8f3cbea2 default
parent child Browse files
Show More
@@ -1,3397 +1,3398
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 Olivia Mackall <olivia@selenic.com>
4 # Copyright 2005-2007 Olivia Mackall <olivia@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 annotations
16 from __future__ import annotations
17
17
18 import abc
18 import abc
19 import collections
19 import collections
20 import contextlib
20 import contextlib
21 import errno
21 import errno
22 import gc
22 import gc
23 import hashlib
23 import hashlib
24 import io
24 import io
25 import itertools
25 import itertools
26 import locale
26 import locale
27 import mmap
27 import mmap
28 import os
28 import os
29 import pickle # provides util.pickle symbol
29 import pickle # provides util.pickle symbol
30 import re as remod
30 import re as remod
31 import shutil
31 import shutil
32 import stat
32 import stat
33 import sys
33 import sys
34 import time
34 import time
35 import traceback
35 import traceback
36 import typing
36 import typing
37 import warnings
37 import warnings
38
38
39 from typing import (
39 from typing import (
40 Any,
40 Any,
41 BinaryIO,
41 BinaryIO,
42 Callable,
42 Callable,
43 Iterable,
43 Iterable,
44 Iterator,
44 Iterator,
45 List,
45 List,
46 Optional,
46 Optional,
47 Tuple,
47 Tuple,
48 )
48 )
49
49
50 from .node import hex
50 from .node import hex
51 from .thirdparty import attr
51 from .thirdparty import attr
52
52
53 # Force pytype to use the non-vendored package
53 # Force pytype to use the non-vendored package
54 if typing.TYPE_CHECKING:
54 if typing.TYPE_CHECKING:
55 # noinspection PyPackageRequirements
55 # noinspection PyPackageRequirements
56 import attr
56 import attr
57
57
58 from .pycompat import (
58 from .pycompat import (
59 open,
59 open,
60 )
60 )
61 from hgdemandimport import tracing
61 from hgdemandimport import tracing
62 from . import (
62 from . import (
63 encoding,
63 encoding,
64 error,
64 error,
65 i18n,
65 i18n,
66 policy,
66 policy,
67 pycompat,
67 pycompat,
68 typelib,
68 typelib,
69 urllibcompat,
69 urllibcompat,
70 )
70 )
71 from .utils import (
71 from .utils import (
72 compression,
72 compression,
73 hashutil,
73 hashutil,
74 procutil,
74 procutil,
75 stringutil,
75 stringutil,
76 )
76 )
77
77
78 # keeps pyflakes happy
78 # keeps pyflakes happy
79 assert [
79 assert [
80 Iterable,
80 Iterable,
81 Iterator,
81 Iterator,
82 List,
82 List,
83 Optional,
83 Optional,
84 Tuple,
84 Tuple,
85 ]
85 ]
86
86
87
87
88 base85 = policy.importmod('base85')
88 base85 = policy.importmod('base85')
89 osutil = policy.importmod('osutil')
89 osutil = policy.importmod('osutil')
90
90
91 b85decode = base85.b85decode
91 b85decode = base85.b85decode
92 b85encode = base85.b85encode
92 b85encode = base85.b85encode
93
93
94 cookielib = pycompat.cookielib
94 cookielib = pycompat.cookielib
95 httplib = pycompat.httplib
95 httplib = pycompat.httplib
96 safehasattr = pycompat.safehasattr
96 safehasattr = pycompat.safehasattr
97 socketserver = pycompat.socketserver
97 socketserver = pycompat.socketserver
98 bytesio = io.BytesIO
98 bytesio = io.BytesIO
99 # TODO deprecate stringio name, as it is a lie on Python 3.
99 # TODO deprecate stringio name, as it is a lie on Python 3.
100 stringio = bytesio
100 stringio = bytesio
101 xmlrpclib = pycompat.xmlrpclib
101 xmlrpclib = pycompat.xmlrpclib
102
102
103 httpserver = urllibcompat.httpserver
103 httpserver = urllibcompat.httpserver
104 urlerr = urllibcompat.urlerr
104 urlerr = urllibcompat.urlerr
105 urlreq = urllibcompat.urlreq
105 urlreq = urllibcompat.urlreq
106
106
107 # workaround for win32mbcs
107 # workaround for win32mbcs
108 _filenamebytestr = pycompat.bytestr
108 _filenamebytestr = pycompat.bytestr
109
109
110 if pycompat.iswindows:
110 if pycompat.iswindows:
111 from . import windows as platform
111 from . import windows as platform
112 else:
112 else:
113 from . import posix as platform
113 from . import posix as platform
114
114
115 _ = i18n._
115 _ = i18n._
116
116
117 abspath = platform.abspath
117 abspath = platform.abspath
118 bindunixsocket = platform.bindunixsocket
118 bindunixsocket = platform.bindunixsocket
119 cachestat = platform.cachestat
119 cachestat = platform.cachestat
120 checkexec = platform.checkexec
120 checkexec = platform.checkexec
121 checklink = platform.checklink
121 checklink = platform.checklink
122 copymode = platform.copymode
122 copymode = platform.copymode
123 expandglobs = platform.expandglobs
123 expandglobs = platform.expandglobs
124 getfsmountpoint = platform.getfsmountpoint
124 getfsmountpoint = platform.getfsmountpoint
125 getfstype = platform.getfstype
125 getfstype = platform.getfstype
126 get_password = platform.get_password
126 get_password = platform.get_password
127 groupmembers = platform.groupmembers
127 groupmembers = platform.groupmembers
128 groupname = platform.groupname
128 groupname = platform.groupname
129 isexec = platform.isexec
129 isexec = platform.isexec
130 isowner = platform.isowner
130 isowner = platform.isowner
131 listdir = osutil.listdir
131 listdir = osutil.listdir
132 localpath = platform.localpath
132 localpath = platform.localpath
133 lookupreg = platform.lookupreg
133 lookupreg = platform.lookupreg
134 makedir = platform.makedir
134 makedir = platform.makedir
135 nlinks = platform.nlinks
135 nlinks = platform.nlinks
136 normpath = platform.normpath
136 normpath = platform.normpath
137 normcase = platform.normcase
137 normcase = platform.normcase
138 normcasespec = platform.normcasespec
138 normcasespec = platform.normcasespec
139 normcasefallback = platform.normcasefallback
139 normcasefallback = platform.normcasefallback
140 openhardlinks = platform.openhardlinks
140 openhardlinks = platform.openhardlinks
141 oslink = platform.oslink
141 oslink = platform.oslink
142 parsepatchoutput = platform.parsepatchoutput
142 parsepatchoutput = platform.parsepatchoutput
143 pconvert = platform.pconvert
143 pconvert = platform.pconvert
144 poll = platform.poll
144 poll = platform.poll
145 posixfile = platform.posixfile
145 posixfile = platform.posixfile
146 readlink = platform.readlink
146 readlink = platform.readlink
147 rename = platform.rename
147 rename = platform.rename
148 removedirs = platform.removedirs
148 removedirs = platform.removedirs
149 samedevice = platform.samedevice
149 samedevice = platform.samedevice
150 samefile = platform.samefile
150 samefile = platform.samefile
151 samestat = platform.samestat
151 samestat = platform.samestat
152 setflags = platform.setflags
152 setflags = platform.setflags
153 split = platform.split
153 split = platform.split
154 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
154 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
155 statisexec = platform.statisexec
155 statisexec = platform.statisexec
156 statislink = platform.statislink
156 statislink = platform.statislink
157 umask = platform.umask
157 umask = platform.umask
158 unlink = platform.unlink
158 unlink = platform.unlink
159 username = platform.username
159 username = platform.username
160
160
161
161
162 def setumask(val: int) -> None:
162 def setumask(val: int) -> None:
163 '''updates the umask. used by chg server'''
163 '''updates the umask. used by chg server'''
164 if pycompat.iswindows:
164 if pycompat.iswindows:
165 return
165 return
166 os.umask(val)
166 os.umask(val)
167 global umask
167 global umask
168 platform.umask = umask = val & 0o777
168 platform.umask = umask = val & 0o777
169
169
170
170
171 # small compat layer
171 # small compat layer
172 compengines = compression.compengines
172 compengines = compression.compengines
173 SERVERROLE = compression.SERVERROLE
173 SERVERROLE = compression.SERVERROLE
174 CLIENTROLE = compression.CLIENTROLE
174 CLIENTROLE = compression.CLIENTROLE
175
175
176 # Python compatibility
176 # Python compatibility
177
177
178 _notset = object()
178 _notset = object()
179
179
180
180
181 def bitsfrom(container):
181 def bitsfrom(container):
182 bits = 0
182 bits = 0
183 for bit in container:
183 for bit in container:
184 bits |= bit
184 bits |= bit
185 return bits
185 return bits
186
186
187
187
188 # python 2.6 still have deprecation warning enabled by default. We do not want
188 # python 2.6 still have deprecation warning enabled by default. We do not want
189 # to display anything to standard user so detect if we are running test and
189 # to display anything to standard user so detect if we are running test and
190 # only use python deprecation warning in this case.
190 # only use python deprecation warning in this case.
191 _dowarn = bool(encoding.environ.get(b'HGEMITWARNINGS'))
191 _dowarn = bool(encoding.environ.get(b'HGEMITWARNINGS'))
192 if _dowarn:
192 if _dowarn:
193 # explicitly unfilter our warning for python 2.7
193 # explicitly unfilter our warning for python 2.7
194 #
194 #
195 # The option of setting PYTHONWARNINGS in the test runner was investigated.
195 # The option of setting PYTHONWARNINGS in the test runner was investigated.
196 # However, module name set through PYTHONWARNINGS was exactly matched, so
196 # However, module name set through PYTHONWARNINGS was exactly matched, so
197 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
197 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
198 # makes the whole PYTHONWARNINGS thing useless for our usecase.
198 # makes the whole PYTHONWARNINGS thing useless for our usecase.
199 warnings.filterwarnings('default', '', DeprecationWarning, 'mercurial')
199 warnings.filterwarnings('default', '', DeprecationWarning, 'mercurial')
200 warnings.filterwarnings('default', '', DeprecationWarning, 'hgext')
200 warnings.filterwarnings('default', '', DeprecationWarning, 'hgext')
201 warnings.filterwarnings('default', '', DeprecationWarning, 'hgext3rd')
201 warnings.filterwarnings('default', '', DeprecationWarning, 'hgext3rd')
202 if _dowarn:
202 if _dowarn:
203 # silence warning emitted by passing user string to re.sub()
203 # silence warning emitted by passing user string to re.sub()
204 warnings.filterwarnings(
204 warnings.filterwarnings(
205 'ignore', 'bad escape', DeprecationWarning, 'mercurial'
205 'ignore', 'bad escape', DeprecationWarning, 'mercurial'
206 )
206 )
207 warnings.filterwarnings(
207 warnings.filterwarnings(
208 'ignore', 'invalid escape sequence', DeprecationWarning, 'mercurial'
208 'ignore', 'invalid escape sequence', DeprecationWarning, 'mercurial'
209 )
209 )
210 # TODO: reinvent imp.is_frozen()
210 # TODO: reinvent imp.is_frozen()
211 warnings.filterwarnings(
211 warnings.filterwarnings(
212 'ignore',
212 'ignore',
213 'the imp module is deprecated',
213 'the imp module is deprecated',
214 DeprecationWarning,
214 DeprecationWarning,
215 'mercurial',
215 'mercurial',
216 )
216 )
217
217
218
218
219 def nouideprecwarn(msg, version, stacklevel=1):
219 def nouideprecwarn(msg, version, stacklevel=1):
220 """Issue an python native deprecation warning
220 """Issue an python native deprecation warning
221
221
222 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
222 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
223 """
223 """
224 if _dowarn:
224 if _dowarn:
225 msg += (
225 msg += (
226 b"\n(compatibility will be dropped after Mercurial-%s,"
226 b"\n(compatibility will be dropped after Mercurial-%s,"
227 b" update your code.)"
227 b" update your code.)"
228 ) % version
228 ) % version
229 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
229 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
230 # on python 3 with chg, we will need to explicitly flush the output
230 # on python 3 with chg, we will need to explicitly flush the output
231 sys.stderr.flush()
231 sys.stderr.flush()
232
232
233
233
234 DIGESTS = {
234 DIGESTS = {
235 b'md5': hashlib.md5,
235 b'md5': hashlib.md5,
236 b'sha1': hashutil.sha1,
236 b'sha1': hashutil.sha1,
237 b'sha512': hashlib.sha512,
237 b'sha512': hashlib.sha512,
238 }
238 }
239 # List of digest types from strongest to weakest
239 # List of digest types from strongest to weakest
240 DIGESTS_BY_STRENGTH = [b'sha512', b'sha1', b'md5']
240 DIGESTS_BY_STRENGTH = [b'sha512', b'sha1', b'md5']
241
241
242 for k in DIGESTS_BY_STRENGTH:
242 for k in DIGESTS_BY_STRENGTH:
243 assert k in DIGESTS
243 assert k in DIGESTS
244
244
245
245
246 class digester:
246 class digester:
247 """helper to compute digests.
247 """helper to compute digests.
248
248
249 This helper can be used to compute one or more digests given their name.
249 This helper can be used to compute one or more digests given their name.
250
250
251 >>> d = digester([b'md5', b'sha1'])
251 >>> d = digester([b'md5', b'sha1'])
252 >>> d.update(b'foo')
252 >>> d.update(b'foo')
253 >>> [k for k in sorted(d)]
253 >>> [k for k in sorted(d)]
254 ['md5', 'sha1']
254 ['md5', 'sha1']
255 >>> d[b'md5']
255 >>> d[b'md5']
256 'acbd18db4cc2f85cedef654fccc4a4d8'
256 'acbd18db4cc2f85cedef654fccc4a4d8'
257 >>> d[b'sha1']
257 >>> d[b'sha1']
258 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
258 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
259 >>> digester.preferred([b'md5', b'sha1'])
259 >>> digester.preferred([b'md5', b'sha1'])
260 'sha1'
260 'sha1'
261 """
261 """
262
262
263 def __init__(self, digests, s=b''):
263 def __init__(self, digests, s=b''):
264 self._hashes = {}
264 self._hashes = {}
265 for k in digests:
265 for k in digests:
266 if k not in DIGESTS:
266 if k not in DIGESTS:
267 raise error.Abort(_(b'unknown digest type: %s') % k)
267 raise error.Abort(_(b'unknown digest type: %s') % k)
268 self._hashes[k] = DIGESTS[k]()
268 self._hashes[k] = DIGESTS[k]()
269 if s:
269 if s:
270 self.update(s)
270 self.update(s)
271
271
272 def update(self, data):
272 def update(self, data):
273 for h in self._hashes.values():
273 for h in self._hashes.values():
274 h.update(data)
274 h.update(data)
275
275
276 def __getitem__(self, key):
276 def __getitem__(self, key):
277 if key not in DIGESTS:
277 if key not in DIGESTS:
278 raise error.Abort(_(b'unknown digest type: %s') % k)
278 raise error.Abort(_(b'unknown digest type: %s') % k)
279 return hex(self._hashes[key].digest())
279 return hex(self._hashes[key].digest())
280
280
281 def __iter__(self):
281 def __iter__(self):
282 return iter(self._hashes)
282 return iter(self._hashes)
283
283
284 @staticmethod
284 @staticmethod
285 def preferred(supported):
285 def preferred(supported):
286 """returns the strongest digest type in both supported and DIGESTS."""
286 """returns the strongest digest type in both supported and DIGESTS."""
287
287
288 for k in DIGESTS_BY_STRENGTH:
288 for k in DIGESTS_BY_STRENGTH:
289 if k in supported:
289 if k in supported:
290 return k
290 return k
291 return None
291 return None
292
292
293
293
294 class digestchecker:
294 class digestchecker:
295 """file handle wrapper that additionally checks content against a given
295 """file handle wrapper that additionally checks content against a given
296 size and digests.
296 size and digests.
297
297
298 d = digestchecker(fh, size, {'md5': '...'})
298 d = digestchecker(fh, size, {'md5': '...'})
299
299
300 When multiple digests are given, all of them are validated.
300 When multiple digests are given, all of them are validated.
301 """
301 """
302
302
303 def __init__(self, fh, size, digests):
303 def __init__(self, fh, size, digests):
304 self._fh = fh
304 self._fh = fh
305 self._size = size
305 self._size = size
306 self._got = 0
306 self._got = 0
307 self._digests = dict(digests)
307 self._digests = dict(digests)
308 self._digester = digester(self._digests.keys())
308 self._digester = digester(self._digests.keys())
309
309
310 def read(self, length=-1):
310 def read(self, length=-1):
311 content = self._fh.read(length)
311 content = self._fh.read(length)
312 self._digester.update(content)
312 self._digester.update(content)
313 self._got += len(content)
313 self._got += len(content)
314 return content
314 return content
315
315
316 def validate(self):
316 def validate(self):
317 if self._size != self._got:
317 if self._size != self._got:
318 raise error.Abort(
318 raise error.Abort(
319 _(b'size mismatch: expected %d, got %d')
319 _(b'size mismatch: expected %d, got %d')
320 % (self._size, self._got)
320 % (self._size, self._got)
321 )
321 )
322 for k, v in self._digests.items():
322 for k, v in self._digests.items():
323 if v != self._digester[k]:
323 if v != self._digester[k]:
324 # i18n: first parameter is a digest name
324 # i18n: first parameter is a digest name
325 raise error.Abort(
325 raise error.Abort(
326 _(b'%s mismatch: expected %s, got %s')
326 _(b'%s mismatch: expected %s, got %s')
327 % (k, v, self._digester[k])
327 % (k, v, self._digester[k])
328 )
328 )
329
329
330
330
331 try:
331 try:
332 buffer = buffer # pytype: disable=name-error
332 buffer = buffer # pytype: disable=name-error
333 except NameError:
333 except NameError:
334
334
335 def buffer(sliceable, offset=0, length=None):
335 def buffer(sliceable, offset=0, length=None):
336 if length is not None:
336 if length is not None:
337 view = memoryview(sliceable)[offset : offset + length]
337 view = memoryview(sliceable)[offset : offset + length]
338 else:
338 else:
339 view = memoryview(sliceable)[offset:]
339 view = memoryview(sliceable)[offset:]
340 return view.toreadonly()
340 return view.toreadonly()
341
341
342
342
343 _chunksize = 4096
343 _chunksize = 4096
344
344
345
345
346 class bufferedinputpipe:
346 class bufferedinputpipe:
347 """a manually buffered input pipe
347 """a manually buffered input pipe
348
348
349 Python will not let us use buffered IO and lazy reading with 'polling' at
349 Python will not let us use buffered IO and lazy reading with 'polling' at
350 the same time. We cannot probe the buffer state and select will not detect
350 the same time. We cannot probe the buffer state and select will not detect
351 that data are ready to read if they are already buffered.
351 that data are ready to read if they are already buffered.
352
352
353 This class let us work around that by implementing its own buffering
353 This class let us work around that by implementing its own buffering
354 (allowing efficient readline) while offering a way to know if the buffer is
354 (allowing efficient readline) while offering a way to know if the buffer is
355 empty from the output (allowing collaboration of the buffer with polling).
355 empty from the output (allowing collaboration of the buffer with polling).
356
356
357 This class lives in the 'util' module because it makes use of the 'os'
357 This class lives in the 'util' module because it makes use of the 'os'
358 module from the python stdlib.
358 module from the python stdlib.
359 """
359 """
360
360
361 def __new__(cls, fh):
361 def __new__(cls, fh):
362 # If we receive a fileobjectproxy, we need to use a variation of this
362 # If we receive a fileobjectproxy, we need to use a variation of this
363 # class that notifies observers about activity.
363 # class that notifies observers about activity.
364 if isinstance(fh, fileobjectproxy):
364 if isinstance(fh, fileobjectproxy):
365 cls = observedbufferedinputpipe
365 cls = observedbufferedinputpipe
366
366
367 return super(bufferedinputpipe, cls).__new__(cls)
367 return super(bufferedinputpipe, cls).__new__(cls)
368
368
369 def __init__(self, input):
369 def __init__(self, input):
370 self._input = input
370 self._input = input
371 self._buffer = []
371 self._buffer = []
372 self._eof = False
372 self._eof = False
373 self._lenbuf = 0
373 self._lenbuf = 0
374
374
375 @property
375 @property
376 def hasbuffer(self):
376 def hasbuffer(self):
377 """True is any data is currently buffered
377 """True is any data is currently buffered
378
378
379 This will be used externally a pre-step for polling IO. If there is
379 This will be used externally a pre-step for polling IO. If there is
380 already data then no polling should be set in place."""
380 already data then no polling should be set in place."""
381 return bool(self._buffer)
381 return bool(self._buffer)
382
382
383 @property
383 @property
384 def closed(self):
384 def closed(self):
385 return self._input.closed
385 return self._input.closed
386
386
387 def fileno(self):
387 def fileno(self):
388 return self._input.fileno()
388 return self._input.fileno()
389
389
390 def close(self):
390 def close(self):
391 return self._input.close()
391 return self._input.close()
392
392
393 def read(self, size):
393 def read(self, size):
394 while (not self._eof) and (self._lenbuf < size):
394 while (not self._eof) and (self._lenbuf < size):
395 self._fillbuffer()
395 self._fillbuffer()
396 return self._frombuffer(size)
396 return self._frombuffer(size)
397
397
398 def unbufferedread(self, size):
398 def unbufferedread(self, size):
399 if not self._eof and self._lenbuf == 0:
399 if not self._eof and self._lenbuf == 0:
400 self._fillbuffer(max(size, _chunksize))
400 self._fillbuffer(max(size, _chunksize))
401 return self._frombuffer(min(self._lenbuf, size))
401 return self._frombuffer(min(self._lenbuf, size))
402
402
403 def readline(self, *args, **kwargs):
403 def readline(self, *args, **kwargs):
404 if len(self._buffer) > 1:
404 if len(self._buffer) > 1:
405 # this should not happen because both read and readline end with a
405 # this should not happen because both read and readline end with a
406 # _frombuffer call that collapse it.
406 # _frombuffer call that collapse it.
407 self._buffer = [b''.join(self._buffer)]
407 self._buffer = [b''.join(self._buffer)]
408 self._lenbuf = len(self._buffer[0])
408 self._lenbuf = len(self._buffer[0])
409 lfi = -1
409 lfi = -1
410 if self._buffer:
410 if self._buffer:
411 lfi = self._buffer[-1].find(b'\n')
411 lfi = self._buffer[-1].find(b'\n')
412 while (not self._eof) and lfi < 0:
412 while (not self._eof) and lfi < 0:
413 self._fillbuffer()
413 self._fillbuffer()
414 if self._buffer:
414 if self._buffer:
415 lfi = self._buffer[-1].find(b'\n')
415 lfi = self._buffer[-1].find(b'\n')
416 size = lfi + 1
416 size = lfi + 1
417 if lfi < 0: # end of file
417 if lfi < 0: # end of file
418 size = self._lenbuf
418 size = self._lenbuf
419 elif len(self._buffer) > 1:
419 elif len(self._buffer) > 1:
420 # we need to take previous chunks into account
420 # we need to take previous chunks into account
421 size += self._lenbuf - len(self._buffer[-1])
421 size += self._lenbuf - len(self._buffer[-1])
422 return self._frombuffer(size)
422 return self._frombuffer(size)
423
423
424 def _frombuffer(self, size):
424 def _frombuffer(self, size):
425 """return at most 'size' data from the buffer
425 """return at most 'size' data from the buffer
426
426
427 The data are removed from the buffer."""
427 The data are removed from the buffer."""
428 if size == 0 or not self._buffer:
428 if size == 0 or not self._buffer:
429 return b''
429 return b''
430 buf = self._buffer[0]
430 buf = self._buffer[0]
431 if len(self._buffer) > 1:
431 if len(self._buffer) > 1:
432 buf = b''.join(self._buffer)
432 buf = b''.join(self._buffer)
433
433
434 data = buf[:size]
434 data = buf[:size]
435 buf = buf[len(data) :]
435 buf = buf[len(data) :]
436 if buf:
436 if buf:
437 self._buffer = [buf]
437 self._buffer = [buf]
438 self._lenbuf = len(buf)
438 self._lenbuf = len(buf)
439 else:
439 else:
440 self._buffer = []
440 self._buffer = []
441 self._lenbuf = 0
441 self._lenbuf = 0
442 return data
442 return data
443
443
444 def _fillbuffer(self, size=_chunksize):
444 def _fillbuffer(self, size=_chunksize):
445 """read data to the buffer"""
445 """read data to the buffer"""
446 data = os.read(self._input.fileno(), size)
446 data = os.read(self._input.fileno(), size)
447 if not data:
447 if not data:
448 self._eof = True
448 self._eof = True
449 else:
449 else:
450 self._lenbuf += len(data)
450 self._lenbuf += len(data)
451 self._buffer.append(data)
451 self._buffer.append(data)
452
452
453 return data
453 return data
454
454
455
455
456 def has_mmap_populate():
456 def has_mmap_populate():
457 return hasattr(osutil, "background_mmap_populate") or hasattr(
457 return hasattr(osutil, "background_mmap_populate") or hasattr(
458 mmap, 'MAP_POPULATE'
458 mmap, 'MAP_POPULATE'
459 )
459 )
460
460
461
461
462 def mmapread(fp, size=None, pre_populate=True):
462 def mmapread(fp, size=None, pre_populate=True):
463 """Read a file content using mmap
463 """Read a file content using mmap
464
464
465 The responsability of checking the file system is mmap safe is the
465 The responsability of checking the file system is mmap safe is the
466 responsability of the caller (see `vfs.is_mmap_safe`).
466 responsability of the caller (see `vfs.is_mmap_safe`).
467
467
468 In some case, a normal string might be returned.
468 In some case, a normal string might be returned.
469
469
470 If `pre_populate` is True (the default), the mmapped data will be
470 If `pre_populate` is True (the default), the mmapped data will be
471 pre-populated in memory if the system support this option, this slow down
471 pre-populated in memory if the system support this option, this slow down
472 the initial mmaping but avoid potentially crippling page fault on later
472 the initial mmaping but avoid potentially crippling page fault on later
473 access. If this is not the desired behavior, set `pre_populate` to False.
473 access. If this is not the desired behavior, set `pre_populate` to False.
474 """
474 """
475 if size == 0:
475 if size == 0:
476 # size of 0 to mmap.mmap() means "all data"
476 # size of 0 to mmap.mmap() means "all data"
477 # rather than "zero bytes", so special case that.
477 # rather than "zero bytes", so special case that.
478 return b''
478 return b''
479 elif size is None:
479 elif size is None:
480 size = 0
480 size = 0
481 fd = getattr(fp, 'fileno', lambda: fp)()
481 fd = getattr(fp, 'fileno', lambda: fp)()
482 flags = mmap.MAP_PRIVATE
482 flags = mmap.MAP_PRIVATE
483 bg_populate = hasattr(osutil, "background_mmap_populate")
483 bg_populate = hasattr(osutil, "background_mmap_populate")
484 if pre_populate and not bg_populate:
484 if pre_populate and not bg_populate:
485 flags |= getattr(mmap, 'MAP_POPULATE', 0)
485 flags |= getattr(mmap, 'MAP_POPULATE', 0)
486 try:
486 try:
487 m = mmap.mmap(fd, size, flags=flags, prot=mmap.PROT_READ)
487 m = mmap.mmap(fd, size, flags=flags, prot=mmap.PROT_READ)
488 if pre_populate and bg_populate:
488 if pre_populate and bg_populate:
489 osutil.background_mmap_populate(m)
489 osutil.background_mmap_populate(m)
490 return m
490 return m
491 except ValueError:
491 except ValueError:
492 # Empty files cannot be mmapped, but mmapread should still work. Check
492 # Empty files cannot be mmapped, but mmapread should still work. Check
493 # if the file is empty, and if so, return an empty buffer.
493 # if the file is empty, and if so, return an empty buffer.
494 if os.fstat(fd).st_size == 0:
494 if os.fstat(fd).st_size == 0:
495 return b''
495 return b''
496 raise
496 raise
497
497
498
498
499 class fileobjectproxy:
499 class fileobjectproxy:
500 """A proxy around file objects that tells a watcher when events occur.
500 """A proxy around file objects that tells a watcher when events occur.
501
501
502 This type is intended to only be used for testing purposes. Think hard
502 This type is intended to only be used for testing purposes. Think hard
503 before using it in important code.
503 before using it in important code.
504 """
504 """
505
505
506 __slots__ = (
506 __slots__ = (
507 '_orig',
507 '_orig',
508 '_observer',
508 '_observer',
509 )
509 )
510
510
511 def __init__(self, fh, observer):
511 def __init__(self, fh, observer):
512 object.__setattr__(self, '_orig', fh)
512 object.__setattr__(self, '_orig', fh)
513 object.__setattr__(self, '_observer', observer)
513 object.__setattr__(self, '_observer', observer)
514
514
515 def __getattribute__(self, name):
515 def __getattribute__(self, name):
516 ours = {
516 ours = {
517 '_observer',
517 '_observer',
518 # IOBase
518 # IOBase
519 'close',
519 'close',
520 # closed if a property
520 # closed if a property
521 'fileno',
521 'fileno',
522 'flush',
522 'flush',
523 'isatty',
523 'isatty',
524 'readable',
524 'readable',
525 'readline',
525 'readline',
526 'readlines',
526 'readlines',
527 'seek',
527 'seek',
528 'seekable',
528 'seekable',
529 'tell',
529 'tell',
530 'truncate',
530 'truncate',
531 'writable',
531 'writable',
532 'writelines',
532 'writelines',
533 # RawIOBase
533 # RawIOBase
534 'read',
534 'read',
535 'readall',
535 'readall',
536 'readinto',
536 'readinto',
537 'write',
537 'write',
538 # BufferedIOBase
538 # BufferedIOBase
539 # raw is a property
539 # raw is a property
540 'detach',
540 'detach',
541 # read defined above
541 # read defined above
542 'read1',
542 'read1',
543 # readinto defined above
543 # readinto defined above
544 # write defined above
544 # write defined above
545 }
545 }
546
546
547 # We only observe some methods.
547 # We only observe some methods.
548 if name in ours:
548 if name in ours:
549 return object.__getattribute__(self, name)
549 return object.__getattribute__(self, name)
550
550
551 return getattr(object.__getattribute__(self, '_orig'), name)
551 return getattr(object.__getattribute__(self, '_orig'), name)
552
552
553 def __nonzero__(self):
553 def __nonzero__(self):
554 return bool(object.__getattribute__(self, '_orig'))
554 return bool(object.__getattribute__(self, '_orig'))
555
555
556 __bool__ = __nonzero__
556 __bool__ = __nonzero__
557
557
558 def __delattr__(self, name):
558 def __delattr__(self, name):
559 return delattr(object.__getattribute__(self, '_orig'), name)
559 return delattr(object.__getattribute__(self, '_orig'), name)
560
560
561 def __setattr__(self, name, value):
561 def __setattr__(self, name, value):
562 return setattr(object.__getattribute__(self, '_orig'), name, value)
562 return setattr(object.__getattribute__(self, '_orig'), name, value)
563
563
564 def __iter__(self):
564 def __iter__(self):
565 return object.__getattribute__(self, '_orig').__iter__()
565 return object.__getattribute__(self, '_orig').__iter__()
566
566
567 def _observedcall(self, name, *args, **kwargs):
567 def _observedcall(self, name, *args, **kwargs):
568 # Call the original object.
568 # Call the original object.
569 orig = object.__getattribute__(self, '_orig')
569 orig = object.__getattribute__(self, '_orig')
570 res = getattr(orig, name)(*args, **kwargs)
570 res = getattr(orig, name)(*args, **kwargs)
571
571
572 # Call a method on the observer of the same name with arguments
572 # Call a method on the observer of the same name with arguments
573 # so it can react, log, etc.
573 # so it can react, log, etc.
574 observer = object.__getattribute__(self, '_observer')
574 observer = object.__getattribute__(self, '_observer')
575 fn = getattr(observer, name, None)
575 fn = getattr(observer, name, None)
576 if fn:
576 if fn:
577 fn(res, *args, **kwargs)
577 fn(res, *args, **kwargs)
578
578
579 return res
579 return res
580
580
581 def close(self, *args, **kwargs):
581 def close(self, *args, **kwargs):
582 return object.__getattribute__(self, '_observedcall')(
582 return object.__getattribute__(self, '_observedcall')(
583 'close', *args, **kwargs
583 'close', *args, **kwargs
584 )
584 )
585
585
586 def fileno(self, *args, **kwargs):
586 def fileno(self, *args, **kwargs):
587 return object.__getattribute__(self, '_observedcall')(
587 return object.__getattribute__(self, '_observedcall')(
588 'fileno', *args, **kwargs
588 'fileno', *args, **kwargs
589 )
589 )
590
590
591 def flush(self, *args, **kwargs):
591 def flush(self, *args, **kwargs):
592 return object.__getattribute__(self, '_observedcall')(
592 return object.__getattribute__(self, '_observedcall')(
593 'flush', *args, **kwargs
593 'flush', *args, **kwargs
594 )
594 )
595
595
596 def isatty(self, *args, **kwargs):
596 def isatty(self, *args, **kwargs):
597 return object.__getattribute__(self, '_observedcall')(
597 return object.__getattribute__(self, '_observedcall')(
598 'isatty', *args, **kwargs
598 'isatty', *args, **kwargs
599 )
599 )
600
600
601 def readable(self, *args, **kwargs):
601 def readable(self, *args, **kwargs):
602 return object.__getattribute__(self, '_observedcall')(
602 return object.__getattribute__(self, '_observedcall')(
603 'readable', *args, **kwargs
603 'readable', *args, **kwargs
604 )
604 )
605
605
606 def readline(self, *args, **kwargs):
606 def readline(self, *args, **kwargs):
607 return object.__getattribute__(self, '_observedcall')(
607 return object.__getattribute__(self, '_observedcall')(
608 'readline', *args, **kwargs
608 'readline', *args, **kwargs
609 )
609 )
610
610
611 def readlines(self, *args, **kwargs):
611 def readlines(self, *args, **kwargs):
612 return object.__getattribute__(self, '_observedcall')(
612 return object.__getattribute__(self, '_observedcall')(
613 'readlines', *args, **kwargs
613 'readlines', *args, **kwargs
614 )
614 )
615
615
616 def seek(self, *args, **kwargs):
616 def seek(self, *args, **kwargs):
617 return object.__getattribute__(self, '_observedcall')(
617 return object.__getattribute__(self, '_observedcall')(
618 'seek', *args, **kwargs
618 'seek', *args, **kwargs
619 )
619 )
620
620
621 def seekable(self, *args, **kwargs):
621 def seekable(self, *args, **kwargs):
622 return object.__getattribute__(self, '_observedcall')(
622 return object.__getattribute__(self, '_observedcall')(
623 'seekable', *args, **kwargs
623 'seekable', *args, **kwargs
624 )
624 )
625
625
626 def tell(self, *args, **kwargs):
626 def tell(self, *args, **kwargs):
627 return object.__getattribute__(self, '_observedcall')(
627 return object.__getattribute__(self, '_observedcall')(
628 'tell', *args, **kwargs
628 'tell', *args, **kwargs
629 )
629 )
630
630
631 def truncate(self, *args, **kwargs):
631 def truncate(self, *args, **kwargs):
632 return object.__getattribute__(self, '_observedcall')(
632 return object.__getattribute__(self, '_observedcall')(
633 'truncate', *args, **kwargs
633 'truncate', *args, **kwargs
634 )
634 )
635
635
636 def writable(self, *args, **kwargs):
636 def writable(self, *args, **kwargs):
637 return object.__getattribute__(self, '_observedcall')(
637 return object.__getattribute__(self, '_observedcall')(
638 'writable', *args, **kwargs
638 'writable', *args, **kwargs
639 )
639 )
640
640
641 def writelines(self, *args, **kwargs):
641 def writelines(self, *args, **kwargs):
642 return object.__getattribute__(self, '_observedcall')(
642 return object.__getattribute__(self, '_observedcall')(
643 'writelines', *args, **kwargs
643 'writelines', *args, **kwargs
644 )
644 )
645
645
646 def read(self, *args, **kwargs):
646 def read(self, *args, **kwargs):
647 return object.__getattribute__(self, '_observedcall')(
647 return object.__getattribute__(self, '_observedcall')(
648 'read', *args, **kwargs
648 'read', *args, **kwargs
649 )
649 )
650
650
651 def readall(self, *args, **kwargs):
651 def readall(self, *args, **kwargs):
652 return object.__getattribute__(self, '_observedcall')(
652 return object.__getattribute__(self, '_observedcall')(
653 'readall', *args, **kwargs
653 'readall', *args, **kwargs
654 )
654 )
655
655
656 def readinto(self, *args, **kwargs):
656 def readinto(self, *args, **kwargs):
657 return object.__getattribute__(self, '_observedcall')(
657 return object.__getattribute__(self, '_observedcall')(
658 'readinto', *args, **kwargs
658 'readinto', *args, **kwargs
659 )
659 )
660
660
661 def write(self, *args, **kwargs):
661 def write(self, *args, **kwargs):
662 return object.__getattribute__(self, '_observedcall')(
662 return object.__getattribute__(self, '_observedcall')(
663 'write', *args, **kwargs
663 'write', *args, **kwargs
664 )
664 )
665
665
666 def detach(self, *args, **kwargs):
666 def detach(self, *args, **kwargs):
667 return object.__getattribute__(self, '_observedcall')(
667 return object.__getattribute__(self, '_observedcall')(
668 'detach', *args, **kwargs
668 'detach', *args, **kwargs
669 )
669 )
670
670
671 def read1(self, *args, **kwargs):
671 def read1(self, *args, **kwargs):
672 return object.__getattribute__(self, '_observedcall')(
672 return object.__getattribute__(self, '_observedcall')(
673 'read1', *args, **kwargs
673 'read1', *args, **kwargs
674 )
674 )
675
675
676
676
677 class observedbufferedinputpipe(bufferedinputpipe):
677 class observedbufferedinputpipe(bufferedinputpipe):
678 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
678 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
679
679
680 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
680 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
681 bypass ``fileobjectproxy``. Because of this, we need to make
681 bypass ``fileobjectproxy``. Because of this, we need to make
682 ``bufferedinputpipe`` aware of these operations.
682 ``bufferedinputpipe`` aware of these operations.
683
683
684 This variation of ``bufferedinputpipe`` can notify observers about
684 This variation of ``bufferedinputpipe`` can notify observers about
685 ``os.read()`` events. It also re-publishes other events, such as
685 ``os.read()`` events. It also re-publishes other events, such as
686 ``read()`` and ``readline()``.
686 ``read()`` and ``readline()``.
687 """
687 """
688
688
689 def _fillbuffer(self, size=_chunksize):
689 def _fillbuffer(self, size=_chunksize):
690 res = super(observedbufferedinputpipe, self)._fillbuffer(size=size)
690 res = super(observedbufferedinputpipe, self)._fillbuffer(size=size)
691
691
692 fn = getattr(self._input._observer, 'osread', None)
692 fn = getattr(self._input._observer, 'osread', None)
693 if fn:
693 if fn:
694 fn(res, size)
694 fn(res, size)
695
695
696 return res
696 return res
697
697
698 # We use different observer methods because the operation isn't
698 # We use different observer methods because the operation isn't
699 # performed on the actual file object but on us.
699 # performed on the actual file object but on us.
700 def read(self, size):
700 def read(self, size):
701 res = super(observedbufferedinputpipe, self).read(size)
701 res = super(observedbufferedinputpipe, self).read(size)
702
702
703 fn = getattr(self._input._observer, 'bufferedread', None)
703 fn = getattr(self._input._observer, 'bufferedread', None)
704 if fn:
704 if fn:
705 fn(res, size)
705 fn(res, size)
706
706
707 return res
707 return res
708
708
709 def readline(self, *args, **kwargs):
709 def readline(self, *args, **kwargs):
710 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
710 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
711
711
712 fn = getattr(self._input._observer, 'bufferedreadline', None)
712 fn = getattr(self._input._observer, 'bufferedreadline', None)
713 if fn:
713 if fn:
714 fn(res)
714 fn(res)
715
715
716 return res
716 return res
717
717
718
718
719 PROXIED_SOCKET_METHODS = {
719 PROXIED_SOCKET_METHODS = {
720 'makefile',
720 'makefile',
721 'recv',
721 'recv',
722 'recvfrom',
722 'recvfrom',
723 'recvfrom_into',
723 'recvfrom_into',
724 'recv_into',
724 'recv_into',
725 'send',
725 'send',
726 'sendall',
726 'sendall',
727 'sendto',
727 'sendto',
728 'setblocking',
728 'setblocking',
729 'settimeout',
729 'settimeout',
730 'gettimeout',
730 'gettimeout',
731 'setsockopt',
731 'setsockopt',
732 }
732 }
733
733
734
734
735 class socketproxy:
735 class socketproxy:
736 """A proxy around a socket that tells a watcher when events occur.
736 """A proxy around a socket that tells a watcher when events occur.
737
737
738 This is like ``fileobjectproxy`` except for sockets.
738 This is like ``fileobjectproxy`` except for sockets.
739
739
740 This type is intended to only be used for testing purposes. Think hard
740 This type is intended to only be used for testing purposes. Think hard
741 before using it in important code.
741 before using it in important code.
742 """
742 """
743
743
744 __slots__ = (
744 __slots__ = (
745 '_orig',
745 '_orig',
746 '_observer',
746 '_observer',
747 )
747 )
748
748
749 def __init__(self, sock, observer):
749 def __init__(self, sock, observer):
750 object.__setattr__(self, '_orig', sock)
750 object.__setattr__(self, '_orig', sock)
751 object.__setattr__(self, '_observer', observer)
751 object.__setattr__(self, '_observer', observer)
752
752
753 def __getattribute__(self, name):
753 def __getattribute__(self, name):
754 if name in PROXIED_SOCKET_METHODS:
754 if name in PROXIED_SOCKET_METHODS:
755 return object.__getattribute__(self, name)
755 return object.__getattribute__(self, name)
756
756
757 return getattr(object.__getattribute__(self, '_orig'), name)
757 return getattr(object.__getattribute__(self, '_orig'), name)
758
758
759 def __delattr__(self, name):
759 def __delattr__(self, name):
760 return delattr(object.__getattribute__(self, '_orig'), name)
760 return delattr(object.__getattribute__(self, '_orig'), name)
761
761
762 def __setattr__(self, name, value):
762 def __setattr__(self, name, value):
763 return setattr(object.__getattribute__(self, '_orig'), name, value)
763 return setattr(object.__getattribute__(self, '_orig'), name, value)
764
764
765 def __nonzero__(self):
765 def __nonzero__(self):
766 return bool(object.__getattribute__(self, '_orig'))
766 return bool(object.__getattribute__(self, '_orig'))
767
767
768 __bool__ = __nonzero__
768 __bool__ = __nonzero__
769
769
770 def _observedcall(self, name, *args, **kwargs):
770 def _observedcall(self, name, *args, **kwargs):
771 # Call the original object.
771 # Call the original object.
772 orig = object.__getattribute__(self, '_orig')
772 orig = object.__getattribute__(self, '_orig')
773 res = getattr(orig, name)(*args, **kwargs)
773 res = getattr(orig, name)(*args, **kwargs)
774
774
775 # Call a method on the observer of the same name with arguments
775 # Call a method on the observer of the same name with arguments
776 # so it can react, log, etc.
776 # so it can react, log, etc.
777 observer = object.__getattribute__(self, '_observer')
777 observer = object.__getattribute__(self, '_observer')
778 fn = getattr(observer, name, None)
778 fn = getattr(observer, name, None)
779 if fn:
779 if fn:
780 fn(res, *args, **kwargs)
780 fn(res, *args, **kwargs)
781
781
782 return res
782 return res
783
783
784 def makefile(self, *args, **kwargs):
784 def makefile(self, *args, **kwargs):
785 res = object.__getattribute__(self, '_observedcall')(
785 res = object.__getattribute__(self, '_observedcall')(
786 'makefile', *args, **kwargs
786 'makefile', *args, **kwargs
787 )
787 )
788
788
789 # The file object may be used for I/O. So we turn it into a
789 # The file object may be used for I/O. So we turn it into a
790 # proxy using our observer.
790 # proxy using our observer.
791 observer = object.__getattribute__(self, '_observer')
791 observer = object.__getattribute__(self, '_observer')
792 return makeloggingfileobject(
792 return makeloggingfileobject(
793 observer.fh,
793 observer.fh,
794 res,
794 res,
795 observer.name,
795 observer.name,
796 reads=observer.reads,
796 reads=observer.reads,
797 writes=observer.writes,
797 writes=observer.writes,
798 logdata=observer.logdata,
798 logdata=observer.logdata,
799 logdataapis=observer.logdataapis,
799 logdataapis=observer.logdataapis,
800 )
800 )
801
801
802 def recv(self, *args, **kwargs):
802 def recv(self, *args, **kwargs):
803 return object.__getattribute__(self, '_observedcall')(
803 return object.__getattribute__(self, '_observedcall')(
804 'recv', *args, **kwargs
804 'recv', *args, **kwargs
805 )
805 )
806
806
807 def recvfrom(self, *args, **kwargs):
807 def recvfrom(self, *args, **kwargs):
808 return object.__getattribute__(self, '_observedcall')(
808 return object.__getattribute__(self, '_observedcall')(
809 'recvfrom', *args, **kwargs
809 'recvfrom', *args, **kwargs
810 )
810 )
811
811
812 def recvfrom_into(self, *args, **kwargs):
812 def recvfrom_into(self, *args, **kwargs):
813 return object.__getattribute__(self, '_observedcall')(
813 return object.__getattribute__(self, '_observedcall')(
814 'recvfrom_into', *args, **kwargs
814 'recvfrom_into', *args, **kwargs
815 )
815 )
816
816
817 def recv_into(self, *args, **kwargs):
817 def recv_into(self, *args, **kwargs):
818 return object.__getattribute__(self, '_observedcall')(
818 return object.__getattribute__(self, '_observedcall')(
819 'recv_info', *args, **kwargs
819 'recv_info', *args, **kwargs
820 )
820 )
821
821
822 def send(self, *args, **kwargs):
822 def send(self, *args, **kwargs):
823 return object.__getattribute__(self, '_observedcall')(
823 return object.__getattribute__(self, '_observedcall')(
824 'send', *args, **kwargs
824 'send', *args, **kwargs
825 )
825 )
826
826
827 def sendall(self, *args, **kwargs):
827 def sendall(self, *args, **kwargs):
828 return object.__getattribute__(self, '_observedcall')(
828 return object.__getattribute__(self, '_observedcall')(
829 'sendall', *args, **kwargs
829 'sendall', *args, **kwargs
830 )
830 )
831
831
832 def sendto(self, *args, **kwargs):
832 def sendto(self, *args, **kwargs):
833 return object.__getattribute__(self, '_observedcall')(
833 return object.__getattribute__(self, '_observedcall')(
834 'sendto', *args, **kwargs
834 'sendto', *args, **kwargs
835 )
835 )
836
836
837 def setblocking(self, *args, **kwargs):
837 def setblocking(self, *args, **kwargs):
838 return object.__getattribute__(self, '_observedcall')(
838 return object.__getattribute__(self, '_observedcall')(
839 'setblocking', *args, **kwargs
839 'setblocking', *args, **kwargs
840 )
840 )
841
841
842 def settimeout(self, *args, **kwargs):
842 def settimeout(self, *args, **kwargs):
843 return object.__getattribute__(self, '_observedcall')(
843 return object.__getattribute__(self, '_observedcall')(
844 'settimeout', *args, **kwargs
844 'settimeout', *args, **kwargs
845 )
845 )
846
846
847 def gettimeout(self, *args, **kwargs):
847 def gettimeout(self, *args, **kwargs):
848 return object.__getattribute__(self, '_observedcall')(
848 return object.__getattribute__(self, '_observedcall')(
849 'gettimeout', *args, **kwargs
849 'gettimeout', *args, **kwargs
850 )
850 )
851
851
852 def setsockopt(self, *args, **kwargs):
852 def setsockopt(self, *args, **kwargs):
853 return object.__getattribute__(self, '_observedcall')(
853 return object.__getattribute__(self, '_observedcall')(
854 'setsockopt', *args, **kwargs
854 'setsockopt', *args, **kwargs
855 )
855 )
856
856
857
857
858 class baseproxyobserver:
858 class baseproxyobserver:
859 def __init__(self, fh, name, logdata, logdataapis):
859 def __init__(self, fh, name, logdata, logdataapis):
860 self.fh = fh
860 self.fh = fh
861 self.name = name
861 self.name = name
862 self.logdata = logdata
862 self.logdata = logdata
863 self.logdataapis = logdataapis
863 self.logdataapis = logdataapis
864
864
865 def _writedata(self, data):
865 def _writedata(self, data):
866 if not self.logdata:
866 if not self.logdata:
867 if self.logdataapis:
867 if self.logdataapis:
868 self.fh.write(b'\n')
868 self.fh.write(b'\n')
869 self.fh.flush()
869 self.fh.flush()
870 return
870 return
871
871
872 # Simple case writes all data on a single line.
872 # Simple case writes all data on a single line.
873 if b'\n' not in data:
873 if b'\n' not in data:
874 if self.logdataapis:
874 if self.logdataapis:
875 self.fh.write(b': %s\n' % stringutil.escapestr(data))
875 self.fh.write(b': %s\n' % stringutil.escapestr(data))
876 else:
876 else:
877 self.fh.write(
877 self.fh.write(
878 b'%s> %s\n' % (self.name, stringutil.escapestr(data))
878 b'%s> %s\n' % (self.name, stringutil.escapestr(data))
879 )
879 )
880 self.fh.flush()
880 self.fh.flush()
881 return
881 return
882
882
883 # Data with newlines is written to multiple lines.
883 # Data with newlines is written to multiple lines.
884 if self.logdataapis:
884 if self.logdataapis:
885 self.fh.write(b':\n')
885 self.fh.write(b':\n')
886
886
887 lines = data.splitlines(True)
887 lines = data.splitlines(True)
888 for line in lines:
888 for line in lines:
889 self.fh.write(
889 self.fh.write(
890 b'%s> %s\n' % (self.name, stringutil.escapestr(line))
890 b'%s> %s\n' % (self.name, stringutil.escapestr(line))
891 )
891 )
892 self.fh.flush()
892 self.fh.flush()
893
893
894
894
895 class fileobjectobserver(baseproxyobserver):
895 class fileobjectobserver(baseproxyobserver):
896 """Logs file object activity."""
896 """Logs file object activity."""
897
897
898 def __init__(
898 def __init__(
899 self, fh, name, reads=True, writes=True, logdata=False, logdataapis=True
899 self, fh, name, reads=True, writes=True, logdata=False, logdataapis=True
900 ):
900 ):
901 super(fileobjectobserver, self).__init__(fh, name, logdata, logdataapis)
901 super(fileobjectobserver, self).__init__(fh, name, logdata, logdataapis)
902 self.reads = reads
902 self.reads = reads
903 self.writes = writes
903 self.writes = writes
904
904
905 def read(self, res, size=-1):
905 def read(self, res, size=-1):
906 if not self.reads:
906 if not self.reads:
907 return
907 return
908 # Python 3 can return None from reads at EOF instead of empty strings.
908 # Python 3 can return None from reads at EOF instead of empty strings.
909 if res is None:
909 if res is None:
910 res = b''
910 res = b''
911
911
912 if size == -1 and res == b'':
912 if size == -1 and res == b'':
913 # Suppress pointless read(-1) calls that return
913 # Suppress pointless read(-1) calls that return
914 # nothing. These happen _a lot_ on Python 3, and there
914 # nothing. These happen _a lot_ on Python 3, and there
915 # doesn't seem to be a better workaround to have matching
915 # doesn't seem to be a better workaround to have matching
916 # Python 2 and 3 behavior. :(
916 # Python 2 and 3 behavior. :(
917 return
917 return
918
918
919 if self.logdataapis:
919 if self.logdataapis:
920 self.fh.write(b'%s> read(%d) -> %d' % (self.name, size, len(res)))
920 self.fh.write(b'%s> read(%d) -> %d' % (self.name, size, len(res)))
921
921
922 self._writedata(res)
922 self._writedata(res)
923
923
924 def readline(self, res, limit=-1):
924 def readline(self, res, limit=-1):
925 if not self.reads:
925 if not self.reads:
926 return
926 return
927
927
928 if self.logdataapis:
928 if self.logdataapis:
929 self.fh.write(b'%s> readline() -> %d' % (self.name, len(res)))
929 self.fh.write(b'%s> readline() -> %d' % (self.name, len(res)))
930
930
931 self._writedata(res)
931 self._writedata(res)
932
932
933 def readinto(self, res, dest):
933 def readinto(self, res, dest):
934 if not self.reads:
934 if not self.reads:
935 return
935 return
936
936
937 if self.logdataapis:
937 if self.logdataapis:
938 self.fh.write(
938 self.fh.write(
939 b'%s> readinto(%d) -> %r' % (self.name, len(dest), res)
939 b'%s> readinto(%d) -> %r' % (self.name, len(dest), res)
940 )
940 )
941
941
942 data = dest[0:res] if res is not None else b''
942 data = dest[0:res] if res is not None else b''
943
943
944 # _writedata() uses "in" operator and is confused by memoryview because
944 # _writedata() uses "in" operator and is confused by memoryview because
945 # characters are ints on Python 3.
945 # characters are ints on Python 3.
946 if isinstance(data, memoryview):
946 if isinstance(data, memoryview):
947 data = data.tobytes()
947 data = data.tobytes()
948
948
949 self._writedata(data)
949 self._writedata(data)
950
950
951 def write(self, res, data):
951 def write(self, res, data):
952 if not self.writes:
952 if not self.writes:
953 return
953 return
954
954
955 # Python 2 returns None from some write() calls. Python 3 (reasonably)
955 # Python 2 returns None from some write() calls. Python 3 (reasonably)
956 # returns the integer bytes written.
956 # returns the integer bytes written.
957 if res is None and data:
957 if res is None and data:
958 res = len(data)
958 res = len(data)
959
959
960 if self.logdataapis:
960 if self.logdataapis:
961 self.fh.write(b'%s> write(%d) -> %r' % (self.name, len(data), res))
961 self.fh.write(b'%s> write(%d) -> %r' % (self.name, len(data), res))
962
962
963 self._writedata(data)
963 self._writedata(data)
964
964
965 def flush(self, res):
965 def flush(self, res):
966 if not self.writes:
966 if not self.writes:
967 return
967 return
968
968
969 self.fh.write(b'%s> flush() -> %r\n' % (self.name, res))
969 self.fh.write(b'%s> flush() -> %r\n' % (self.name, res))
970
970
971 # For observedbufferedinputpipe.
971 # For observedbufferedinputpipe.
972 def bufferedread(self, res, size):
972 def bufferedread(self, res, size):
973 if not self.reads:
973 if not self.reads:
974 return
974 return
975
975
976 if self.logdataapis:
976 if self.logdataapis:
977 self.fh.write(
977 self.fh.write(
978 b'%s> bufferedread(%d) -> %d' % (self.name, size, len(res))
978 b'%s> bufferedread(%d) -> %d' % (self.name, size, len(res))
979 )
979 )
980
980
981 self._writedata(res)
981 self._writedata(res)
982
982
983 def bufferedreadline(self, res):
983 def bufferedreadline(self, res):
984 if not self.reads:
984 if not self.reads:
985 return
985 return
986
986
987 if self.logdataapis:
987 if self.logdataapis:
988 self.fh.write(
988 self.fh.write(
989 b'%s> bufferedreadline() -> %d' % (self.name, len(res))
989 b'%s> bufferedreadline() -> %d' % (self.name, len(res))
990 )
990 )
991
991
992 self._writedata(res)
992 self._writedata(res)
993
993
994
994
995 def makeloggingfileobject(
995 def makeloggingfileobject(
996 logh, fh, name, reads=True, writes=True, logdata=False, logdataapis=True
996 logh, fh, name, reads=True, writes=True, logdata=False, logdataapis=True
997 ):
997 ):
998 """Turn a file object into a logging file object."""
998 """Turn a file object into a logging file object."""
999
999
1000 observer = fileobjectobserver(
1000 observer = fileobjectobserver(
1001 logh,
1001 logh,
1002 name,
1002 name,
1003 reads=reads,
1003 reads=reads,
1004 writes=writes,
1004 writes=writes,
1005 logdata=logdata,
1005 logdata=logdata,
1006 logdataapis=logdataapis,
1006 logdataapis=logdataapis,
1007 )
1007 )
1008 return fileobjectproxy(fh, observer)
1008 return fileobjectproxy(fh, observer)
1009
1009
1010
1010
1011 class socketobserver(baseproxyobserver):
1011 class socketobserver(baseproxyobserver):
1012 """Logs socket activity."""
1012 """Logs socket activity."""
1013
1013
1014 def __init__(
1014 def __init__(
1015 self,
1015 self,
1016 fh,
1016 fh,
1017 name,
1017 name,
1018 reads=True,
1018 reads=True,
1019 writes=True,
1019 writes=True,
1020 states=True,
1020 states=True,
1021 logdata=False,
1021 logdata=False,
1022 logdataapis=True,
1022 logdataapis=True,
1023 ):
1023 ):
1024 super(socketobserver, self).__init__(fh, name, logdata, logdataapis)
1024 super(socketobserver, self).__init__(fh, name, logdata, logdataapis)
1025 self.reads = reads
1025 self.reads = reads
1026 self.writes = writes
1026 self.writes = writes
1027 self.states = states
1027 self.states = states
1028
1028
1029 def makefile(self, res, mode=None, bufsize=None):
1029 def makefile(self, res, mode=None, bufsize=None):
1030 if not self.states:
1030 if not self.states:
1031 return
1031 return
1032
1032
1033 self.fh.write(b'%s> makefile(%r, %r)\n' % (self.name, mode, bufsize))
1033 self.fh.write(b'%s> makefile(%r, %r)\n' % (self.name, mode, bufsize))
1034
1034
1035 def recv(self, res, size, flags=0):
1035 def recv(self, res, size, flags=0):
1036 if not self.reads:
1036 if not self.reads:
1037 return
1037 return
1038
1038
1039 if self.logdataapis:
1039 if self.logdataapis:
1040 self.fh.write(
1040 self.fh.write(
1041 b'%s> recv(%d, %d) -> %d' % (self.name, size, flags, len(res))
1041 b'%s> recv(%d, %d) -> %d' % (self.name, size, flags, len(res))
1042 )
1042 )
1043 self._writedata(res)
1043 self._writedata(res)
1044
1044
1045 def recvfrom(self, res, size, flags=0):
1045 def recvfrom(self, res, size, flags=0):
1046 if not self.reads:
1046 if not self.reads:
1047 return
1047 return
1048
1048
1049 if self.logdataapis:
1049 if self.logdataapis:
1050 self.fh.write(
1050 self.fh.write(
1051 b'%s> recvfrom(%d, %d) -> %d'
1051 b'%s> recvfrom(%d, %d) -> %d'
1052 % (self.name, size, flags, len(res[0]))
1052 % (self.name, size, flags, len(res[0]))
1053 )
1053 )
1054
1054
1055 self._writedata(res[0])
1055 self._writedata(res[0])
1056
1056
1057 def recvfrom_into(self, res, buf, size, flags=0):
1057 def recvfrom_into(self, res, buf, size, flags=0):
1058 if not self.reads:
1058 if not self.reads:
1059 return
1059 return
1060
1060
1061 if self.logdataapis:
1061 if self.logdataapis:
1062 self.fh.write(
1062 self.fh.write(
1063 b'%s> recvfrom_into(%d, %d) -> %d'
1063 b'%s> recvfrom_into(%d, %d) -> %d'
1064 % (self.name, size, flags, res[0])
1064 % (self.name, size, flags, res[0])
1065 )
1065 )
1066
1066
1067 self._writedata(buf[0 : res[0]])
1067 self._writedata(buf[0 : res[0]])
1068
1068
1069 def recv_into(self, res, buf, size=0, flags=0):
1069 def recv_into(self, res, buf, size=0, flags=0):
1070 if not self.reads:
1070 if not self.reads:
1071 return
1071 return
1072
1072
1073 if self.logdataapis:
1073 if self.logdataapis:
1074 self.fh.write(
1074 self.fh.write(
1075 b'%s> recv_into(%d, %d) -> %d' % (self.name, size, flags, res)
1075 b'%s> recv_into(%d, %d) -> %d' % (self.name, size, flags, res)
1076 )
1076 )
1077
1077
1078 self._writedata(buf[0:res])
1078 self._writedata(buf[0:res])
1079
1079
1080 def send(self, res, data, flags=0):
1080 def send(self, res, data, flags=0):
1081 if not self.writes:
1081 if not self.writes:
1082 return
1082 return
1083
1083
1084 self.fh.write(
1084 self.fh.write(
1085 b'%s> send(%d, %d) -> %d' % (self.name, len(data), flags, len(res))
1085 b'%s> send(%d, %d) -> %d' % (self.name, len(data), flags, len(res))
1086 )
1086 )
1087 self._writedata(data)
1087 self._writedata(data)
1088
1088
1089 def sendall(self, res, data, flags=0):
1089 def sendall(self, res, data, flags=0):
1090 if not self.writes:
1090 if not self.writes:
1091 return
1091 return
1092
1092
1093 if self.logdataapis:
1093 if self.logdataapis:
1094 # Returns None on success. So don't bother reporting return value.
1094 # Returns None on success. So don't bother reporting return value.
1095 self.fh.write(
1095 self.fh.write(
1096 b'%s> sendall(%d, %d)' % (self.name, len(data), flags)
1096 b'%s> sendall(%d, %d)' % (self.name, len(data), flags)
1097 )
1097 )
1098
1098
1099 self._writedata(data)
1099 self._writedata(data)
1100
1100
1101 def sendto(self, res, data, flagsoraddress, address=None):
1101 def sendto(self, res, data, flagsoraddress, address=None):
1102 if not self.writes:
1102 if not self.writes:
1103 return
1103 return
1104
1104
1105 if address:
1105 if address:
1106 flags = flagsoraddress
1106 flags = flagsoraddress
1107 else:
1107 else:
1108 flags = 0
1108 flags = 0
1109
1109
1110 if self.logdataapis:
1110 if self.logdataapis:
1111 self.fh.write(
1111 self.fh.write(
1112 b'%s> sendto(%d, %d, %r) -> %d'
1112 b'%s> sendto(%d, %d, %r) -> %d'
1113 % (self.name, len(data), flags, address, res)
1113 % (self.name, len(data), flags, address, res)
1114 )
1114 )
1115
1115
1116 self._writedata(data)
1116 self._writedata(data)
1117
1117
1118 def setblocking(self, res, flag):
1118 def setblocking(self, res, flag):
1119 if not self.states:
1119 if not self.states:
1120 return
1120 return
1121
1121
1122 self.fh.write(b'%s> setblocking(%r)\n' % (self.name, flag))
1122 self.fh.write(b'%s> setblocking(%r)\n' % (self.name, flag))
1123
1123
1124 def settimeout(self, res, value):
1124 def settimeout(self, res, value):
1125 if not self.states:
1125 if not self.states:
1126 return
1126 return
1127
1127
1128 self.fh.write(b'%s> settimeout(%r)\n' % (self.name, value))
1128 self.fh.write(b'%s> settimeout(%r)\n' % (self.name, value))
1129
1129
1130 def gettimeout(self, res):
1130 def gettimeout(self, res):
1131 if not self.states:
1131 if not self.states:
1132 return
1132 return
1133
1133
1134 self.fh.write(b'%s> gettimeout() -> %f\n' % (self.name, res))
1134 self.fh.write(b'%s> gettimeout() -> %f\n' % (self.name, res))
1135
1135
1136 def setsockopt(self, res, level, optname, value):
1136 def setsockopt(self, res, level, optname, value):
1137 if not self.states:
1137 if not self.states:
1138 return
1138 return
1139
1139
1140 self.fh.write(
1140 self.fh.write(
1141 b'%s> setsockopt(%r, %r, %r) -> %r\n'
1141 b'%s> setsockopt(%r, %r, %r) -> %r\n'
1142 % (self.name, level, optname, value, res)
1142 % (self.name, level, optname, value, res)
1143 )
1143 )
1144
1144
1145
1145
1146 def makeloggingsocket(
1146 def makeloggingsocket(
1147 logh,
1147 logh,
1148 fh,
1148 fh,
1149 name,
1149 name,
1150 reads=True,
1150 reads=True,
1151 writes=True,
1151 writes=True,
1152 states=True,
1152 states=True,
1153 logdata=False,
1153 logdata=False,
1154 logdataapis=True,
1154 logdataapis=True,
1155 ):
1155 ):
1156 """Turn a socket into a logging socket."""
1156 """Turn a socket into a logging socket."""
1157
1157
1158 observer = socketobserver(
1158 observer = socketobserver(
1159 logh,
1159 logh,
1160 name,
1160 name,
1161 reads=reads,
1161 reads=reads,
1162 writes=writes,
1162 writes=writes,
1163 states=states,
1163 states=states,
1164 logdata=logdata,
1164 logdata=logdata,
1165 logdataapis=logdataapis,
1165 logdataapis=logdataapis,
1166 )
1166 )
1167 return socketproxy(fh, observer)
1167 return socketproxy(fh, observer)
1168
1168
1169
1169
1170 def version():
1170 def version():
1171 """Return version information if available."""
1171 """Return version information if available."""
1172 try:
1172 try:
1173 from . import __version__ # pytype: disable=import-error
1173 from . import __version__ # pytype: disable=import-error
1174
1174
1175 return __version__.version
1175 return __version__.version
1176 except ImportError:
1176 except ImportError:
1177 return b'unknown'
1177 return b'unknown'
1178
1178
1179
1179
1180 def versiontuple(v=None, n=4):
1180 def versiontuple(v=None, n=4):
1181 """Parses a Mercurial version string into an N-tuple.
1181 """Parses a Mercurial version string into an N-tuple.
1182
1182
1183 The version string to be parsed is specified with the ``v`` argument.
1183 The version string to be parsed is specified with the ``v`` argument.
1184 If it isn't defined, the current Mercurial version string will be parsed.
1184 If it isn't defined, the current Mercurial version string will be parsed.
1185
1185
1186 ``n`` can be 2, 3, or 4. Here is how some version strings map to
1186 ``n`` can be 2, 3, or 4. Here is how some version strings map to
1187 returned values:
1187 returned values:
1188
1188
1189 >>> v = b'3.6.1+190-df9b73d2d444'
1189 >>> v = b'3.6.1+190-df9b73d2d444'
1190 >>> versiontuple(v, 2)
1190 >>> versiontuple(v, 2)
1191 (3, 6)
1191 (3, 6)
1192 >>> versiontuple(v, 3)
1192 >>> versiontuple(v, 3)
1193 (3, 6, 1)
1193 (3, 6, 1)
1194 >>> versiontuple(v, 4)
1194 >>> versiontuple(v, 4)
1195 (3, 6, 1, '190-df9b73d2d444')
1195 (3, 6, 1, '190-df9b73d2d444')
1196
1196
1197 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
1197 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
1198 (3, 6, 1, '190-df9b73d2d444+20151118')
1198 (3, 6, 1, '190-df9b73d2d444+20151118')
1199
1199
1200 >>> v = b'3.6'
1200 >>> v = b'3.6'
1201 >>> versiontuple(v, 2)
1201 >>> versiontuple(v, 2)
1202 (3, 6)
1202 (3, 6)
1203 >>> versiontuple(v, 3)
1203 >>> versiontuple(v, 3)
1204 (3, 6, None)
1204 (3, 6, None)
1205 >>> versiontuple(v, 4)
1205 >>> versiontuple(v, 4)
1206 (3, 6, None, None)
1206 (3, 6, None, None)
1207
1207
1208 >>> v = b'3.9-rc'
1208 >>> v = b'3.9-rc'
1209 >>> versiontuple(v, 2)
1209 >>> versiontuple(v, 2)
1210 (3, 9)
1210 (3, 9)
1211 >>> versiontuple(v, 3)
1211 >>> versiontuple(v, 3)
1212 (3, 9, None)
1212 (3, 9, None)
1213 >>> versiontuple(v, 4)
1213 >>> versiontuple(v, 4)
1214 (3, 9, None, 'rc')
1214 (3, 9, None, 'rc')
1215
1215
1216 >>> v = b'3.9-rc+2-02a8fea4289b'
1216 >>> v = b'3.9-rc+2-02a8fea4289b'
1217 >>> versiontuple(v, 2)
1217 >>> versiontuple(v, 2)
1218 (3, 9)
1218 (3, 9)
1219 >>> versiontuple(v, 3)
1219 >>> versiontuple(v, 3)
1220 (3, 9, None)
1220 (3, 9, None)
1221 >>> versiontuple(v, 4)
1221 >>> versiontuple(v, 4)
1222 (3, 9, None, 'rc+2-02a8fea4289b')
1222 (3, 9, None, 'rc+2-02a8fea4289b')
1223
1223
1224 >>> versiontuple(b'4.6rc0')
1224 >>> versiontuple(b'4.6rc0')
1225 (4, 6, None, 'rc0')
1225 (4, 6, None, 'rc0')
1226 >>> versiontuple(b'4.6rc0+12-425d55e54f98')
1226 >>> versiontuple(b'4.6rc0+12-425d55e54f98')
1227 (4, 6, None, 'rc0+12-425d55e54f98')
1227 (4, 6, None, 'rc0+12-425d55e54f98')
1228 >>> versiontuple(b'.1.2.3')
1228 >>> versiontuple(b'.1.2.3')
1229 (None, None, None, '.1.2.3')
1229 (None, None, None, '.1.2.3')
1230 >>> versiontuple(b'12.34..5')
1230 >>> versiontuple(b'12.34..5')
1231 (12, 34, None, '..5')
1231 (12, 34, None, '..5')
1232 >>> versiontuple(b'1.2.3.4.5.6')
1232 >>> versiontuple(b'1.2.3.4.5.6')
1233 (1, 2, 3, '.4.5.6')
1233 (1, 2, 3, '.4.5.6')
1234 """
1234 """
1235 if not v:
1235 if not v:
1236 v = version()
1236 v = version()
1237 m = remod.match(br'(\d+(?:\.\d+){,2})[+-]?(.*)', v)
1237 m = remod.match(br'(\d+(?:\.\d+){,2})[+-]?(.*)', v)
1238 if not m:
1238 if not m:
1239 vparts, extra = b'', v
1239 vparts, extra = b'', v
1240 elif m.group(2):
1240 elif m.group(2):
1241 vparts, extra = m.groups()
1241 vparts, extra = m.groups()
1242 else:
1242 else:
1243 vparts, extra = m.group(1), None
1243 vparts, extra = m.group(1), None
1244
1244
1245 assert vparts is not None # help pytype
1245 assert vparts is not None # help pytype
1246
1246
1247 vints = []
1247 vints = []
1248 for i in vparts.split(b'.'):
1248 for i in vparts.split(b'.'):
1249 try:
1249 try:
1250 vints.append(int(i))
1250 vints.append(int(i))
1251 except ValueError:
1251 except ValueError:
1252 break
1252 break
1253 # (3, 6) -> (3, 6, None)
1253 # (3, 6) -> (3, 6, None)
1254 while len(vints) < 3:
1254 while len(vints) < 3:
1255 vints.append(None)
1255 vints.append(None)
1256
1256
1257 if n == 2:
1257 if n == 2:
1258 return (vints[0], vints[1])
1258 return (vints[0], vints[1])
1259 if n == 3:
1259 if n == 3:
1260 return (vints[0], vints[1], vints[2])
1260 return (vints[0], vints[1], vints[2])
1261 if n == 4:
1261 if n == 4:
1262 return (vints[0], vints[1], vints[2], extra)
1262 return (vints[0], vints[1], vints[2], extra)
1263
1263
1264 raise error.ProgrammingError(b"invalid version part request: %d" % n)
1264 raise error.ProgrammingError(b"invalid version part request: %d" % n)
1265
1265
1266
1266
1267 def cachefunc(func):
1267 def cachefunc(func):
1268 '''cache the result of function calls'''
1268 '''cache the result of function calls'''
1269 # XXX doesn't handle keywords args
1269 # XXX doesn't handle keywords args
1270 if func.__code__.co_argcount == 0:
1270 if func.__code__.co_argcount == 0:
1271 listcache = []
1271 listcache = []
1272
1272
1273 def f():
1273 def f():
1274 if len(listcache) == 0:
1274 if len(listcache) == 0:
1275 listcache.append(func())
1275 listcache.append(func())
1276 return listcache[0]
1276 return listcache[0]
1277
1277
1278 return f
1278 return f
1279 cache = {}
1279 cache = {}
1280 if func.__code__.co_argcount == 1:
1280 if func.__code__.co_argcount == 1:
1281 # we gain a small amount of time because
1281 # we gain a small amount of time because
1282 # we don't need to pack/unpack the list
1282 # we don't need to pack/unpack the list
1283 def f(arg):
1283 def f(arg):
1284 if arg not in cache:
1284 if arg not in cache:
1285 cache[arg] = func(arg)
1285 cache[arg] = func(arg)
1286 return cache[arg]
1286 return cache[arg]
1287
1287
1288 else:
1288 else:
1289
1289
1290 def f(*args):
1290 def f(*args):
1291 if args not in cache:
1291 if args not in cache:
1292 cache[args] = func(*args)
1292 cache[args] = func(*args)
1293 return cache[args]
1293 return cache[args]
1294
1294
1295 return f
1295 return f
1296
1296
1297
1297
1298 class cow:
1298 class cow:
1299 """helper class to make copy-on-write easier
1299 """helper class to make copy-on-write easier
1300
1300
1301 Call preparewrite before doing any writes.
1301 Call preparewrite before doing any writes.
1302 """
1302 """
1303
1303
1304 def preparewrite(self):
1304 def preparewrite(self):
1305 """call this before writes, return self or a copied new object"""
1305 """call this before writes, return self or a copied new object"""
1306 if getattr(self, '_copied', 0):
1306 if getattr(self, '_copied', 0):
1307 self._copied -= 1
1307 self._copied -= 1
1308 # Function cow.__init__ expects 1 arg(s), got 2 [wrong-arg-count]
1308 # Function cow.__init__ expects 1 arg(s), got 2 [wrong-arg-count]
1309 return self.__class__(self) # pytype: disable=wrong-arg-count
1309 return self.__class__(self) # pytype: disable=wrong-arg-count
1310 return self
1310 return self
1311
1311
1312 def copy(self):
1312 def copy(self):
1313 """always do a cheap copy"""
1313 """always do a cheap copy"""
1314 self._copied = getattr(self, '_copied', 0) + 1
1314 self._copied = getattr(self, '_copied', 0) + 1
1315 return self
1315 return self
1316
1316
1317
1317
1318 class sortdict(collections.OrderedDict):
1318 class sortdict(collections.OrderedDict):
1319 """a simple sorted dictionary
1319 """a simple sorted dictionary
1320
1320
1321 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1321 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1322 >>> d2 = d1.copy()
1322 >>> d2 = d1.copy()
1323 >>> list(d2.items())
1323 >>> list(d2.items())
1324 [('a', 0), ('b', 1)]
1324 [('a', 0), ('b', 1)]
1325 >>> d2.update([(b'a', 2)])
1325 >>> d2.update([(b'a', 2)])
1326 >>> list(d2.keys()) # should still be in last-set order
1326 >>> list(d2.keys()) # should still be in last-set order
1327 ['b', 'a']
1327 ['b', 'a']
1328 >>> d1.insert(1, b'a.5', 0.5)
1328 >>> d1.insert(1, b'a.5', 0.5)
1329 >>> list(d1.items())
1329 >>> list(d1.items())
1330 [('a', 0), ('a.5', 0.5), ('b', 1)]
1330 [('a', 0), ('a.5', 0.5), ('b', 1)]
1331 """
1331 """
1332
1332
1333 def __setitem__(self, key, value):
1333 def __setitem__(self, key, value):
1334 if key in self:
1334 if key in self:
1335 del self[key]
1335 del self[key]
1336 super(sortdict, self).__setitem__(key, value)
1336 super(sortdict, self).__setitem__(key, value)
1337
1337
1338 if pycompat.ispypy:
1338 if pycompat.ispypy:
1339 # __setitem__() isn't called as of PyPy 5.8.0
1339 # __setitem__() isn't called as of PyPy 5.8.0
1340 def update(self, src, **f):
1340 def update(self, src, **f):
1341 if isinstance(src, dict):
1341 if isinstance(src, dict):
1342 src = src.items()
1342 src = src.items()
1343 for k, v in src:
1343 for k, v in src:
1344 self[k] = v
1344 self[k] = v
1345 for k in f:
1345 for k in f:
1346 self[k] = f[k]
1346 self[k] = f[k]
1347
1347
1348 def insert(self, position, key, value):
1348 def insert(self, position, key, value):
1349 for i, (k, v) in enumerate(list(self.items())):
1349 for i, (k, v) in enumerate(list(self.items())):
1350 if i == position:
1350 if i == position:
1351 self[key] = value
1351 self[key] = value
1352 if i >= position:
1352 if i >= position:
1353 del self[k]
1353 del self[k]
1354 self[k] = v
1354 self[k] = v
1355
1355
1356
1356
1357 class cowdict(cow, dict):
1357 class cowdict(cow, dict):
1358 """copy-on-write dict
1358 """copy-on-write dict
1359
1359
1360 Be sure to call d = d.preparewrite() before writing to d.
1360 Be sure to call d = d.preparewrite() before writing to d.
1361
1361
1362 >>> a = cowdict()
1362 >>> a = cowdict()
1363 >>> a is a.preparewrite()
1363 >>> a is a.preparewrite()
1364 True
1364 True
1365 >>> b = a.copy()
1365 >>> b = a.copy()
1366 >>> b is a
1366 >>> b is a
1367 True
1367 True
1368 >>> c = b.copy()
1368 >>> c = b.copy()
1369 >>> c is a
1369 >>> c is a
1370 True
1370 True
1371 >>> a = a.preparewrite()
1371 >>> a = a.preparewrite()
1372 >>> b is a
1372 >>> b is a
1373 False
1373 False
1374 >>> a is a.preparewrite()
1374 >>> a is a.preparewrite()
1375 True
1375 True
1376 >>> c = c.preparewrite()
1376 >>> c = c.preparewrite()
1377 >>> b is c
1377 >>> b is c
1378 False
1378 False
1379 >>> b is b.preparewrite()
1379 >>> b is b.preparewrite()
1380 True
1380 True
1381 """
1381 """
1382
1382
1383
1383
1384 class cowsortdict(cow, sortdict):
1384 class cowsortdict(cow, sortdict):
1385 """copy-on-write sortdict
1385 """copy-on-write sortdict
1386
1386
1387 Be sure to call d = d.preparewrite() before writing to d.
1387 Be sure to call d = d.preparewrite() before writing to d.
1388 """
1388 """
1389
1389
1390
1390
1391 class transactional: # pytype: disable=ignored-metaclass
1391 class transactional: # pytype: disable=ignored-metaclass
1392 """Base class for making a transactional type into a context manager."""
1392 """Base class for making a transactional type into a context manager."""
1393
1393
1394 __metaclass__ = abc.ABCMeta
1394 __metaclass__ = abc.ABCMeta
1395
1395
1396 @abc.abstractmethod
1396 @abc.abstractmethod
1397 def close(self):
1397 def close(self):
1398 """Successfully closes the transaction."""
1398 """Successfully closes the transaction."""
1399
1399
1400 @abc.abstractmethod
1400 @abc.abstractmethod
1401 def release(self):
1401 def release(self):
1402 """Marks the end of the transaction.
1402 """Marks the end of the transaction.
1403
1403
1404 If the transaction has not been closed, it will be aborted.
1404 If the transaction has not been closed, it will be aborted.
1405 """
1405 """
1406
1406
1407 def __enter__(self):
1407 def __enter__(self):
1408 return self
1408 return self
1409
1409
1410 def __exit__(self, exc_type, exc_val, exc_tb):
1410 def __exit__(self, exc_type, exc_val, exc_tb):
1411 try:
1411 try:
1412 if exc_type is None:
1412 if exc_type is None:
1413 self.close()
1413 self.close()
1414 finally:
1414 finally:
1415 self.release()
1415 self.release()
1416
1416
1417
1417
1418 @contextlib.contextmanager
1418 @contextlib.contextmanager
1419 def acceptintervention(tr=None):
1419 def acceptintervention(tr=None):
1420 """A context manager that closes the transaction on InterventionRequired
1420 """A context manager that closes the transaction on InterventionRequired
1421
1421
1422 If no transaction was provided, this simply runs the body and returns
1422 If no transaction was provided, this simply runs the body and returns
1423 """
1423 """
1424 if not tr:
1424 if not tr:
1425 yield
1425 yield
1426 return
1426 return
1427 try:
1427 try:
1428 yield
1428 yield
1429 tr.close()
1429 tr.close()
1430 except error.InterventionRequired:
1430 except error.InterventionRequired:
1431 tr.close()
1431 tr.close()
1432 raise
1432 raise
1433 finally:
1433 finally:
1434 tr.release()
1434 tr.release()
1435
1435
1436
1436
1437 @contextlib.contextmanager
1437 @contextlib.contextmanager
1438 def nullcontextmanager(enter_result=None):
1438 def nullcontextmanager(enter_result=None):
1439 yield enter_result
1439 yield enter_result
1440
1440
1441
1441
1442 class _lrucachenode:
1442 class _lrucachenode:
1443 """A node in a doubly linked list.
1443 """A node in a doubly linked list.
1444
1444
1445 Holds a reference to nodes on either side as well as a key-value
1445 Holds a reference to nodes on either side as well as a key-value
1446 pair for the dictionary entry.
1446 pair for the dictionary entry.
1447 """
1447 """
1448
1448
1449 __slots__ = ('next', 'prev', 'key', 'value', 'cost')
1449 __slots__ = ('next', 'prev', 'key', 'value', 'cost')
1450
1450
1451 def __init__(self):
1451 def __init__(self):
1452 self.next = self
1452 self.next = self
1453 self.prev = self
1453 self.prev = self
1454
1454
1455 self.key = _notset
1455 self.key = _notset
1456 self.value = None
1456 self.value = None
1457 self.cost = 0
1457 self.cost = 0
1458
1458
1459 def markempty(self):
1459 def markempty(self):
1460 """Mark the node as emptied."""
1460 """Mark the node as emptied."""
1461 self.key = _notset
1461 self.key = _notset
1462 self.value = None
1462 self.value = None
1463 self.cost = 0
1463 self.cost = 0
1464
1464
1465
1465
1466 class lrucachedict:
1466 class lrucachedict:
1467 """Dict that caches most recent accesses and sets.
1467 """Dict that caches most recent accesses and sets.
1468
1468
1469 The dict consists of an actual backing dict - indexed by original
1469 The dict consists of an actual backing dict - indexed by original
1470 key - and a doubly linked circular list defining the order of entries in
1470 key - and a doubly linked circular list defining the order of entries in
1471 the cache.
1471 the cache.
1472
1472
1473 The head node is the newest entry in the cache. If the cache is full,
1473 The head node is the newest entry in the cache. If the cache is full,
1474 we recycle head.prev and make it the new head. Cache accesses result in
1474 we recycle head.prev and make it the new head. Cache accesses result in
1475 the node being moved to before the existing head and being marked as the
1475 the node being moved to before the existing head and being marked as the
1476 new head node.
1476 new head node.
1477
1477
1478 Items in the cache can be inserted with an optional "cost" value. This is
1478 Items in the cache can be inserted with an optional "cost" value. This is
1479 simply an integer that is specified by the caller. The cache can be queried
1479 simply an integer that is specified by the caller. The cache can be queried
1480 for the total cost of all items presently in the cache.
1480 for the total cost of all items presently in the cache.
1481
1481
1482 The cache can also define a maximum cost. If a cache insertion would
1482 The cache can also define a maximum cost. If a cache insertion would
1483 cause the total cost of the cache to go beyond the maximum cost limit,
1483 cause the total cost of the cache to go beyond the maximum cost limit,
1484 nodes will be evicted to make room for the new code. This can be used
1484 nodes will be evicted to make room for the new code. This can be used
1485 to e.g. set a max memory limit and associate an estimated bytes size
1485 to e.g. set a max memory limit and associate an estimated bytes size
1486 cost to each item in the cache. By default, no maximum cost is enforced.
1486 cost to each item in the cache. By default, no maximum cost is enforced.
1487 """
1487 """
1488
1488
1489 def __init__(self, max, maxcost=0):
1489 def __init__(self, max, maxcost=0):
1490 self._cache = {}
1490 self._cache = {}
1491
1491
1492 self._head = _lrucachenode()
1492 self._head = _lrucachenode()
1493 self._size = 1
1493 self._size = 1
1494 self.capacity = max
1494 self.capacity = max
1495 self.totalcost = 0
1495 self.totalcost = 0
1496 self.maxcost = maxcost
1496 self.maxcost = maxcost
1497
1497
1498 def __len__(self):
1498 def __len__(self):
1499 return len(self._cache)
1499 return len(self._cache)
1500
1500
1501 def __contains__(self, k):
1501 def __contains__(self, k):
1502 return k in self._cache
1502 return k in self._cache
1503
1503
1504 def __iter__(self):
1504 def __iter__(self):
1505 # We don't have to iterate in cache order, but why not.
1505 # We don't have to iterate in cache order, but why not.
1506 n = self._head
1506 n = self._head
1507 for i in range(len(self._cache)):
1507 for i in range(len(self._cache)):
1508 yield n.key
1508 yield n.key
1509 n = n.next
1509 n = n.next
1510
1510
1511 def __getitem__(self, k):
1511 def __getitem__(self, k):
1512 node = self._cache[k]
1512 node = self._cache[k]
1513 self._movetohead(node)
1513 self._movetohead(node)
1514 return node.value
1514 return node.value
1515
1515
1516 def insert(self, k, v, cost=0):
1516 def insert(self, k, v, cost=0):
1517 """Insert a new item in the cache with optional cost value."""
1517 """Insert a new item in the cache with optional cost value."""
1518 node = self._cache.get(k)
1518 node = self._cache.get(k)
1519 # Replace existing value and mark as newest.
1519 # Replace existing value and mark as newest.
1520 if node is not None:
1520 if node is not None:
1521 self.totalcost -= node.cost
1521 self.totalcost -= node.cost
1522 node.value = v
1522 node.value = v
1523 node.cost = cost
1523 node.cost = cost
1524 self.totalcost += cost
1524 self.totalcost += cost
1525 self._movetohead(node)
1525 self._movetohead(node)
1526
1526
1527 if self.maxcost:
1527 if self.maxcost:
1528 self._enforcecostlimit()
1528 self._enforcecostlimit()
1529
1529
1530 return
1530 return
1531
1531
1532 if self._size < self.capacity:
1532 if self._size < self.capacity:
1533 node = self._addcapacity()
1533 node = self._addcapacity()
1534 else:
1534 else:
1535 # Grab the last/oldest item.
1535 # Grab the last/oldest item.
1536 node = self._head.prev
1536 node = self._head.prev
1537
1537
1538 # At capacity. Kill the old entry.
1538 # At capacity. Kill the old entry.
1539 if node.key is not _notset:
1539 if node.key is not _notset:
1540 self.totalcost -= node.cost
1540 self.totalcost -= node.cost
1541 del self._cache[node.key]
1541 del self._cache[node.key]
1542
1542
1543 node.key = k
1543 node.key = k
1544 node.value = v
1544 node.value = v
1545 node.cost = cost
1545 node.cost = cost
1546 self.totalcost += cost
1546 self.totalcost += cost
1547 self._cache[k] = node
1547 self._cache[k] = node
1548 # And mark it as newest entry. No need to adjust order since it
1548 # And mark it as newest entry. No need to adjust order since it
1549 # is already self._head.prev.
1549 # is already self._head.prev.
1550 self._head = node
1550 self._head = node
1551
1551
1552 if self.maxcost:
1552 if self.maxcost:
1553 self._enforcecostlimit()
1553 self._enforcecostlimit()
1554
1554
1555 def __setitem__(self, k, v):
1555 def __setitem__(self, k, v):
1556 self.insert(k, v)
1556 self.insert(k, v)
1557
1557
1558 def __delitem__(self, k):
1558 def __delitem__(self, k):
1559 self.pop(k)
1559 self.pop(k)
1560
1560
1561 def pop(self, k, default=_notset):
1561 def pop(self, k, default=_notset):
1562 try:
1562 try:
1563 node = self._cache.pop(k)
1563 node = self._cache.pop(k)
1564 except KeyError:
1564 except KeyError:
1565 if default is _notset:
1565 if default is _notset:
1566 raise
1566 raise
1567 return default
1567 return default
1568
1568
1569 value = node.value
1569 value = node.value
1570 self.totalcost -= node.cost
1570 self.totalcost -= node.cost
1571 node.markempty()
1571 node.markempty()
1572
1572
1573 # Temporarily mark as newest item before re-adjusting head to make
1573 # Temporarily mark as newest item before re-adjusting head to make
1574 # this node the oldest item.
1574 # this node the oldest item.
1575 self._movetohead(node)
1575 self._movetohead(node)
1576 self._head = node.next
1576 self._head = node.next
1577
1577
1578 return value
1578 return value
1579
1579
1580 # Additional dict methods.
1580 # Additional dict methods.
1581
1581
1582 def get(self, k, default=None):
1582 def get(self, k, default=None):
1583 try:
1583 try:
1584 return self.__getitem__(k)
1584 return self.__getitem__(k)
1585 except KeyError:
1585 except KeyError:
1586 return default
1586 return default
1587
1587
1588 def peek(self, k, default=_notset):
1588 def peek(self, k, default=_notset):
1589 """Get the specified item without moving it to the head
1589 """Get the specified item without moving it to the head
1590
1590
1591 Unlike get(), this doesn't mutate the internal state. But be aware
1591 Unlike get(), this doesn't mutate the internal state. But be aware
1592 that it doesn't mean peek() is thread safe.
1592 that it doesn't mean peek() is thread safe.
1593 """
1593 """
1594 try:
1594 try:
1595 node = self._cache[k]
1595 node = self._cache[k]
1596 return node.value
1596 return node.value
1597 except KeyError:
1597 except KeyError:
1598 if default is _notset:
1598 if default is _notset:
1599 raise
1599 raise
1600 return default
1600 return default
1601
1601
1602 def clear(self):
1602 def clear(self):
1603 n = self._head
1603 n = self._head
1604 while n.key is not _notset:
1604 while n.key is not _notset:
1605 self.totalcost -= n.cost
1605 self.totalcost -= n.cost
1606 n.markempty()
1606 n.markempty()
1607 n = n.next
1607 n = n.next
1608
1608
1609 self._cache.clear()
1609 self._cache.clear()
1610
1610
1611 def copy(self, capacity=None, maxcost=0):
1611 def copy(self, capacity=None, maxcost=0):
1612 """Create a new cache as a copy of the current one.
1612 """Create a new cache as a copy of the current one.
1613
1613
1614 By default, the new cache has the same capacity as the existing one.
1614 By default, the new cache has the same capacity as the existing one.
1615 But, the cache capacity can be changed as part of performing the
1615 But, the cache capacity can be changed as part of performing the
1616 copy.
1616 copy.
1617
1617
1618 Items in the copy have an insertion/access order matching this
1618 Items in the copy have an insertion/access order matching this
1619 instance.
1619 instance.
1620 """
1620 """
1621
1621
1622 capacity = capacity or self.capacity
1622 capacity = capacity or self.capacity
1623 maxcost = maxcost or self.maxcost
1623 maxcost = maxcost or self.maxcost
1624 result = lrucachedict(capacity, maxcost=maxcost)
1624 result = lrucachedict(capacity, maxcost=maxcost)
1625
1625
1626 # We copy entries by iterating in oldest-to-newest order so the copy
1626 # We copy entries by iterating in oldest-to-newest order so the copy
1627 # has the correct ordering.
1627 # has the correct ordering.
1628
1628
1629 # Find the first non-empty entry.
1629 # Find the first non-empty entry.
1630 n = self._head.prev
1630 n = self._head.prev
1631 while n.key is _notset and n is not self._head:
1631 while n.key is _notset and n is not self._head:
1632 n = n.prev
1632 n = n.prev
1633
1633
1634 # We could potentially skip the first N items when decreasing capacity.
1634 # We could potentially skip the first N items when decreasing capacity.
1635 # But let's keep it simple unless it is a performance problem.
1635 # But let's keep it simple unless it is a performance problem.
1636 for i in range(len(self._cache)):
1636 for i in range(len(self._cache)):
1637 result.insert(n.key, n.value, cost=n.cost)
1637 result.insert(n.key, n.value, cost=n.cost)
1638 n = n.prev
1638 n = n.prev
1639
1639
1640 return result
1640 return result
1641
1641
1642 def popoldest(self):
1642 def popoldest(self):
1643 """Remove the oldest item from the cache.
1643 """Remove the oldest item from the cache.
1644
1644
1645 Returns the (key, value) describing the removed cache entry.
1645 Returns the (key, value) describing the removed cache entry.
1646 """
1646 """
1647 if not self._cache:
1647 if not self._cache:
1648 return
1648 return
1649
1649
1650 # Walk the linked list backwards starting at tail node until we hit
1650 # Walk the linked list backwards starting at tail node until we hit
1651 # a non-empty node.
1651 # a non-empty node.
1652 n = self._head.prev
1652 n = self._head.prev
1653
1653
1654 while n.key is _notset:
1654 while n.key is _notset:
1655 n = n.prev
1655 n = n.prev
1656
1656
1657 key, value = n.key, n.value
1657 key, value = n.key, n.value
1658
1658
1659 # And remove it from the cache and mark it as empty.
1659 # And remove it from the cache and mark it as empty.
1660 del self._cache[n.key]
1660 del self._cache[n.key]
1661 self.totalcost -= n.cost
1661 self.totalcost -= n.cost
1662 n.markempty()
1662 n.markempty()
1663
1663
1664 return key, value
1664 return key, value
1665
1665
1666 def _movetohead(self, node: _lrucachenode):
1666 def _movetohead(self, node: _lrucachenode):
1667 """Mark a node as the newest, making it the new head.
1667 """Mark a node as the newest, making it the new head.
1668
1668
1669 When a node is accessed, it becomes the freshest entry in the LRU
1669 When a node is accessed, it becomes the freshest entry in the LRU
1670 list, which is denoted by self._head.
1670 list, which is denoted by self._head.
1671
1671
1672 Visually, let's make ``N`` the new head node (* denotes head):
1672 Visually, let's make ``N`` the new head node (* denotes head):
1673
1673
1674 previous/oldest <-> head <-> next/next newest
1674 previous/oldest <-> head <-> next/next newest
1675
1675
1676 ----<->--- A* ---<->-----
1676 ----<->--- A* ---<->-----
1677 | |
1677 | |
1678 E <-> D <-> N <-> C <-> B
1678 E <-> D <-> N <-> C <-> B
1679
1679
1680 To:
1680 To:
1681
1681
1682 ----<->--- N* ---<->-----
1682 ----<->--- N* ---<->-----
1683 | |
1683 | |
1684 E <-> D <-> C <-> B <-> A
1684 E <-> D <-> C <-> B <-> A
1685
1685
1686 This requires the following moves:
1686 This requires the following moves:
1687
1687
1688 C.next = D (node.prev.next = node.next)
1688 C.next = D (node.prev.next = node.next)
1689 D.prev = C (node.next.prev = node.prev)
1689 D.prev = C (node.next.prev = node.prev)
1690 E.next = N (head.prev.next = node)
1690 E.next = N (head.prev.next = node)
1691 N.prev = E (node.prev = head.prev)
1691 N.prev = E (node.prev = head.prev)
1692 N.next = A (node.next = head)
1692 N.next = A (node.next = head)
1693 A.prev = N (head.prev = node)
1693 A.prev = N (head.prev = node)
1694 """
1694 """
1695 head = self._head
1695 head = self._head
1696 # C.next = D
1696 # C.next = D
1697 node.prev.next = node.next
1697 node.prev.next = node.next
1698 # D.prev = C
1698 # D.prev = C
1699 node.next.prev = node.prev
1699 node.next.prev = node.prev
1700 # N.prev = E
1700 # N.prev = E
1701 node.prev = head.prev
1701 node.prev = head.prev
1702 # N.next = A
1702 # N.next = A
1703 # It is tempting to do just "head" here, however if node is
1703 # It is tempting to do just "head" here, however if node is
1704 # adjacent to head, this will do bad things.
1704 # adjacent to head, this will do bad things.
1705 node.next = head.prev.next
1705 node.next = head.prev.next
1706 # E.next = N
1706 # E.next = N
1707 node.next.prev = node
1707 node.next.prev = node
1708 # A.prev = N
1708 # A.prev = N
1709 node.prev.next = node
1709 node.prev.next = node
1710
1710
1711 self._head = node
1711 self._head = node
1712
1712
1713 def _addcapacity(self) -> _lrucachenode:
1713 def _addcapacity(self) -> _lrucachenode:
1714 """Add a node to the circular linked list.
1714 """Add a node to the circular linked list.
1715
1715
1716 The new node is inserted before the head node.
1716 The new node is inserted before the head node.
1717 """
1717 """
1718 head = self._head
1718 head = self._head
1719 node = _lrucachenode()
1719 node = _lrucachenode()
1720 head.prev.next = node
1720 head.prev.next = node
1721 node.prev = head.prev
1721 node.prev = head.prev
1722 node.next = head
1722 node.next = head
1723 head.prev = node
1723 head.prev = node
1724 self._size += 1
1724 self._size += 1
1725 return node
1725 return node
1726
1726
1727 def _enforcecostlimit(self):
1727 def _enforcecostlimit(self):
1728 # This should run after an insertion. It should only be called if total
1728 # This should run after an insertion. It should only be called if total
1729 # cost limits are being enforced.
1729 # cost limits are being enforced.
1730 # The most recently inserted node is never evicted.
1730 # The most recently inserted node is never evicted.
1731 if len(self) <= 1 or self.totalcost <= self.maxcost:
1731 if len(self) <= 1 or self.totalcost <= self.maxcost:
1732 return
1732 return
1733
1733
1734 # This is logically equivalent to calling popoldest() until we
1734 # This is logically equivalent to calling popoldest() until we
1735 # free up enough cost. We don't do that since popoldest() needs
1735 # free up enough cost. We don't do that since popoldest() needs
1736 # to walk the linked list and doing this in a loop would be
1736 # to walk the linked list and doing this in a loop would be
1737 # quadratic. So we find the first non-empty node and then
1737 # quadratic. So we find the first non-empty node and then
1738 # walk nodes until we free up enough capacity.
1738 # walk nodes until we free up enough capacity.
1739 #
1739 #
1740 # If we only removed the minimum number of nodes to free enough
1740 # If we only removed the minimum number of nodes to free enough
1741 # cost at insert time, chances are high that the next insert would
1741 # cost at insert time, chances are high that the next insert would
1742 # also require pruning. This would effectively constitute quadratic
1742 # also require pruning. This would effectively constitute quadratic
1743 # behavior for insert-heavy workloads. To mitigate this, we set a
1743 # behavior for insert-heavy workloads. To mitigate this, we set a
1744 # target cost that is a percentage of the max cost. This will tend
1744 # target cost that is a percentage of the max cost. This will tend
1745 # to free more nodes when the high water mark is reached, which
1745 # to free more nodes when the high water mark is reached, which
1746 # lowers the chances of needing to prune on the subsequent insert.
1746 # lowers the chances of needing to prune on the subsequent insert.
1747 targetcost = int(self.maxcost * 0.75)
1747 targetcost = int(self.maxcost * 0.75)
1748
1748
1749 n = self._head.prev
1749 n = self._head.prev
1750 while n.key is _notset:
1750 while n.key is _notset:
1751 n = n.prev
1751 n = n.prev
1752
1752
1753 while len(self) > 1 and self.totalcost > targetcost:
1753 while len(self) > 1 and self.totalcost > targetcost:
1754 del self._cache[n.key]
1754 del self._cache[n.key]
1755 self.totalcost -= n.cost
1755 self.totalcost -= n.cost
1756 n.markempty()
1756 n.markempty()
1757 n = n.prev
1757 n = n.prev
1758
1758
1759
1759
1760 def lrucachefunc(func):
1760 def lrucachefunc(func):
1761 '''cache most recent results of function calls'''
1761 '''cache most recent results of function calls'''
1762 cache = {}
1762 cache = {}
1763 order = collections.deque()
1763 order = collections.deque()
1764 if func.__code__.co_argcount == 1:
1764 if func.__code__.co_argcount == 1:
1765
1765
1766 def f(arg):
1766 def f(arg):
1767 if arg not in cache:
1767 if arg not in cache:
1768 if len(cache) > 20:
1768 if len(cache) > 20:
1769 del cache[order.popleft()]
1769 del cache[order.popleft()]
1770 cache[arg] = func(arg)
1770 cache[arg] = func(arg)
1771 else:
1771 else:
1772 order.remove(arg)
1772 order.remove(arg)
1773 order.append(arg)
1773 order.append(arg)
1774 return cache[arg]
1774 return cache[arg]
1775
1775
1776 else:
1776 else:
1777
1777
1778 def f(*args):
1778 def f(*args):
1779 if args not in cache:
1779 if args not in cache:
1780 if len(cache) > 20:
1780 if len(cache) > 20:
1781 del cache[order.popleft()]
1781 del cache[order.popleft()]
1782 cache[args] = func(*args)
1782 cache[args] = func(*args)
1783 else:
1783 else:
1784 order.remove(args)
1784 order.remove(args)
1785 order.append(args)
1785 order.append(args)
1786 return cache[args]
1786 return cache[args]
1787
1787
1788 return f
1788 return f
1789
1789
1790
1790
1791 class propertycache:
1791 class propertycache:
1792 def __init__(self, func):
1792 def __init__(self, func):
1793 self.func = func
1793 self.func = func
1794 self.name = func.__name__
1794 self.name = func.__name__
1795
1795
1796 def __get__(self, obj, type=None):
1796 def __get__(self, obj, type=None):
1797 result = self.func(obj)
1797 result = self.func(obj)
1798 self.cachevalue(obj, result)
1798 self.cachevalue(obj, result)
1799 return result
1799 return result
1800
1800
1801 def cachevalue(self, obj, value):
1801 def cachevalue(self, obj, value):
1802 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1802 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1803 obj.__dict__[self.name] = value
1803 obj.__dict__[self.name] = value
1804
1804
1805
1805
1806 def clearcachedproperty(obj, prop):
1806 def clearcachedproperty(obj, prop):
1807 '''clear a cached property value, if one has been set'''
1807 '''clear a cached property value, if one has been set'''
1808 prop = pycompat.sysstr(prop)
1808 prop = pycompat.sysstr(prop)
1809 if prop in obj.__dict__:
1809 if prop in obj.__dict__:
1810 del obj.__dict__[prop]
1810 del obj.__dict__[prop]
1811
1811
1812
1812
1813 def increasingchunks(source, min=1024, max=65536):
1813 def increasingchunks(source, min=1024, max=65536):
1814 """return no less than min bytes per chunk while data remains,
1814 """return no less than min bytes per chunk while data remains,
1815 doubling min after each chunk until it reaches max"""
1815 doubling min after each chunk until it reaches max"""
1816
1816
1817 def log2(x):
1817 def log2(x):
1818 if not x:
1818 if not x:
1819 return 0
1819 return 0
1820 i = 0
1820 i = 0
1821 while x:
1821 while x:
1822 x >>= 1
1822 x >>= 1
1823 i += 1
1823 i += 1
1824 return i - 1
1824 return i - 1
1825
1825
1826 buf = []
1826 buf = []
1827 blen = 0
1827 blen = 0
1828 for chunk in source:
1828 for chunk in source:
1829 buf.append(chunk)
1829 buf.append(chunk)
1830 blen += len(chunk)
1830 blen += len(chunk)
1831 if blen >= min:
1831 if blen >= min:
1832 if min < max:
1832 if min < max:
1833 min = min << 1
1833 min = min << 1
1834 nmin = 1 << log2(blen)
1834 nmin = 1 << log2(blen)
1835 if nmin > min:
1835 if nmin > min:
1836 min = nmin
1836 min = nmin
1837 if min > max:
1837 if min > max:
1838 min = max
1838 min = max
1839 yield b''.join(buf)
1839 yield b''.join(buf)
1840 blen = 0
1840 blen = 0
1841 buf = []
1841 buf = []
1842 if buf:
1842 if buf:
1843 yield b''.join(buf)
1843 yield b''.join(buf)
1844
1844
1845
1845
1846 def always(fn):
1846 def always(fn):
1847 return True
1847 return True
1848
1848
1849
1849
1850 def never(fn):
1850 def never(fn):
1851 return False
1851 return False
1852
1852
1853
1853
1854 def nogc(func=None) -> Any:
1854 def nogc(func=None) -> Any:
1855 """disable garbage collector
1855 """disable garbage collector
1856
1856
1857 Python's garbage collector triggers a GC each time a certain number of
1857 Python's garbage collector triggers a GC each time a certain number of
1858 container objects (the number being defined by gc.get_threshold()) are
1858 container objects (the number being defined by gc.get_threshold()) are
1859 allocated even when marked not to be tracked by the collector. Tracking has
1859 allocated even when marked not to be tracked by the collector. Tracking has
1860 no effect on when GCs are triggered, only on what objects the GC looks
1860 no effect on when GCs are triggered, only on what objects the GC looks
1861 into. As a workaround, disable GC while building complex (huge)
1861 into. As a workaround, disable GC while building complex (huge)
1862 containers.
1862 containers.
1863
1863
1864 This garbage collector issue have been fixed in 2.7. But it still affect
1864 This garbage collector issue have been fixed in 2.7. But it still affect
1865 CPython's performance.
1865 CPython's performance.
1866 """
1866 """
1867 if func is None:
1867 if func is None:
1868 return _nogc_context()
1868 return _nogc_context()
1869 else:
1869 else:
1870 return _nogc_decorator(func)
1870 return _nogc_decorator(func)
1871
1871
1872
1872
1873 @contextlib.contextmanager
1873 @contextlib.contextmanager
1874 def _nogc_context():
1874 def _nogc_context():
1875 gcenabled = gc.isenabled()
1875 gcenabled = gc.isenabled()
1876 gc.disable()
1876 gc.disable()
1877 try:
1877 try:
1878 yield
1878 yield
1879 finally:
1879 finally:
1880 if gcenabled:
1880 if gcenabled:
1881 gc.enable()
1881 gc.enable()
1882
1882
1883
1883
1884 def _nogc_decorator(func):
1884 def _nogc_decorator(func):
1885 def wrapper(*args, **kwargs):
1885 def wrapper(*args, **kwargs):
1886 with _nogc_context():
1886 with _nogc_context():
1887 return func(*args, **kwargs)
1887 return func(*args, **kwargs)
1888
1888
1889 return wrapper
1889 return wrapper
1890
1890
1891
1891
1892 if pycompat.ispypy:
1892 if pycompat.ispypy:
1893 # PyPy runs slower with gc disabled
1893 # PyPy runs slower with gc disabled
1894 nogc = lambda x: x
1894 nogc = lambda x: x
1895
1895
1896
1896
1897 def pathto(root: bytes, n1: bytes, n2: bytes) -> bytes:
1897 def pathto(root: bytes, n1: bytes, n2: bytes) -> bytes:
1898 """return the relative path from one place to another.
1898 """return the relative path from one place to another.
1899 root should use os.sep to separate directories
1899 root should use os.sep to separate directories
1900 n1 should use os.sep to separate directories
1900 n1 should use os.sep to separate directories
1901 n2 should use "/" to separate directories
1901 n2 should use "/" to separate directories
1902 returns an os.sep-separated path.
1902 returns an os.sep-separated path.
1903
1903
1904 If n1 is a relative path, it's assumed it's
1904 If n1 is a relative path, it's assumed it's
1905 relative to root.
1905 relative to root.
1906 n2 should always be relative to root.
1906 n2 should always be relative to root.
1907 """
1907 """
1908 if not n1:
1908 if not n1:
1909 return localpath(n2)
1909 return localpath(n2)
1910 if os.path.isabs(n1):
1910 if os.path.isabs(n1):
1911 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1911 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1912 return os.path.join(root, localpath(n2))
1912 return os.path.join(root, localpath(n2))
1913 n2 = b'/'.join((pconvert(root), n2))
1913 n2 = b'/'.join((pconvert(root), n2))
1914 a, b = splitpath(n1), n2.split(b'/')
1914 a, b = splitpath(n1), n2.split(b'/')
1915 a.reverse()
1915 a.reverse()
1916 b.reverse()
1916 b.reverse()
1917 while a and b and a[-1] == b[-1]:
1917 while a and b and a[-1] == b[-1]:
1918 a.pop()
1918 a.pop()
1919 b.pop()
1919 b.pop()
1920 b.reverse()
1920 b.reverse()
1921 return pycompat.ossep.join(([b'..'] * len(a)) + b) or b'.'
1921 return pycompat.ossep.join(([b'..'] * len(a)) + b) or b'.'
1922
1922
1923
1923
1924 def checksignature(func, depth=1):
1924 def checksignature(func, depth=1):
1925 '''wrap a function with code to check for calling errors'''
1925 '''wrap a function with code to check for calling errors'''
1926
1926
1927 def check(*args, **kwargs):
1927 def check(*args, **kwargs):
1928 try:
1928 try:
1929 return func(*args, **kwargs)
1929 return func(*args, **kwargs)
1930 except TypeError:
1930 except TypeError:
1931 if len(traceback.extract_tb(sys.exc_info()[2])) == depth:
1931 if len(traceback.extract_tb(sys.exc_info()[2])) == depth:
1932 raise error.SignatureError
1932 raise error.SignatureError
1933 raise
1933 raise
1934
1934
1935 return check
1935 return check
1936
1936
1937
1937
1938 # a whilelist of known filesystems where hardlink works reliably
1938 # a whilelist of known filesystems where hardlink works reliably
1939 _hardlinkfswhitelist = {
1939 _hardlinkfswhitelist = {
1940 b'apfs',
1940 b'apfs',
1941 b'btrfs',
1941 b'btrfs',
1942 b'ext2',
1942 b'ext2',
1943 b'ext3',
1943 b'ext3',
1944 b'ext4',
1944 b'ext4',
1945 b'hfs',
1945 b'hfs',
1946 b'jfs',
1946 b'jfs',
1947 b'NTFS',
1947 b'NTFS',
1948 b'reiserfs',
1948 b'reiserfs',
1949 b'tmpfs',
1949 b'tmpfs',
1950 b'ufs',
1950 b'ufs',
1951 b'xfs',
1951 b'xfs',
1952 b'zfs',
1952 b'zfs',
1953 }
1953 }
1954
1954
1955
1955
1956 def copyfile(
1956 def copyfile(
1957 src,
1957 src,
1958 dest,
1958 dest,
1959 hardlink=False,
1959 hardlink=False,
1960 copystat=False,
1960 copystat=False,
1961 checkambig=False,
1961 checkambig=False,
1962 nb_bytes=None,
1962 nb_bytes=None,
1963 no_hardlink_cb=None,
1963 no_hardlink_cb=None,
1964 check_fs_hardlink=True,
1964 check_fs_hardlink=True,
1965 ):
1965 ):
1966 """copy a file, preserving mode and optionally other stat info like
1966 """copy a file, preserving mode and optionally other stat info like
1967 atime/mtime
1967 atime/mtime
1968
1968
1969 checkambig argument is used with filestat, and is useful only if
1969 checkambig argument is used with filestat, and is useful only if
1970 destination file is guarded by any lock (e.g. repo.lock or
1970 destination file is guarded by any lock (e.g. repo.lock or
1971 repo.wlock).
1971 repo.wlock).
1972
1972
1973 copystat and checkambig should be exclusive.
1973 copystat and checkambig should be exclusive.
1974
1974
1975 nb_bytes: if set only copy the first `nb_bytes` of the source file.
1975 nb_bytes: if set only copy the first `nb_bytes` of the source file.
1976 """
1976 """
1977 assert not (copystat and checkambig)
1977 assert not (copystat and checkambig)
1978 oldstat = None
1978 oldstat = None
1979 if os.path.lexists(dest):
1979 if os.path.lexists(dest):
1980 if checkambig:
1980 if checkambig:
1981 oldstat = checkambig and filestat.frompath(dest)
1981 oldstat = checkambig and filestat.frompath(dest)
1982 unlink(dest)
1982 unlink(dest)
1983 if hardlink and check_fs_hardlink:
1983 if hardlink and check_fs_hardlink:
1984 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1984 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1985 # unless we are confident that dest is on a whitelisted filesystem.
1985 # unless we are confident that dest is on a whitelisted filesystem.
1986 try:
1986 try:
1987 fstype = getfstype(os.path.dirname(dest))
1987 fstype = getfstype(os.path.dirname(dest))
1988 except OSError:
1988 except OSError:
1989 fstype = None
1989 fstype = None
1990 if fstype not in _hardlinkfswhitelist:
1990 if fstype not in _hardlinkfswhitelist:
1991 if no_hardlink_cb is not None:
1991 if no_hardlink_cb is not None:
1992 no_hardlink_cb()
1992 no_hardlink_cb()
1993 hardlink = False
1993 hardlink = False
1994 if hardlink:
1994 if hardlink:
1995 try:
1995 try:
1996 oslink(src, dest)
1996 oslink(src, dest)
1997 if nb_bytes is not None:
1997 if nb_bytes is not None:
1998 m = "the `nb_bytes` argument is incompatible with `hardlink`"
1998 m = "the `nb_bytes` argument is incompatible with `hardlink`"
1999 raise error.ProgrammingError(m)
1999 raise error.ProgrammingError(m)
2000 return
2000 return
2001 except (IOError, OSError) as exc:
2001 except (IOError, OSError) as exc:
2002 if exc.errno != errno.EEXIST and no_hardlink_cb is not None:
2002 if exc.errno != errno.EEXIST and no_hardlink_cb is not None:
2003 no_hardlink_cb()
2003 no_hardlink_cb()
2004 # fall back to normal copy
2004 # fall back to normal copy
2005 if os.path.islink(src):
2005 if os.path.islink(src):
2006 os.symlink(os.readlink(src), dest)
2006 os.symlink(os.readlink(src), dest)
2007 # copytime is ignored for symlinks, but in general copytime isn't needed
2007 # copytime is ignored for symlinks, but in general copytime isn't needed
2008 # for them anyway
2008 # for them anyway
2009 if nb_bytes is not None:
2009 if nb_bytes is not None:
2010 m = "cannot use `nb_bytes` on a symlink"
2010 m = "cannot use `nb_bytes` on a symlink"
2011 raise error.ProgrammingError(m)
2011 raise error.ProgrammingError(m)
2012 else:
2012 else:
2013 try:
2013 try:
2014 shutil.copyfile(src, dest)
2014 shutil.copyfile(src, dest)
2015 if copystat:
2015 if copystat:
2016 # copystat also copies mode
2016 # copystat also copies mode
2017 shutil.copystat(src, dest)
2017 shutil.copystat(src, dest)
2018 else:
2018 else:
2019 shutil.copymode(src, dest)
2019 shutil.copymode(src, dest)
2020 if oldstat and oldstat.stat:
2020 if oldstat and oldstat.stat:
2021 newstat = filestat.frompath(dest)
2021 newstat = filestat.frompath(dest)
2022 if newstat.isambig(oldstat):
2022 if newstat.isambig(oldstat):
2023 # stat of copied file is ambiguous to original one
2023 # stat of copied file is ambiguous to original one
2024 advanced = (
2024 advanced = (
2025 oldstat.stat[stat.ST_MTIME] + 1
2025 oldstat.stat[stat.ST_MTIME] + 1
2026 ) & 0x7FFFFFFF
2026 ) & 0x7FFFFFFF
2027 os.utime(dest, (advanced, advanced))
2027 os.utime(dest, (advanced, advanced))
2028 # We could do something smarter using `copy_file_range` call or similar
2028 # We could do something smarter using `copy_file_range` call or similar
2029 if nb_bytes is not None:
2029 if nb_bytes is not None:
2030 with open(dest, mode='r+') as f:
2030 with open(dest, mode='r+') as f:
2031 f.truncate(nb_bytes)
2031 f.truncate(nb_bytes)
2032 except shutil.Error as inst:
2032 except shutil.Error as inst:
2033 raise error.Abort(stringutil.forcebytestr(inst))
2033 raise error.Abort(stringutil.forcebytestr(inst))
2034
2034
2035
2035
2036 def copyfiles(src, dst, hardlink=None, progress=None):
2036 def copyfiles(src, dst, hardlink=None, progress=None):
2037 """Copy a directory tree using hardlinks if possible."""
2037 """Copy a directory tree using hardlinks if possible."""
2038 num = 0
2038 num = 0
2039
2039
2040 def settopic():
2040 def settopic():
2041 if progress:
2041 if progress:
2042 progress.topic = _(b'linking') if hardlink else _(b'copying')
2042 progress.topic = _(b'linking') if hardlink else _(b'copying')
2043
2043
2044 if os.path.isdir(src):
2044 if os.path.isdir(src):
2045 if hardlink is None:
2045 if hardlink is None:
2046 hardlink = (
2046 hardlink = (
2047 os.stat(src).st_dev == os.stat(os.path.dirname(dst)).st_dev
2047 os.stat(src).st_dev == os.stat(os.path.dirname(dst)).st_dev
2048 )
2048 )
2049 settopic()
2049 settopic()
2050 os.mkdir(dst)
2050 os.mkdir(dst)
2051 for name, kind in listdir(src):
2051 for name, kind in listdir(src):
2052 srcname = os.path.join(src, name)
2052 srcname = os.path.join(src, name)
2053 dstname = os.path.join(dst, name)
2053 dstname = os.path.join(dst, name)
2054 hardlink, n = copyfiles(srcname, dstname, hardlink, progress)
2054 hardlink, n = copyfiles(srcname, dstname, hardlink, progress)
2055 num += n
2055 num += n
2056 else:
2056 else:
2057 if hardlink is None:
2057 if hardlink is None:
2058 hardlink = (
2058 hardlink = (
2059 os.stat(os.path.dirname(src)).st_dev
2059 os.stat(os.path.dirname(src)).st_dev
2060 == os.stat(os.path.dirname(dst)).st_dev
2060 == os.stat(os.path.dirname(dst)).st_dev
2061 )
2061 )
2062 settopic()
2062 settopic()
2063
2063
2064 if hardlink:
2064 if hardlink:
2065 try:
2065 try:
2066 oslink(src, dst)
2066 oslink(src, dst)
2067 except (IOError, OSError) as exc:
2067 except (IOError, OSError) as exc:
2068 if exc.errno != errno.EEXIST:
2068 if exc.errno != errno.EEXIST:
2069 hardlink = False
2069 hardlink = False
2070 # XXX maybe try to relink if the file exist ?
2070 # XXX maybe try to relink if the file exist ?
2071 shutil.copy(src, dst)
2071 shutil.copy(src, dst)
2072 else:
2072 else:
2073 shutil.copy(src, dst)
2073 shutil.copy(src, dst)
2074 num += 1
2074 num += 1
2075 if progress:
2075 if progress:
2076 progress.increment()
2076 progress.increment()
2077
2077
2078 return hardlink, num
2078 return hardlink, num
2079
2079
2080
2080
2081 _winreservednames = {
2081 _winreservednames = {
2082 b'con',
2082 b'con',
2083 b'prn',
2083 b'prn',
2084 b'aux',
2084 b'aux',
2085 b'nul',
2085 b'nul',
2086 b'com1',
2086 b'com1',
2087 b'com2',
2087 b'com2',
2088 b'com3',
2088 b'com3',
2089 b'com4',
2089 b'com4',
2090 b'com5',
2090 b'com5',
2091 b'com6',
2091 b'com6',
2092 b'com7',
2092 b'com7',
2093 b'com8',
2093 b'com8',
2094 b'com9',
2094 b'com9',
2095 b'lpt1',
2095 b'lpt1',
2096 b'lpt2',
2096 b'lpt2',
2097 b'lpt3',
2097 b'lpt3',
2098 b'lpt4',
2098 b'lpt4',
2099 b'lpt5',
2099 b'lpt5',
2100 b'lpt6',
2100 b'lpt6',
2101 b'lpt7',
2101 b'lpt7',
2102 b'lpt8',
2102 b'lpt8',
2103 b'lpt9',
2103 b'lpt9',
2104 }
2104 }
2105 _winreservedchars = b':*?"<>|'
2105 _winreservedchars = b':*?"<>|'
2106
2106
2107
2107
2108 def checkwinfilename(path: bytes) -> Optional[bytes]:
2108 def checkwinfilename(path: bytes) -> Optional[bytes]:
2109 r"""Check that the base-relative path is a valid filename on Windows.
2109 r"""Check that the base-relative path is a valid filename on Windows.
2110 Returns None if the path is ok, or a UI string describing the problem.
2110 Returns None if the path is ok, or a UI string describing the problem.
2111
2111
2112 >>> checkwinfilename(b"just/a/normal/path")
2112 >>> checkwinfilename(b"just/a/normal/path")
2113 >>> checkwinfilename(b"foo/bar/con.xml")
2113 >>> checkwinfilename(b"foo/bar/con.xml")
2114 "filename contains 'con', which is reserved on Windows"
2114 "filename contains 'con', which is reserved on Windows"
2115 >>> checkwinfilename(b"foo/con.xml/bar")
2115 >>> checkwinfilename(b"foo/con.xml/bar")
2116 "filename contains 'con', which is reserved on Windows"
2116 "filename contains 'con', which is reserved on Windows"
2117 >>> checkwinfilename(b"foo/bar/xml.con")
2117 >>> checkwinfilename(b"foo/bar/xml.con")
2118 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
2118 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
2119 "filename contains 'AUX', which is reserved on Windows"
2119 "filename contains 'AUX', which is reserved on Windows"
2120 >>> checkwinfilename(b"foo/bar/bla:.txt")
2120 >>> checkwinfilename(b"foo/bar/bla:.txt")
2121 "filename contains ':', which is reserved on Windows"
2121 "filename contains ':', which is reserved on Windows"
2122 >>> checkwinfilename(b"foo/bar/b\07la.txt")
2122 >>> checkwinfilename(b"foo/bar/b\07la.txt")
2123 "filename contains '\\x07', which is invalid on Windows"
2123 "filename contains '\\x07', which is invalid on Windows"
2124 >>> checkwinfilename(b"foo/bar/bla ")
2124 >>> checkwinfilename(b"foo/bar/bla ")
2125 "filename ends with ' ', which is not allowed on Windows"
2125 "filename ends with ' ', which is not allowed on Windows"
2126 >>> checkwinfilename(b"../bar")
2126 >>> checkwinfilename(b"../bar")
2127 >>> checkwinfilename(b"foo\\")
2127 >>> checkwinfilename(b"foo\\")
2128 "filename ends with '\\', which is invalid on Windows"
2128 "filename ends with '\\', which is invalid on Windows"
2129 >>> checkwinfilename(b"foo\\/bar")
2129 >>> checkwinfilename(b"foo\\/bar")
2130 "directory name ends with '\\', which is invalid on Windows"
2130 "directory name ends with '\\', which is invalid on Windows"
2131 """
2131 """
2132 if path.endswith(b'\\'):
2132 if path.endswith(b'\\'):
2133 return _(b"filename ends with '\\', which is invalid on Windows")
2133 return _(b"filename ends with '\\', which is invalid on Windows")
2134 if b'\\/' in path:
2134 if b'\\/' in path:
2135 return _(b"directory name ends with '\\', which is invalid on Windows")
2135 return _(b"directory name ends with '\\', which is invalid on Windows")
2136 for n in path.replace(b'\\', b'/').split(b'/'):
2136 for n in path.replace(b'\\', b'/').split(b'/'):
2137 if not n:
2137 if not n:
2138 continue
2138 continue
2139 for c in _filenamebytestr(n):
2139 for c in _filenamebytestr(n):
2140 if c in _winreservedchars:
2140 if c in _winreservedchars:
2141 return (
2141 return (
2142 _(
2142 _(
2143 b"filename contains '%s', which is reserved "
2143 b"filename contains '%s', which is reserved "
2144 b"on Windows"
2144 b"on Windows"
2145 )
2145 )
2146 % c
2146 % c
2147 )
2147 )
2148 if ord(c) <= 31:
2148 if ord(c) <= 31:
2149 return _(
2149 return _(
2150 b"filename contains '%s', which is invalid on Windows"
2150 b"filename contains '%s', which is invalid on Windows"
2151 ) % stringutil.escapestr(c)
2151 ) % stringutil.escapestr(c)
2152 base = n.split(b'.')[0]
2152 base = n.split(b'.')[0]
2153 if base and base.lower() in _winreservednames:
2153 if base and base.lower() in _winreservednames:
2154 return (
2154 return (
2155 _(b"filename contains '%s', which is reserved on Windows")
2155 _(b"filename contains '%s', which is reserved on Windows")
2156 % base
2156 % base
2157 )
2157 )
2158 t = n[-1:]
2158 t = n[-1:]
2159 if t in b'. ' and n not in b'..':
2159 if t in b'. ' and n not in b'..':
2160 return (
2160 return (
2161 _(
2161 _(
2162 b"filename ends with '%s', which is not allowed "
2162 b"filename ends with '%s', which is not allowed "
2163 b"on Windows"
2163 b"on Windows"
2164 )
2164 )
2165 % t
2165 % t
2166 )
2166 )
2167
2167
2168
2168
2169 timer = getattr(time, "perf_counter", None)
2169 timer = getattr(time, "perf_counter", None)
2170
2170
2171 if pycompat.iswindows:
2171 if pycompat.iswindows:
2172 checkosfilename = checkwinfilename
2172 checkosfilename = checkwinfilename
2173 if not timer:
2173 if not timer:
2174 timer = time.clock # pytype: disable=module-attr
2174 timer = time.clock # pytype: disable=module-attr
2175 else:
2175 else:
2176 # mercurial.windows doesn't have platform.checkosfilename
2176 # mercurial.windows doesn't have platform.checkosfilename
2177 checkosfilename = platform.checkosfilename # pytype: disable=module-attr
2177 checkosfilename = platform.checkosfilename # pytype: disable=module-attr
2178 if not timer:
2178 if not timer:
2179 timer = time.time
2179 timer = time.time
2180
2180
2181
2181
2182 def makelock(info, pathname):
2182 def makelock(info, pathname):
2183 """Create a lock file atomically if possible
2183 """Create a lock file atomically if possible
2184
2184
2185 This may leave a stale lock file if symlink isn't supported and signal
2185 This may leave a stale lock file if symlink isn't supported and signal
2186 interrupt is enabled.
2186 interrupt is enabled.
2187 """
2187 """
2188 try:
2188 try:
2189 return os.symlink(info, pathname)
2189 return os.symlink(info, pathname)
2190 except OSError as why:
2190 except OSError as why:
2191 if why.errno == errno.EEXIST:
2191 if why.errno == errno.EEXIST:
2192 raise
2192 raise
2193 except AttributeError: # no symlink in os
2193 except AttributeError: # no symlink in os
2194 pass
2194 pass
2195
2195
2196 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
2196 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
2197 ld = os.open(pathname, flags)
2197 ld = os.open(pathname, flags)
2198 os.write(ld, info)
2198 os.write(ld, info)
2199 os.close(ld)
2199 os.close(ld)
2200
2200
2201
2201
2202 def readlock(pathname: bytes) -> bytes:
2202 def readlock(pathname: bytes) -> bytes:
2203 try:
2203 try:
2204 return readlink(pathname)
2204 return readlink(pathname)
2205 except OSError as why:
2205 except OSError as why:
2206 if why.errno not in (errno.EINVAL, errno.ENOSYS):
2206 if why.errno not in (errno.EINVAL, errno.ENOSYS):
2207 raise
2207 raise
2208 except AttributeError: # no symlink in os
2208 except AttributeError: # no symlink in os
2209 pass
2209 pass
2210 with posixfile(pathname, b'rb') as fp:
2210 with posixfile(pathname, b'rb') as fp:
2211 return fp.read()
2211 return fp.read()
2212
2212
2213
2213
2214 def fstat(fp):
2214 def fstat(fp):
2215 '''stat file object that may not have fileno method.'''
2215 '''stat file object that may not have fileno method.'''
2216 try:
2216 try:
2217 return os.fstat(fp.fileno())
2217 return os.fstat(fp.fileno())
2218 except AttributeError:
2218 except AttributeError:
2219 return os.stat(fp.name)
2219 return os.stat(fp.name)
2220
2220
2221
2221
2222 # File system features
2222 # File system features
2223
2223
2224
2224
2225 def fscasesensitive(path: bytes) -> bool:
2225 def fscasesensitive(path: bytes) -> bool:
2226 """
2226 """
2227 Return true if the given path is on a case-sensitive filesystem
2227 Return true if the given path is on a case-sensitive filesystem
2228
2228
2229 Requires a path (like /foo/.hg) ending with a foldable final
2229 Requires a path (like /foo/.hg) ending with a foldable final
2230 directory component.
2230 directory component.
2231 """
2231 """
2232 s1 = os.lstat(path)
2232 s1 = os.lstat(path)
2233 d, b = os.path.split(path)
2233 d, b = os.path.split(path)
2234 b2 = b.upper()
2234 b2 = b.upper()
2235 if b == b2:
2235 if b == b2:
2236 b2 = b.lower()
2236 b2 = b.lower()
2237 if b == b2:
2237 if b == b2:
2238 return True # no evidence against case sensitivity
2238 return True # no evidence against case sensitivity
2239 p2 = os.path.join(d, b2)
2239 p2 = os.path.join(d, b2)
2240 try:
2240 try:
2241 s2 = os.lstat(p2)
2241 s2 = os.lstat(p2)
2242 if s2 == s1:
2242 if s2 == s1:
2243 return False
2243 return False
2244 return True
2244 return True
2245 except OSError:
2245 except OSError:
2246 return True
2246 return True
2247
2247
2248
2248
2249 _re2_input = lambda x: x
2249 _re2_input = lambda x: x
2250 # google-re2 will need to be tell to not output error on its own
2250 # google-re2 will need to be tell to not output error on its own
2251 _re2_options = None
2251 _re2_options = None
2252 try:
2252 try:
2253 import re2 # pytype: disable=import-error
2253 import re2 # pytype: disable=import-error
2254
2254
2255 _re2 = None
2255 _re2 = None
2256 except ImportError:
2256 except ImportError:
2257 _re2 = False
2257 _re2 = False
2258
2258
2259
2259
2260 def has_re2():
2260 def has_re2():
2261 """return True is re2 is available, False otherwise"""
2261 """return True is re2 is available, False otherwise"""
2262 if _re2 is None:
2262 if _re2 is None:
2263 _re._checkre2()
2263 _re._checkre2()
2264 return _re2
2264 return _re2
2265
2265
2266
2266
2267 class _re:
2267 class _re:
2268 @staticmethod
2268 @staticmethod
2269 def _checkre2():
2269 def _checkre2():
2270 global _re2
2270 global _re2
2271 global _re2_input
2271 global _re2_input
2272 global _re2_options
2272 global _re2_options
2273 if _re2 is not None:
2273 if _re2 is not None:
2274 # we already have the answer
2274 # we already have the answer
2275 return
2275 return
2276
2276
2277 check_pattern = br'\[([^\[]+)\]'
2277 check_pattern = br'\[([^\[]+)\]'
2278 check_input = b'[ui]'
2278 check_input = b'[ui]'
2279 try:
2279 try:
2280 # check if match works, see issue3964
2280 # check if match works, see issue3964
2281 _re2 = bool(re2.match(check_pattern, check_input))
2281 _re2 = bool(re2.match(check_pattern, check_input))
2282 except ImportError:
2282 except ImportError:
2283 _re2 = False
2283 _re2 = False
2284 except TypeError:
2284 except TypeError:
2285 # the `pyre-2` project provides a re2 module that accept bytes
2285 # the `pyre-2` project provides a re2 module that accept bytes
2286 # the `fb-re2` project provides a re2 module that acccept sysstr
2286 # the `fb-re2` project provides a re2 module that acccept sysstr
2287 check_pattern = pycompat.sysstr(check_pattern)
2287 check_pattern = pycompat.sysstr(check_pattern)
2288 check_input = pycompat.sysstr(check_input)
2288 check_input = pycompat.sysstr(check_input)
2289 _re2 = bool(re2.match(check_pattern, check_input))
2289 _re2 = bool(re2.match(check_pattern, check_input))
2290 _re2_input = pycompat.sysstr
2290 _re2_input = pycompat.sysstr
2291 try:
2291 try:
2292 quiet = re2.Options()
2292 quiet = re2.Options()
2293 quiet.log_errors = False
2293 quiet.log_errors = False
2294 _re2_options = quiet
2294 _re2_options = quiet
2295 except AttributeError:
2295 except AttributeError:
2296 pass
2296 pass
2297
2297
2298 def compile(self, pat, flags=0):
2298 def compile(self, pat, flags=0):
2299 """Compile a regular expression, using re2 if possible
2299 """Compile a regular expression, using re2 if possible
2300
2300
2301 For best performance, use only re2-compatible regexp features. The
2301 For best performance, use only re2-compatible regexp features. The
2302 only flags from the re module that are re2-compatible are
2302 only flags from the re module that are re2-compatible are
2303 IGNORECASE and MULTILINE."""
2303 IGNORECASE and MULTILINE."""
2304 if _re2 is None:
2304 if _re2 is None:
2305 self._checkre2()
2305 self._checkre2()
2306 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
2306 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
2307 if flags & remod.IGNORECASE:
2307 if flags & remod.IGNORECASE:
2308 pat = b'(?i)' + pat
2308 pat = b'(?i)' + pat
2309 if flags & remod.MULTILINE:
2309 if flags & remod.MULTILINE:
2310 pat = b'(?m)' + pat
2310 pat = b'(?m)' + pat
2311 try:
2311 try:
2312 input_regex = _re2_input(pat)
2312 input_regex = _re2_input(pat)
2313 if _re2_options is not None:
2313 if _re2_options is not None:
2314 compiled = re2.compile(input_regex, options=_re2_options)
2314 compiled = re2.compile(input_regex, options=_re2_options)
2315 else:
2315 else:
2316 compiled = re2.compile(input_regex)
2316 compiled = re2.compile(input_regex)
2317 return compiled
2317 return compiled
2318 except re2.error:
2318 except re2.error:
2319 pass
2319 pass
2320 return remod.compile(pat, flags)
2320 return remod.compile(pat, flags)
2321
2321
2322 @propertycache
2322 @propertycache
2323 def escape(self):
2323 def escape(self):
2324 """Return the version of escape corresponding to self.compile.
2324 """Return the version of escape corresponding to self.compile.
2325
2325
2326 This is imperfect because whether re2 or re is used for a particular
2326 This is imperfect because whether re2 or re is used for a particular
2327 function depends on the flags, etc, but it's the best we can do.
2327 function depends on the flags, etc, but it's the best we can do.
2328 """
2328 """
2329 global _re2
2329 global _re2
2330 if _re2 is None:
2330 if _re2 is None:
2331 self._checkre2()
2331 self._checkre2()
2332 if _re2:
2332 if _re2:
2333 return re2.escape
2333 return re2.escape
2334 else:
2334 else:
2335 return remod.escape
2335 return remod.escape
2336
2336
2337
2337
2338 re = _re()
2338 re = _re()
2339
2339
2340 _fspathcache = {}
2340 _fspathcache = {}
2341
2341
2342
2342
2343 def fspath(name: bytes, root: bytes) -> bytes:
2343 def fspath(name: bytes, root: bytes) -> bytes:
2344 """Get name in the case stored in the filesystem
2344 """Get name in the case stored in the filesystem
2345
2345
2346 The name should be relative to root, and be normcase-ed for efficiency.
2346 The name should be relative to root, and be normcase-ed for efficiency.
2347
2347
2348 Note that this function is unnecessary, and should not be
2348 Note that this function is unnecessary, and should not be
2349 called, for case-sensitive filesystems (simply because it's expensive).
2349 called, for case-sensitive filesystems (simply because it's expensive).
2350
2350
2351 The root should be normcase-ed, too.
2351 The root should be normcase-ed, too.
2352 """
2352 """
2353
2353
2354 def _makefspathcacheentry(dir):
2354 def _makefspathcacheentry(dir):
2355 return {normcase(n): n for n in os.listdir(dir)}
2355 return {normcase(n): n for n in os.listdir(dir)}
2356
2356
2357 seps = pycompat.ossep
2357 seps = pycompat.ossep
2358 if pycompat.osaltsep:
2358 if pycompat.osaltsep:
2359 seps = seps + pycompat.osaltsep
2359 seps = seps + pycompat.osaltsep
2360 # Protect backslashes. This gets silly very quickly.
2360 # Protect backslashes. This gets silly very quickly.
2361 seps.replace(b'\\', b'\\\\')
2361 seps.replace(b'\\', b'\\\\')
2362 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
2362 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
2363 dir = os.path.normpath(root)
2363 dir = os.path.normpath(root)
2364 result = []
2364 result = []
2365 for part, sep in pattern.findall(name):
2365 for part, sep in pattern.findall(name):
2366 if sep:
2366 if sep:
2367 result.append(sep)
2367 result.append(sep)
2368 continue
2368 continue
2369
2369
2370 if dir not in _fspathcache:
2370 if dir not in _fspathcache:
2371 _fspathcache[dir] = _makefspathcacheentry(dir)
2371 _fspathcache[dir] = _makefspathcacheentry(dir)
2372 contents = _fspathcache[dir]
2372 contents = _fspathcache[dir]
2373
2373
2374 found = contents.get(part)
2374 found = contents.get(part)
2375 if not found:
2375 if not found:
2376 # retry "once per directory" per "dirstate.walk" which
2376 # retry "once per directory" per "dirstate.walk" which
2377 # may take place for each patches of "hg qpush", for example
2377 # may take place for each patches of "hg qpush", for example
2378 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
2378 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
2379 found = contents.get(part)
2379 found = contents.get(part)
2380
2380
2381 result.append(found or part)
2381 result.append(found or part)
2382 dir = os.path.join(dir, part)
2382 dir = os.path.join(dir, part)
2383
2383
2384 return b''.join(result)
2384 return b''.join(result)
2385
2385
2386
2386
2387 def checknlink(testfile: bytes) -> bool:
2387 def checknlink(testfile: bytes) -> bool:
2388 '''check whether hardlink count reporting works properly'''
2388 '''check whether hardlink count reporting works properly'''
2389
2389
2390 # testfile may be open, so we need a separate file for checking to
2390 # testfile may be open, so we need a separate file for checking to
2391 # work around issue2543 (or testfile may get lost on Samba shares)
2391 # work around issue2543 (or testfile may get lost on Samba shares)
2392 f1, f2, fp = None, None, None
2392 f1, f2, fp = None, None, None
2393 try:
2393 try:
2394 fd, f1 = pycompat.mkstemp(
2394 fd, f1 = pycompat.mkstemp(
2395 prefix=b'.%s-' % os.path.basename(testfile),
2395 prefix=b'.%s-' % os.path.basename(testfile),
2396 suffix=b'1~',
2396 suffix=b'1~',
2397 dir=os.path.dirname(testfile),
2397 dir=os.path.dirname(testfile),
2398 )
2398 )
2399 os.close(fd)
2399 os.close(fd)
2400 f2 = b'%s2~' % f1[:-2]
2400 f2 = b'%s2~' % f1[:-2]
2401
2401
2402 oslink(f1, f2)
2402 oslink(f1, f2)
2403 # nlinks() may behave differently for files on Windows shares if
2403 # nlinks() may behave differently for files on Windows shares if
2404 # the file is open.
2404 # the file is open.
2405 fp = posixfile(f2)
2405 fp = posixfile(f2)
2406 return nlinks(f2) > 1
2406 return nlinks(f2) > 1
2407 except OSError:
2407 except OSError:
2408 return False
2408 return False
2409 finally:
2409 finally:
2410 if fp is not None:
2410 if fp is not None:
2411 fp.close()
2411 fp.close()
2412 for f in (f1, f2):
2412 for f in (f1, f2):
2413 try:
2413 try:
2414 if f is not None:
2414 if f is not None:
2415 os.unlink(f)
2415 os.unlink(f)
2416 except OSError:
2416 except OSError:
2417 pass
2417 pass
2418
2418
2419
2419
2420 def endswithsep(path: bytes) -> bool:
2420 def endswithsep(path: bytes) -> bool:
2421 '''Check path ends with os.sep or os.altsep.'''
2421 '''Check path ends with os.sep or os.altsep.'''
2422 return bool( # help pytype
2422 return bool( # help pytype
2423 path.endswith(pycompat.ossep)
2423 path.endswith(pycompat.ossep)
2424 or pycompat.osaltsep
2424 or pycompat.osaltsep
2425 and path.endswith(pycompat.osaltsep)
2425 and path.endswith(pycompat.osaltsep)
2426 )
2426 )
2427
2427
2428
2428
2429 def splitpath(path: bytes) -> List[bytes]:
2429 def splitpath(path: bytes) -> List[bytes]:
2430 """Split path by os.sep.
2430 """Split path by os.sep.
2431 Note that this function does not use os.altsep because this is
2431 Note that this function does not use os.altsep because this is
2432 an alternative of simple "xxx.split(os.sep)".
2432 an alternative of simple "xxx.split(os.sep)".
2433 It is recommended to use os.path.normpath() before using this
2433 It is recommended to use os.path.normpath() before using this
2434 function if need."""
2434 function if need."""
2435 return path.split(pycompat.ossep)
2435 return path.split(pycompat.ossep)
2436
2436
2437
2437
2438 def mktempcopy(
2438 def mktempcopy(
2439 name: bytes,
2439 name: bytes,
2440 emptyok: bool = False,
2440 emptyok: bool = False,
2441 createmode: Optional[int] = None,
2441 createmode: Optional[int] = None,
2442 enforcewritable: bool = False,
2442 enforcewritable: bool = False,
2443 ) -> bytes:
2443 ) -> bytes:
2444 """Create a temporary file with the same contents from name
2444 """Create a temporary file with the same contents from name
2445
2445
2446 The permission bits are copied from the original file.
2446 The permission bits are copied from the original file.
2447
2447
2448 If the temporary file is going to be truncated immediately, you
2448 If the temporary file is going to be truncated immediately, you
2449 can use emptyok=True as an optimization.
2449 can use emptyok=True as an optimization.
2450
2450
2451 Returns the name of the temporary file.
2451 Returns the name of the temporary file.
2452 """
2452 """
2453 d, fn = os.path.split(name)
2453 d, fn = os.path.split(name)
2454 fd, temp = pycompat.mkstemp(prefix=b'.%s-' % fn, suffix=b'~', dir=d)
2454 fd, temp = pycompat.mkstemp(prefix=b'.%s-' % fn, suffix=b'~', dir=d)
2455 os.close(fd)
2455 os.close(fd)
2456 # Temporary files are created with mode 0600, which is usually not
2456 # Temporary files are created with mode 0600, which is usually not
2457 # what we want. If the original file already exists, just copy
2457 # what we want. If the original file already exists, just copy
2458 # its mode. Otherwise, manually obey umask.
2458 # its mode. Otherwise, manually obey umask.
2459 copymode(name, temp, createmode, enforcewritable)
2459 copymode(name, temp, createmode, enforcewritable)
2460
2460
2461 if emptyok:
2461 if emptyok:
2462 return temp
2462 return temp
2463 try:
2463 try:
2464 try:
2464 try:
2465 ifp = posixfile(name, b"rb")
2465 ifp = posixfile(name, b"rb")
2466 except IOError as inst:
2466 except IOError as inst:
2467 if inst.errno == errno.ENOENT:
2467 if inst.errno == errno.ENOENT:
2468 return temp
2468 return temp
2469 if not getattr(inst, 'filename', None):
2469 if not getattr(inst, 'filename', None):
2470 inst.filename = name
2470 inst.filename = name
2471 raise
2471 raise
2472 ofp = posixfile(temp, b"wb")
2472 ofp = posixfile(temp, b"wb")
2473 for chunk in filechunkiter(ifp):
2473 for chunk in filechunkiter(ifp):
2474 ofp.write(chunk)
2474 ofp.write(chunk)
2475 ifp.close()
2475 ifp.close()
2476 ofp.close()
2476 ofp.close()
2477 except: # re-raises
2477 except: # re-raises
2478 try:
2478 try:
2479 os.unlink(temp)
2479 os.unlink(temp)
2480 except OSError:
2480 except OSError:
2481 pass
2481 pass
2482 raise
2482 raise
2483 return temp
2483 return temp
2484
2484
2485
2485
2486 class filestat:
2486 class filestat:
2487 """help to exactly detect change of a file
2487 """help to exactly detect change of a file
2488
2488
2489 'stat' attribute is result of 'os.stat()' if specified 'path'
2489 'stat' attribute is result of 'os.stat()' if specified 'path'
2490 exists. Otherwise, it is None. This can avoid preparative
2490 exists. Otherwise, it is None. This can avoid preparative
2491 'exists()' examination on client side of this class.
2491 'exists()' examination on client side of this class.
2492 """
2492 """
2493
2493
2494 def __init__(self, stat):
2494 def __init__(self, stat):
2495 self.stat = stat
2495 self.stat = stat
2496
2496
2497 @classmethod
2497 @classmethod
2498 def frompath(cls, path):
2498 def frompath(cls, path):
2499 try:
2499 try:
2500 stat = os.stat(path)
2500 stat = os.stat(path)
2501 except FileNotFoundError:
2501 except FileNotFoundError:
2502 stat = None
2502 stat = None
2503 return cls(stat)
2503 return cls(stat)
2504
2504
2505 @classmethod
2505 @classmethod
2506 def fromfp(cls, fp):
2506 def fromfp(cls, fp):
2507 stat = os.fstat(fp.fileno())
2507 stat = os.fstat(fp.fileno())
2508 return cls(stat)
2508 return cls(stat)
2509
2509
2510 __hash__ = object.__hash__
2510 __hash__ = object.__hash__
2511
2511
2512 def __eq__(self, old):
2512 def __eq__(self, old):
2513 try:
2513 try:
2514 # if ambiguity between stat of new and old file is
2514 # if ambiguity between stat of new and old file is
2515 # avoided, comparison of size, ctime and mtime is enough
2515 # avoided, comparison of size, ctime and mtime is enough
2516 # to exactly detect change of a file regardless of platform
2516 # to exactly detect change of a file regardless of platform
2517 return (
2517 return (
2518 self.stat.st_size == old.stat.st_size
2518 self.stat.st_size == old.stat.st_size
2519 and self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME]
2519 and self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME]
2520 and self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME]
2520 and self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME]
2521 )
2521 )
2522 except AttributeError:
2522 except AttributeError:
2523 pass
2523 pass
2524 try:
2524 try:
2525 return self.stat is None and old.stat is None
2525 return self.stat is None and old.stat is None
2526 except AttributeError:
2526 except AttributeError:
2527 return False
2527 return False
2528
2528
2529 def isambig(self, old):
2529 def isambig(self, old):
2530 """Examine whether new (= self) stat is ambiguous against old one
2530 """Examine whether new (= self) stat is ambiguous against old one
2531
2531
2532 "S[N]" below means stat of a file at N-th change:
2532 "S[N]" below means stat of a file at N-th change:
2533
2533
2534 - S[n-1].ctime < S[n].ctime: can detect change of a file
2534 - S[n-1].ctime < S[n].ctime: can detect change of a file
2535 - S[n-1].ctime == S[n].ctime
2535 - S[n-1].ctime == S[n].ctime
2536 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
2536 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
2537 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
2537 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
2538 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
2538 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
2539 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
2539 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
2540
2540
2541 Case (*2) above means that a file was changed twice or more at
2541 Case (*2) above means that a file was changed twice or more at
2542 same time in sec (= S[n-1].ctime), and comparison of timestamp
2542 same time in sec (= S[n-1].ctime), and comparison of timestamp
2543 is ambiguous.
2543 is ambiguous.
2544
2544
2545 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
2545 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
2546 timestamp is ambiguous".
2546 timestamp is ambiguous".
2547
2547
2548 But advancing mtime only in case (*2) doesn't work as
2548 But advancing mtime only in case (*2) doesn't work as
2549 expected, because naturally advanced S[n].mtime in case (*1)
2549 expected, because naturally advanced S[n].mtime in case (*1)
2550 might be equal to manually advanced S[n-1 or earlier].mtime.
2550 might be equal to manually advanced S[n-1 or earlier].mtime.
2551
2551
2552 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
2552 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
2553 treated as ambiguous regardless of mtime, to avoid overlooking
2553 treated as ambiguous regardless of mtime, to avoid overlooking
2554 by confliction between such mtime.
2554 by confliction between such mtime.
2555
2555
2556 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2556 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2557 S[n].mtime", even if size of a file isn't changed.
2557 S[n].mtime", even if size of a file isn't changed.
2558 """
2558 """
2559 try:
2559 try:
2560 return self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME]
2560 return self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME]
2561 except AttributeError:
2561 except AttributeError:
2562 return False
2562 return False
2563
2563
2564 def avoidambig(self, path, old):
2564 def avoidambig(self, path, old):
2565 """Change file stat of specified path to avoid ambiguity
2565 """Change file stat of specified path to avoid ambiguity
2566
2566
2567 'old' should be previous filestat of 'path'.
2567 'old' should be previous filestat of 'path'.
2568
2568
2569 This skips avoiding ambiguity, if a process doesn't have
2569 This skips avoiding ambiguity, if a process doesn't have
2570 appropriate privileges for 'path'. This returns False in this
2570 appropriate privileges for 'path'. This returns False in this
2571 case.
2571 case.
2572
2572
2573 Otherwise, this returns True, as "ambiguity is avoided".
2573 Otherwise, this returns True, as "ambiguity is avoided".
2574 """
2574 """
2575 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7FFFFFFF
2575 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7FFFFFFF
2576 try:
2576 try:
2577 os.utime(path, (advanced, advanced))
2577 os.utime(path, (advanced, advanced))
2578 except PermissionError:
2578 except PermissionError:
2579 # utime() on the file created by another user causes EPERM,
2579 # utime() on the file created by another user causes EPERM,
2580 # if a process doesn't have appropriate privileges
2580 # if a process doesn't have appropriate privileges
2581 return False
2581 return False
2582 return True
2582 return True
2583
2583
2584 def __ne__(self, other):
2584 def __ne__(self, other):
2585 return not self == other
2585 return not self == other
2586
2586
2587
2587
2588 class atomictempfile:
2588 class atomictempfile:
2589 """writable file object that atomically updates a file
2589 """writable file object that atomically updates a file
2590
2590
2591 All writes will go to a temporary copy of the original file. Call
2591 All writes will go to a temporary copy of the original file. Call
2592 close() when you are done writing, and atomictempfile will rename
2592 close() when you are done writing, and atomictempfile will rename
2593 the temporary copy to the original name, making the changes
2593 the temporary copy to the original name, making the changes
2594 visible. If the object is destroyed without being closed, all your
2594 visible. If the object is destroyed without being closed, all your
2595 writes are discarded.
2595 writes are discarded.
2596
2596
2597 checkambig argument of constructor is used with filestat, and is
2597 checkambig argument of constructor is used with filestat, and is
2598 useful only if target file is guarded by any lock (e.g. repo.lock
2598 useful only if target file is guarded by any lock (e.g. repo.lock
2599 or repo.wlock).
2599 or repo.wlock).
2600 """
2600 """
2601
2601
2602 def __init__(self, name, mode=b'w+b', createmode=None, checkambig=False):
2602 def __init__(self, name, mode=b'w+b', createmode=None, checkambig=False):
2603 self.__name = name # permanent name
2603 self.__name = name # permanent name
2604 self._tempname = mktempcopy(
2604 self._tempname = mktempcopy(
2605 name,
2605 name,
2606 emptyok=(b'w' in mode),
2606 emptyok=(b'w' in mode),
2607 createmode=createmode,
2607 createmode=createmode,
2608 enforcewritable=(b'w' in mode),
2608 enforcewritable=(b'w' in mode),
2609 )
2609 )
2610
2610
2611 self._fp = posixfile(self._tempname, mode)
2611 self._fp = posixfile(self._tempname, mode)
2612 self._checkambig = checkambig
2612 self._checkambig = checkambig
2613
2613
2614 # delegated methods
2614 # delegated methods
2615 self.read = self._fp.read
2615 self.read = self._fp.read
2616 self.write = self._fp.write
2616 self.write = self._fp.write
2617 self.writelines = self._fp.writelines
2617 self.writelines = self._fp.writelines
2618 self.seek = self._fp.seek
2618 self.seek = self._fp.seek
2619 self.tell = self._fp.tell
2619 self.tell = self._fp.tell
2620 self.fileno = self._fp.fileno
2620 self.fileno = self._fp.fileno
2621
2621
2622 def close(self):
2622 def close(self):
2623 if not self._fp.closed:
2623 if not self._fp.closed:
2624 self._fp.close()
2624 self._fp.close()
2625 filename = localpath(self.__name)
2625 filename = localpath(self.__name)
2626 oldstat = self._checkambig and filestat.frompath(filename)
2626 oldstat = self._checkambig and filestat.frompath(filename)
2627 if oldstat and oldstat.stat:
2627 if oldstat and oldstat.stat:
2628 rename(self._tempname, filename)
2628 rename(self._tempname, filename)
2629 newstat = filestat.frompath(filename)
2629 newstat = filestat.frompath(filename)
2630 if newstat.isambig(oldstat):
2630 if newstat.isambig(oldstat):
2631 # stat of changed file is ambiguous to original one
2631 # stat of changed file is ambiguous to original one
2632 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7FFFFFFF
2632 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7FFFFFFF
2633 os.utime(filename, (advanced, advanced))
2633 os.utime(filename, (advanced, advanced))
2634 else:
2634 else:
2635 rename(self._tempname, filename)
2635 rename(self._tempname, filename)
2636
2636
2637 def discard(self):
2637 def discard(self):
2638 if not self._fp.closed:
2638 if not self._fp.closed:
2639 try:
2639 try:
2640 os.unlink(self._tempname)
2640 os.unlink(self._tempname)
2641 except OSError:
2641 except OSError:
2642 pass
2642 pass
2643 self._fp.close()
2643 self._fp.close()
2644
2644
2645 def __del__(self):
2645 def __del__(self):
2646 if hasattr(self, '_fp'): # constructor actually did something
2646 if hasattr(self, '_fp'): # constructor actually did something
2647 self.discard()
2647 self.discard()
2648
2648
2649 def __enter__(self):
2649 def __enter__(self):
2650 return self
2650 return self
2651
2651
2652 def __exit__(self, exctype, excvalue, traceback):
2652 def __exit__(self, exctype, excvalue, traceback):
2653 if exctype is not None:
2653 if exctype is not None:
2654 self.discard()
2654 self.discard()
2655 else:
2655 else:
2656 self.close()
2656 self.close()
2657
2657
2658
2658
2659 def tryrmdir(f):
2659 def tryrmdir(f):
2660 try:
2660 try:
2661 removedirs(f)
2661 removedirs(f)
2662 except OSError as e:
2662 except OSError as e:
2663 if e.errno != errno.ENOENT and e.errno != errno.ENOTEMPTY:
2663 if e.errno != errno.ENOENT and e.errno != errno.ENOTEMPTY:
2664 raise
2664 raise
2665
2665
2666
2666
2667 def unlinkpath(
2667 def unlinkpath(
2668 f: bytes, ignoremissing: bool = False, rmdir: bool = True
2668 f: bytes, ignoremissing: bool = False, rmdir: bool = True
2669 ) -> None:
2669 ) -> None:
2670 """unlink and remove the directory if it is empty"""
2670 """unlink and remove the directory if it is empty"""
2671 if ignoremissing:
2671 if ignoremissing:
2672 tryunlink(f)
2672 tryunlink(f)
2673 else:
2673 else:
2674 unlink(f)
2674 unlink(f)
2675 if rmdir:
2675 if rmdir:
2676 # try removing directories that might now be empty
2676 # try removing directories that might now be empty
2677 try:
2677 try:
2678 removedirs(os.path.dirname(f))
2678 removedirs(os.path.dirname(f))
2679 except OSError:
2679 except OSError:
2680 pass
2680 pass
2681
2681
2682
2682
2683 def tryunlink(f: bytes) -> bool:
2683 def tryunlink(f: bytes) -> bool:
2684 """Attempt to remove a file, ignoring FileNotFoundError.
2684 """Attempt to remove a file, ignoring FileNotFoundError.
2685
2685
2686 Returns False in case the file did not exit, True otherwise
2686 Returns False in case the file did not exit, True otherwise
2687 """
2687 """
2688 try:
2688 try:
2689 unlink(f)
2689 unlink(f)
2690 return True
2690 return True
2691 except FileNotFoundError:
2691 except FileNotFoundError:
2692 return False
2692 return False
2693
2693
2694
2694
2695 def makedirs(
2695 def makedirs(
2696 name: bytes, mode: Optional[int] = None, notindexed: bool = False
2696 name: bytes, mode: Optional[int] = None, notindexed: bool = False
2697 ) -> None:
2697 ) -> None:
2698 """recursive directory creation with parent mode inheritance
2698 """recursive directory creation with parent mode inheritance
2699
2699
2700 Newly created directories are marked as "not to be indexed by
2700 Newly created directories are marked as "not to be indexed by
2701 the content indexing service", if ``notindexed`` is specified
2701 the content indexing service", if ``notindexed`` is specified
2702 for "write" mode access.
2702 for "write" mode access.
2703 """
2703 """
2704 try:
2704 try:
2705 makedir(name, notindexed)
2705 makedir(name, notindexed)
2706 except OSError as err:
2706 except OSError as err:
2707 if err.errno == errno.EEXIST:
2707 if err.errno == errno.EEXIST:
2708 return
2708 return
2709 if err.errno != errno.ENOENT or not name:
2709 if err.errno != errno.ENOENT or not name:
2710 raise
2710 raise
2711 parent = os.path.dirname(abspath(name))
2711 parent = os.path.dirname(abspath(name))
2712 if parent == name:
2712 if parent == name:
2713 raise
2713 raise
2714 makedirs(parent, mode, notindexed)
2714 makedirs(parent, mode, notindexed)
2715 try:
2715 try:
2716 makedir(name, notindexed)
2716 makedir(name, notindexed)
2717 except OSError as err:
2717 except OSError as err:
2718 # Catch EEXIST to handle races
2718 # Catch EEXIST to handle races
2719 if err.errno == errno.EEXIST:
2719 if err.errno == errno.EEXIST:
2720 return
2720 return
2721 raise
2721 raise
2722 if mode is not None:
2722 if mode is not None:
2723 os.chmod(name, mode)
2723 os.chmod(name, mode)
2724
2724
2725
2725
2726 def readfile(path: bytes) -> bytes:
2726 def readfile(path: bytes) -> bytes:
2727 with open(path, b'rb') as fp:
2727 with open(path, b'rb') as fp:
2728 return fp.read()
2728 return fp.read()
2729
2729
2730
2730
2731 def writefile(path: bytes, text: bytes) -> None:
2731 def writefile(path: bytes, text: bytes) -> None:
2732 with open(path, b'wb') as fp:
2732 with open(path, b'wb') as fp:
2733 fp.write(text)
2733 fp.write(text)
2734
2734
2735
2735
2736 def appendfile(path: bytes, text: bytes) -> None:
2736 def appendfile(path: bytes, text: bytes) -> None:
2737 with open(path, b'ab') as fp:
2737 with open(path, b'ab') as fp:
2738 fp.write(text)
2738 fp.write(text)
2739
2739
2740
2740
2741 class chunkbuffer:
2741 class chunkbuffer:
2742 """Allow arbitrary sized chunks of data to be efficiently read from an
2742 """Allow arbitrary sized chunks of data to be efficiently read from an
2743 iterator over chunks of arbitrary size."""
2743 iterator over chunks of arbitrary size."""
2744
2744
2745 def __init__(self, in_iter):
2745 def __init__(self, in_iter):
2746 """in_iter is the iterator that's iterating over the input chunks."""
2746 """in_iter is the iterator that's iterating over the input chunks."""
2747
2747
2748 def splitbig(chunks):
2748 def splitbig(chunks):
2749 for chunk in chunks:
2749 for chunk in chunks:
2750 if len(chunk) > 2**20:
2750 if len(chunk) > 2**20:
2751 pos = 0
2751 pos = 0
2752 while pos < len(chunk):
2752 while pos < len(chunk):
2753 end = pos + 2**18
2753 end = pos + 2**18
2754 yield chunk[pos:end]
2754 yield chunk[pos:end]
2755 pos = end
2755 pos = end
2756 else:
2756 else:
2757 yield chunk
2757 yield chunk
2758
2758
2759 self.iter = splitbig(in_iter)
2759 self.iter = splitbig(in_iter)
2760 self._queue = collections.deque()
2760 self._queue = collections.deque()
2761 self._chunkoffset = 0
2761 self._chunkoffset = 0
2762
2762
2763 def read(self, l=None):
2763 def read(self, l=None):
2764 """Read L bytes of data from the iterator of chunks of data.
2764 """Read L bytes of data from the iterator of chunks of data.
2765 Returns less than L bytes if the iterator runs dry.
2765 Returns less than L bytes if the iterator runs dry.
2766
2766
2767 If size parameter is omitted, read everything"""
2767 If size parameter is omitted, read everything"""
2768 if l is None:
2768 if l is None:
2769 return b''.join(self.iter)
2769 return b''.join(self.iter)
2770
2770
2771 left = l
2771 left = l
2772 buf = []
2772 buf = []
2773 queue = self._queue
2773 queue = self._queue
2774 while left > 0:
2774 while left > 0:
2775 # refill the queue
2775 # refill the queue
2776 if not queue:
2776 if not queue:
2777 target = 2**18
2777 target = 2**18
2778 for chunk in self.iter:
2778 for chunk in self.iter:
2779 queue.append(chunk)
2779 queue.append(chunk)
2780 target -= len(chunk)
2780 target -= len(chunk)
2781 if target <= 0:
2781 if target <= 0:
2782 break
2782 break
2783 if not queue:
2783 if not queue:
2784 break
2784 break
2785
2785
2786 # The easy way to do this would be to queue.popleft(), modify the
2786 # The easy way to do this would be to queue.popleft(), modify the
2787 # chunk (if necessary), then queue.appendleft(). However, for cases
2787 # chunk (if necessary), then queue.appendleft(). However, for cases
2788 # where we read partial chunk content, this incurs 2 dequeue
2788 # where we read partial chunk content, this incurs 2 dequeue
2789 # mutations and creates a new str for the remaining chunk in the
2789 # mutations and creates a new str for the remaining chunk in the
2790 # queue. Our code below avoids this overhead.
2790 # queue. Our code below avoids this overhead.
2791
2791
2792 chunk = queue[0]
2792 chunk = queue[0]
2793 chunkl = len(chunk)
2793 chunkl = len(chunk)
2794 offset = self._chunkoffset
2794 offset = self._chunkoffset
2795
2795
2796 # Use full chunk.
2796 # Use full chunk.
2797 if offset == 0 and left >= chunkl:
2797 if offset == 0 and left >= chunkl:
2798 left -= chunkl
2798 left -= chunkl
2799 queue.popleft()
2799 queue.popleft()
2800 buf.append(chunk)
2800 buf.append(chunk)
2801 # self._chunkoffset remains at 0.
2801 # self._chunkoffset remains at 0.
2802 continue
2802 continue
2803
2803
2804 chunkremaining = chunkl - offset
2804 chunkremaining = chunkl - offset
2805
2805
2806 # Use all of unconsumed part of chunk.
2806 # Use all of unconsumed part of chunk.
2807 if left >= chunkremaining:
2807 if left >= chunkremaining:
2808 left -= chunkremaining
2808 left -= chunkremaining
2809 queue.popleft()
2809 queue.popleft()
2810 # offset == 0 is enabled by block above, so this won't merely
2810 # offset == 0 is enabled by block above, so this won't merely
2811 # copy via ``chunk[0:]``.
2811 # copy via ``chunk[0:]``.
2812 buf.append(chunk[offset:])
2812 buf.append(chunk[offset:])
2813 self._chunkoffset = 0
2813 self._chunkoffset = 0
2814
2814
2815 # Partial chunk needed.
2815 # Partial chunk needed.
2816 else:
2816 else:
2817 buf.append(chunk[offset : offset + left])
2817 buf.append(chunk[offset : offset + left])
2818 self._chunkoffset += left
2818 self._chunkoffset += left
2819 left -= chunkremaining
2819 left -= chunkremaining
2820
2820
2821 return b''.join(buf)
2821 return b''.join(buf)
2822
2822
2823
2823
2824 def filechunkiter(f, size=131072, limit=None):
2824 def filechunkiter(f, size=131072, limit=None):
2825 """Create a generator that produces the data in the file size
2825 """Create a generator that produces the data in the file size
2826 (default 131072) bytes at a time, up to optional limit (default is
2826 (default 131072) bytes at a time, up to optional limit (default is
2827 to read all data). Chunks may be less than size bytes if the
2827 to read all data). Chunks may be less than size bytes if the
2828 chunk is the last chunk in the file, or the file is a socket or
2828 chunk is the last chunk in the file, or the file is a socket or
2829 some other type of file that sometimes reads less data than is
2829 some other type of file that sometimes reads less data than is
2830 requested."""
2830 requested."""
2831 assert size >= 0
2831 assert size >= 0
2832 assert limit is None or limit >= 0
2832 assert limit is None or limit >= 0
2833 while True:
2833 while True:
2834 if limit is None:
2834 if limit is None:
2835 nbytes = size
2835 nbytes = size
2836 else:
2836 else:
2837 nbytes = min(limit, size)
2837 nbytes = min(limit, size)
2838 s = nbytes and f.read(nbytes)
2838 s = nbytes and f.read(nbytes)
2839 if not s:
2839 if not s:
2840 break
2840 break
2841 if limit:
2841 if limit:
2842 limit -= len(s)
2842 limit -= len(s)
2843 yield s
2843 yield s
2844
2844
2845
2845
2846 class cappedreader:
2846 class cappedreader:
2847 """A file object proxy that allows reading up to N bytes.
2847 """A file object proxy that allows reading up to N bytes.
2848
2848
2849 Given a source file object, instances of this type allow reading up to
2849 Given a source file object, instances of this type allow reading up to
2850 N bytes from that source file object. Attempts to read past the allowed
2850 N bytes from that source file object. Attempts to read past the allowed
2851 limit are treated as EOF.
2851 limit are treated as EOF.
2852
2852
2853 It is assumed that I/O is not performed on the original file object
2853 It is assumed that I/O is not performed on the original file object
2854 in addition to I/O that is performed by this instance. If there is,
2854 in addition to I/O that is performed by this instance. If there is,
2855 state tracking will get out of sync and unexpected results will ensue.
2855 state tracking will get out of sync and unexpected results will ensue.
2856 """
2856 """
2857
2857
2858 def __init__(self, fh, limit):
2858 def __init__(self, fh, limit):
2859 """Allow reading up to <limit> bytes from <fh>."""
2859 """Allow reading up to <limit> bytes from <fh>."""
2860 self._fh = fh
2860 self._fh = fh
2861 self._left = limit
2861 self._left = limit
2862
2862
2863 def read(self, n=-1):
2863 def read(self, n=-1):
2864 if not self._left:
2864 if not self._left:
2865 return b''
2865 return b''
2866
2866
2867 if n < 0:
2867 if n < 0:
2868 n = self._left
2868 n = self._left
2869
2869
2870 data = self._fh.read(min(n, self._left))
2870 data = self._fh.read(min(n, self._left))
2871 self._left -= len(data)
2871 self._left -= len(data)
2872 assert self._left >= 0
2872 assert self._left >= 0
2873
2873
2874 return data
2874 return data
2875
2875
2876 def readinto(self, b):
2876 def readinto(self, b):
2877 res = self.read(len(b))
2877 res = self.read(len(b))
2878 if res is None:
2878 if res is None:
2879 return None
2879 return None
2880
2880
2881 b[0 : len(res)] = res
2881 b[0 : len(res)] = res
2882 return len(res)
2882 return len(res)
2883
2883
2884
2884
2885 def unitcountfn(*unittable):
2885 def unitcountfn(*unittable):
2886 '''return a function that renders a readable count of some quantity'''
2886 '''return a function that renders a readable count of some quantity'''
2887
2887
2888 def go(count):
2888 def go(count):
2889 for multiplier, divisor, format in unittable:
2889 for multiplier, divisor, format in unittable:
2890 if abs(count) >= divisor * multiplier:
2890 if abs(count) >= divisor * multiplier:
2891 return format % (count / float(divisor))
2891 return format % (count / float(divisor))
2892 return unittable[-1][2] % count
2892 return unittable[-1][2] % count
2893
2893
2894 return go
2894 return go
2895
2895
2896
2896
2897 def processlinerange(fromline: int, toline: int) -> Tuple[int, int]:
2897 def processlinerange(fromline: int, toline: int) -> Tuple[int, int]:
2898 """Check that linerange <fromline>:<toline> makes sense and return a
2898 """Check that linerange <fromline>:<toline> makes sense and return a
2899 0-based range.
2899 0-based range.
2900
2900
2901 >>> processlinerange(10, 20)
2901 >>> processlinerange(10, 20)
2902 (9, 20)
2902 (9, 20)
2903 >>> processlinerange(2, 1)
2903 >>> processlinerange(2, 1)
2904 Traceback (most recent call last):
2904 Traceback (most recent call last):
2905 ...
2905 ...
2906 ParseError: line range must be positive
2906 ParseError: line range must be positive
2907 >>> processlinerange(0, 5)
2907 >>> processlinerange(0, 5)
2908 Traceback (most recent call last):
2908 Traceback (most recent call last):
2909 ...
2909 ...
2910 ParseError: fromline must be strictly positive
2910 ParseError: fromline must be strictly positive
2911 """
2911 """
2912 if toline - fromline < 0:
2912 if toline - fromline < 0:
2913 raise error.ParseError(_(b"line range must be positive"))
2913 raise error.ParseError(_(b"line range must be positive"))
2914 if fromline < 1:
2914 if fromline < 1:
2915 raise error.ParseError(_(b"fromline must be strictly positive"))
2915 raise error.ParseError(_(b"fromline must be strictly positive"))
2916 return fromline - 1, toline
2916 return fromline - 1, toline
2917
2917
2918
2918
2919 bytecount = unitcountfn(
2919 bytecount = unitcountfn(
2920 (100, 1 << 30, _(b'%.0f GB')),
2920 (100, 1 << 30, _(b'%.0f GB')),
2921 (10, 1 << 30, _(b'%.1f GB')),
2921 (10, 1 << 30, _(b'%.1f GB')),
2922 (1, 1 << 30, _(b'%.2f GB')),
2922 (1, 1 << 30, _(b'%.2f GB')),
2923 (100, 1 << 20, _(b'%.0f MB')),
2923 (100, 1 << 20, _(b'%.0f MB')),
2924 (10, 1 << 20, _(b'%.1f MB')),
2924 (10, 1 << 20, _(b'%.1f MB')),
2925 (1, 1 << 20, _(b'%.2f MB')),
2925 (1, 1 << 20, _(b'%.2f MB')),
2926 (100, 1 << 10, _(b'%.0f KB')),
2926 (100, 1 << 10, _(b'%.0f KB')),
2927 (10, 1 << 10, _(b'%.1f KB')),
2927 (10, 1 << 10, _(b'%.1f KB')),
2928 (1, 1 << 10, _(b'%.2f KB')),
2928 (1, 1 << 10, _(b'%.2f KB')),
2929 (1, 1, _(b'%.0f bytes')),
2929 (1, 1, _(b'%.0f bytes')),
2930 )
2930 )
2931
2931
2932
2932
2933 class transformingwriter(typelib.BinaryIO_Proxy):
2933 class transformingwriter(typelib.BinaryIO_Proxy):
2934 """Writable file wrapper to transform data by function"""
2934 """Writable file wrapper to transform data by function"""
2935
2935
2936 def __init__(self, fp: BinaryIO, encode: Callable[[bytes], bytes]) -> None:
2936 def __init__(self, fp: BinaryIO, encode: Callable[[bytes], bytes]) -> None:
2937 self._fp = fp
2937 self._fp = fp
2938 self._encode = encode
2938 self._encode = encode
2939
2939
2940 def close(self) -> None:
2940 def close(self) -> None:
2941 self._fp.close()
2941 self._fp.close()
2942
2942
2943 def flush(self) -> None:
2943 def flush(self) -> None:
2944 self._fp.flush()
2944 self._fp.flush()
2945
2945
2946 def write(self, data: bytes) -> int:
2946 def write(self, data: bytes) -> int:
2947 return self._fp.write(self._encode(data))
2947 return self._fp.write(self._encode(data))
2948
2948
2949
2949
2950 # Matches a single EOL which can either be a CRLF where repeated CR
2950 # Matches a single EOL which can either be a CRLF where repeated CR
2951 # are removed or a LF. We do not care about old Macintosh files, so a
2951 # are removed or a LF. We do not care about old Macintosh files, so a
2952 # stray CR is an error.
2952 # stray CR is an error.
2953 _eolre = remod.compile(br'\r*\n')
2953 _eolre = remod.compile(br'\r*\n')
2954
2954
2955
2955
2956 def tolf(s: bytes) -> bytes:
2956 def tolf(s: bytes) -> bytes:
2957 return _eolre.sub(b'\n', s)
2957 return _eolre.sub(b'\n', s)
2958
2958
2959
2959
2960 def tocrlf(s: bytes) -> bytes:
2960 def tocrlf(s: bytes) -> bytes:
2961 return _eolre.sub(b'\r\n', s)
2961 return _eolre.sub(b'\r\n', s)
2962
2962
2963
2963
2964 def _crlfwriter(fp: typelib.BinaryIO_Proxy) -> typelib.BinaryIO_Proxy:
2964 def _crlfwriter(fp: typelib.BinaryIO_Proxy) -> typelib.BinaryIO_Proxy:
2965 return transformingwriter(fp, tocrlf)
2965 return transformingwriter(fp, tocrlf)
2966
2966
2967
2967
2968 if pycompat.oslinesep == b'\r\n':
2968 if pycompat.oslinesep == b'\r\n':
2969 tonativeeol = tocrlf
2969 tonativeeol = tocrlf
2970 fromnativeeol = tolf
2970 fromnativeeol = tolf
2971 nativeeolwriter = _crlfwriter
2971 nativeeolwriter = _crlfwriter
2972 else:
2972 else:
2973 tonativeeol = pycompat.identity
2973 tonativeeol = pycompat.identity
2974 fromnativeeol = pycompat.identity
2974 fromnativeeol = pycompat.identity
2975 nativeeolwriter = pycompat.identity
2975 nativeeolwriter = pycompat.identity
2976
2976
2977 if typing.TYPE_CHECKING:
2977 if typing.TYPE_CHECKING:
2978 # Replace the various overloads that come along with aliasing other methods
2978 # Replace the various overloads that come along with aliasing other methods
2979 # with the narrow definition that we care about in the type checking phase
2979 # with the narrow definition that we care about in the type checking phase
2980 # only. This ensures that both Windows and POSIX see only the definition
2980 # only. This ensures that both Windows and POSIX see only the definition
2981 # that is actually available.
2981 # that is actually available.
2982
2982
2983 def tonativeeol(s: bytes) -> bytes:
2983 def tonativeeol(s: bytes) -> bytes:
2984 raise NotImplementedError
2984 raise NotImplementedError
2985
2985
2986 def fromnativeeol(s: bytes) -> bytes:
2986 def fromnativeeol(s: bytes) -> bytes:
2987 raise NotImplementedError
2987 raise NotImplementedError
2988
2988
2989 def nativeeolwriter(fp: typelib.BinaryIO_Proxy) -> typelib.BinaryIO_Proxy:
2989 def nativeeolwriter(fp: typelib.BinaryIO_Proxy) -> typelib.BinaryIO_Proxy:
2990 raise NotImplementedError
2990 raise NotImplementedError
2991
2991
2992
2992
2993 # TODO delete since workaround variant for Python 2 no longer needed.
2993 # TODO delete since workaround variant for Python 2 no longer needed.
2994 def iterfile(fp):
2994 def iterfile(fp):
2995 return fp
2995 return fp
2996
2996
2997
2997
2998 def iterlines(iterator: Iterable[bytes]) -> Iterator[bytes]:
2998 def iterlines(iterator: Iterable[bytes]) -> Iterator[bytes]:
2999 for chunk in iterator:
2999 for chunk in iterator:
3000 for line in chunk.splitlines():
3000 for line in chunk.splitlines():
3001 yield line
3001 yield line
3002
3002
3003
3003
3004 def expandpath(path: bytes) -> bytes:
3004 def expandpath(path: bytes) -> bytes:
3005 return os.path.expanduser(os.path.expandvars(path))
3005 return os.path.expanduser(os.path.expandvars(path))
3006
3006
3007
3007
3008 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
3008 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
3009 """Return the result of interpolating items in the mapping into string s.
3009 """Return the result of interpolating items in the mapping into string s.
3010
3010
3011 prefix is a single character string, or a two character string with
3011 prefix is a single character string, or a two character string with
3012 a backslash as the first character if the prefix needs to be escaped in
3012 a backslash as the first character if the prefix needs to be escaped in
3013 a regular expression.
3013 a regular expression.
3014
3014
3015 fn is an optional function that will be applied to the replacement text
3015 fn is an optional function that will be applied to the replacement text
3016 just before replacement.
3016 just before replacement.
3017
3017
3018 escape_prefix is an optional flag that allows using doubled prefix for
3018 escape_prefix is an optional flag that allows using doubled prefix for
3019 its escaping.
3019 its escaping.
3020 """
3020 """
3021 fn = fn or (lambda s: s)
3021 fn = fn or (lambda s: s)
3022 patterns = b'|'.join(mapping.keys())
3022 patterns = b'|'.join(mapping.keys())
3023 if escape_prefix:
3023 if escape_prefix:
3024 patterns += b'|' + prefix
3024 patterns += b'|' + prefix
3025 if len(prefix) > 1:
3025 if len(prefix) > 1:
3026 prefix_char = prefix[1:]
3026 prefix_char = prefix[1:]
3027 else:
3027 else:
3028 prefix_char = prefix
3028 prefix_char = prefix
3029 mapping[prefix_char] = prefix_char
3029 mapping[prefix_char] = prefix_char
3030 r = remod.compile(br'%s(%s)' % (prefix, patterns))
3030 r = remod.compile(br'%s(%s)' % (prefix, patterns))
3031 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
3031 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
3032
3032
3033
3033
3034 timecount = unitcountfn(
3034 timecount = unitcountfn(
3035 (1, 1e3, _(b'%.0f s')),
3035 (1, 1e3, _(b'%.0f s')),
3036 (100, 1, _(b'%.1f s')),
3036 (100, 1, _(b'%.1f s')),
3037 (10, 1, _(b'%.2f s')),
3037 (10, 1, _(b'%.2f s')),
3038 (1, 1, _(b'%.3f s')),
3038 (1, 1, _(b'%.3f s')),
3039 (100, 0.001, _(b'%.1f ms')),
3039 (100, 0.001, _(b'%.1f ms')),
3040 (10, 0.001, _(b'%.2f ms')),
3040 (10, 0.001, _(b'%.2f ms')),
3041 (1, 0.001, _(b'%.3f ms')),
3041 (1, 0.001, _(b'%.3f ms')),
3042 (100, 0.000001, _(b'%.1f us')),
3042 (100, 0.000001, _(b'%.1f us')),
3043 (10, 0.000001, _(b'%.2f us')),
3043 (10, 0.000001, _(b'%.2f us')),
3044 (1, 0.000001, _(b'%.3f us')),
3044 (1, 0.000001, _(b'%.3f us')),
3045 (100, 0.000000001, _(b'%.1f ns')),
3045 (100, 0.000000001, _(b'%.1f ns')),
3046 (10, 0.000000001, _(b'%.2f ns')),
3046 (10, 0.000000001, _(b'%.2f ns')),
3047 (1, 0.000000001, _(b'%.3f ns')),
3047 (1, 0.000000001, _(b'%.3f ns')),
3048 )
3048 )
3049
3049
3050
3050
3051 @attr.s
3051 @attr.s
3052 class timedcmstats:
3052 class timedcmstats:
3053 """Stats information produced by the timedcm context manager on entering."""
3053 """Stats information produced by the timedcm context manager on entering."""
3054
3054
3055 # the starting value of the timer as a float (meaning and resulution is
3055 # the starting value of the timer as a float (meaning and resulution is
3056 # platform dependent, see util.timer)
3056 # platform dependent, see util.timer)
3057 start = attr.ib(default=attr.Factory(lambda: timer()))
3057 start = attr.ib(default=attr.Factory(lambda: timer()))
3058 # the number of seconds as a floating point value; starts at 0, updated when
3058 # the number of seconds as a floating point value; starts at 0, updated when
3059 # the context is exited.
3059 # the context is exited.
3060 elapsed = attr.ib(default=0)
3060 elapsed = attr.ib(default=0)
3061 # the number of nested timedcm context managers.
3061 # the number of nested timedcm context managers.
3062 level = attr.ib(default=1)
3062 level = attr.ib(default=1)
3063
3063
3064 def __bytes__(self):
3064 def __bytes__(self):
3065 return timecount(self.elapsed) if self.elapsed else b'<unknown>'
3065 return timecount(self.elapsed) if self.elapsed else b'<unknown>'
3066
3066
3067 __str__ = encoding.strmethod(__bytes__)
3067 __str__ = encoding.strmethod(__bytes__)
3068
3068
3069
3069
3070 @contextlib.contextmanager
3070 @contextlib.contextmanager
3071 def timedcm(whencefmt, *whenceargs):
3071 def timedcm(whencefmt, *whenceargs):
3072 """A context manager that produces timing information for a given context.
3072 """A context manager that produces timing information for a given context.
3073
3073
3074 On entering a timedcmstats instance is produced.
3074 On entering a timedcmstats instance is produced.
3075
3075
3076 This context manager is reentrant.
3076 This context manager is reentrant.
3077
3077
3078 """
3078 """
3079 # track nested context managers
3079 # track nested context managers
3080 timedcm._nested += 1
3080 timedcm._nested += 1
3081 timing_stats = timedcmstats(level=timedcm._nested)
3081 timing_stats = timedcmstats(level=timedcm._nested)
3082 try:
3082 try:
3083 with tracing.log(whencefmt, *whenceargs):
3083 with tracing.log(whencefmt, *whenceargs):
3084 yield timing_stats
3084 yield timing_stats
3085 finally:
3085 finally:
3086 timing_stats.elapsed = timer() - timing_stats.start
3086 timing_stats.elapsed = timer() - timing_stats.start
3087 timedcm._nested -= 1
3087 timedcm._nested -= 1
3088
3088
3089
3089
3090 timedcm._nested = 0
3090 timedcm._nested = 0
3091
3091
3092
3092
3093 def timed(func):
3093 def timed(func):
3094 """Report the execution time of a function call to stderr.
3094 """Report the execution time of a function call to stderr.
3095
3095
3096 During development, use as a decorator when you need to measure
3096 During development, use as a decorator when you need to measure
3097 the cost of a function, e.g. as follows:
3097 the cost of a function, e.g. as follows:
3098
3098
3099 @util.timed
3099 @util.timed
3100 def foo(a, b, c):
3100 def foo(a, b, c):
3101 pass
3101 pass
3102 """
3102 """
3103
3103
3104 def wrapper(*args, **kwargs):
3104 def wrapper(*args, **kwargs):
3105 with timedcm(pycompat.bytestr(func.__name__)) as time_stats:
3105 with timedcm(pycompat.bytestr(func.__name__)) as time_stats:
3106 result = func(*args, **kwargs)
3106 result = func(*args, **kwargs)
3107 stderr = procutil.stderr
3107 stderr = procutil.stderr
3108 stderr.write(
3108 stderr.write(
3109 b'%s%s: %s\n'
3109 b'%s%s: %s\n'
3110 % (
3110 % (
3111 b' ' * time_stats.level * 2,
3111 b' ' * time_stats.level * 2,
3112 pycompat.bytestr(func.__name__),
3112 pycompat.bytestr(func.__name__),
3113 time_stats,
3113 time_stats,
3114 )
3114 )
3115 )
3115 )
3116 return result
3116 return result
3117
3117
3118 return wrapper
3118 return wrapper
3119
3119
3120
3120
3121 _sizeunits = (
3121 _sizeunits = (
3122 (b'm', 2**20),
3122 (b'm', 2**20),
3123 (b'k', 2**10),
3123 (b'k', 2**10),
3124 (b'g', 2**30),
3124 (b'g', 2**30),
3125 (b'kb', 2**10),
3125 (b'kb', 2**10),
3126 (b'mb', 2**20),
3126 (b'mb', 2**20),
3127 (b'gb', 2**30),
3127 (b'gb', 2**30),
3128 (b'b', 1),
3128 (b'b', 1),
3129 )
3129 )
3130
3130
3131
3131
3132 def sizetoint(s: bytes) -> int:
3132 def sizetoint(s: bytes) -> int:
3133 """Convert a space specifier to a byte count.
3133 """Convert a space specifier to a byte count.
3134
3134
3135 >>> sizetoint(b'30')
3135 >>> sizetoint(b'30')
3136 30
3136 30
3137 >>> sizetoint(b'2.2kb')
3137 >>> sizetoint(b'2.2kb')
3138 2252
3138 2252
3139 >>> sizetoint(b'6M')
3139 >>> sizetoint(b'6M')
3140 6291456
3140 6291456
3141 """
3141 """
3142 t = s.strip().lower()
3142 t = s.strip().lower()
3143 try:
3143 try:
3144 for k, u in _sizeunits:
3144 for k, u in _sizeunits:
3145 if t.endswith(k):
3145 if t.endswith(k):
3146 return int(float(t[: -len(k)]) * u)
3146 return int(float(t[: -len(k)]) * u)
3147 return int(t)
3147 return int(t)
3148 except ValueError:
3148 except ValueError:
3149 raise error.ParseError(_(b"couldn't parse size: %s") % s)
3149 raise error.ParseError(_(b"couldn't parse size: %s") % s)
3150
3150
3151
3151
3152 class hooks:
3152 class hooks:
3153 """A collection of hook functions that can be used to extend a
3153 """A collection of hook functions that can be used to extend a
3154 function's behavior. Hooks are called in lexicographic order,
3154 function's behavior. Hooks are called in lexicographic order,
3155 based on the names of their sources."""
3155 based on the names of their sources."""
3156
3156
3157 def __init__(self):
3157 def __init__(self):
3158 self._hooks = []
3158 self._hooks = []
3159
3159
3160 def add(self, source, hook):
3160 def add(self, source, hook):
3161 self._hooks.append((source, hook))
3161 self._hooks.append((source, hook))
3162
3162
3163 def __call__(self, *args):
3163 def __call__(self, *args):
3164 self._hooks.sort(key=lambda x: x[0])
3164 self._hooks.sort(key=lambda x: x[0])
3165 results = []
3165 results = []
3166 for source, hook in self._hooks:
3166 for source, hook in self._hooks:
3167 results.append(hook(*args))
3167 results.append(hook(*args))
3168 return results
3168 return results
3169
3169
3170
3170
3171 def getstackframes(skip=0, line=b' %-*s in %s\n', fileline=b'%s:%d', depth=0):
3171 def getstackframes(skip=0, line=b' %-*s in %s\n', fileline=b'%s:%d', depth=0):
3172 """Yields lines for a nicely formatted stacktrace.
3172 """Yields lines for a nicely formatted stacktrace.
3173 Skips the 'skip' last entries, then return the last 'depth' entries.
3173 Skips the 'skip' last entries, then return the last 'depth' entries.
3174 Each file+linenumber is formatted according to fileline.
3174 Each file+linenumber is formatted according to fileline.
3175 Each line is formatted according to line.
3175 Each line is formatted according to line.
3176 If line is None, it yields:
3176 If line is None, it yields:
3177 length of longest filepath+line number,
3177 length of longest filepath+line number,
3178 filepath+linenumber,
3178 filepath+linenumber,
3179 function
3179 function
3180
3180
3181 Not be used in production code but very convenient while developing.
3181 Not be used in production code but very convenient while developing.
3182 """
3182 """
3183 entries = [
3183 entries = [
3184 (fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func))
3184 (fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func))
3185 for fn, ln, func, _text in traceback.extract_stack()[: -skip - 1]
3185 for fn, ln, func, _text in traceback.extract_stack()[: -skip - 1]
3186 ][-depth:]
3186 ][-depth:]
3187 if entries:
3187 if entries:
3188 fnmax = max(len(entry[0]) for entry in entries)
3188 fnmax = max(len(entry[0]) for entry in entries)
3189 for fnln, func in entries:
3189 for fnln, func in entries:
3190 if line is None:
3190 if line is None:
3191 yield (fnmax, fnln, func)
3191 yield (fnmax, fnln, func)
3192 else:
3192 else:
3193 yield line % (fnmax, fnln, func)
3193 yield line % (fnmax, fnln, func)
3194
3194
3195
3195
3196 def debugstacktrace(
3196 def debugstacktrace(
3197 msg=b'stacktrace',
3197 msg=b'stacktrace',
3198 skip=0,
3198 skip=0,
3199 f=procutil.stderr,
3199 f=procutil.stderr,
3200 otherf=procutil.stdout,
3200 otherf=procutil.stdout,
3201 depth=0,
3201 depth=0,
3202 prefix=b'',
3202 prefix=b'',
3203 ):
3203 ):
3204 """Writes a message to f (stderr) with a nicely formatted stacktrace.
3204 """Writes a message to f (stderr) with a nicely formatted stacktrace.
3205 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3205 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3206 By default it will flush stdout first.
3206 By default it will flush stdout first.
3207 It can be used everywhere and intentionally does not require an ui object.
3207 It can be used everywhere and intentionally does not require an ui object.
3208 Not be used in production code but very convenient while developing.
3208 Not be used in production code but very convenient while developing.
3209 """
3209 """
3210 if otherf:
3210 if otherf:
3211 otherf.flush()
3211 otherf.flush()
3212 f.write(b'%s%s at:\n' % (prefix, msg.rstrip()))
3212 f.write(b'%s%s at:\n' % (prefix, msg.rstrip()))
3213 for line in getstackframes(skip + 1, depth=depth):
3213 for line in getstackframes(skip + 1, depth=depth):
3214 f.write(prefix + line)
3214 f.write(prefix + line)
3215 f.flush()
3215 f.flush()
3216
3216
3217
3217
3218 # convenient shortcut
3218 # convenient shortcut
3219 dst = debugstacktrace
3219 dst = debugstacktrace
3220
3220
3221
3221
3222 def safename(f, tag, ctx, others=None):
3222 def safename(f, tag, ctx, others=None):
3223 """
3223 """
3224 Generate a name that it is safe to rename f to in the given context.
3224 Generate a name that it is safe to rename f to in the given context.
3225
3225
3226 f: filename to rename
3226 f: filename to rename
3227 tag: a string tag that will be included in the new name
3227 tag: a string tag that will be included in the new name
3228 ctx: a context, in which the new name must not exist
3228 ctx: a context, in which the new name must not exist
3229 others: a set of other filenames that the new name must not be in
3229 others: a set of other filenames that the new name must not be in
3230
3230
3231 Returns a file name of the form oldname~tag[~number] which does not exist
3231 Returns a file name of the form oldname~tag[~number] which does not exist
3232 in the provided context and is not in the set of other names.
3232 in the provided context and is not in the set of other names.
3233 """
3233 """
3234 if others is None:
3234 if others is None:
3235 others = set()
3235 others = set()
3236
3236
3237 fn = b'%s~%s' % (f, tag)
3237 fn = b'%s~%s' % (f, tag)
3238 if fn not in ctx and fn not in others:
3238 if fn not in ctx and fn not in others:
3239 return fn
3239 return fn
3240 for n in itertools.count(1):
3240 for n in itertools.count(1):
3241 fn = b'%s~%s~%s' % (f, tag, n)
3241 fn = b'%s~%s~%s' % (f, tag, n)
3242 if fn not in ctx and fn not in others:
3242 if fn not in ctx and fn not in others:
3243 return fn
3243 return fn
3244
3244
3245
3245
3246 def readexactly(stream, n):
3246 def readexactly(stream, n):
3247 '''read n bytes from stream.read and abort if less was available'''
3247 '''read n bytes from stream.read and abort if less was available'''
3248 s = stream.read(n)
3248 s = stream.read(n)
3249 if len(s) < n:
3249 if len(s) < n:
3250 raise error.Abort(
3250 raise error.Abort(
3251 _(b"stream ended unexpectedly (got %d bytes, expected %d)")
3251 _(b"stream ended unexpectedly (got %d bytes, expected %d)")
3252 % (len(s), n)
3252 % (len(s), n)
3253 )
3253 )
3254 return s
3254 return s
3255
3255
3256
3256
3257 def uvarintencode(value):
3257 def uvarintencode(value):
3258 """Encode an unsigned integer value to a varint.
3258 """Encode an unsigned integer value to a varint.
3259
3259
3260 A varint is a variable length integer of 1 or more bytes. Each byte
3260 A varint is a variable length integer of 1 or more bytes. Each byte
3261 except the last has the most significant bit set. The lower 7 bits of
3261 except the last has the most significant bit set. The lower 7 bits of
3262 each byte store the 2's complement representation, least significant group
3262 each byte store the 2's complement representation, least significant group
3263 first.
3263 first.
3264
3264
3265 >>> uvarintencode(0)
3265 >>> uvarintencode(0)
3266 '\\x00'
3266 '\\x00'
3267 >>> uvarintencode(1)
3267 >>> uvarintencode(1)
3268 '\\x01'
3268 '\\x01'
3269 >>> uvarintencode(127)
3269 >>> uvarintencode(127)
3270 '\\x7f'
3270 '\\x7f'
3271 >>> uvarintencode(1337)
3271 >>> uvarintencode(1337)
3272 '\\xb9\\n'
3272 '\\xb9\\n'
3273 >>> uvarintencode(65536)
3273 >>> uvarintencode(65536)
3274 '\\x80\\x80\\x04'
3274 '\\x80\\x80\\x04'
3275 >>> uvarintencode(-1)
3275 >>> uvarintencode(-1)
3276 Traceback (most recent call last):
3276 Traceback (most recent call last):
3277 ...
3277 ...
3278 ProgrammingError: negative value for uvarint: -1
3278 ProgrammingError: negative value for uvarint: -1
3279 """
3279 """
3280 if value < 0:
3280 if value < 0:
3281 raise error.ProgrammingError(b'negative value for uvarint: %d' % value)
3281 raise error.ProgrammingError(b'negative value for uvarint: %d' % value)
3282 bits = value & 0x7F
3282 bits = value & 0x7F
3283 value >>= 7
3283 value >>= 7
3284 bytes = []
3284 bytes = []
3285 while value:
3285 while value:
3286 bytes.append(pycompat.bytechr(0x80 | bits))
3286 bytes.append(pycompat.bytechr(0x80 | bits))
3287 bits = value & 0x7F
3287 bits = value & 0x7F
3288 value >>= 7
3288 value >>= 7
3289 bytes.append(pycompat.bytechr(bits))
3289 bytes.append(pycompat.bytechr(bits))
3290
3290
3291 return b''.join(bytes)
3291 return b''.join(bytes)
3292
3292
3293
3293
3294 def uvarintdecodestream(fh):
3294 def uvarintdecodestream(fh):
3295 """Decode an unsigned variable length integer from a stream.
3295 """Decode an unsigned variable length integer from a stream.
3296
3296
3297 The passed argument is anything that has a ``.read(N)`` method.
3297 The passed argument is anything that has a ``.read(N)`` method.
3298
3298
3299 >>> from io import BytesIO
3299 >>> from io import BytesIO
3300 >>> uvarintdecodestream(BytesIO(b'\\x00'))
3300 >>> uvarintdecodestream(BytesIO(b'\\x00'))
3301 0
3301 0
3302 >>> uvarintdecodestream(BytesIO(b'\\x01'))
3302 >>> uvarintdecodestream(BytesIO(b'\\x01'))
3303 1
3303 1
3304 >>> uvarintdecodestream(BytesIO(b'\\x7f'))
3304 >>> uvarintdecodestream(BytesIO(b'\\x7f'))
3305 127
3305 127
3306 >>> uvarintdecodestream(BytesIO(b'\\xb9\\n'))
3306 >>> uvarintdecodestream(BytesIO(b'\\xb9\\n'))
3307 1337
3307 1337
3308 >>> uvarintdecodestream(BytesIO(b'\\x80\\x80\\x04'))
3308 >>> uvarintdecodestream(BytesIO(b'\\x80\\x80\\x04'))
3309 65536
3309 65536
3310 >>> uvarintdecodestream(BytesIO(b'\\x80'))
3310 >>> uvarintdecodestream(BytesIO(b'\\x80'))
3311 Traceback (most recent call last):
3311 Traceback (most recent call last):
3312 ...
3312 ...
3313 Abort: stream ended unexpectedly (got 0 bytes, expected 1)
3313 Abort: stream ended unexpectedly (got 0 bytes, expected 1)
3314 """
3314 """
3315 result = 0
3315 result = 0
3316 shift = 0
3316 shift = 0
3317 while True:
3317 while True:
3318 byte = ord(readexactly(fh, 1))
3318 byte = ord(readexactly(fh, 1))
3319 result |= (byte & 0x7F) << shift
3319 result |= (byte & 0x7F) << shift
3320 if not (byte & 0x80):
3320 if not (byte & 0x80):
3321 return result
3321 return result
3322 shift += 7
3322 shift += 7
3323
3323
3324
3324
3325 # Passing the '' locale means that the locale should be set according to the
3325 # Passing the '' locale means that the locale should be set according to the
3326 # user settings (environment variables).
3326 # user settings (environment variables).
3327 # Python sometimes avoids setting the global locale settings. When interfacing
3327 # Python sometimes avoids setting the global locale settings. When interfacing
3328 # with C code (e.g. the curses module or the Subversion bindings), the global
3328 # with C code (e.g. the curses module or the Subversion bindings), the global
3329 # locale settings must be initialized correctly. Python 2 does not initialize
3329 # locale settings must be initialized correctly. Python 2 does not initialize
3330 # the global locale settings on interpreter startup. Python 3 sometimes
3330 # the global locale settings on interpreter startup. Python 3 sometimes
3331 # initializes LC_CTYPE, but not consistently at least on Windows. Therefore we
3331 # initializes LC_CTYPE, but not consistently at least on Windows. Therefore we
3332 # explicitly initialize it to get consistent behavior if it's not already
3332 # explicitly initialize it to get consistent behavior if it's not already
3333 # initialized. Since CPython commit 177d921c8c03d30daa32994362023f777624b10d,
3333 # initialized. Since CPython commit 177d921c8c03d30daa32994362023f777624b10d,
3334 # LC_CTYPE is always initialized. If we require Python 3.8+, we should re-check
3334 # LC_CTYPE is always initialized. If we require Python 3.8+, we should re-check
3335 # if we can remove this code.
3335 # if we can remove this code.
3336 @contextlib.contextmanager
3336 @contextlib.contextmanager
3337 def with_lc_ctype():
3337 def with_lc_ctype():
3338 oldloc = locale.setlocale(locale.LC_CTYPE, None)
3338 oldloc = locale.setlocale(locale.LC_CTYPE, None)
3339 if oldloc == 'C':
3339 if oldloc == 'C':
3340 try:
3340 try:
3341 try:
3341 try:
3342 locale.setlocale(locale.LC_CTYPE, '')
3342 locale.setlocale(locale.LC_CTYPE, '')
3343 except locale.Error:
3343 except locale.Error:
3344 # The likely case is that the locale from the environment
3344 # The likely case is that the locale from the environment
3345 # variables is unknown.
3345 # variables is unknown.
3346 pass
3346 pass
3347 yield
3347 yield
3348 finally:
3348 finally:
3349 locale.setlocale(locale.LC_CTYPE, oldloc)
3349 locale.setlocale(locale.LC_CTYPE, oldloc)
3350 else:
3350 else:
3351 yield
3351 yield
3352
3352
3353
3353
3354 def _estimatememory() -> Optional[int]:
3354 def _estimatememory() -> Optional[int]:
3355 """Provide an estimate for the available system memory in Bytes.
3355 """Provide an estimate for the available system memory in Bytes.
3356
3356
3357 If no estimate can be provided on the platform, returns None.
3357 If no estimate can be provided on the platform, returns None.
3358 """
3358 """
3359 if pycompat.sysplatform.startswith(b'win'):
3359 if pycompat.sysplatform.startswith(b'win'):
3360 # On Windows, use the GlobalMemoryStatusEx kernel function directly.
3360 # On Windows, use the GlobalMemoryStatusEx kernel function directly.
3361 # noinspection PyPep8Naming
3361 from ctypes import c_long as DWORD, c_ulonglong as DWORDLONG
3362 from ctypes import c_long as DWORD, c_ulonglong as DWORDLONG
3362 from ctypes.wintypes import ( # pytype: disable=import-error
3363 from ctypes.wintypes import ( # pytype: disable=import-error
3363 Structure,
3364 Structure,
3364 byref,
3365 byref,
3365 sizeof,
3366 sizeof,
3366 windll,
3367 windll,
3367 )
3368 )
3368
3369
3369 class MEMORYSTATUSEX(Structure):
3370 class MEMORYSTATUSEX(Structure):
3370 _fields_ = [
3371 _fields_ = [
3371 ('dwLength', DWORD),
3372 ('dwLength', DWORD),
3372 ('dwMemoryLoad', DWORD),
3373 ('dwMemoryLoad', DWORD),
3373 ('ullTotalPhys', DWORDLONG),
3374 ('ullTotalPhys', DWORDLONG),
3374 ('ullAvailPhys', DWORDLONG),
3375 ('ullAvailPhys', DWORDLONG),
3375 ('ullTotalPageFile', DWORDLONG),
3376 ('ullTotalPageFile', DWORDLONG),
3376 ('ullAvailPageFile', DWORDLONG),
3377 ('ullAvailPageFile', DWORDLONG),
3377 ('ullTotalVirtual', DWORDLONG),
3378 ('ullTotalVirtual', DWORDLONG),
3378 ('ullAvailVirtual', DWORDLONG),
3379 ('ullAvailVirtual', DWORDLONG),
3379 ('ullExtendedVirtual', DWORDLONG),
3380 ('ullExtendedVirtual', DWORDLONG),
3380 ]
3381 ]
3381
3382
3382 x = MEMORYSTATUSEX()
3383 x = MEMORYSTATUSEX()
3383 x.dwLength = sizeof(x)
3384 x.dwLength = sizeof(x)
3384 windll.kernel32.GlobalMemoryStatusEx(byref(x))
3385 windll.kernel32.GlobalMemoryStatusEx(byref(x))
3385 return x.ullAvailPhys
3386 return x.ullAvailPhys
3386
3387
3387 # On newer Unix-like systems and Mac OSX, the sysconf interface
3388 # On newer Unix-like systems and Mac OSX, the sysconf interface
3388 # can be used. _SC_PAGE_SIZE is part of POSIX; _SC_PHYS_PAGES
3389 # can be used. _SC_PAGE_SIZE is part of POSIX; _SC_PHYS_PAGES
3389 # seems to be implemented on most systems.
3390 # seems to be implemented on most systems.
3390 try:
3391 try:
3391 pagesize = os.sysconf(os.sysconf_names['SC_PAGE_SIZE'])
3392 pagesize = os.sysconf(os.sysconf_names['SC_PAGE_SIZE'])
3392 pages = os.sysconf(os.sysconf_names['SC_PHYS_PAGES'])
3393 pages = os.sysconf(os.sysconf_names['SC_PHYS_PAGES'])
3393 return pagesize * pages
3394 return pagesize * pages
3394 except OSError: # sysconf can fail
3395 except OSError: # sysconf can fail
3395 pass
3396 pass
3396 except KeyError: # unknown parameter
3397 except KeyError: # unknown parameter
3397 pass
3398 pass
General Comments 0
You need to be logged in to leave comments. Login now