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 void _Py_DumpPathConfig(PyThreadState *tstate);
|
||||
extern PyObject* _PyPathConfig_AsDict(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -471,6 +471,31 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
('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
|
||||
|
||||
@classmethod
|
||||
|
@ -535,7 +560,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
configs[config_key] = config
|
||||
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):
|
||||
cls = self.__class__
|
||||
configs = self._get_expected_config()
|
||||
|
@ -545,6 +571,11 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
if value is self.GET_DEFAULT_CONFIG:
|
||||
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:
|
||||
# there is no easy way to get the locale encoding before
|
||||
# setlocale(LC_CTYPE, "") is called: don't test encodings
|
||||
|
@ -637,8 +668,19 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
|
||||
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,
|
||||
expected_preconfig=None, modify_path_cb=None,
|
||||
expected_preconfig=None, expected_pathconfig=None,
|
||||
modify_path_cb=None,
|
||||
stderr=None, *, api, preconfig_api=None,
|
||||
env=None, ignore_stderr=False, cwd=None):
|
||||
new_env = remove_python_envvars()
|
||||
|
@ -657,9 +699,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
if expected_preconfig is None:
|
||||
expected_preconfig = {}
|
||||
expected_preconfig = dict(default_preconfig, **expected_preconfig)
|
||||
|
||||
if expected_config is None:
|
||||
expected_config = {}
|
||||
|
||||
if expected_pathconfig is None:
|
||||
expected_pathconfig = {}
|
||||
expected_pathconfig = dict(self.PATH_CONFIG, **expected_pathconfig)
|
||||
|
||||
if api == API_PYTHON:
|
||||
default_config = self.CONFIG_PYTHON
|
||||
elif api == API_ISOLATED:
|
||||
|
@ -669,7 +716,9 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
expected_config = dict(default_config, **expected_config)
|
||||
|
||||
self.get_expected_config(expected_preconfig,
|
||||
expected_config, env,
|
||||
expected_config,
|
||||
expected_pathconfig,
|
||||
env,
|
||||
api, modify_path_cb)
|
||||
|
||||
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_config(configs, expected_config)
|
||||
self.check_global_config(configs)
|
||||
self.check_path_config(configs, expected_pathconfig)
|
||||
return configs
|
||||
|
||||
def test_init_default_config(self):
|
||||
|
@ -1258,22 +1308,24 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
'executable': executable,
|
||||
'module_search_paths': paths,
|
||||
}
|
||||
path_config = {}
|
||||
if MS_WINDOWS:
|
||||
config['base_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)
|
||||
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,
|
||||
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):
|
||||
# Test C API functions getting the path configuration:
|
||||
|
|
|
@ -18,53 +18,10 @@
|
|||
#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 *
|
||||
get_configs(PyObject *self, PyObject *Py_UNUSED(args))
|
||||
{
|
||||
PyObject *dict = _Py_GetConfigsAsDict();
|
||||
#ifdef MS_WINDOWS
|
||||
if (dict) {
|
||||
if (_add_windows_config(dict) < 0) {
|
||||
Py_CLEAR(dict);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return dict;
|
||||
return _Py_GetConfigsAsDict();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -868,9 +868,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
|
|||
static PyObject *
|
||||
config_as_dict(const PyConfig *config)
|
||||
{
|
||||
PyObject *dict;
|
||||
|
||||
dict = PyDict_New();
|
||||
PyObject *dict = PyDict_New();
|
||||
if (dict == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2643,6 +2641,16 @@ _Py_GetConfigsAsDict(void)
|
|||
}
|
||||
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;
|
||||
|
||||
error:
|
||||
|
|
|
@ -186,6 +186,80 @@ done:
|
|||
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
|
||||
_PyConfig_WritePathConfig(const PyConfig *config)
|
||||
|
|
Loading…
Reference in New Issue