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