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