bpo-45582: Port getpath[p].c to Python (GH-29041)

The getpath.py file is frozen at build time and executed as code over a namespace. It is never imported, nor is it meant to be importable or reusable. However, it should be easier to read, modify, and patch than the previous code.

This commit attempts to preserve every previously tested quirk, but these may be changed in the future to better align platforms.
This commit is contained in:
Steve Dower 2021-12-03 00:08:42 +00:00 committed by GitHub
parent 9f2f7e4226
commit 99fcf15052
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 3686 additions and 3838 deletions

View file

@ -22,11 +22,6 @@
# endif
#endif
#ifndef PLATLIBDIR
# error "PLATLIBDIR macro must be defined"
#endif
/* --- Command line options --------------------------------------- */
/* Short usage message (with %s for argv0) */
@ -632,7 +627,6 @@ config_check_consistency(const PyConfig *config)
assert(config->parse_argv >= 0);
assert(config->configure_c_stdio >= 0);
assert(config->buffered_stdio >= 0);
assert(config->program_name != NULL);
assert(_PyWideStringList_CheckConsistency(&config->orig_argv));
assert(_PyWideStringList_CheckConsistency(&config->argv));
/* sys.argv must be non-empty: empty argv is replaced with [''] */
@ -641,7 +635,6 @@ config_check_consistency(const PyConfig *config)
assert(_PyWideStringList_CheckConsistency(&config->warnoptions));
assert(_PyWideStringList_CheckConsistency(&config->module_search_paths));
assert(config->module_search_paths_set >= 0);
assert(config->platlibdir != NULL);
assert(config->filesystem_encoding != NULL);
assert(config->filesystem_errors != NULL);
assert(config->stdio_encoding != NULL);
@ -740,6 +733,7 @@ _PyConfig_InitCompatConfig(PyConfig *config)
config->legacy_windows_stdio = -1;
#endif
config->use_frozen_modules = -1;
config->_is_python_build = 0;
config->code_debug_ranges = 1;
}
@ -962,6 +956,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
COPY_ATTR(_isolated_interpreter);
COPY_ATTR(use_frozen_modules);
COPY_WSTRLIST(orig_argv);
COPY_ATTR(_is_python_build);
#undef COPY_ATTR
#undef COPY_WSTR_ATTR
@ -1066,6 +1061,7 @@ _PyConfig_AsDict(const PyConfig *config)
SET_ITEM_INT(_isolated_interpreter);
SET_ITEM_WSTRLIST(orig_argv);
SET_ITEM_INT(use_frozen_modules);
SET_ITEM_INT(_is_python_build);
return dict;
@ -1350,6 +1346,7 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
GET_UINT(_init_main);
GET_UINT(_isolated_interpreter);
GET_UINT(use_frozen_modules);
GET_UINT(_is_python_build);
#undef CHECK_VALUE
#undef GET_UINT
@ -1489,117 +1486,6 @@ config_set_global_vars(const PyConfig *config)
}
/* Get the program name: use PYTHONEXECUTABLE and __PYVENV_LAUNCHER__
environment variables on macOS if available. */
static PyStatus
config_init_program_name(PyConfig *config)
{
PyStatus status;
/* If Py_SetProgramName() was called, use its value */
const wchar_t *program_name = _Py_path_config.program_name;
if (program_name != NULL) {
config->program_name = _PyMem_RawWcsdup(program_name);
if (config->program_name == NULL) {
return _PyStatus_NO_MEMORY();
}
return _PyStatus_OK();
}
#ifdef __APPLE__
/* On MacOS X, when the Python interpreter is embedded in an
application bundle, it gets executed by a bootstrapping script
that does os.execve() with an argv[0] that's different from the
actual Python executable. This is needed to keep the Finder happy,
or rather, to work around Apple's overly strict requirements of
the process name. However, we still need a usable sys.executable,
so the actual executable path is passed in an environment variable.
See Lib/plat-mac/bundlebuilder.py for details about the bootstrap
script. */
const char *p = config_get_env(config, "PYTHONEXECUTABLE");
if (p != NULL) {
status = CONFIG_SET_BYTES_STR(config, &config->program_name, p,
"PYTHONEXECUTABLE environment variable");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
#ifdef WITH_NEXT_FRAMEWORK
else {
const char* pyvenv_launcher = getenv("__PYVENV_LAUNCHER__");
if (pyvenv_launcher && *pyvenv_launcher) {
/* Used by Mac/Tools/pythonw.c to forward
* the argv0 of the stub executable
*/
status = CONFIG_SET_BYTES_STR(config,
&config->program_name,
pyvenv_launcher,
"__PYVENV_LAUNCHER__ environment variable");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
/*
* This environment variable is used to communicate between
* the stub launcher and the real interpreter and isn't needed
* beyond this point.
*
* Clean up to avoid problems when launching other programs
* later on.
*/
(void)unsetenv("__PYVENV_LAUNCHER__");
return _PyStatus_OK();
}
}
#endif /* WITH_NEXT_FRAMEWORK */
#endif /* __APPLE__ */
/* Use argv[0] if available and non-empty */
const PyWideStringList *argv = &config->argv;
if (argv->length >= 1 && argv->items[0][0] != L'\0') {
config->program_name = _PyMem_RawWcsdup(argv->items[0]);
if (config->program_name == NULL) {
return _PyStatus_NO_MEMORY();
}
return _PyStatus_OK();
}
/* Last fall back: hardcoded name */
#ifdef MS_WINDOWS
const wchar_t *default_program_name = L"python";
#else
const wchar_t *default_program_name = L"python3";
#endif
status = PyConfig_SetString(config, &config->program_name,
default_program_name);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
static PyStatus
config_init_executable(PyConfig *config)
{
assert(config->executable == NULL);
/* If Py_SetProgramFullPath() was called, use its value */
const wchar_t *program_full_path = _Py_path_config.program_full_path;
if (program_full_path != NULL) {
PyStatus status = PyConfig_SetString(config,
&config->executable,
program_full_path);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
return _PyStatus_OK();
}
static const wchar_t*
config_get_xoption(const PyConfig *config, wchar_t *name)
{
@ -1618,25 +1504,6 @@ config_get_xoption_value(const PyConfig *config, wchar_t *name)
}
static PyStatus
config_init_home(PyConfig *config)
{
assert(config->home == NULL);
/* If Py_SetPythonHome() was called, use its value */
wchar_t *home = _Py_path_config.home;
if (home) {
PyStatus status = PyConfig_SetString(config, &config->home, home);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
return CONFIG_GET_ENV_DUP(config, &config->home,
L"PYTHONHOME", "PYTHONHOME");
}
static PyStatus
config_init_hash_seed(PyConfig *config)
{
@ -2092,44 +1959,6 @@ config_init_fs_encoding(PyConfig *config, const PyPreConfig *preconfig)
}
/* Determine if the current build is a "development" build (e.g. running
out of the source tree) or not.
A return value of -1 indicates that we do not know.
*/
static int
is_dev_env(PyConfig *config)
{
// This should only ever get called early in runtime initialization,
// before the global path config is written. Otherwise we would
// use Py_GetProgramFullPath() and _Py_GetStdlibDir().
assert(config != NULL);
const wchar_t *executable = config->executable;
const wchar_t *stdlib = config->stdlib_dir;
if (executable == NULL || *executable == L'\0' ||
stdlib == NULL || *stdlib == L'\0') {
// _PyPathConfig_Calculate() hasn't run yet.
return -1;
}
size_t len = _Py_find_basename(executable);
if (wcscmp(executable + len, L"python") != 0 &&
wcscmp(executable + len, L"python.exe") != 0) {
return 0;
}
/* If dirname() is the same for both then it is a dev build. */
if (len != _Py_find_basename(stdlib)) {
return 0;
}
// We do not bother normalizing the two filenames first since
// for config_init_import() is does the right thing as-is.
if (wcsncmp(stdlib, executable, len) != 0) {
return 0;
}
return 1;
}
static PyStatus
config_init_import(PyConfig *config, int compute_path_config)
{
@ -2144,10 +1973,7 @@ config_init_import(PyConfig *config, int compute_path_config)
if (config->use_frozen_modules < 0) {
const wchar_t *value = config_get_xoption_value(config, L"frozen_modules");
if (value == NULL) {
int isdev = is_dev_env(config);
if (isdev >= 0) {
config->use_frozen_modules = !isdev;
}
config->use_frozen_modules = !config->_is_python_build;
}
else if (wcscmp(value, L"on") == 0) {
config->use_frozen_modules = 1;
@ -2246,28 +2072,6 @@ config_read(PyConfig *config, int compute_path_config)
return status;
}
if (config->home == NULL) {
status = config_init_home(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->executable == NULL) {
status = config_init_executable(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if(config->platlibdir == NULL) {
status = CONFIG_SET_BYTES_STR(config, &config->platlibdir, PLATLIBDIR,
"PLATLIBDIR macro");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->_install_importlib) {
status = config_init_import(config, compute_path_config);
if (_PyStatus_EXCEPTION(status)) {
@ -2890,13 +2694,6 @@ config_read_cmdline(PyConfig *config)
config->parse_argv = 1;
}
if (config->program_name == NULL) {
status = config_init_program_name(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->parse_argv == 1) {
Py_ssize_t opt_index;
status = config_parse_cmdline(config, &cmdline_warnoptions, &opt_index);
@ -3076,7 +2873,7 @@ done:
PyStatus
PyConfig_Read(PyConfig *config)
{
return _PyConfig_Read(config, 1);
return _PyConfig_Read(config, 0);
}
@ -3124,16 +2921,6 @@ _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:
@ -3199,6 +2986,7 @@ _Py_DumpPathConfig(PyThreadState *tstate)
PySys_WriteStderr(" environment = %i\n", config->use_environment);
PySys_WriteStderr(" user site = %i\n", config->user_site_directory);
PySys_WriteStderr(" import site = %i\n", config->site_import);
PySys_WriteStderr(" is in build tree = %i\n", config->_is_python_build);
DUMP_CONFIG("stdlib dir", stdlib_dir);
#undef DUMP_CONFIG