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