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