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