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