mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
bpo-37369: Fix initialization of sys members when launched via an app container (GH-14467)
sys._base_executable is now always defined on all platforms, and can be overridden through configuration. Also adds test.support.PythonSymlink to encapsulate platform-specific logic for symlinking sys.executable
This commit is contained in:
parent
0cba121029
commit
323e743d48
17 changed files with 411 additions and 278 deletions
|
@ -373,10 +373,11 @@ typedef struct {
|
||||||
module_search_paths_set is equal
|
module_search_paths_set is equal
|
||||||
to zero. */
|
to zero. */
|
||||||
|
|
||||||
wchar_t *executable; /* sys.executable */
|
wchar_t *executable; /* sys.executable */
|
||||||
wchar_t *prefix; /* sys.prefix */
|
wchar_t *base_executable; /* sys._base_executable */
|
||||||
wchar_t *base_prefix; /* sys.base_prefix */
|
wchar_t *prefix; /* sys.prefix */
|
||||||
wchar_t *exec_prefix; /* sys.exec_prefix */
|
wchar_t *base_prefix; /* sys.base_prefix */
|
||||||
|
wchar_t *exec_prefix; /* sys.exec_prefix */
|
||||||
wchar_t *base_exec_prefix; /* sys.base_exec_prefix */
|
wchar_t *base_exec_prefix; /* sys.base_exec_prefix */
|
||||||
|
|
||||||
/* --- Parameter only used by Py_Main() ---------- */
|
/* --- Parameter only used by Py_Main() ---------- */
|
||||||
|
|
|
@ -27,6 +27,8 @@ typedef struct _PyPathConfig {
|
||||||
are ignored when their value are equal to -1 (unset). */
|
are ignored when their value are equal to -1 (unset). */
|
||||||
int isolated;
|
int isolated;
|
||||||
int site_import;
|
int site_import;
|
||||||
|
/* Set when a venv is detected */
|
||||||
|
wchar_t *base_executable;
|
||||||
} _PyPathConfig;
|
} _PyPathConfig;
|
||||||
|
|
||||||
#define _PyPathConfig_INIT \
|
#define _PyPathConfig_INIT \
|
||||||
|
|
|
@ -22,8 +22,7 @@ WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
|
||||||
def _path_eq(p1, p2):
|
def _path_eq(p1, p2):
|
||||||
return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2)
|
return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2)
|
||||||
|
|
||||||
WINENV = (hasattr(sys, '_base_executable') and
|
WINENV = not _path_eq(sys.executable, sys._base_executable)
|
||||||
not _path_eq(sys.executable, sys._base_executable))
|
|
||||||
|
|
||||||
|
|
||||||
def _close_handles(*handles):
|
def _close_handles(*handles):
|
||||||
|
|
|
@ -459,13 +459,6 @@ def venv(known_paths):
|
||||||
env = os.environ
|
env = os.environ
|
||||||
if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
|
if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
|
||||||
executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__']
|
executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__']
|
||||||
elif sys.platform == 'win32' and '__PYVENV_LAUNCHER__' in env:
|
|
||||||
executable = sys.executable
|
|
||||||
import _winapi
|
|
||||||
sys._base_executable = _winapi.GetModuleFileName(0)
|
|
||||||
# bpo-35873: Clear the environment variable to avoid it being
|
|
||||||
# inherited by child processes.
|
|
||||||
del os.environ['__PYVENV_LAUNCHER__']
|
|
||||||
else:
|
else:
|
||||||
executable = sys.executable
|
executable = sys.executable
|
||||||
exe_dir, _ = os.path.split(os.path.abspath(executable))
|
exe_dir, _ = os.path.split(os.path.abspath(executable))
|
||||||
|
|
|
@ -12,6 +12,7 @@ import faulthandler
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import functools
|
import functools
|
||||||
import gc
|
import gc
|
||||||
|
import glob
|
||||||
import importlib
|
import importlib
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import io
|
import io
|
||||||
|
@ -2498,6 +2499,84 @@ def skip_unless_symlink(test):
|
||||||
msg = "Requires functional symlink implementation"
|
msg = "Requires functional symlink implementation"
|
||||||
return test if ok else unittest.skip(msg)(test)
|
return test if ok else unittest.skip(msg)(test)
|
||||||
|
|
||||||
|
class PythonSymlink:
|
||||||
|
"""Creates a symlink for the current Python executable"""
|
||||||
|
def __init__(self, link=None):
|
||||||
|
self.link = link or os.path.abspath(TESTFN)
|
||||||
|
self._linked = []
|
||||||
|
self.real = os.path.realpath(sys.executable)
|
||||||
|
self._also_link = []
|
||||||
|
|
||||||
|
self._env = None
|
||||||
|
|
||||||
|
self._platform_specific()
|
||||||
|
|
||||||
|
def _platform_specific(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if sys.platform == "win32":
|
||||||
|
def _platform_specific(self):
|
||||||
|
import _winapi
|
||||||
|
|
||||||
|
if os.path.lexists(self.real) and not os.path.exists(self.real):
|
||||||
|
# App symlink appears to not exist, but we want the
|
||||||
|
# real executable here anyway
|
||||||
|
self.real = _winapi.GetModuleFileName(0)
|
||||||
|
|
||||||
|
dll = _winapi.GetModuleFileName(sys.dllhandle)
|
||||||
|
src_dir = os.path.dirname(dll)
|
||||||
|
dest_dir = os.path.dirname(self.link)
|
||||||
|
self._also_link.append((
|
||||||
|
dll,
|
||||||
|
os.path.join(dest_dir, os.path.basename(dll))
|
||||||
|
))
|
||||||
|
for runtime in glob.glob(os.path.join(src_dir, "vcruntime*.dll")):
|
||||||
|
self._also_link.append((
|
||||||
|
runtime,
|
||||||
|
os.path.join(dest_dir, os.path.basename(runtime))
|
||||||
|
))
|
||||||
|
|
||||||
|
self._env = {k.upper(): os.getenv(k) for k in os.environ}
|
||||||
|
self._env["PYTHONHOME"] = os.path.dirname(self.real)
|
||||||
|
if sysconfig.is_python_build(True):
|
||||||
|
self._env["PYTHONPATH"] = os.path.dirname(os.__file__)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
os.symlink(self.real, self.link)
|
||||||
|
self._linked.append(self.link)
|
||||||
|
for real, link in self._also_link:
|
||||||
|
os.symlink(real, link)
|
||||||
|
self._linked.append(link)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||||
|
for link in self._linked:
|
||||||
|
try:
|
||||||
|
os.remove(link)
|
||||||
|
except IOError as ex:
|
||||||
|
if verbose:
|
||||||
|
print("failed to clean up {}: {}".format(link, ex))
|
||||||
|
|
||||||
|
def _call(self, python, args, env, returncode):
|
||||||
|
cmd = [python, *args]
|
||||||
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE, env=env)
|
||||||
|
r = p.communicate()
|
||||||
|
if p.returncode != returncode:
|
||||||
|
if verbose:
|
||||||
|
print(repr(r[0]))
|
||||||
|
print(repr(r[1]), file=sys.stderr)
|
||||||
|
raise RuntimeError(
|
||||||
|
'unexpected return code: {0} (0x{0:08X})'.format(p.returncode))
|
||||||
|
return r
|
||||||
|
|
||||||
|
def call_real(self, *args, returncode=0):
|
||||||
|
return self._call(self.real, args, None, returncode)
|
||||||
|
|
||||||
|
def call_link(self, *args, returncode=0):
|
||||||
|
return self._call(self.link, args, self._env, returncode)
|
||||||
|
|
||||||
|
|
||||||
_can_xattr = None
|
_can_xattr = None
|
||||||
def can_xattr():
|
def can_xattr():
|
||||||
global _can_xattr
|
global _can_xattr
|
||||||
|
|
|
@ -362,6 +362,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'pythonpath_env': None,
|
'pythonpath_env': None,
|
||||||
'home': None,
|
'home': None,
|
||||||
'executable': GET_DEFAULT_CONFIG,
|
'executable': GET_DEFAULT_CONFIG,
|
||||||
|
'base_executable': GET_DEFAULT_CONFIG,
|
||||||
|
|
||||||
'prefix': GET_DEFAULT_CONFIG,
|
'prefix': GET_DEFAULT_CONFIG,
|
||||||
'base_prefix': GET_DEFAULT_CONFIG,
|
'base_prefix': GET_DEFAULT_CONFIG,
|
||||||
|
@ -534,14 +535,16 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
if expected['stdio_errors'] is self.GET_DEFAULT_CONFIG:
|
if expected['stdio_errors'] is self.GET_DEFAULT_CONFIG:
|
||||||
expected['stdio_errors'] = 'surrogateescape'
|
expected['stdio_errors'] = 'surrogateescape'
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
default_executable = self.test_exe
|
||||||
|
elif expected['program_name'] is not self.GET_DEFAULT_CONFIG:
|
||||||
|
default_executable = os.path.abspath(expected['program_name'])
|
||||||
|
else:
|
||||||
|
default_executable = os.path.join(os.getcwd(), '_testembed')
|
||||||
if expected['executable'] is self.GET_DEFAULT_CONFIG:
|
if expected['executable'] is self.GET_DEFAULT_CONFIG:
|
||||||
if sys.platform == 'win32':
|
expected['executable'] = default_executable
|
||||||
expected['executable'] = self.test_exe
|
if expected['base_executable'] is self.GET_DEFAULT_CONFIG:
|
||||||
else:
|
expected['base_executable'] = default_executable
|
||||||
if expected['program_name'] is not self.GET_DEFAULT_CONFIG:
|
|
||||||
expected['executable'] = os.path.abspath(expected['program_name'])
|
|
||||||
else:
|
|
||||||
expected['executable'] = os.path.join(os.getcwd(), '_testembed')
|
|
||||||
if expected['program_name'] is self.GET_DEFAULT_CONFIG:
|
if expected['program_name'] is self.GET_DEFAULT_CONFIG:
|
||||||
expected['program_name'] = './_testembed'
|
expected['program_name'] = './_testembed'
|
||||||
|
|
||||||
|
|
|
@ -610,9 +610,10 @@ class CGIHTTPServerTestCase(BaseTestCase):
|
||||||
|
|
||||||
# The shebang line should be pure ASCII: use symlink if possible.
|
# The shebang line should be pure ASCII: use symlink if possible.
|
||||||
# See issue #7668.
|
# See issue #7668.
|
||||||
|
self._pythonexe_symlink = None
|
||||||
if support.can_symlink():
|
if support.can_symlink():
|
||||||
self.pythonexe = os.path.join(self.parent_dir, 'python')
|
self.pythonexe = os.path.join(self.parent_dir, 'python')
|
||||||
os.symlink(sys.executable, self.pythonexe)
|
self._pythonexe_symlink = support.PythonSymlink(self.pythonexe).__enter__()
|
||||||
else:
|
else:
|
||||||
self.pythonexe = sys.executable
|
self.pythonexe = sys.executable
|
||||||
|
|
||||||
|
@ -655,8 +656,8 @@ class CGIHTTPServerTestCase(BaseTestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
try:
|
try:
|
||||||
os.chdir(self.cwd)
|
os.chdir(self.cwd)
|
||||||
if self.pythonexe != sys.executable:
|
if self._pythonexe_symlink:
|
||||||
os.remove(self.pythonexe)
|
self._pythonexe_symlink.__exit__(None, None, None)
|
||||||
if self.nocgi_path:
|
if self.nocgi_path:
|
||||||
os.remove(self.nocgi_path)
|
os.remove(self.nocgi_path)
|
||||||
if self.file1_path:
|
if self.file1_path:
|
||||||
|
|
|
@ -20,37 +20,9 @@ class PlatformTest(unittest.TestCase):
|
||||||
|
|
||||||
@support.skip_unless_symlink
|
@support.skip_unless_symlink
|
||||||
def test_architecture_via_symlink(self): # issue3762
|
def test_architecture_via_symlink(self): # issue3762
|
||||||
# On Windows, the EXE needs to know where pythonXY.dll and *.pyd is at
|
with support.PythonSymlink() as py:
|
||||||
# so we add the directory to the path, PYTHONHOME and PYTHONPATH.
|
cmd = "-c", "import platform; print(platform.architecture())"
|
||||||
env = None
|
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
||||||
if sys.platform == "win32":
|
|
||||||
env = {k.upper(): os.environ[k] for k in os.environ}
|
|
||||||
env["PATH"] = "{};{}".format(
|
|
||||||
os.path.dirname(sys.executable), env.get("PATH", ""))
|
|
||||||
env["PYTHONHOME"] = os.path.dirname(sys.executable)
|
|
||||||
if sysconfig.is_python_build(True):
|
|
||||||
env["PYTHONPATH"] = os.path.dirname(os.__file__)
|
|
||||||
|
|
||||||
def get(python, env=None):
|
|
||||||
cmd = [python, '-c',
|
|
||||||
'import platform; print(platform.architecture())']
|
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE, env=env)
|
|
||||||
r = p.communicate()
|
|
||||||
if p.returncode:
|
|
||||||
print(repr(r[0]))
|
|
||||||
print(repr(r[1]), file=sys.stderr)
|
|
||||||
self.fail('unexpected return code: {0} (0x{0:08X})'
|
|
||||||
.format(p.returncode))
|
|
||||||
return r
|
|
||||||
|
|
||||||
real = os.path.realpath(sys.executable)
|
|
||||||
link = os.path.abspath(support.TESTFN)
|
|
||||||
os.symlink(real, link)
|
|
||||||
try:
|
|
||||||
self.assertEqual(get(real), get(link, env=env))
|
|
||||||
finally:
|
|
||||||
os.remove(link)
|
|
||||||
|
|
||||||
def test_platform(self):
|
def test_platform(self):
|
||||||
for aliased in (False, True):
|
for aliased in (False, True):
|
||||||
|
@ -275,6 +247,11 @@ class PlatformTest(unittest.TestCase):
|
||||||
os.path.exists(sys.executable+'.exe'):
|
os.path.exists(sys.executable+'.exe'):
|
||||||
# Cygwin horror
|
# Cygwin horror
|
||||||
executable = sys.executable + '.exe'
|
executable = sys.executable + '.exe'
|
||||||
|
elif sys.platform == "win32" and not os.path.exists(sys.executable):
|
||||||
|
# App symlink appears to not exist, but we want the
|
||||||
|
# real executable here anyway
|
||||||
|
import _winapi
|
||||||
|
executable = _winapi.GetModuleFileName(0)
|
||||||
else:
|
else:
|
||||||
executable = sys.executable
|
executable = sys.executable
|
||||||
platform.libc_ver(executable)
|
platform.libc_ver(executable)
|
||||||
|
|
|
@ -6,7 +6,8 @@ import shutil
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
from test.support import (import_module, TESTFN, unlink, check_warnings,
|
from test.support import (import_module, TESTFN, unlink, check_warnings,
|
||||||
captured_stdout, skip_unless_symlink, change_cwd)
|
captured_stdout, skip_unless_symlink, change_cwd,
|
||||||
|
PythonSymlink)
|
||||||
|
|
||||||
import sysconfig
|
import sysconfig
|
||||||
from sysconfig import (get_paths, get_platform, get_config_vars,
|
from sysconfig import (get_paths, get_platform, get_config_vars,
|
||||||
|
@ -232,39 +233,10 @@ class TestSysConfig(unittest.TestCase):
|
||||||
self.assertEqual(get_scheme_names(), wanted)
|
self.assertEqual(get_scheme_names(), wanted)
|
||||||
|
|
||||||
@skip_unless_symlink
|
@skip_unless_symlink
|
||||||
def test_symlink(self):
|
def test_symlink(self): # Issue 7880
|
||||||
# On Windows, the EXE needs to know where pythonXY.dll is at so we have
|
with PythonSymlink() as py:
|
||||||
# to add the directory to the path.
|
cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
|
||||||
env = None
|
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
||||||
if sys.platform == "win32":
|
|
||||||
env = {k.upper(): os.environ[k] for k in os.environ}
|
|
||||||
env["PATH"] = "{};{}".format(
|
|
||||||
os.path.dirname(sys.executable), env.get("PATH", ""))
|
|
||||||
# Requires PYTHONHOME as well since we locate stdlib from the
|
|
||||||
# EXE path and not the DLL path (which should be fixed)
|
|
||||||
env["PYTHONHOME"] = os.path.dirname(sys.executable)
|
|
||||||
if sysconfig.is_python_build(True):
|
|
||||||
env["PYTHONPATH"] = os.path.dirname(os.__file__)
|
|
||||||
|
|
||||||
# Issue 7880
|
|
||||||
def get(python, env=None):
|
|
||||||
cmd = [python, '-c',
|
|
||||||
'import sysconfig; print(sysconfig.get_platform())']
|
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE, env=env)
|
|
||||||
out, err = p.communicate()
|
|
||||||
if p.returncode:
|
|
||||||
print((out, err))
|
|
||||||
self.fail('Non-zero return code {0} (0x{0:08X})'
|
|
||||||
.format(p.returncode))
|
|
||||||
return out, err
|
|
||||||
real = os.path.realpath(sys.executable)
|
|
||||||
link = os.path.abspath(TESTFN)
|
|
||||||
os.symlink(real, link)
|
|
||||||
try:
|
|
||||||
self.assertEqual(get(real), get(link, env))
|
|
||||||
finally:
|
|
||||||
unlink(link)
|
|
||||||
|
|
||||||
def test_user_similar(self):
|
def test_user_similar(self):
|
||||||
# Issue #8759: make sure the posix scheme for the users
|
# Issue #8759: make sure the posix scheme for the users
|
||||||
|
|
|
@ -28,8 +28,8 @@ except ImportError:
|
||||||
# Platforms that set sys._base_executable can create venvs from within
|
# Platforms that set sys._base_executable can create venvs from within
|
||||||
# another venv, so no need to skip tests that require venv.create().
|
# another venv, so no need to skip tests that require venv.create().
|
||||||
requireVenvCreate = unittest.skipUnless(
|
requireVenvCreate = unittest.skipUnless(
|
||||||
hasattr(sys, '_base_executable')
|
sys.prefix == sys.base_prefix
|
||||||
or sys.prefix == sys.base_prefix,
|
or sys._base_executable != sys.executable,
|
||||||
'cannot run venv.create from within a venv on this platform')
|
'cannot run venv.create from within a venv on this platform')
|
||||||
|
|
||||||
def check_output(cmd, encoding=None):
|
def check_output(cmd, encoding=None):
|
||||||
|
@ -57,8 +57,14 @@ class BaseTest(unittest.TestCase):
|
||||||
self.bindir = 'bin'
|
self.bindir = 'bin'
|
||||||
self.lib = ('lib', 'python%d.%d' % sys.version_info[:2])
|
self.lib = ('lib', 'python%d.%d' % sys.version_info[:2])
|
||||||
self.include = 'include'
|
self.include = 'include'
|
||||||
executable = getattr(sys, '_base_executable', sys.executable)
|
executable = sys._base_executable
|
||||||
self.exe = os.path.split(executable)[-1]
|
self.exe = os.path.split(executable)[-1]
|
||||||
|
if (sys.platform == 'win32'
|
||||||
|
and os.path.lexists(executable)
|
||||||
|
and not os.path.exists(executable)):
|
||||||
|
self.cannot_link_exe = True
|
||||||
|
else:
|
||||||
|
self.cannot_link_exe = False
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
rmtree(self.env_dir)
|
rmtree(self.env_dir)
|
||||||
|
@ -102,7 +108,7 @@ class BasicTest(BaseTest):
|
||||||
else:
|
else:
|
||||||
self.assertFalse(os.path.exists(p))
|
self.assertFalse(os.path.exists(p))
|
||||||
data = self.get_text_file_contents('pyvenv.cfg')
|
data = self.get_text_file_contents('pyvenv.cfg')
|
||||||
executable = getattr(sys, '_base_executable', sys.executable)
|
executable = sys._base_executable
|
||||||
path = os.path.dirname(executable)
|
path = os.path.dirname(executable)
|
||||||
self.assertIn('home = %s' % path, data)
|
self.assertIn('home = %s' % path, data)
|
||||||
fn = self.get_env_file(self.bindir, self.exe)
|
fn = self.get_env_file(self.bindir, self.exe)
|
||||||
|
@ -136,10 +142,6 @@ class BasicTest(BaseTest):
|
||||||
"""
|
"""
|
||||||
Test that the prefix values are as expected.
|
Test that the prefix values are as expected.
|
||||||
"""
|
"""
|
||||||
#check our prefixes
|
|
||||||
self.assertEqual(sys.base_prefix, sys.prefix)
|
|
||||||
self.assertEqual(sys.base_exec_prefix, sys.exec_prefix)
|
|
||||||
|
|
||||||
# check a venv's prefixes
|
# check a venv's prefixes
|
||||||
rmtree(self.env_dir)
|
rmtree(self.env_dir)
|
||||||
self.run_with_capture(venv.create, self.env_dir)
|
self.run_with_capture(venv.create, self.env_dir)
|
||||||
|
@ -147,9 +149,9 @@ class BasicTest(BaseTest):
|
||||||
cmd = [envpy, '-c', None]
|
cmd = [envpy, '-c', None]
|
||||||
for prefix, expected in (
|
for prefix, expected in (
|
||||||
('prefix', self.env_dir),
|
('prefix', self.env_dir),
|
||||||
('prefix', self.env_dir),
|
('exec_prefix', self.env_dir),
|
||||||
('base_prefix', sys.prefix),
|
('base_prefix', sys.base_prefix),
|
||||||
('base_exec_prefix', sys.exec_prefix)):
|
('base_exec_prefix', sys.base_exec_prefix)):
|
||||||
cmd[2] = 'import sys; print(sys.%s)' % prefix
|
cmd[2] = 'import sys; print(sys.%s)' % prefix
|
||||||
out, err = check_output(cmd)
|
out, err = check_output(cmd)
|
||||||
self.assertEqual(out.strip(), expected.encode())
|
self.assertEqual(out.strip(), expected.encode())
|
||||||
|
@ -261,7 +263,12 @@ class BasicTest(BaseTest):
|
||||||
# symlinked to 'python3.3' in the env, even when symlinking in
|
# symlinked to 'python3.3' in the env, even when symlinking in
|
||||||
# general isn't wanted.
|
# general isn't wanted.
|
||||||
if usl:
|
if usl:
|
||||||
self.assertTrue(os.path.islink(fn))
|
if self.cannot_link_exe:
|
||||||
|
# Symlinking is skipped when our executable is already a
|
||||||
|
# special app symlink
|
||||||
|
self.assertFalse(os.path.islink(fn))
|
||||||
|
else:
|
||||||
|
self.assertTrue(os.path.islink(fn))
|
||||||
|
|
||||||
# If a venv is created from a source build and that venv is used to
|
# If a venv is created from a source build and that venv is used to
|
||||||
# run the test, the pyvenv.cfg in the venv created in the test will
|
# run the test, the pyvenv.cfg in the venv created in the test will
|
||||||
|
|
|
@ -105,8 +105,7 @@ class EnvBuilder:
|
||||||
prompt = self.prompt if self.prompt is not None else context.env_name
|
prompt = self.prompt if self.prompt is not None else context.env_name
|
||||||
context.prompt = '(%s) ' % prompt
|
context.prompt = '(%s) ' % prompt
|
||||||
create_if_needed(env_dir)
|
create_if_needed(env_dir)
|
||||||
env = os.environ
|
executable = sys._base_executable
|
||||||
executable = getattr(sys, '_base_executable', sys.executable)
|
|
||||||
dirname, exename = os.path.split(os.path.abspath(executable))
|
dirname, exename = os.path.split(os.path.abspath(executable))
|
||||||
context.executable = executable
|
context.executable = executable
|
||||||
context.python_dir = dirname
|
context.python_dir = dirname
|
||||||
|
@ -157,47 +156,66 @@ class EnvBuilder:
|
||||||
if self.prompt is not None:
|
if self.prompt is not None:
|
||||||
f.write(f'prompt = {self.prompt!r}\n')
|
f.write(f'prompt = {self.prompt!r}\n')
|
||||||
|
|
||||||
def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
|
if os.name != 'nt':
|
||||||
"""
|
def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
|
||||||
Try symlinking a file, and if that fails, fall back to copying.
|
"""
|
||||||
"""
|
Try symlinking a file, and if that fails, fall back to copying.
|
||||||
force_copy = not self.symlinks
|
"""
|
||||||
if not force_copy:
|
force_copy = not self.symlinks
|
||||||
try:
|
if not force_copy:
|
||||||
if not os.path.islink(dst): # can't link to itself!
|
try:
|
||||||
|
if not os.path.islink(dst): # can't link to itself!
|
||||||
|
if relative_symlinks_ok:
|
||||||
|
assert os.path.dirname(src) == os.path.dirname(dst)
|
||||||
|
os.symlink(os.path.basename(src), dst)
|
||||||
|
else:
|
||||||
|
os.symlink(src, dst)
|
||||||
|
except Exception: # may need to use a more specific exception
|
||||||
|
logger.warning('Unable to symlink %r to %r', src, dst)
|
||||||
|
force_copy = True
|
||||||
|
if force_copy:
|
||||||
|
shutil.copyfile(src, dst)
|
||||||
|
else:
|
||||||
|
def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
|
||||||
|
"""
|
||||||
|
Try symlinking a file, and if that fails, fall back to copying.
|
||||||
|
"""
|
||||||
|
bad_src = os.path.lexists(src) and not os.path.exists(src)
|
||||||
|
if self.symlinks and not bad_src and not os.path.islink(dst):
|
||||||
|
try:
|
||||||
if relative_symlinks_ok:
|
if relative_symlinks_ok:
|
||||||
assert os.path.dirname(src) == os.path.dirname(dst)
|
assert os.path.dirname(src) == os.path.dirname(dst)
|
||||||
os.symlink(os.path.basename(src), dst)
|
os.symlink(os.path.basename(src), dst)
|
||||||
else:
|
else:
|
||||||
os.symlink(src, dst)
|
os.symlink(src, dst)
|
||||||
except Exception: # may need to use a more specific exception
|
|
||||||
logger.warning('Unable to symlink %r to %r', src, dst)
|
|
||||||
force_copy = True
|
|
||||||
if force_copy:
|
|
||||||
if os.name == 'nt':
|
|
||||||
# On Windows, we rewrite symlinks to our base python.exe into
|
|
||||||
# copies of venvlauncher.exe
|
|
||||||
basename, ext = os.path.splitext(os.path.basename(src))
|
|
||||||
srcfn = os.path.join(os.path.dirname(__file__),
|
|
||||||
"scripts",
|
|
||||||
"nt",
|
|
||||||
basename + ext)
|
|
||||||
# Builds or venv's from builds need to remap source file
|
|
||||||
# locations, as we do not put them into Lib/venv/scripts
|
|
||||||
if sysconfig.is_python_build(True) or not os.path.isfile(srcfn):
|
|
||||||
if basename.endswith('_d'):
|
|
||||||
ext = '_d' + ext
|
|
||||||
basename = basename[:-2]
|
|
||||||
if basename == 'python':
|
|
||||||
basename = 'venvlauncher'
|
|
||||||
elif basename == 'pythonw':
|
|
||||||
basename = 'venvwlauncher'
|
|
||||||
src = os.path.join(os.path.dirname(src), basename + ext)
|
|
||||||
else:
|
|
||||||
src = srcfn
|
|
||||||
if not os.path.exists(src):
|
|
||||||
logger.warning('Unable to copy %r', src)
|
|
||||||
return
|
return
|
||||||
|
except Exception: # may need to use a more specific exception
|
||||||
|
logger.warning('Unable to symlink %r to %r', src, dst)
|
||||||
|
|
||||||
|
# On Windows, we rewrite symlinks to our base python.exe into
|
||||||
|
# copies of venvlauncher.exe
|
||||||
|
basename, ext = os.path.splitext(os.path.basename(src))
|
||||||
|
srcfn = os.path.join(os.path.dirname(__file__),
|
||||||
|
"scripts",
|
||||||
|
"nt",
|
||||||
|
basename + ext)
|
||||||
|
# Builds or venv's from builds need to remap source file
|
||||||
|
# locations, as we do not put them into Lib/venv/scripts
|
||||||
|
if sysconfig.is_python_build(True) or not os.path.isfile(srcfn):
|
||||||
|
if basename.endswith('_d'):
|
||||||
|
ext = '_d' + ext
|
||||||
|
basename = basename[:-2]
|
||||||
|
if basename == 'python':
|
||||||
|
basename = 'venvlauncher'
|
||||||
|
elif basename == 'pythonw':
|
||||||
|
basename = 'venvwlauncher'
|
||||||
|
src = os.path.join(os.path.dirname(src), basename + ext)
|
||||||
|
else:
|
||||||
|
src = srcfn
|
||||||
|
if not os.path.exists(src):
|
||||||
|
if not bad_src:
|
||||||
|
logger.warning('Unable to copy %r', src)
|
||||||
|
return
|
||||||
|
|
||||||
shutil.copyfile(src, dst)
|
shutil.copyfile(src, dst)
|
||||||
|
|
||||||
|
@ -245,7 +263,7 @@ class EnvBuilder:
|
||||||
|
|
||||||
for suffix in suffixes:
|
for suffix in suffixes:
|
||||||
src = os.path.join(dirname, suffix)
|
src = os.path.join(dirname, suffix)
|
||||||
if os.path.exists(src):
|
if os.path.lexists(src):
|
||||||
copier(src, os.path.join(binpath, suffix))
|
copier(src, os.path.join(binpath, suffix))
|
||||||
|
|
||||||
if sysconfig.is_python_build(True):
|
if sysconfig.is_python_build(True):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fixes path for :data:`sys.executable` when running from the Microsoft Store.
|
|
@ -537,14 +537,28 @@ get_program_full_path(const PyConfig *config,
|
||||||
wchar_t program_full_path[MAXPATHLEN+1];
|
wchar_t program_full_path[MAXPATHLEN+1];
|
||||||
memset(program_full_path, 0, sizeof(program_full_path));
|
memset(program_full_path, 0, sizeof(program_full_path));
|
||||||
|
|
||||||
|
if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
|
||||||
|
/* GetModuleFileName should never fail when passed NULL */
|
||||||
|
return _PyStatus_ERR("Cannot determine program path");
|
||||||
|
}
|
||||||
|
|
||||||
/* The launcher may need to force the executable path to a
|
/* The launcher may need to force the executable path to a
|
||||||
* different environment, so override it here. */
|
* different environment, so override it here. */
|
||||||
pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__");
|
pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__");
|
||||||
if (pyvenv_launcher && pyvenv_launcher[0]) {
|
if (pyvenv_launcher && pyvenv_launcher[0]) {
|
||||||
|
/* If overridden, preserve the original full path */
|
||||||
|
pathconfig->base_executable = PyMem_RawMalloc(
|
||||||
|
sizeof(wchar_t) * (MAXPATHLEN + 1));
|
||||||
|
PyStatus status = canonicalize(pathconfig->base_executable,
|
||||||
|
program_full_path);
|
||||||
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher);
|
wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher);
|
||||||
} else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
|
/* bpo-35873: Clear the environment variable to avoid it being
|
||||||
/* GetModuleFileName should never fail when passed NULL */
|
* inherited by child processes. */
|
||||||
return _PyStatus_ERR("Cannot determine program path");
|
_wputenv_s(L"__PYVENV_LAUNCHER__", L"");
|
||||||
}
|
}
|
||||||
|
|
||||||
pathconfig->program_full_path = PyMem_RawMalloc(
|
pathconfig->program_full_path = PyMem_RawMalloc(
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <winrt\Windows.ApplicationModel.h>
|
#include <winrt\Windows.ApplicationModel.h>
|
||||||
#include <winrt\Windows.Storage.h>
|
#include <winrt\Windows.Storage.h>
|
||||||
|
@ -24,192 +27,220 @@ const wchar_t *PROGNAME = L"python.exe";
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static std::wstring
|
||||||
set_user_base()
|
get_user_base()
|
||||||
{
|
{
|
||||||
wchar_t envBuffer[2048];
|
|
||||||
try {
|
try {
|
||||||
const auto appData = winrt::Windows::Storage::ApplicationData::Current();
|
const auto appData = winrt::Windows::Storage::ApplicationData::Current();
|
||||||
if (appData) {
|
if (appData) {
|
||||||
const auto localCache = appData.LocalCacheFolder();
|
const auto localCache = appData.LocalCacheFolder();
|
||||||
if (localCache) {
|
if (localCache) {
|
||||||
auto path = localCache.Path();
|
auto path = localCache.Path();
|
||||||
if (!path.empty() &&
|
if (!path.empty()) {
|
||||||
!wcscpy_s(envBuffer, path.c_str()) &&
|
return std::wstring(path) + L"\\local-packages";
|
||||||
!wcscat_s(envBuffer, L"\\local-packages")
|
|
||||||
) {
|
|
||||||
_wputenv_s(L"PYTHONUSERBASE", envBuffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
|
return std::wstring();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const wchar_t *
|
static std::wstring
|
||||||
get_argv0(const wchar_t *argv0)
|
get_package_family()
|
||||||
{
|
{
|
||||||
winrt::hstring installPath;
|
|
||||||
const wchar_t *launcherPath;
|
|
||||||
wchar_t *buffer;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
|
|
||||||
if (launcherPath && launcherPath[0]) {
|
|
||||||
len = wcslen(launcherPath) + 1;
|
|
||||||
buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
|
||||||
if (!buffer) {
|
|
||||||
Py_FatalError("out of memory");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (wcscpy_s(buffer, len, launcherPath)) {
|
|
||||||
Py_FatalError("failed to copy to buffer");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auto package = winrt::Windows::ApplicationModel::Package::Current();
|
const auto package = winrt::Windows::ApplicationModel::Package::Current();
|
||||||
if (package) {
|
if (package) {
|
||||||
const auto install = package.InstalledLocation();
|
const auto id = package.Id();
|
||||||
if (install) {
|
if (id) {
|
||||||
installPath = install.Path();
|
return std::wstring(id.FamilyName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!installPath.empty()) {
|
return std::wstring();
|
||||||
len = installPath.size() + wcslen(PROGNAME) + 2;
|
|
||||||
} else {
|
|
||||||
len = wcslen(argv0) + wcslen(PROGNAME) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
|
||||||
if (!buffer) {
|
|
||||||
Py_FatalError("out of memory");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!installPath.empty()) {
|
|
||||||
if (wcscpy_s(buffer, len, installPath.c_str())) {
|
|
||||||
Py_FatalError("failed to copy to buffer");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (wcscat_s(buffer, len, L"\\")) {
|
|
||||||
Py_FatalError("failed to concatenate backslash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (wcscpy_s(buffer, len, argv0)) {
|
|
||||||
Py_FatalError("failed to copy argv[0]");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t *name = wcsrchr(buffer, L'\\');
|
|
||||||
if (name) {
|
|
||||||
name[1] = L'\0';
|
|
||||||
} else {
|
|
||||||
buffer[0] = L'\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wcscat_s(buffer, len, PROGNAME)) {
|
|
||||||
Py_FatalError("failed to concatenate program name");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static wchar_t *
|
static std::wstring
|
||||||
get_process_name()
|
get_package_home()
|
||||||
{
|
{
|
||||||
DWORD bufferLen = MAX_PATH;
|
try {
|
||||||
DWORD len = bufferLen;
|
const auto package = winrt::Windows::ApplicationModel::Package::Current();
|
||||||
wchar_t *r = NULL;
|
if (package) {
|
||||||
|
const auto path = package.InstalledLocation();
|
||||||
while (!r) {
|
if (path) {
|
||||||
r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t));
|
return std::wstring(path.Path());
|
||||||
if (!r) {
|
}
|
||||||
Py_FatalError("out of memory");
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
len = GetModuleFileNameW(NULL, r, bufferLen);
|
}
|
||||||
if (len == 0) {
|
catch (...) {
|
||||||
free((void *)r);
|
}
|
||||||
return NULL;
|
|
||||||
} else if (len == bufferLen &&
|
return std::wstring();
|
||||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
}
|
||||||
free(r);
|
|
||||||
r = NULL;
|
static PyStatus
|
||||||
bufferLen *= 2;
|
set_process_name(PyConfig *config)
|
||||||
|
{
|
||||||
|
PyStatus status = PyStatus_Ok();
|
||||||
|
std::wstring executable;
|
||||||
|
|
||||||
|
const auto home = get_package_home();
|
||||||
|
const auto family = get_package_family();
|
||||||
|
|
||||||
|
if (!family.empty()) {
|
||||||
|
PWSTR localAppData;
|
||||||
|
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0,
|
||||||
|
NULL, &localAppData))) {
|
||||||
|
executable = std::wstring(localAppData)
|
||||||
|
+ L"\\Microsoft\\WindowsApps\\"
|
||||||
|
+ family
|
||||||
|
+ L"\\"
|
||||||
|
+ PROGNAME;
|
||||||
|
|
||||||
|
CoTaskMemFree(localAppData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
/* Only use module filename if we don't have a home */
|
||||||
|
if (home.empty() && executable.empty()) {
|
||||||
|
executable.resize(MAX_PATH);
|
||||||
|
while (true) {
|
||||||
|
DWORD len = GetModuleFileNameW(
|
||||||
|
NULL, executable.data(), (DWORD)executable.size());
|
||||||
|
if (len == 0) {
|
||||||
|
executable.clear();
|
||||||
|
break;
|
||||||
|
} else if (len == executable.size() &&
|
||||||
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
executable.resize(len * 2);
|
||||||
|
} else {
|
||||||
|
executable.resize(len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!home.empty()) {
|
||||||
|
status = PyConfig_SetString(config, &config->home, home.c_str());
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t *launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
|
||||||
|
if (launcherPath) {
|
||||||
|
if (!executable.empty()) {
|
||||||
|
status = PyConfig_SetString(config, &config->base_executable,
|
||||||
|
executable.c_str());
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = PyConfig_SetString(
|
||||||
|
config, &config->executable, launcherPath);
|
||||||
|
|
||||||
|
/* bpo-35873: Clear the environment variable to avoid it being
|
||||||
|
* inherited by child processes. */
|
||||||
|
_wputenv_s(L"__PYVENV_LAUNCHER__", L"");
|
||||||
|
} else if (!executable.empty()) {
|
||||||
|
status = PyConfig_SetString(
|
||||||
|
config, &config->executable, executable.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
wmain(int argc, wchar_t **argv)
|
wmain(int argc, wchar_t **argv)
|
||||||
{
|
{
|
||||||
const wchar_t **new_argv;
|
PyStatus status;
|
||||||
int new_argc;
|
|
||||||
const wchar_t *exeName;
|
|
||||||
|
|
||||||
new_argc = argc;
|
PyPreConfig preconfig;
|
||||||
new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2));
|
PyConfig config;
|
||||||
if (new_argv == NULL) {
|
|
||||||
Py_FatalError("out of memory");
|
PyPreConfig_InitPythonConfig(&preconfig);
|
||||||
return -1;
|
status = Py_PreInitializeFromArgs(&preconfig, argc, argv);
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
|
goto fail_without_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
exeName = get_process_name();
|
status = PyConfig_InitPythonConfig(&config);
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
new_argv[0] = get_argv0(exeName ? exeName : argv[0]);
|
goto fail_without_config;
|
||||||
for (int i = 1; i < argc; ++i) {
|
|
||||||
new_argv[i] = argv[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_user_base();
|
status = PyConfig_SetArgv(&config, argc, argv);
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (exeName) {
|
status = set_process_name(&config);
|
||||||
const wchar_t *p = wcsrchr(exeName, L'\\');
|
if (PyStatus_Exception(status)) {
|
||||||
if (p) {
|
goto fail;
|
||||||
const wchar_t *moduleName = NULL;
|
}
|
||||||
if (*p++ == L'\\') {
|
|
||||||
if (wcsnicmp(p, L"pip", 3) == 0) {
|
const wchar_t *p = _wgetenv(L"PYTHONUSERBASE");
|
||||||
moduleName = L"pip";
|
if (!p || !*p) {
|
||||||
/* No longer required when pip 19.1 is added */
|
_wputenv_s(L"PYTHONUSERBASE", get_user_base().c_str());
|
||||||
_wputenv_s(L"PIP_USER", L"true");
|
}
|
||||||
} else if (wcsnicmp(p, L"idle", 4) == 0) {
|
|
||||||
moduleName = L"idlelib";
|
p = wcsrchr(argv[0], L'\\');
|
||||||
}
|
if (!p) {
|
||||||
|
p = argv[0];
|
||||||
|
}
|
||||||
|
if (p) {
|
||||||
|
if (*p == L'\\') {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t *moduleName = NULL;
|
||||||
|
if (wcsnicmp(p, L"pip", 3) == 0) {
|
||||||
|
moduleName = L"pip";
|
||||||
|
/* No longer required when pip 19.1 is added */
|
||||||
|
_wputenv_s(L"PIP_USER", L"true");
|
||||||
|
} else if (wcsnicmp(p, L"idle", 4) == 0) {
|
||||||
|
moduleName = L"idlelib";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moduleName) {
|
||||||
|
status = PyConfig_SetString(&config, &config.run_module, moduleName);
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
status = PyConfig_SetString(&config, &config.run_filename, NULL);
|
||||||
if (moduleName) {
|
if (PyStatus_Exception(status)) {
|
||||||
new_argc += 2;
|
goto fail;
|
||||||
for (int i = argc; i >= 1; --i) {
|
}
|
||||||
new_argv[i + 2] = new_argv[i];
|
status = PyConfig_SetString(&config, &config.run_command, NULL);
|
||||||
}
|
if (PyStatus_Exception(status)) {
|
||||||
new_argv[1] = L"-m";
|
goto fail;
|
||||||
new_argv[2] = moduleName;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Override program_full_path from here so that
|
status = Py_InitializeFromConfig(&config);
|
||||||
sys.executable is set correctly. */
|
if (PyStatus_Exception(status)) {
|
||||||
_Py_SetProgramFullPath(new_argv[0]);
|
goto fail;
|
||||||
|
}
|
||||||
|
PyConfig_Clear(&config);
|
||||||
|
|
||||||
int result = Py_Main(new_argc, (wchar_t **)new_argv);
|
return Py_RunMain();
|
||||||
|
|
||||||
free((void *)exeName);
|
fail:
|
||||||
free((void *)new_argv);
|
PyConfig_Clear(&config);
|
||||||
|
fail_without_config:
|
||||||
return result;
|
if (PyStatus_IsExit(status)) {
|
||||||
|
return status.exitcode;
|
||||||
|
}
|
||||||
|
assert(PyStatus_Exception(status));
|
||||||
|
Py_ExitStatusException(status);
|
||||||
|
/* Unreachable code */
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PYTHONW
|
#ifdef PYTHONW
|
||||||
|
|
|
@ -528,6 +528,7 @@ PyConfig_Clear(PyConfig *config)
|
||||||
config->module_search_paths_set = 0;
|
config->module_search_paths_set = 0;
|
||||||
|
|
||||||
CLEAR(config->executable);
|
CLEAR(config->executable);
|
||||||
|
CLEAR(config->base_executable);
|
||||||
CLEAR(config->prefix);
|
CLEAR(config->prefix);
|
||||||
CLEAR(config->base_prefix);
|
CLEAR(config->base_prefix);
|
||||||
CLEAR(config->exec_prefix);
|
CLEAR(config->exec_prefix);
|
||||||
|
@ -765,6 +766,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
|
||||||
COPY_ATTR(module_search_paths_set);
|
COPY_ATTR(module_search_paths_set);
|
||||||
|
|
||||||
COPY_WSTR_ATTR(executable);
|
COPY_WSTR_ATTR(executable);
|
||||||
|
COPY_WSTR_ATTR(base_executable);
|
||||||
COPY_WSTR_ATTR(prefix);
|
COPY_WSTR_ATTR(prefix);
|
||||||
COPY_WSTR_ATTR(base_prefix);
|
COPY_WSTR_ATTR(base_prefix);
|
||||||
COPY_WSTR_ATTR(exec_prefix);
|
COPY_WSTR_ATTR(exec_prefix);
|
||||||
|
@ -865,6 +867,7 @@ config_as_dict(const PyConfig *config)
|
||||||
SET_ITEM_WSTR(home);
|
SET_ITEM_WSTR(home);
|
||||||
SET_ITEM_WSTRLIST(module_search_paths);
|
SET_ITEM_WSTRLIST(module_search_paths);
|
||||||
SET_ITEM_WSTR(executable);
|
SET_ITEM_WSTR(executable);
|
||||||
|
SET_ITEM_WSTR(base_executable);
|
||||||
SET_ITEM_WSTR(prefix);
|
SET_ITEM_WSTR(prefix);
|
||||||
SET_ITEM_WSTR(base_prefix);
|
SET_ITEM_WSTR(base_prefix);
|
||||||
SET_ITEM_WSTR(exec_prefix);
|
SET_ITEM_WSTR(exec_prefix);
|
||||||
|
@ -2357,6 +2360,7 @@ PyConfig_Read(PyConfig *config)
|
||||||
assert(config->module_search_paths_set != 0);
|
assert(config->module_search_paths_set != 0);
|
||||||
/* don't check config->module_search_paths */
|
/* don't check config->module_search_paths */
|
||||||
assert(config->executable != NULL);
|
assert(config->executable != NULL);
|
||||||
|
assert(config->base_executable != NULL);
|
||||||
assert(config->prefix != NULL);
|
assert(config->prefix != NULL);
|
||||||
assert(config->base_prefix != NULL);
|
assert(config->base_prefix != NULL);
|
||||||
assert(config->exec_prefix != NULL);
|
assert(config->exec_prefix != NULL);
|
||||||
|
|
|
@ -57,6 +57,7 @@ pathconfig_clear(_PyPathConfig *config)
|
||||||
CLEAR(config->module_search_path);
|
CLEAR(config->module_search_path);
|
||||||
CLEAR(config->home);
|
CLEAR(config->home);
|
||||||
CLEAR(config->program_name);
|
CLEAR(config->program_name);
|
||||||
|
CLEAR(config->base_executable);
|
||||||
#undef CLEAR
|
#undef CLEAR
|
||||||
|
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
|
@ -89,6 +90,14 @@ pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config)
|
||||||
status = _PyStatus_NO_MEMORY();
|
status = _PyStatus_NO_MEMORY();
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
if (config->base_executable) {
|
||||||
|
PyMem_RawFree(new_config.base_executable);
|
||||||
|
if (copy_wstr(&new_config.base_executable,
|
||||||
|
config->base_executable) < 0) {
|
||||||
|
status = _PyStatus_NO_MEMORY();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pathconfig_clear(pathconfig);
|
pathconfig_clear(pathconfig);
|
||||||
*pathconfig = new_config;
|
*pathconfig = new_config;
|
||||||
|
@ -132,6 +141,7 @@ _PyPathConfig_SetGlobal(const _PyPathConfig *config)
|
||||||
COPY_ATTR(module_search_path);
|
COPY_ATTR(module_search_path);
|
||||||
COPY_ATTR(program_name);
|
COPY_ATTR(program_name);
|
||||||
COPY_ATTR(home);
|
COPY_ATTR(home);
|
||||||
|
COPY_ATTR(base_executable);
|
||||||
|
|
||||||
pathconfig_clear(&_Py_path_config);
|
pathconfig_clear(&_Py_path_config);
|
||||||
/* Steal new_config strings; don't clear new_config */
|
/* Steal new_config strings; don't clear new_config */
|
||||||
|
@ -224,6 +234,9 @@ _PyConfig_SetPathConfig(const PyConfig *config)
|
||||||
if (copy_wstr(&pathconfig.home, config->home) < 0) {
|
if (copy_wstr(&pathconfig.home, config->home) < 0) {
|
||||||
goto no_memory;
|
goto no_memory;
|
||||||
}
|
}
|
||||||
|
if (copy_wstr(&pathconfig.base_executable, config->base_executable) < 0) {
|
||||||
|
goto no_memory;
|
||||||
|
}
|
||||||
|
|
||||||
status = _PyPathConfig_SetGlobal(&pathconfig);
|
status = _PyPathConfig_SetGlobal(&pathconfig);
|
||||||
if (_PyStatus_EXCEPTION(status)) {
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
|
@ -321,6 +334,13 @@ config_calculate_pathconfig(PyConfig *config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config->base_executable == NULL) {
|
||||||
|
if (copy_wstr(&config->base_executable,
|
||||||
|
pathconfig.base_executable) < 0) {
|
||||||
|
goto no_memory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pathconfig.isolated != -1) {
|
if (pathconfig.isolated != -1) {
|
||||||
config->isolated = pathconfig.isolated;
|
config->isolated = pathconfig.isolated;
|
||||||
}
|
}
|
||||||
|
@ -367,6 +387,14 @@ _PyConfig_InitPathConfig(PyConfig *config)
|
||||||
return _PyStatus_NO_MEMORY();
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config->base_executable == NULL) {
|
||||||
|
if (copy_wstr(&config->base_executable,
|
||||||
|
config->executable) < 0) {
|
||||||
|
return _PyStatus_NO_MEMORY();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,6 +462,8 @@ Py_SetPath(const wchar_t *path)
|
||||||
_Py_path_config.home = NULL;
|
_Py_path_config.home = NULL;
|
||||||
new_config.program_name = _Py_path_config.program_name;
|
new_config.program_name = _Py_path_config.program_name;
|
||||||
_Py_path_config.program_name = NULL;
|
_Py_path_config.program_name = NULL;
|
||||||
|
new_config.base_executable = _Py_path_config.base_executable;
|
||||||
|
_Py_path_config.base_executable = NULL;
|
||||||
|
|
||||||
pathconfig_clear(&_Py_path_config);
|
pathconfig_clear(&_Py_path_config);
|
||||||
_Py_path_config = new_config;
|
_Py_path_config = new_config;
|
||||||
|
|
|
@ -2879,6 +2879,7 @@ _PySys_InitMain(_PyRuntimeState *runtime, PyInterpreterState *interp)
|
||||||
COPY_LIST("path", config->module_search_paths);
|
COPY_LIST("path", config->module_search_paths);
|
||||||
|
|
||||||
SET_SYS_FROM_WSTR("executable", config->executable);
|
SET_SYS_FROM_WSTR("executable", config->executable);
|
||||||
|
SET_SYS_FROM_WSTR("_base_executable", config->base_executable);
|
||||||
SET_SYS_FROM_WSTR("prefix", config->prefix);
|
SET_SYS_FROM_WSTR("prefix", config->prefix);
|
||||||
SET_SYS_FROM_WSTR("base_prefix", config->base_prefix);
|
SET_SYS_FROM_WSTR("base_prefix", config->base_prefix);
|
||||||
SET_SYS_FROM_WSTR("exec_prefix", config->exec_prefix);
|
SET_SYS_FROM_WSTR("exec_prefix", config->exec_prefix);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue