Cross platform spawning
Trent Mick
trentm at ActiveState.com
Tue Jun 11 12:08:23 EDT 2002
[Peter Scott wrote]
> I am trying to make a script spawn a seperate process, like you would
> do like this on unix:
>
> os.system('python myscript.py myargument &')
>
> But the & at the end (to make a separate process) doesn't work on
> Windows. I want the script to run on both Linux (*BSD, whatever) and
> Windows. I didn't want to have to find out the path to the python
> interpreter, so os.spawnlp (with no blocking) sounded perfect until I
> saw that it wasn't available on Windows.
>
> Does anyone out there know what I can do in this situation?
You could try to use os.spawnl(), which *is* availble on Windows. The
only pain is that you have to manually determine the full path to the
executable yourself via some which-like algorithm. I have attached a
script of mine that can do that on Windows and Linux (though I haven't
teseted it a *whole* lot.
Cheers,
Trent
--
Trent Mick
TrentM at ActiveState.com
-------------- next part --------------
#!/usr/bin/env python
"""
Show full path of commands.
Usage:
which [<options>...] [<command-name>...]
Options:
-h, --help Print this help and exit.
-a, --all Print *all* matching paths.
-v, --verbose Print out near misses on stderr.
--version Print the version info and exit.
Show the full path to the program that would be run for each given
command name, if any. Which, like GNU's which, returns the number of
failed arguments, or -1 when no <command-name> was given.
"""
#REQUIREMENTS:
# - Python >= 2.2 (because of the use of generators)
#
#TODO:
# - improve the test suite
# * launcher app? make the launcher app generic, it should be able to
# use argv[0] to determine what to launch, then it can be built and
# just renamed.
# * explain what *near* misses are, perhaps that should be the default
# and -q can be used to suppress them. I think so, yes.
from __future__ import generators
import os
import sys
import getopt
import stat
#---- exceptions
class WhichError(Exception):
pass
#---- global data
_version_ = (0, 1, 2)
gVerbose = 0
#---- internal support stuff
def _getRegisteredExecutable(exeName):
"""Windows allow application paths to be registered in the registry."""
if sys.platform.startswith('win'):
if os.path.splitext(exeName)[1].lower() != '.exe':
exeName += '.exe'
import _winreg
try:
return _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"\
+ exeName)
except _winreg.error:
pass
return None
def _samefile(fname1, fname2):
if sys.platform.startswith('win'):
return ( os.path.normpath(os.path.normcase(fname1)) ==\
os.path.normpath(os.path.normcase(fname2)) )
else:
return os.path.samefile(fname1, fname2)
def _cull(potential, matches):
"""Cull inappropriate matches. Possible reasons:
- a duplicate of a previous match
- not a disk file
- not executable (non-Windows)
If 'potential' is approved it is returned and added to 'matches'.
Otherwise, None is returned.
"""
global gVerbose
for match in matches: # don't yield duplicates
if _samefile(potential, match):
if gVerbose:
sys.stderr.write("duplicate: %s\n" % potential)
return None
else:
if not stat.S_ISREG(os.stat(potential).st_mode):
if gVerbose:
sys.stderr.write("not a regular file: %s\n" % potential)
elif not os.access(potential, os.X_OK):
if gVerbose:
sys.stderr.write("no executable access: %s\n" % potential)
else:
matches.append(potential)
return potential
#---- public stuff
def which(command):
"""Generate a list of found apps for the given 'command' on the path."""
matches = []
path = os.environ.get("PATH", "").split(os.pathsep)
if sys.platform.startswith("win"):
path.insert(0, os.curdir) # implied by Windows shell
# Windows has the concept of a list of extensions (PATHEXT env var).
exts = []
if sys.platform.startswith("win"):
exts = os.environ.get("PATHEXT", "").split(os.pathsep)
# If '.exe' is not in exts then obviously this is Win9x and or a
# bogus PATHEXT, then use a reasonable default.
for ext in exts:
if ext.lower() == ".exe":
break
else:
exts = ['.COM', '.EXE', '.BAT']
# File name cannot have path separators because PATH lookup does not
# work that way.
if os.sep in command or os.altsep and os.altsep in command:
pass
else:
for dirName in path:
# On windows the dirName *could* be quoted, drop the quotes
if sys.platform.startswith("win") and len(dirName) >= 2\
and dirName[0] == '"' and dirName[-1] == '"':
dirName = dirName[1:-1]
for ext in ['']+exts:
absName = os.path.abspath(
os.path.normpath(os.path.join(dirName, command+ext)))
if os.path.isfile(absName):
match = _cull(absName, matches)
if match:
yield match
absName = _getRegisteredExecutable(command)
if absName is not None:
match = _cull(absName, matches)
if match:
yield match
#---- mainline
def main(argv):
global gVerbose, _version_
all = 0
try:
optlist, args = getopt.getopt(argv[1:], 'hav',
['help', 'all', 'version', 'verbose'])
except getopt.GetoptError, msg:
print "which: error: %s. Your invocation was: %s\n" % (msg, argv)
print __doc__
return 1
for opt, optarg in optlist:
if opt in ('-h', '--help'):
print __doc__
return 0
elif opt == '--version':
print "Which %s" % '.'.join([str(i) for i in _version_])
return 0
elif opt in ('-a', '--all'):
all = 1
elif opt in ('-v', '--verbose'):
gVerbose = 1
if len(args) == 0:
return -1
failures = 0
for arg in args:
#print "debug: search for %r" % arg
npaths = 0
for path in which(arg):
print path
npaths += 1
if not all:
break
if not npaths:
failures += 1
return failures
if __name__ == "__main__":
sys.exit( main(sys.argv) )
More information about the Python-list
mailing list