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