5
Implements exec_command function that is (almost) equivalent to
6
commands.getstatusoutput function but on NT, DOS systems the
7
returned status is actually correct (though, the returned status
8
values may be different by a factor). In addition, exec_command
9
takes keyword arguments for (re-)defining environment variables.
12
exec_command --- execute command in a specified directory and
13
in the modified environment.
14
splitcmdline --- inverse of ' '.join(argv)
15
find_executable --- locate a command using info from environment
16
variable PATH. Equivalent to posix `which`
19
Author: Pearu Peterson <pearu@cens.ioc.ee>
20
Created: 11 January 2003
24
Succesfully tested on:
25
os.name | sys.platform | comments
26
--------+--------------+----------
27
posix | linux2 | Debian (sid) Linux, Python 2.1.3+, 2.2.3+, 2.3.3
28
PyCrust 0.9.3, Idle 1.0.2
29
posix | linux2 | Red Hat 9 Linux, Python 2.1.3, 2.2.2, 2.3.2
30
posix | sunos5 | SunOS 5.9, Python 2.2, 2.3.2
31
posix | darwin | Darwin 7.2.0, Python 2.3
32
nt | win32 | Windows Me
33
Python 2.3(EE), Idle 1.0, PyCrust 0.7.2
35
nt | win32 | Windows 98, Python 2.1.1. Idle 0.8
36
nt | win32 | Cygwin 98-4.10, Python 2.1.1(MSC) - echo tests
37
fail i.e. redefining environment variables may
38
not work. FIXED: don't use cygwin echo!
39
Comment: also `cmd /c echo` will not work
40
but redefining environment variables do work.
41
posix | cygwin | Cygwin 98-4.10, Python 2.3.3(cygming special)
42
nt | win32 | Windows XP, Python 2.3.3
45
- Tests, that send messages to stderr, fail when executed from MSYS prompt
46
because the messages are lost at some point.
49
__all__ = ['exec_command','find_executable']
56
from numpy.distutils.misc_util import is_sequence
58
############################################################
60
from log import _global_log as log
62
############################################################
65
pythonexe = sys.executable
66
if os.name in ['nt','dos']:
67
fdir,fn = os.path.split(pythonexe)
68
fn = fn.upper().replace('PYTHONW','PYTHON')
69
pythonexe = os.path.join(fdir,fn)
70
assert os.path.isfile(pythonexe), '%r is not a file' % (pythonexe,)
73
############################################################
75
def splitcmdline(line):
76
""" Inverse of ' '.join(sys.argv).
78
log.debug('splitcmdline(%r)' % (line))
84
flag = (pc != '\\' and \
85
((cc=='"' and 1) or (cc=="'" and 2) or \
86
(cc==' ' and pc!=' ' and -2))) or flag
88
flag = (cc=='"' and pc!='\\' and nc==' ' and -1) or flag
90
flag = (cc=="'" and pc!='\\' and nc==' ' and -1) or flag
104
log.debug('splitcmdline -> %r' % (lst))
107
def test_splitcmdline():
108
l = splitcmdline('a b cc')
109
assert l==['a','b','cc'], repr(l)
110
l = splitcmdline('a')
111
assert l==['a'], repr(l)
112
l = splitcmdline('a " b cc"')
113
assert l==['a','" b cc"'], repr(l)
114
l = splitcmdline('"a bcc" -h')
115
assert l==['"a bcc"','-h'], repr(l)
116
l = splitcmdline(r'"\"a \" bcc" -h')
117
assert l==[r'"\"a \" bcc"','-h'], repr(l)
118
l = splitcmdline(" 'a bcc' -h")
119
assert l==["'a bcc'",'-h'], repr(l)
120
l = splitcmdline(r"'\'a \' bcc' -h")
121
assert l==[r"'\'a \' bcc'",'-h'], repr(l)
123
############################################################
125
def find_executable(exe, path=None):
126
""" Return full path of a executable.
128
log.debug('find_executable(%r)' % exe)
131
path = os.environ.get('PATH',os.defpath)
132
if os.name=='posix' and sys.version[:3]>'2.1':
133
realpath = os.path.realpath
135
realpath = lambda a:a
139
if os.name in ['nt','dos','os2']:
140
fn,ext = os.path.splitext(exe)
141
extra_suffices = ['.exe','.com','.bat']
142
if ext.lower() not in extra_suffices:
143
suffices = extra_suffices
144
if os.path.isabs(exe):
147
paths = map(os.path.abspath, path.split(os.pathsep))
148
if 0 and os.name == 'nt':
152
d,p = os.path.splitdrive(path)
153
if p.lower().find('cygwin') >= 0:
154
cygwin_paths.append(path)
156
new_paths.append(path)
157
paths = new_paths + cygwin_paths
159
fn = os.path.join(path,exe)
162
if not os.path.islink(f_ext):
164
f_ext = realpath(f_ext)
165
if os.path.isfile(f_ext) and os.access(f_ext,os.X_OK):
166
log.debug('Found executable %s' % f_ext)
168
if os.path.islink(exe):
169
# Don't follow symbolic links. E.g. when using colorgcc then
170
# gcc -> /usr/bin/colorgcc
171
# g77 -> /usr/bin/colorgcc
175
if not os.path.isfile(exe) or os.access(exe,os.X_OK):
176
log.warn('Could not locate executable %s' % orig_exe)
180
############################################################
182
def _preserve_environment( names ):
183
log.debug('_preserve_environment(%r)' % (names))
186
env[name] = os.environ.get(name)
189
def _update_environment( **env ):
190
log.debug('_update_environment(...)')
191
for name,value in env.items():
192
os.environ[name] = value or ''
194
def exec_command( command,
195
execute_in='', use_shell=None, use_tee = None,
198
""" Return (status,output) of executed command.
200
command is a concatenated string of executable and arguments.
201
The output contains both stdout and stderr messages.
202
The following special keyword arguments can be used:
203
use_shell - execute `sh -c command`
204
use_tee - pipe the output of command through tee
205
execute_in - before command `cd execute_in` and after `cd -`.
207
On NT, DOS systems the returned status is correct for external commands.
208
Wild cards will not work for non-posix systems or when use_shell=0.
210
log.debug('exec_command(%r,%s)' % (command,\
211
','.join(['%s=%r'%kv for kv in env.items()])))
214
use_tee = os.name=='posix'
215
if use_shell is None:
216
use_shell = os.name=='posix'
217
execute_in = os.path.abspath(execute_in)
218
oldcwd = os.path.abspath(os.getcwd())
220
if __name__[-12:] == 'exec_command':
221
exec_dir = os.path.dirname(os.path.abspath(__file__))
222
elif os.path.isfile('exec_command.py'):
223
exec_dir = os.path.abspath('.')
225
exec_dir = os.path.abspath(sys.argv[0])
226
if os.path.isfile(exec_dir):
227
exec_dir = os.path.dirname(exec_dir)
229
if oldcwd!=execute_in:
231
log.debug('New cwd: %s' % execute_in)
233
log.debug('Retaining cwd: %s' % oldcwd)
235
oldenv = _preserve_environment( env.keys() )
236
_update_environment( **env )
239
# _exec_command is robust but slow, it relies on
240
# usable sys.std*.fileno() descriptors. If they
241
# are bad (like in win32 Idle, PyCrust environments)
242
# then _exec_command_python (even slower)
243
# will be used as a last resort.
245
# _exec_command_posix uses os.system and is faster
246
# but not on all platforms os.system will return
248
if _with_python and (0 or sys.__stdout__.fileno()==-1):
249
st = _exec_command_python(command,
250
exec_command_dir = exec_dir,
252
elif os.name=='posix':
253
st = _exec_command_posix(command,
258
st = _exec_command(command, use_shell=use_shell,
259
use_tee=use_tee,**env)
261
if oldcwd!=execute_in:
263
log.debug('Restored cwd to %s' % oldcwd)
264
_update_environment(**oldenv)
268
def _exec_command_posix( command,
272
log.debug('_exec_command_posix(...)')
274
if is_sequence(command):
275
command_str = ' '.join(list(command))
277
command_str = command
279
tmpfile = tempfile.mktemp()
282
stsfile = tempfile.mktemp()
285
filter = r'| tr -cd "\n" | tr "\n" "."; echo'
286
command_posix = '( %s ; echo $? > %s ) 2>&1 | tee %s %s'\
287
% (command_str,stsfile,tmpfile,filter)
289
stsfile = tempfile.mktemp()
290
command_posix = '( %s ; echo $? > %s ) > %s 2>&1'\
291
% (command_str,stsfile,tmpfile)
292
#command_posix = '( %s ) > %s 2>&1' % (command_str,tmpfile)
294
log.debug('Running os.system(%r)' % (command_posix))
295
status = os.system(command_posix)
299
# if command_tee fails then fall back to robust exec_command
300
log.warn('_exec_command_posix failed (status=%s)' % status)
301
return _exec_command(command, use_shell=use_shell, **env)
303
if stsfile is not None:
304
f = open(stsfile,'r')
305
status_text = f.read()
306
status = int(status_text)
310
f = open(tmpfile,'r')
321
def _exec_command_python(command,
322
exec_command_dir='', **env):
323
log.debug('_exec_command_python(...)')
325
python_exe = get_pythonexe()
326
cmdfile = tempfile.mktemp()
327
stsfile = tempfile.mktemp()
328
outfile = tempfile.mktemp()
330
f = open(cmdfile,'w')
331
f.write('import os\n')
332
f.write('import sys\n')
333
f.write('sys.path.insert(0,%r)\n' % (exec_command_dir))
334
f.write('from exec_command import exec_command\n')
335
f.write('del sys.path[0]\n')
336
f.write('cmd = %r\n' % command)
337
f.write('os.environ = %r\n' % (os.environ))
338
f.write('s,o = exec_command(cmd, _with_python=0, **%r)\n' % (env))
339
f.write('f=open(%r,"w")\nf.write(str(s))\nf.close()\n' % (stsfile))
340
f.write('f=open(%r,"w")\nf.write(o)\nf.close()\n' % (outfile))
343
cmd = '%s %s' % (python_exe, cmdfile)
344
status = os.system(cmd)
346
raise RuntimeError("%r failed" % (cmd,))
349
f = open(stsfile,'r')
350
status = int(f.read())
354
f = open(outfile,'r')
362
if arg[0]!='"' and ' ' in arg:
366
def _exec_command( command, use_shell=None, use_tee = None, **env ):
367
log.debug('_exec_command(...)')
369
if use_shell is None:
370
use_shell = os.name=='posix'
372
use_tee = os.name=='posix'
376
# We use shell (unless use_shell==0) so that wildcards can be
378
sh = os.environ.get('SHELL','/bin/sh')
379
if is_sequence(command):
380
argv = [sh,'-c',' '.join(list(command))]
382
argv = [sh,'-c',command]
384
# On NT, DOS we avoid using command.com as it's exit status is
385
# not related to the exit status of a command.
386
if is_sequence(command):
389
argv = splitcmdline(command)
391
if hasattr(os,'spawnvpe'):
392
spawn_command = os.spawnvpe
394
spawn_command = os.spawnve
395
argv[0] = find_executable(argv[0])
396
if not os.path.isfile(argv[0]):
397
log.warn('Executable %s does not exist' % (argv[0]))
398
if os.name in ['nt','dos']:
399
# argv[0] might be internal command
400
argv = [os.environ['COMSPEC'],'/C'] + argv
403
# sys.__std*__ is used instead of sys.std* because environments
404
# like IDLE, PyCrust, etc overwrite sys.std* commands.
405
so_fileno = sys.__stdout__.fileno()
406
se_fileno = sys.__stderr__.fileno()
407
so_flush = sys.__stdout__.flush
408
se_flush = sys.__stderr__.flush
409
so_dup = os.dup(so_fileno)
410
se_dup = os.dup(se_fileno)
412
outfile = tempfile.mktemp()
413
fout = open(outfile,'w')
415
errfile = tempfile.mktemp()
416
ferr = open(errfile,'w')
418
log.debug('Running %s(%s,%r,%r,os.environ)' \
419
% (spawn_command.__name__,os.P_WAIT,argv[0],argv))
422
if not using_command:
423
argv[0] = quote_arg(argv0)
427
os.dup2(fout.fileno(),so_fileno)
429
#XXX: disabled for now as it does not work from cmd under win32.
431
os.dup2(ferr.fileno(),se_fileno)
433
os.dup2(fout.fileno(),se_fileno)
435
status = spawn_command(os.P_WAIT,argv0,argv,os.environ)
436
except OSError,errmess:
438
sys.stderr.write('%s: %s'%(errmess,argv[0]))
442
os.dup2(so_dup,so_fileno)
443
os.dup2(se_dup,se_fileno)
446
fout = open(outfile,'r')
453
ferr = open(errfile,'r')
454
errmess = ferr.read()
457
if errmess and not status:
458
# Not sure how to handle the case where errmess
459
# contains only warning messages and that should
460
# not be treated as errors.
464
#text = '%sCOMMAND %r FAILED: %s' %(text,command,errmess)
465
text = text + errmess
479
pythonexe = get_pythonexe()
480
echo = find_executable('echo')
481
using_cygwin_echo = echo != 'echo'
482
if using_cygwin_echo:
483
log.warn('Using cygwin echo in win32 environment is not supported')
485
s,o=exec_command(pythonexe\
486
+' -c "import os;print os.environ.get(\'AAA\',\'\')"')
487
assert s==0 and o=='',(s,o)
489
s,o=exec_command(pythonexe\
490
+' -c "import os;print os.environ.get(\'AAA\')"',
492
assert s==0 and o=='Tere',(s,o)
494
os.environ['BBB'] = 'Hi'
495
s,o=exec_command(pythonexe\
496
+' -c "import os;print os.environ.get(\'BBB\',\'\')"')
497
assert s==0 and o=='Hi',(s,o)
499
s,o=exec_command(pythonexe\
500
+' -c "import os;print os.environ.get(\'BBB\',\'\')"',
502
assert s==0 and o=='Hey',(s,o)
504
s,o=exec_command(pythonexe\
505
+' -c "import os;print os.environ.get(\'BBB\',\'\')"')
506
assert s==0 and o=='Hi',(s,o)
508
s,o=exec_command('echo Hello')
509
assert s==0 and o=='Hello',(s,o)
511
s,o=exec_command('echo a%AAA%')
512
assert s==0 and o=='a',(s,o)
514
s,o=exec_command('echo a%AAA%',AAA='Tere')
515
assert s==0 and o=='aTere',(s,o)
517
os.environ['BBB'] = 'Hi'
518
s,o=exec_command('echo a%BBB%')
519
assert s==0 and o=='aHi',(s,o)
521
s,o=exec_command('echo a%BBB%',BBB='Hey')
522
assert s==0 and o=='aHey', (s,o)
523
s,o=exec_command('echo a%BBB%')
524
assert s==0 and o=='aHi',(s,o)
526
s,o=exec_command('this_is_not_a_command')
527
assert s and o!='',(s,o)
529
s,o=exec_command('type not_existing_file')
530
assert s and o!='',(s,o)
532
s,o=exec_command('echo path=%path%')
533
assert s==0 and o!='',(s,o)
535
s,o=exec_command('%s -c "import sys;sys.stderr.write(sys.platform)"' \
537
assert s==0 and o=='win32',(s,o)
539
s,o=exec_command('%s -c "raise \'Ignore me.\'"' % pythonexe)
540
assert s==1 and o,(s,o)
542
s,o=exec_command('%s -c "import sys;sys.stderr.write(\'0\');sys.stderr.write(\'1\');sys.stderr.write(\'2\')"'\
544
assert s==0 and o=='012',(s,o)
546
s,o=exec_command('%s -c "import sys;sys.exit(15)"' % pythonexe)
547
assert s==15 and o=='',(s,o)
549
s,o=exec_command('%s -c "print \'Heipa\'"' % pythonexe)
550
assert s==0 and o=='Heipa',(s,o)
554
def test_posix(**kws):
555
s,o=exec_command("echo Hello",**kws)
556
assert s==0 and o=='Hello',(s,o)
558
s,o=exec_command('echo $AAA',**kws)
559
assert s==0 and o=='',(s,o)
561
s,o=exec_command('echo "$AAA"',AAA='Tere',**kws)
562
assert s==0 and o=='Tere',(s,o)
565
s,o=exec_command('echo "$AAA"',**kws)
566
assert s==0 and o=='',(s,o)
568
os.environ['BBB'] = 'Hi'
569
s,o=exec_command('echo "$BBB"',**kws)
570
assert s==0 and o=='Hi',(s,o)
572
s,o=exec_command('echo "$BBB"',BBB='Hey',**kws)
573
assert s==0 and o=='Hey',(s,o)
575
s,o=exec_command('echo "$BBB"',**kws)
576
assert s==0 and o=='Hi',(s,o)
579
s,o=exec_command('this_is_not_a_command',**kws)
580
assert s!=0 and o!='',(s,o)
582
s,o=exec_command('echo path=$PATH',**kws)
583
assert s==0 and o!='',(s,o)
585
s,o=exec_command('python -c "import sys,os;sys.stderr.write(os.name)"',**kws)
586
assert s==0 and o=='posix',(s,o)
588
s,o=exec_command('python -c "raise \'Ignore me.\'"',**kws)
589
assert s==1 and o,(s,o)
591
s,o=exec_command('python -c "import sys;sys.stderr.write(\'0\');sys.stderr.write(\'1\');sys.stderr.write(\'2\')"',**kws)
592
assert s==0 and o=='012',(s,o)
594
s,o=exec_command('python -c "import sys;sys.exit(15)"',**kws)
595
assert s==15 and o=='',(s,o)
597
s,o=exec_command('python -c "print \'Heipa\'"',**kws)
598
assert s==0 and o=='Heipa',(s,o)
602
def test_execute_in(**kws):
603
pythonexe = get_pythonexe()
604
tmpfile = tempfile.mktemp()
605
fn = os.path.basename(tmpfile)
606
tmpdir = os.path.dirname(tmpfile)
607
f = open(tmpfile,'w')
611
s,o = exec_command('%s -c "print \'Ignore the following IOError:\','\
612
'open(%r,\'r\')"' % (pythonexe,fn),**kws)
613
assert s and o!='',(s,o)
614
s,o = exec_command('%s -c "print open(%r,\'r\').read()"' % (pythonexe,fn),
615
execute_in = tmpdir,**kws)
616
assert s==0 and o=='Hello',(s,o)
621
s,o = exec_command(['svn','status'],**kws)
627
s,o = exec_command(['cl','/V'],**kws)
633
elif os.name in ['nt','dos']:
636
raise NotImplementedError,'exec_command tests for '+os.name
638
############################################################
640
if __name__ == "__main__":
645
test_execute_in(use_tee=0)
646
test_execute_in(use_tee=1)