##// END OF EJS Templates
procutil: fix error message of tempfile filter...
Yuya Nishihara -
r37479:538353b8 default
parent child Browse files
Show More
@@ -1,409 +1,409 b''
1 # procutil.py - utility for managing processes and executable environment
1 # procutil.py - utility for managing processes and executable environment
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 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import contextlib
12 import contextlib
13 import imp
13 import imp
14 import io
14 import io
15 import os
15 import os
16 import signal
16 import signal
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20 import time
20 import time
21
21
22 from ..i18n import _
22 from ..i18n import _
23
23
24 from .. import (
24 from .. import (
25 encoding,
25 encoding,
26 error,
26 error,
27 policy,
27 policy,
28 pycompat,
28 pycompat,
29 )
29 )
30
30
31 osutil = policy.importmod(r'osutil')
31 osutil = policy.importmod(r'osutil')
32
32
33 stderr = pycompat.stderr
33 stderr = pycompat.stderr
34 stdin = pycompat.stdin
34 stdin = pycompat.stdin
35 stdout = pycompat.stdout
35 stdout = pycompat.stdout
36
36
37 def isatty(fp):
37 def isatty(fp):
38 try:
38 try:
39 return fp.isatty()
39 return fp.isatty()
40 except AttributeError:
40 except AttributeError:
41 return False
41 return False
42
42
43 # glibc determines buffering on first write to stdout - if we replace a TTY
43 # glibc determines buffering on first write to stdout - if we replace a TTY
44 # destined stdout with a pipe destined stdout (e.g. pager), we want line
44 # destined stdout with a pipe destined stdout (e.g. pager), we want line
45 # buffering
45 # buffering
46 if isatty(stdout):
46 if isatty(stdout):
47 stdout = os.fdopen(stdout.fileno(), r'wb', 1)
47 stdout = os.fdopen(stdout.fileno(), r'wb', 1)
48
48
49 if pycompat.iswindows:
49 if pycompat.iswindows:
50 from .. import windows as platform
50 from .. import windows as platform
51 stdout = platform.winstdout(stdout)
51 stdout = platform.winstdout(stdout)
52 else:
52 else:
53 from .. import posix as platform
53 from .. import posix as platform
54
54
55 findexe = platform.findexe
55 findexe = platform.findexe
56 _gethgcmd = platform.gethgcmd
56 _gethgcmd = platform.gethgcmd
57 getuser = platform.getuser
57 getuser = platform.getuser
58 getpid = os.getpid
58 getpid = os.getpid
59 hidewindow = platform.hidewindow
59 hidewindow = platform.hidewindow
60 quotecommand = platform.quotecommand
60 quotecommand = platform.quotecommand
61 readpipe = platform.readpipe
61 readpipe = platform.readpipe
62 setbinary = platform.setbinary
62 setbinary = platform.setbinary
63 setsignalhandler = platform.setsignalhandler
63 setsignalhandler = platform.setsignalhandler
64 shellquote = platform.shellquote
64 shellquote = platform.shellquote
65 shellsplit = platform.shellsplit
65 shellsplit = platform.shellsplit
66 spawndetached = platform.spawndetached
66 spawndetached = platform.spawndetached
67 sshargs = platform.sshargs
67 sshargs = platform.sshargs
68 testpid = platform.testpid
68 testpid = platform.testpid
69
69
70 try:
70 try:
71 setprocname = osutil.setprocname
71 setprocname = osutil.setprocname
72 except AttributeError:
72 except AttributeError:
73 pass
73 pass
74 try:
74 try:
75 unblocksignal = osutil.unblocksignal
75 unblocksignal = osutil.unblocksignal
76 except AttributeError:
76 except AttributeError:
77 pass
77 pass
78
78
79 closefds = pycompat.isposix
79 closefds = pycompat.isposix
80
80
81 def explainexit(code):
81 def explainexit(code):
82 """return a 2-tuple (desc, code) describing a subprocess status
82 """return a 2-tuple (desc, code) describing a subprocess status
83 (codes from kill are negative - not os.system/wait encoding)"""
83 (codes from kill are negative - not os.system/wait encoding)"""
84 if code >= 0:
84 if code >= 0:
85 return _("exited with status %d") % code, code
85 return _("exited with status %d") % code, code
86 return _("killed by signal %d") % -code, -code
86 return _("killed by signal %d") % -code, -code
87
87
88 class _pfile(object):
88 class _pfile(object):
89 """File-like wrapper for a stream opened by subprocess.Popen()"""
89 """File-like wrapper for a stream opened by subprocess.Popen()"""
90
90
91 def __init__(self, proc, fp):
91 def __init__(self, proc, fp):
92 self._proc = proc
92 self._proc = proc
93 self._fp = fp
93 self._fp = fp
94
94
95 def close(self):
95 def close(self):
96 # unlike os.popen(), this returns an integer in subprocess coding
96 # unlike os.popen(), this returns an integer in subprocess coding
97 self._fp.close()
97 self._fp.close()
98 return self._proc.wait()
98 return self._proc.wait()
99
99
100 def __iter__(self):
100 def __iter__(self):
101 return iter(self._fp)
101 return iter(self._fp)
102
102
103 def __getattr__(self, attr):
103 def __getattr__(self, attr):
104 return getattr(self._fp, attr)
104 return getattr(self._fp, attr)
105
105
106 def __enter__(self):
106 def __enter__(self):
107 return self
107 return self
108
108
109 def __exit__(self, exc_type, exc_value, exc_tb):
109 def __exit__(self, exc_type, exc_value, exc_tb):
110 self.close()
110 self.close()
111
111
112 def popen(cmd, mode='rb', bufsize=-1):
112 def popen(cmd, mode='rb', bufsize=-1):
113 if mode == 'rb':
113 if mode == 'rb':
114 return _popenreader(cmd, bufsize)
114 return _popenreader(cmd, bufsize)
115 elif mode == 'wb':
115 elif mode == 'wb':
116 return _popenwriter(cmd, bufsize)
116 return _popenwriter(cmd, bufsize)
117 raise error.ProgrammingError('unsupported mode: %r' % mode)
117 raise error.ProgrammingError('unsupported mode: %r' % mode)
118
118
119 def _popenreader(cmd, bufsize):
119 def _popenreader(cmd, bufsize):
120 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
120 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
121 close_fds=closefds,
121 close_fds=closefds,
122 stdout=subprocess.PIPE)
122 stdout=subprocess.PIPE)
123 return _pfile(p, p.stdout)
123 return _pfile(p, p.stdout)
124
124
125 def _popenwriter(cmd, bufsize):
125 def _popenwriter(cmd, bufsize):
126 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
126 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
127 close_fds=closefds,
127 close_fds=closefds,
128 stdin=subprocess.PIPE)
128 stdin=subprocess.PIPE)
129 return _pfile(p, p.stdin)
129 return _pfile(p, p.stdin)
130
130
131 def popen2(cmd, env=None, newlines=False):
131 def popen2(cmd, env=None, newlines=False):
132 # Setting bufsize to -1 lets the system decide the buffer size.
132 # Setting bufsize to -1 lets the system decide the buffer size.
133 # The default for bufsize is 0, meaning unbuffered. This leads to
133 # The default for bufsize is 0, meaning unbuffered. This leads to
134 # poor performance on Mac OS X: http://bugs.python.org/issue4194
134 # poor performance on Mac OS X: http://bugs.python.org/issue4194
135 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
135 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
136 close_fds=closefds,
136 close_fds=closefds,
137 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
137 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
138 universal_newlines=newlines,
138 universal_newlines=newlines,
139 env=env)
139 env=env)
140 return p.stdin, p.stdout
140 return p.stdin, p.stdout
141
141
142 def popen3(cmd, env=None, newlines=False):
142 def popen3(cmd, env=None, newlines=False):
143 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
143 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
144 return stdin, stdout, stderr
144 return stdin, stdout, stderr
145
145
146 def popen4(cmd, env=None, newlines=False, bufsize=-1):
146 def popen4(cmd, env=None, newlines=False, bufsize=-1):
147 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
147 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
148 close_fds=closefds,
148 close_fds=closefds,
149 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
149 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
150 stderr=subprocess.PIPE,
150 stderr=subprocess.PIPE,
151 universal_newlines=newlines,
151 universal_newlines=newlines,
152 env=env)
152 env=env)
153 return p.stdin, p.stdout, p.stderr, p
153 return p.stdin, p.stdout, p.stderr, p
154
154
155 def pipefilter(s, cmd):
155 def pipefilter(s, cmd):
156 '''filter string S through command CMD, returning its output'''
156 '''filter string S through command CMD, returning its output'''
157 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
157 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
158 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
158 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
159 pout, perr = p.communicate(s)
159 pout, perr = p.communicate(s)
160 return pout
160 return pout
161
161
162 def tempfilter(s, cmd):
162 def tempfilter(s, cmd):
163 '''filter string S through a pair of temporary files with CMD.
163 '''filter string S through a pair of temporary files with CMD.
164 CMD is used as a template to create the real command to be run,
164 CMD is used as a template to create the real command to be run,
165 with the strings INFILE and OUTFILE replaced by the real names of
165 with the strings INFILE and OUTFILE replaced by the real names of
166 the temporary files generated.'''
166 the temporary files generated.'''
167 inname, outname = None, None
167 inname, outname = None, None
168 try:
168 try:
169 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
169 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
170 fp = os.fdopen(infd, r'wb')
170 fp = os.fdopen(infd, r'wb')
171 fp.write(s)
171 fp.write(s)
172 fp.close()
172 fp.close()
173 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
173 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
174 os.close(outfd)
174 os.close(outfd)
175 cmd = cmd.replace('INFILE', inname)
175 cmd = cmd.replace('INFILE', inname)
176 cmd = cmd.replace('OUTFILE', outname)
176 cmd = cmd.replace('OUTFILE', outname)
177 code = os.system(cmd)
177 code = system(cmd)
178 if pycompat.sysplatform == 'OpenVMS' and code & 1:
178 if pycompat.sysplatform == 'OpenVMS' and code & 1:
179 code = 0
179 code = 0
180 if code:
180 if code:
181 raise error.Abort(_("command '%s' failed: %s") %
181 raise error.Abort(_("command '%s' failed: %s") %
182 (cmd, explainexit(code)))
182 (cmd, explainexit(code)[0]))
183 with open(outname, 'rb') as fp:
183 with open(outname, 'rb') as fp:
184 return fp.read()
184 return fp.read()
185 finally:
185 finally:
186 try:
186 try:
187 if inname:
187 if inname:
188 os.unlink(inname)
188 os.unlink(inname)
189 except OSError:
189 except OSError:
190 pass
190 pass
191 try:
191 try:
192 if outname:
192 if outname:
193 os.unlink(outname)
193 os.unlink(outname)
194 except OSError:
194 except OSError:
195 pass
195 pass
196
196
197 _filtertable = {
197 _filtertable = {
198 'tempfile:': tempfilter,
198 'tempfile:': tempfilter,
199 'pipe:': pipefilter,
199 'pipe:': pipefilter,
200 }
200 }
201
201
202 def filter(s, cmd):
202 def filter(s, cmd):
203 "filter a string through a command that transforms its input to its output"
203 "filter a string through a command that transforms its input to its output"
204 for name, fn in _filtertable.iteritems():
204 for name, fn in _filtertable.iteritems():
205 if cmd.startswith(name):
205 if cmd.startswith(name):
206 return fn(s, cmd[len(name):].lstrip())
206 return fn(s, cmd[len(name):].lstrip())
207 return pipefilter(s, cmd)
207 return pipefilter(s, cmd)
208
208
209 def mainfrozen():
209 def mainfrozen():
210 """return True if we are a frozen executable.
210 """return True if we are a frozen executable.
211
211
212 The code supports py2exe (most common, Windows only) and tools/freeze
212 The code supports py2exe (most common, Windows only) and tools/freeze
213 (portable, not much used).
213 (portable, not much used).
214 """
214 """
215 return (pycompat.safehasattr(sys, "frozen") or # new py2exe
215 return (pycompat.safehasattr(sys, "frozen") or # new py2exe
216 pycompat.safehasattr(sys, "importers") or # old py2exe
216 pycompat.safehasattr(sys, "importers") or # old py2exe
217 imp.is_frozen(u"__main__")) # tools/freeze
217 imp.is_frozen(u"__main__")) # tools/freeze
218
218
219 _hgexecutable = None
219 _hgexecutable = None
220
220
221 def hgexecutable():
221 def hgexecutable():
222 """return location of the 'hg' executable.
222 """return location of the 'hg' executable.
223
223
224 Defaults to $HG or 'hg' in the search path.
224 Defaults to $HG or 'hg' in the search path.
225 """
225 """
226 if _hgexecutable is None:
226 if _hgexecutable is None:
227 hg = encoding.environ.get('HG')
227 hg = encoding.environ.get('HG')
228 mainmod = sys.modules[r'__main__']
228 mainmod = sys.modules[r'__main__']
229 if hg:
229 if hg:
230 _sethgexecutable(hg)
230 _sethgexecutable(hg)
231 elif mainfrozen():
231 elif mainfrozen():
232 if getattr(sys, 'frozen', None) == 'macosx_app':
232 if getattr(sys, 'frozen', None) == 'macosx_app':
233 # Env variable set by py2app
233 # Env variable set by py2app
234 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
234 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
235 else:
235 else:
236 _sethgexecutable(pycompat.sysexecutable)
236 _sethgexecutable(pycompat.sysexecutable)
237 elif (os.path.basename(
237 elif (os.path.basename(
238 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
238 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
239 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
239 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
240 else:
240 else:
241 exe = findexe('hg') or os.path.basename(sys.argv[0])
241 exe = findexe('hg') or os.path.basename(sys.argv[0])
242 _sethgexecutable(exe)
242 _sethgexecutable(exe)
243 return _hgexecutable
243 return _hgexecutable
244
244
245 def _sethgexecutable(path):
245 def _sethgexecutable(path):
246 """set location of the 'hg' executable"""
246 """set location of the 'hg' executable"""
247 global _hgexecutable
247 global _hgexecutable
248 _hgexecutable = path
248 _hgexecutable = path
249
249
250 def _testfileno(f, stdf):
250 def _testfileno(f, stdf):
251 fileno = getattr(f, 'fileno', None)
251 fileno = getattr(f, 'fileno', None)
252 try:
252 try:
253 return fileno and fileno() == stdf.fileno()
253 return fileno and fileno() == stdf.fileno()
254 except io.UnsupportedOperation:
254 except io.UnsupportedOperation:
255 return False # fileno() raised UnsupportedOperation
255 return False # fileno() raised UnsupportedOperation
256
256
257 def isstdin(f):
257 def isstdin(f):
258 return _testfileno(f, sys.__stdin__)
258 return _testfileno(f, sys.__stdin__)
259
259
260 def isstdout(f):
260 def isstdout(f):
261 return _testfileno(f, sys.__stdout__)
261 return _testfileno(f, sys.__stdout__)
262
262
263 def protectstdio(uin, uout):
263 def protectstdio(uin, uout):
264 """Duplicate streams and redirect original if (uin, uout) are stdio
264 """Duplicate streams and redirect original if (uin, uout) are stdio
265
265
266 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
266 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
267 redirected to stderr so the output is still readable.
267 redirected to stderr so the output is still readable.
268
268
269 Returns (fin, fout) which point to the original (uin, uout) fds, but
269 Returns (fin, fout) which point to the original (uin, uout) fds, but
270 may be copy of (uin, uout). The returned streams can be considered
270 may be copy of (uin, uout). The returned streams can be considered
271 "owned" in that print(), exec(), etc. never reach to them.
271 "owned" in that print(), exec(), etc. never reach to them.
272 """
272 """
273 uout.flush()
273 uout.flush()
274 fin, fout = uin, uout
274 fin, fout = uin, uout
275 if uin is stdin:
275 if uin is stdin:
276 newfd = os.dup(uin.fileno())
276 newfd = os.dup(uin.fileno())
277 nullfd = os.open(os.devnull, os.O_RDONLY)
277 nullfd = os.open(os.devnull, os.O_RDONLY)
278 os.dup2(nullfd, uin.fileno())
278 os.dup2(nullfd, uin.fileno())
279 os.close(nullfd)
279 os.close(nullfd)
280 fin = os.fdopen(newfd, r'rb')
280 fin = os.fdopen(newfd, r'rb')
281 if uout is stdout:
281 if uout is stdout:
282 newfd = os.dup(uout.fileno())
282 newfd = os.dup(uout.fileno())
283 os.dup2(stderr.fileno(), uout.fileno())
283 os.dup2(stderr.fileno(), uout.fileno())
284 fout = os.fdopen(newfd, r'wb')
284 fout = os.fdopen(newfd, r'wb')
285 return fin, fout
285 return fin, fout
286
286
287 def restorestdio(uin, uout, fin, fout):
287 def restorestdio(uin, uout, fin, fout):
288 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
288 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
289 uout.flush()
289 uout.flush()
290 for f, uif in [(fin, uin), (fout, uout)]:
290 for f, uif in [(fin, uin), (fout, uout)]:
291 if f is not uif:
291 if f is not uif:
292 os.dup2(f.fileno(), uif.fileno())
292 os.dup2(f.fileno(), uif.fileno())
293 f.close()
293 f.close()
294
294
295 @contextlib.contextmanager
295 @contextlib.contextmanager
296 def protectedstdio(uin, uout):
296 def protectedstdio(uin, uout):
297 """Run code block with protected standard streams"""
297 """Run code block with protected standard streams"""
298 fin, fout = protectstdio(uin, uout)
298 fin, fout = protectstdio(uin, uout)
299 try:
299 try:
300 yield fin, fout
300 yield fin, fout
301 finally:
301 finally:
302 restorestdio(uin, uout, fin, fout)
302 restorestdio(uin, uout, fin, fout)
303
303
304 def shellenviron(environ=None):
304 def shellenviron(environ=None):
305 """return environ with optional override, useful for shelling out"""
305 """return environ with optional override, useful for shelling out"""
306 def py2shell(val):
306 def py2shell(val):
307 'convert python object into string that is useful to shell'
307 'convert python object into string that is useful to shell'
308 if val is None or val is False:
308 if val is None or val is False:
309 return '0'
309 return '0'
310 if val is True:
310 if val is True:
311 return '1'
311 return '1'
312 return pycompat.bytestr(val)
312 return pycompat.bytestr(val)
313 env = dict(encoding.environ)
313 env = dict(encoding.environ)
314 if environ:
314 if environ:
315 env.update((k, py2shell(v)) for k, v in environ.iteritems())
315 env.update((k, py2shell(v)) for k, v in environ.iteritems())
316 env['HG'] = hgexecutable()
316 env['HG'] = hgexecutable()
317 return env
317 return env
318
318
319 def system(cmd, environ=None, cwd=None, out=None):
319 def system(cmd, environ=None, cwd=None, out=None):
320 '''enhanced shell command execution.
320 '''enhanced shell command execution.
321 run with environment maybe modified, maybe in different dir.
321 run with environment maybe modified, maybe in different dir.
322
322
323 if out is specified, it is assumed to be a file-like object that has a
323 if out is specified, it is assumed to be a file-like object that has a
324 write() method. stdout and stderr will be redirected to out.'''
324 write() method. stdout and stderr will be redirected to out.'''
325 try:
325 try:
326 stdout.flush()
326 stdout.flush()
327 except Exception:
327 except Exception:
328 pass
328 pass
329 cmd = quotecommand(cmd)
329 cmd = quotecommand(cmd)
330 env = shellenviron(environ)
330 env = shellenviron(environ)
331 if out is None or isstdout(out):
331 if out is None or isstdout(out):
332 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
332 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
333 env=env, cwd=cwd)
333 env=env, cwd=cwd)
334 else:
334 else:
335 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
335 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
336 env=env, cwd=cwd, stdout=subprocess.PIPE,
336 env=env, cwd=cwd, stdout=subprocess.PIPE,
337 stderr=subprocess.STDOUT)
337 stderr=subprocess.STDOUT)
338 for line in iter(proc.stdout.readline, ''):
338 for line in iter(proc.stdout.readline, ''):
339 out.write(line)
339 out.write(line)
340 proc.wait()
340 proc.wait()
341 rc = proc.returncode
341 rc = proc.returncode
342 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
342 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
343 rc = 0
343 rc = 0
344 return rc
344 return rc
345
345
346 def gui():
346 def gui():
347 '''Are we running in a GUI?'''
347 '''Are we running in a GUI?'''
348 if pycompat.isdarwin:
348 if pycompat.isdarwin:
349 if 'SSH_CONNECTION' in encoding.environ:
349 if 'SSH_CONNECTION' in encoding.environ:
350 # handle SSH access to a box where the user is logged in
350 # handle SSH access to a box where the user is logged in
351 return False
351 return False
352 elif getattr(osutil, 'isgui', None):
352 elif getattr(osutil, 'isgui', None):
353 # check if a CoreGraphics session is available
353 # check if a CoreGraphics session is available
354 return osutil.isgui()
354 return osutil.isgui()
355 else:
355 else:
356 # pure build; use a safe default
356 # pure build; use a safe default
357 return True
357 return True
358 else:
358 else:
359 return pycompat.iswindows or encoding.environ.get("DISPLAY")
359 return pycompat.iswindows or encoding.environ.get("DISPLAY")
360
360
361 def hgcmd():
361 def hgcmd():
362 """Return the command used to execute current hg
362 """Return the command used to execute current hg
363
363
364 This is different from hgexecutable() because on Windows we want
364 This is different from hgexecutable() because on Windows we want
365 to avoid things opening new shell windows like batch files, so we
365 to avoid things opening new shell windows like batch files, so we
366 get either the python call or current executable.
366 get either the python call or current executable.
367 """
367 """
368 if mainfrozen():
368 if mainfrozen():
369 if getattr(sys, 'frozen', None) == 'macosx_app':
369 if getattr(sys, 'frozen', None) == 'macosx_app':
370 # Env variable set by py2app
370 # Env variable set by py2app
371 return [encoding.environ['EXECUTABLEPATH']]
371 return [encoding.environ['EXECUTABLEPATH']]
372 else:
372 else:
373 return [pycompat.sysexecutable]
373 return [pycompat.sysexecutable]
374 return _gethgcmd()
374 return _gethgcmd()
375
375
376 def rundetached(args, condfn):
376 def rundetached(args, condfn):
377 """Execute the argument list in a detached process.
377 """Execute the argument list in a detached process.
378
378
379 condfn is a callable which is called repeatedly and should return
379 condfn is a callable which is called repeatedly and should return
380 True once the child process is known to have started successfully.
380 True once the child process is known to have started successfully.
381 At this point, the child process PID is returned. If the child
381 At this point, the child process PID is returned. If the child
382 process fails to start or finishes before condfn() evaluates to
382 process fails to start or finishes before condfn() evaluates to
383 True, return -1.
383 True, return -1.
384 """
384 """
385 # Windows case is easier because the child process is either
385 # Windows case is easier because the child process is either
386 # successfully starting and validating the condition or exiting
386 # successfully starting and validating the condition or exiting
387 # on failure. We just poll on its PID. On Unix, if the child
387 # on failure. We just poll on its PID. On Unix, if the child
388 # process fails to start, it will be left in a zombie state until
388 # process fails to start, it will be left in a zombie state until
389 # the parent wait on it, which we cannot do since we expect a long
389 # the parent wait on it, which we cannot do since we expect a long
390 # running process on success. Instead we listen for SIGCHLD telling
390 # running process on success. Instead we listen for SIGCHLD telling
391 # us our child process terminated.
391 # us our child process terminated.
392 terminated = set()
392 terminated = set()
393 def handler(signum, frame):
393 def handler(signum, frame):
394 terminated.add(os.wait())
394 terminated.add(os.wait())
395 prevhandler = None
395 prevhandler = None
396 SIGCHLD = getattr(signal, 'SIGCHLD', None)
396 SIGCHLD = getattr(signal, 'SIGCHLD', None)
397 if SIGCHLD is not None:
397 if SIGCHLD is not None:
398 prevhandler = signal.signal(SIGCHLD, handler)
398 prevhandler = signal.signal(SIGCHLD, handler)
399 try:
399 try:
400 pid = spawndetached(args)
400 pid = spawndetached(args)
401 while not condfn():
401 while not condfn():
402 if ((pid in terminated or not testpid(pid))
402 if ((pid in terminated or not testpid(pid))
403 and not condfn()):
403 and not condfn()):
404 return -1
404 return -1
405 time.sleep(0.1)
405 time.sleep(0.1)
406 return pid
406 return pid
407 finally:
407 finally:
408 if prevhandler is not None:
408 if prevhandler is not None:
409 signal.signal(signal.SIGCHLD, prevhandler)
409 signal.signal(signal.SIGCHLD, prevhandler)
@@ -1,63 +1,72 b''
1 Test encode/decode filters
1 Test encode/decode filters
2
2
3 $ hg init
3 $ hg init
4 $ cat > .hg/hgrc <<EOF
4 $ cat > .hg/hgrc <<EOF
5 > [encode]
5 > [encode]
6 > not.gz = tr [:lower:] [:upper:]
6 > not.gz = tr [:lower:] [:upper:]
7 > *.gz = gzip -d
7 > *.gz = gzip -d
8 > [decode]
8 > [decode]
9 > not.gz = tr [:upper:] [:lower:]
9 > not.gz = tr [:upper:] [:lower:]
10 > *.gz = gzip
10 > *.gz = gzip
11 > EOF
11 > EOF
12 $ echo "this is a test" | gzip > a.gz
12 $ echo "this is a test" | gzip > a.gz
13 $ echo "this is a test" > not.gz
13 $ echo "this is a test" > not.gz
14 $ hg add *
14 $ hg add *
15 $ hg ci -m "test"
15 $ hg ci -m "test"
16
16
17 no changes
17 no changes
18
18
19 $ hg status
19 $ hg status
20 $ touch *
20 $ touch *
21
21
22 no changes
22 no changes
23
23
24 $ hg status
24 $ hg status
25
25
26 check contents in repo are encoded
26 check contents in repo are encoded
27
27
28 $ hg debugdata a.gz 0
28 $ hg debugdata a.gz 0
29 this is a test
29 this is a test
30 $ hg debugdata not.gz 0
30 $ hg debugdata not.gz 0
31 THIS IS A TEST
31 THIS IS A TEST
32
32
33 check committed content was decoded
33 check committed content was decoded
34
34
35 $ gunzip < a.gz
35 $ gunzip < a.gz
36 this is a test
36 this is a test
37 $ cat not.gz
37 $ cat not.gz
38 this is a test
38 this is a test
39 $ rm *
39 $ rm *
40 $ hg co -C
40 $ hg co -C
41 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42
42
43 check decoding of our new working dir copy
43 check decoding of our new working dir copy
44
44
45 $ gunzip < a.gz
45 $ gunzip < a.gz
46 this is a test
46 this is a test
47 $ cat not.gz
47 $ cat not.gz
48 this is a test
48 this is a test
49
49
50 check hg cat operation
50 check hg cat operation
51
51
52 $ hg cat a.gz
52 $ hg cat a.gz
53 this is a test
53 this is a test
54 $ hg cat --decode a.gz | gunzip
54 $ hg cat --decode a.gz | gunzip
55 this is a test
55 this is a test
56 $ mkdir subdir
56 $ mkdir subdir
57 $ cd subdir
57 $ cd subdir
58 $ hg -R .. cat ../a.gz
58 $ hg -R .. cat ../a.gz
59 this is a test
59 this is a test
60 $ hg -R .. cat --decode ../a.gz | gunzip
60 $ hg -R .. cat --decode ../a.gz | gunzip
61 this is a test
61 this is a test
62 $ cd ..
63
64 check tempfile filter
65
66 $ hg cat a.gz --decode --config 'decode.*.gz=tempfile:gzip -c INFILE > OUTFILE' | gunzip
67 this is a test
68 $ hg cat a.gz --decode --config 'decode.*.gz=tempfile:sh -c "exit 1"'
69 abort: command '*' failed: exited with status 1 (glob)
70 [255]
62
71
63 $ cd ..
72 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now