bpo-29778: test_embed tests the path configuration (GH-21306)
This commit is contained in:
parent
6ae2780be0
commit
8f42748ded
|
@ -65,6 +65,7 @@ extern wchar_t* _Py_GetDLLPath(void);
|
||||||
|
|
||||||
extern PyStatus _PyConfig_WritePathConfig(const PyConfig *config);
|
extern PyStatus _PyConfig_WritePathConfig(const PyConfig *config);
|
||||||
extern void _Py_DumpPathConfig(PyThreadState *tstate);
|
extern void _Py_DumpPathConfig(PyThreadState *tstate);
|
||||||
|
extern PyObject* _PyPathConfig_AsDict(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -471,6 +471,31 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
|
('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# path config
|
||||||
|
if MS_WINDOWS:
|
||||||
|
PATH_CONFIG = {
|
||||||
|
'isolated': -1,
|
||||||
|
'site_import': -1,
|
||||||
|
'python3_dll': GET_DEFAULT_CONFIG,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
PATH_CONFIG = {}
|
||||||
|
# other keys are copied by COPY_PATH_CONFIG
|
||||||
|
|
||||||
|
COPY_PATH_CONFIG = [
|
||||||
|
# Copy core config to global config for expected values
|
||||||
|
'prefix',
|
||||||
|
'exec_prefix',
|
||||||
|
'program_name',
|
||||||
|
'home',
|
||||||
|
# program_full_path and module_search_path are copied indirectly from
|
||||||
|
# the core configuration in check_path_config().
|
||||||
|
]
|
||||||
|
if MS_WINDOWS:
|
||||||
|
COPY_PATH_CONFIG.extend((
|
||||||
|
'base_executable',
|
||||||
|
))
|
||||||
|
|
||||||
EXPECTED_CONFIG = None
|
EXPECTED_CONFIG = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -535,7 +560,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
configs[config_key] = config
|
configs[config_key] = config
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def get_expected_config(self, expected_preconfig, expected, env, api,
|
def get_expected_config(self, expected_preconfig, expected,
|
||||||
|
expected_pathconfig, env, api,
|
||||||
modify_path_cb=None):
|
modify_path_cb=None):
|
||||||
cls = self.__class__
|
cls = self.__class__
|
||||||
configs = self._get_expected_config()
|
configs = self._get_expected_config()
|
||||||
|
@ -545,6 +571,11 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
if value is self.GET_DEFAULT_CONFIG:
|
if value is self.GET_DEFAULT_CONFIG:
|
||||||
expected_preconfig[key] = pre_config[key]
|
expected_preconfig[key] = pre_config[key]
|
||||||
|
|
||||||
|
path_config = configs['path_config']
|
||||||
|
for key, value in expected_pathconfig.items():
|
||||||
|
if value is self.GET_DEFAULT_CONFIG:
|
||||||
|
expected_pathconfig[key] = path_config[key]
|
||||||
|
|
||||||
if not expected_preconfig['configure_locale'] or api == API_COMPAT:
|
if not expected_preconfig['configure_locale'] or api == API_COMPAT:
|
||||||
# there is no easy way to get the locale encoding before
|
# there is no easy way to get the locale encoding before
|
||||||
# setlocale(LC_CTYPE, "") is called: don't test encodings
|
# setlocale(LC_CTYPE, "") is called: don't test encodings
|
||||||
|
@ -637,8 +668,19 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(configs['global_config'], expected)
|
self.assertEqual(configs['global_config'], expected)
|
||||||
|
|
||||||
|
def check_path_config(self, configs, expected):
|
||||||
|
config = configs['config']
|
||||||
|
|
||||||
|
for key in self.COPY_PATH_CONFIG:
|
||||||
|
expected[key] = config[key]
|
||||||
|
expected['module_search_path'] = os.path.pathsep.join(config['module_search_paths'])
|
||||||
|
expected['program_full_path'] = config['executable']
|
||||||
|
|
||||||
|
self.assertEqual(configs['path_config'], expected)
|
||||||
|
|
||||||
def check_all_configs(self, testname, expected_config=None,
|
def check_all_configs(self, testname, expected_config=None,
|
||||||
expected_preconfig=None, modify_path_cb=None,
|
expected_preconfig=None, expected_pathconfig=None,
|
||||||
|
modify_path_cb=None,
|
||||||
stderr=None, *, api, preconfig_api=None,
|
stderr=None, *, api, preconfig_api=None,
|
||||||
env=None, ignore_stderr=False, cwd=None):
|
env=None, ignore_stderr=False, cwd=None):
|
||||||
new_env = remove_python_envvars()
|
new_env = remove_python_envvars()
|
||||||
|
@ -657,9 +699,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
if expected_preconfig is None:
|
if expected_preconfig is None:
|
||||||
expected_preconfig = {}
|
expected_preconfig = {}
|
||||||
expected_preconfig = dict(default_preconfig, **expected_preconfig)
|
expected_preconfig = dict(default_preconfig, **expected_preconfig)
|
||||||
|
|
||||||
if expected_config is None:
|
if expected_config is None:
|
||||||
expected_config = {}
|
expected_config = {}
|
||||||
|
|
||||||
|
if expected_pathconfig is None:
|
||||||
|
expected_pathconfig = {}
|
||||||
|
expected_pathconfig = dict(self.PATH_CONFIG, **expected_pathconfig)
|
||||||
|
|
||||||
if api == API_PYTHON:
|
if api == API_PYTHON:
|
||||||
default_config = self.CONFIG_PYTHON
|
default_config = self.CONFIG_PYTHON
|
||||||
elif api == API_ISOLATED:
|
elif api == API_ISOLATED:
|
||||||
|
@ -669,7 +716,9 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
expected_config = dict(default_config, **expected_config)
|
expected_config = dict(default_config, **expected_config)
|
||||||
|
|
||||||
self.get_expected_config(expected_preconfig,
|
self.get_expected_config(expected_preconfig,
|
||||||
expected_config, env,
|
expected_config,
|
||||||
|
expected_pathconfig,
|
||||||
|
env,
|
||||||
api, modify_path_cb)
|
api, modify_path_cb)
|
||||||
|
|
||||||
out, err = self.run_embedded_interpreter(testname,
|
out, err = self.run_embedded_interpreter(testname,
|
||||||
|
@ -686,6 +735,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
self.check_pre_config(configs, expected_preconfig)
|
self.check_pre_config(configs, expected_preconfig)
|
||||||
self.check_config(configs, expected_config)
|
self.check_config(configs, expected_config)
|
||||||
self.check_global_config(configs)
|
self.check_global_config(configs)
|
||||||
|
self.check_path_config(configs, expected_pathconfig)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def test_init_default_config(self):
|
def test_init_default_config(self):
|
||||||
|
@ -1258,22 +1308,24 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'executable': executable,
|
'executable': executable,
|
||||||
'module_search_paths': paths,
|
'module_search_paths': paths,
|
||||||
}
|
}
|
||||||
|
path_config = {}
|
||||||
if MS_WINDOWS:
|
if MS_WINDOWS:
|
||||||
config['base_prefix'] = pyvenv_home
|
config['base_prefix'] = pyvenv_home
|
||||||
config['prefix'] = pyvenv_home
|
config['prefix'] = pyvenv_home
|
||||||
|
|
||||||
|
ver = sys.version_info
|
||||||
|
dll = f'python{ver.major}'
|
||||||
|
if debug_build(executable):
|
||||||
|
dll += '_d'
|
||||||
|
dll += '.DLL'
|
||||||
|
dll = os.path.join(os.path.dirname(executable), dll)
|
||||||
|
path_config['python3_dll'] = dll
|
||||||
|
|
||||||
env = self.copy_paths_by_env(config)
|
env = self.copy_paths_by_env(config)
|
||||||
actual = self.check_all_configs("test_init_compat_config", config,
|
self.check_all_configs("test_init_compat_config", config,
|
||||||
|
expected_pathconfig=path_config,
|
||||||
api=API_COMPAT, env=env,
|
api=API_COMPAT, env=env,
|
||||||
ignore_stderr=True, cwd=tmpdir)
|
ignore_stderr=True, cwd=tmpdir)
|
||||||
if MS_WINDOWS:
|
|
||||||
self.assertEqual(
|
|
||||||
actual['windows']['python3_dll'],
|
|
||||||
os.path.join(
|
|
||||||
tmpdir,
|
|
||||||
os.path.basename(self.EXPECTED_CONFIG['windows']['python3_dll'])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_global_pathconfig(self):
|
def test_global_pathconfig(self):
|
||||||
# Test C API functions getting the path configuration:
|
# Test C API functions getting the path configuration:
|
||||||
|
|
|
@ -18,53 +18,10 @@
|
||||||
#include "pycore_gc.h" // PyGC_Head
|
#include "pycore_gc.h" // PyGC_Head
|
||||||
|
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
static int
|
|
||||||
_add_windows_config(PyObject *configs)
|
|
||||||
{
|
|
||||||
HMODULE hPython3;
|
|
||||||
wchar_t py3path[MAX_PATH];
|
|
||||||
PyObject *dict = PyDict_New();
|
|
||||||
PyObject *obj = NULL;
|
|
||||||
if (!dict) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
hPython3 = GetModuleHandleW(PY3_DLLNAME);
|
|
||||||
if (hPython3 && GetModuleFileNameW(hPython3, py3path, MAX_PATH)) {
|
|
||||||
obj = PyUnicode_FromWideChar(py3path, -1);
|
|
||||||
} else {
|
|
||||||
obj = Py_None;
|
|
||||||
Py_INCREF(obj);
|
|
||||||
}
|
|
||||||
if (obj &&
|
|
||||||
!PyDict_SetItemString(dict, "python3_dll", obj) &&
|
|
||||||
!PyDict_SetItemString(configs, "windows", dict)) {
|
|
||||||
Py_DECREF(obj);
|
|
||||||
Py_DECREF(dict);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Py_DECREF(obj);
|
|
||||||
Py_DECREF(dict);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
get_configs(PyObject *self, PyObject *Py_UNUSED(args))
|
get_configs(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
{
|
{
|
||||||
PyObject *dict = _Py_GetConfigsAsDict();
|
return _Py_GetConfigsAsDict();
|
||||||
#ifdef MS_WINDOWS
|
|
||||||
if (dict) {
|
|
||||||
if (_add_windows_config(dict) < 0) {
|
|
||||||
Py_CLEAR(dict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return dict;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -868,9 +868,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
config_as_dict(const PyConfig *config)
|
config_as_dict(const PyConfig *config)
|
||||||
{
|
{
|
||||||
PyObject *dict;
|
PyObject *dict = PyDict_New();
|
||||||
|
|
||||||
dict = PyDict_New();
|
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -2643,6 +2641,16 @@ _Py_GetConfigsAsDict(void)
|
||||||
}
|
}
|
||||||
Py_CLEAR(dict);
|
Py_CLEAR(dict);
|
||||||
|
|
||||||
|
/* path config */
|
||||||
|
dict = _PyPathConfig_AsDict();
|
||||||
|
if (dict == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyDict_SetItemString(result, "path_config", dict) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
Py_CLEAR(dict);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
|
@ -186,6 +186,80 @@ done:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyPathConfig_AsDict(void)
|
||||||
|
{
|
||||||
|
PyObject *dict = PyDict_New();
|
||||||
|
if (dict == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_ITEM(KEY, EXPR) \
|
||||||
|
do { \
|
||||||
|
PyObject *obj = (EXPR); \
|
||||||
|
if (obj == NULL) { \
|
||||||
|
goto fail; \
|
||||||
|
} \
|
||||||
|
int res = PyDict_SetItemString(dict, KEY, obj); \
|
||||||
|
Py_DECREF(obj); \
|
||||||
|
if (res < 0) { \
|
||||||
|
goto fail; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define SET_ITEM_STR(KEY) \
|
||||||
|
SET_ITEM(#KEY, \
|
||||||
|
(_Py_path_config.KEY \
|
||||||
|
? PyUnicode_FromWideChar(_Py_path_config.KEY, -1) \
|
||||||
|
: (Py_INCREF(Py_None), Py_None)))
|
||||||
|
#define SET_ITEM_INT(KEY) \
|
||||||
|
SET_ITEM(#KEY, PyLong_FromLong(_Py_path_config.KEY))
|
||||||
|
|
||||||
|
SET_ITEM_STR(program_full_path);
|
||||||
|
SET_ITEM_STR(prefix);
|
||||||
|
SET_ITEM_STR(exec_prefix);
|
||||||
|
SET_ITEM_STR(module_search_path);
|
||||||
|
SET_ITEM_STR(program_name);
|
||||||
|
SET_ITEM_STR(home);
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
SET_ITEM_INT(isolated);
|
||||||
|
SET_ITEM_INT(site_import);
|
||||||
|
SET_ITEM_STR(base_executable);
|
||||||
|
|
||||||
|
{
|
||||||
|
wchar_t py3path[MAX_PATH];
|
||||||
|
HMODULE hPython3 = GetModuleHandleW(PY3_DLLNAME);
|
||||||
|
PyObject *obj;
|
||||||
|
if (hPython3
|
||||||
|
&& GetModuleFileNameW(hPython3, py3path, Py_ARRAY_LENGTH(py3path)))
|
||||||
|
{
|
||||||
|
obj = PyUnicode_FromWideChar(py3path, -1);
|
||||||
|
if (obj == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj = Py_None;
|
||||||
|
Py_INCREF(obj);
|
||||||
|
}
|
||||||
|
if (PyDict_SetItemString(dict, "python3_dll", obj) < 0) {
|
||||||
|
Py_DECREF(obj);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
Py_DECREF(obj);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef SET_ITEM
|
||||||
|
#undef SET_ITEM_STR
|
||||||
|
#undef SET_ITEM_INT
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
Py_DECREF(dict);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyStatus
|
PyStatus
|
||||||
_PyConfig_WritePathConfig(const PyConfig *config)
|
_PyConfig_WritePathConfig(const PyConfig *config)
|
||||||
|
|
Loading…
Reference in New Issue