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