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