Commit b6e8c7e8 authored by Gregory P. Smith's avatar Gregory P. Smith

Add an os.get_exec_path() function to return the list of directories

that launching a subprocess will search for the executable.
Refactors some code in os._execvpe().
parent 9a816974
......@@ -136,6 +136,17 @@ process and user.
These functions are described in :ref:`os-file-dir`.
.. function:: get_exec_path(env=None)
Returns the list of directories that will be searched for a named
executable, similar to a shell, when launching a process.
*env*, when specified, should be an environment variable dictionary
to lookup the PATH in.
By default, when *env* is None, :data:`environ` is used.
.. versionadded:: 3.2
.. function:: ctermid()
Return the filename corresponding to the controlling terminal of the process.
......
......@@ -342,28 +342,23 @@ __all__.extend(["execl","execle","execlp","execlpe","execvp","execvpe"])
def _execvpe(file, args, env=None):
if env is not None:
func = execve
exec_func = execve
argrest = (args, env)
else:
func = execv
exec_func = execv
argrest = (args,)
env = environ
head, tail = path.split(file)
if head:
func(file, *argrest)
exec_func(file, *argrest)
return
if 'PATH' in env:
envpath = env['PATH']
else:
envpath = defpath
PATH = envpath.split(pathsep)
last_exc = saved_exc = None
saved_tb = None
for dir in PATH:
for dir in get_exec_path(env):
fullname = path.join(dir, file)
try:
func(fullname, *argrest)
exec_func(fullname, *argrest)
except error as e:
last_exc = e
tb = sys.exc_info()[2]
......@@ -376,6 +371,18 @@ def _execvpe(file, args, env=None):
raise last_exc.with_traceback(tb)
def get_exec_path(env=None):
"""Returns the sequence of directories that will be searched for the
named executable (similar to a shell) when launching a process.
*env* must be an environment variable dict or None. If *env* is None,
os.environ will be used.
"""
if env is None:
env = environ
return env.get('PATH', defpath).split(pathsep)
# Change environ to automatically call putenv(), unsetenv if they exist.
from _abcoll import MutableMapping # Can't use collections (bootstrap)
......
......@@ -407,6 +407,27 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol):
self.assertTrue(isinstance(env.data, dict))
self.assertEqual(repr(env), 'environ({!r})'.format(env.data))
def test_get_exec_path(self):
defpath_list = os.defpath.split(os.pathsep)
test_path = ['/monty', '/python', '', '/flying/circus']
test_env = {'PATH': os.pathsep.join(test_path)}
saved_environ = os.environ
try:
os.environ = dict(test_env)
# Test that defaulting to os.environ works.
self.assertSequenceEqual(test_path, os.get_exec_path())
self.assertSequenceEqual(test_path, os.get_exec_path(env=None))
finally:
os.environ = saved_environ
# No PATH environment variable
self.assertSequenceEqual(defpath_list, os.get_exec_path({}))
# Empty PATH environment variable
self.assertSequenceEqual(('',), os.get_exec_path({'PATH':''}))
# Supplied PATH environment variable
self.assertSequenceEqual(test_path, os.get_exec_path(test_env))
class WalkTests(unittest.TestCase):
"""Tests for os.walk()."""
......
......@@ -701,6 +701,9 @@ Library
- Issue #6218: io.StringIO and io.BytesIO instances are now picklable.
- The os.get_exec_path() function to return the list of directories that will
be searched for an executable when launching a subprocess was added.
Extension Modules
-----------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment