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