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