mirror of
https://github.com/python/cpython
synced 2024-09-05 16:38:56 +00:00
![Victor Stinner](/assets/img/avatar_default.png)
Remove private pylifecycle.h functions: move them to the internal C API ( pycore_atexit.h, pycore_pylifecycle.h and pycore_signal.h). No longer export most of these functions. Move _testcapi.test_atexit() to _testinternalcapi.
979 lines
25 KiB
C
979 lines
25 KiB
C
#include "Python.h"
|
|
#include "pycore_fileutils.h" // DECODE_LOCALE_ERR
|
|
#include "pycore_getopt.h" // _PyOS_GetOpt()
|
|
#include "pycore_initconfig.h" // _PyArgv
|
|
#include "pycore_pylifecycle.h" // _Py_LegacyLocaleDetected()
|
|
#include "pycore_pymem.h" // _PyMem_GetAllocatorName()
|
|
#include "pycore_runtime.h" // _PyRuntime_Initialize()
|
|
|
|
#include <locale.h> // setlocale()
|
|
#include <stdlib.h> // getenv()
|
|
|
|
|
|
/* Forward declarations */
|
|
static void
|
|
preconfig_copy(PyPreConfig *config, const PyPreConfig *config2);
|
|
|
|
|
|
/* --- File system encoding/errors -------------------------------- */
|
|
|
|
const char *Py_FileSystemDefaultEncoding = NULL;
|
|
int Py_HasFileSystemDefaultEncoding = 0;
|
|
const char *Py_FileSystemDefaultEncodeErrors = NULL;
|
|
int _Py_HasFileSystemDefaultEncodeErrors = 0;
|
|
|
|
void
|
|
_Py_ClearFileSystemEncoding(void)
|
|
{
|
|
_Py_COMP_DIAG_PUSH
|
|
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
|
|
if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) {
|
|
PyMem_RawFree((char*)Py_FileSystemDefaultEncoding);
|
|
Py_FileSystemDefaultEncoding = NULL;
|
|
}
|
|
if (!_Py_HasFileSystemDefaultEncodeErrors && Py_FileSystemDefaultEncodeErrors) {
|
|
PyMem_RawFree((char*)Py_FileSystemDefaultEncodeErrors);
|
|
Py_FileSystemDefaultEncodeErrors = NULL;
|
|
}
|
|
_Py_COMP_DIAG_POP
|
|
}
|
|
|
|
|
|
/* Set Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors
|
|
global configuration variables to PyConfig.filesystem_encoding and
|
|
PyConfig.filesystem_errors (encoded to UTF-8).
|
|
|
|
Function called by _PyUnicode_InitEncodings(). */
|
|
int
|
|
_Py_SetFileSystemEncoding(const char *encoding, const char *errors)
|
|
{
|
|
char *encoding2 = _PyMem_RawStrdup(encoding);
|
|
if (encoding2 == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
char *errors2 = _PyMem_RawStrdup(errors);
|
|
if (errors2 == NULL) {
|
|
PyMem_RawFree(encoding2);
|
|
return -1;
|
|
}
|
|
|
|
_Py_ClearFileSystemEncoding();
|
|
|
|
_Py_COMP_DIAG_PUSH
|
|
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
|
|
Py_FileSystemDefaultEncoding = encoding2;
|
|
Py_HasFileSystemDefaultEncoding = 0;
|
|
|
|
Py_FileSystemDefaultEncodeErrors = errors2;
|
|
_Py_HasFileSystemDefaultEncodeErrors = 0;
|
|
_Py_COMP_DIAG_POP
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* --- _PyArgv ---------------------------------------------------- */
|
|
|
|
/* Decode bytes_argv using Py_DecodeLocale() */
|
|
PyStatus
|
|
_PyArgv_AsWstrList(const _PyArgv *args, PyWideStringList *list)
|
|
{
|
|
PyWideStringList wargv = _PyWideStringList_INIT;
|
|
if (args->use_bytes_argv) {
|
|
size_t size = sizeof(wchar_t*) * args->argc;
|
|
wargv.items = (wchar_t **)PyMem_RawMalloc(size);
|
|
if (wargv.items == NULL) {
|
|
return _PyStatus_NO_MEMORY();
|
|
}
|
|
|
|
for (Py_ssize_t i = 0; i < args->argc; i++) {
|
|
size_t len;
|
|
wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
|
|
if (arg == NULL) {
|
|
_PyWideStringList_Clear(&wargv);
|
|
return DECODE_LOCALE_ERR("command line arguments", len);
|
|
}
|
|
wargv.items[i] = arg;
|
|
wargv.length++;
|
|
}
|
|
|
|
_PyWideStringList_Clear(list);
|
|
*list = wargv;
|
|
}
|
|
else {
|
|
wargv.length = args->argc;
|
|
wargv.items = (wchar_t **)args->wchar_argv;
|
|
if (_PyWideStringList_Copy(list, &wargv) < 0) {
|
|
return _PyStatus_NO_MEMORY();
|
|
}
|
|
}
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
/* --- _PyPreCmdline ------------------------------------------------- */
|
|
|
|
void
|
|
_PyPreCmdline_Clear(_PyPreCmdline *cmdline)
|
|
{
|
|
_PyWideStringList_Clear(&cmdline->argv);
|
|
_PyWideStringList_Clear(&cmdline->xoptions);
|
|
}
|
|
|
|
|
|
PyStatus
|
|
_PyPreCmdline_SetArgv(_PyPreCmdline *cmdline, const _PyArgv *args)
|
|
{
|
|
return _PyArgv_AsWstrList(args, &cmdline->argv);
|
|
}
|
|
|
|
|
|
static void
|
|
precmdline_get_preconfig(_PyPreCmdline *cmdline, const PyPreConfig *config)
|
|
{
|
|
#define COPY_ATTR(ATTR) \
|
|
if (config->ATTR != -1) { \
|
|
cmdline->ATTR = config->ATTR; \
|
|
}
|
|
|
|
COPY_ATTR(isolated);
|
|
COPY_ATTR(use_environment);
|
|
COPY_ATTR(dev_mode);
|
|
|
|
#undef COPY_ATTR
|
|
}
|
|
|
|
|
|
static void
|
|
precmdline_set_preconfig(const _PyPreCmdline *cmdline, PyPreConfig *config)
|
|
{
|
|
#define COPY_ATTR(ATTR) \
|
|
config->ATTR = cmdline->ATTR
|
|
|
|
COPY_ATTR(isolated);
|
|
COPY_ATTR(use_environment);
|
|
COPY_ATTR(dev_mode);
|
|
|
|
#undef COPY_ATTR
|
|
}
|
|
|
|
|
|
PyStatus
|
|
_PyPreCmdline_SetConfig(const _PyPreCmdline *cmdline, PyConfig *config)
|
|
{
|
|
#define COPY_ATTR(ATTR) \
|
|
config->ATTR = cmdline->ATTR
|
|
|
|
PyStatus status = _PyWideStringList_Extend(&config->xoptions, &cmdline->xoptions);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
|
|
COPY_ATTR(isolated);
|
|
COPY_ATTR(use_environment);
|
|
COPY_ATTR(dev_mode);
|
|
COPY_ATTR(warn_default_encoding);
|
|
return _PyStatus_OK();
|
|
|
|
#undef COPY_ATTR
|
|
}
|
|
|
|
|
|
/* Parse the command line arguments */
|
|
static PyStatus
|
|
precmdline_parse_cmdline(_PyPreCmdline *cmdline)
|
|
{
|
|
const PyWideStringList *argv = &cmdline->argv;
|
|
|
|
_PyOS_ResetGetOpt();
|
|
/* Don't log parsing errors into stderr here: PyConfig_Read()
|
|
is responsible for that */
|
|
_PyOS_opterr = 0;
|
|
do {
|
|
int longindex = -1;
|
|
int c = _PyOS_GetOpt(argv->length, argv->items, &longindex);
|
|
|
|
if (c == EOF || c == 'c' || c == 'm') {
|
|
break;
|
|
}
|
|
|
|
switch (c) {
|
|
case 'E':
|
|
cmdline->use_environment = 0;
|
|
break;
|
|
|
|
case 'I':
|
|
cmdline->isolated = 1;
|
|
break;
|
|
|
|
case 'X':
|
|
{
|
|
PyStatus status = PyWideStringList_Append(&cmdline->xoptions,
|
|
_PyOS_optarg);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
/* ignore other argument:
|
|
handled by PyConfig_Read() */
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
PyStatus
|
|
_PyPreCmdline_Read(_PyPreCmdline *cmdline, const PyPreConfig *preconfig)
|
|
{
|
|
precmdline_get_preconfig(cmdline, preconfig);
|
|
|
|
if (preconfig->parse_argv) {
|
|
PyStatus status = precmdline_parse_cmdline(cmdline);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/* isolated, use_environment */
|
|
if (cmdline->isolated < 0) {
|
|
cmdline->isolated = 0;
|
|
}
|
|
if (cmdline->isolated > 0) {
|
|
cmdline->use_environment = 0;
|
|
}
|
|
if (cmdline->use_environment < 0) {
|
|
cmdline->use_environment = 0;
|
|
}
|
|
|
|
/* dev_mode */
|
|
if ((cmdline->dev_mode < 0)
|
|
&& (_Py_get_xoption(&cmdline->xoptions, L"dev")
|
|
|| _Py_GetEnv(cmdline->use_environment, "PYTHONDEVMODE")))
|
|
{
|
|
cmdline->dev_mode = 1;
|
|
}
|
|
if (cmdline->dev_mode < 0) {
|
|
cmdline->dev_mode = 0;
|
|
}
|
|
|
|
// warn_default_encoding
|
|
if (_Py_get_xoption(&cmdline->xoptions, L"warn_default_encoding")
|
|
|| _Py_GetEnv(cmdline->use_environment, "PYTHONWARNDEFAULTENCODING"))
|
|
{
|
|
cmdline->warn_default_encoding = 1;
|
|
}
|
|
|
|
assert(cmdline->use_environment >= 0);
|
|
assert(cmdline->isolated >= 0);
|
|
assert(cmdline->dev_mode >= 0);
|
|
assert(cmdline->warn_default_encoding >= 0);
|
|
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
/* --- PyPreConfig ----------------------------------------------- */
|
|
|
|
|
|
void
|
|
_PyPreConfig_InitCompatConfig(PyPreConfig *config)
|
|
{
|
|
memset(config, 0, sizeof(*config));
|
|
|
|
config->_config_init = (int)_PyConfig_INIT_COMPAT;
|
|
config->parse_argv = 0;
|
|
config->isolated = -1;
|
|
config->use_environment = -1;
|
|
config->configure_locale = 1;
|
|
|
|
/* bpo-36443: C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
|
|
are disabled by default using the Compat configuration.
|
|
|
|
Py_UTF8Mode=1 enables the UTF-8 mode. PYTHONUTF8 environment variable
|
|
is ignored (even if use_environment=1). */
|
|
config->utf8_mode = 0;
|
|
config->coerce_c_locale = 0;
|
|
config->coerce_c_locale_warn = 0;
|
|
|
|
config->dev_mode = -1;
|
|
config->allocator = PYMEM_ALLOCATOR_NOT_SET;
|
|
#ifdef MS_WINDOWS
|
|
config->legacy_windows_fs_encoding = -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
PyPreConfig_InitPythonConfig(PyPreConfig *config)
|
|
{
|
|
_PyPreConfig_InitCompatConfig(config);
|
|
|
|
config->_config_init = (int)_PyConfig_INIT_PYTHON;
|
|
config->isolated = 0;
|
|
config->parse_argv = 1;
|
|
config->use_environment = 1;
|
|
/* Set to -1 to enable C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
|
|
depending on the LC_CTYPE locale, PYTHONUTF8 and PYTHONCOERCECLOCALE
|
|
environment variables. */
|
|
config->coerce_c_locale = -1;
|
|
config->coerce_c_locale_warn = -1;
|
|
config->utf8_mode = -1;
|
|
#ifdef MS_WINDOWS
|
|
config->legacy_windows_fs_encoding = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
PyPreConfig_InitIsolatedConfig(PyPreConfig *config)
|
|
{
|
|
_PyPreConfig_InitCompatConfig(config);
|
|
|
|
config->_config_init = (int)_PyConfig_INIT_ISOLATED;
|
|
config->configure_locale = 0;
|
|
config->isolated = 1;
|
|
config->use_environment = 0;
|
|
config->utf8_mode = 0;
|
|
config->dev_mode = 0;
|
|
#ifdef MS_WINDOWS
|
|
config->legacy_windows_fs_encoding = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
PyStatus
|
|
_PyPreConfig_InitFromPreConfig(PyPreConfig *config,
|
|
const PyPreConfig *config2)
|
|
{
|
|
PyPreConfig_InitPythonConfig(config);
|
|
preconfig_copy(config, config2);
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
void
|
|
_PyPreConfig_InitFromConfig(PyPreConfig *preconfig, const PyConfig *config)
|
|
{
|
|
_PyConfigInitEnum config_init = (_PyConfigInitEnum)config->_config_init;
|
|
switch (config_init) {
|
|
case _PyConfig_INIT_PYTHON:
|
|
PyPreConfig_InitPythonConfig(preconfig);
|
|
break;
|
|
case _PyConfig_INIT_ISOLATED:
|
|
PyPreConfig_InitIsolatedConfig(preconfig);
|
|
break;
|
|
case _PyConfig_INIT_COMPAT:
|
|
default:
|
|
_PyPreConfig_InitCompatConfig(preconfig);
|
|
}
|
|
|
|
_PyPreConfig_GetConfig(preconfig, config);
|
|
}
|
|
|
|
|
|
static void
|
|
preconfig_copy(PyPreConfig *config, const PyPreConfig *config2)
|
|
{
|
|
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
|
|
|
|
COPY_ATTR(_config_init);
|
|
COPY_ATTR(parse_argv);
|
|
COPY_ATTR(isolated);
|
|
COPY_ATTR(use_environment);
|
|
COPY_ATTR(configure_locale);
|
|
COPY_ATTR(dev_mode);
|
|
COPY_ATTR(coerce_c_locale);
|
|
COPY_ATTR(coerce_c_locale_warn);
|
|
COPY_ATTR(utf8_mode);
|
|
COPY_ATTR(allocator);
|
|
#ifdef MS_WINDOWS
|
|
COPY_ATTR(legacy_windows_fs_encoding);
|
|
#endif
|
|
|
|
#undef COPY_ATTR
|
|
}
|
|
|
|
|
|
PyObject*
|
|
_PyPreConfig_AsDict(const PyPreConfig *config)
|
|
{
|
|
PyObject *dict;
|
|
|
|
dict = PyDict_New();
|
|
if (dict == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
#define SET_ITEM_INT(ATTR) \
|
|
do { \
|
|
PyObject *obj = PyLong_FromLong(config->ATTR); \
|
|
if (obj == NULL) { \
|
|
goto fail; \
|
|
} \
|
|
int res = PyDict_SetItemString(dict, #ATTR, obj); \
|
|
Py_DECREF(obj); \
|
|
if (res < 0) { \
|
|
goto fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
SET_ITEM_INT(_config_init);
|
|
SET_ITEM_INT(parse_argv);
|
|
SET_ITEM_INT(isolated);
|
|
SET_ITEM_INT(use_environment);
|
|
SET_ITEM_INT(configure_locale);
|
|
SET_ITEM_INT(coerce_c_locale);
|
|
SET_ITEM_INT(coerce_c_locale_warn);
|
|
SET_ITEM_INT(utf8_mode);
|
|
#ifdef MS_WINDOWS
|
|
SET_ITEM_INT(legacy_windows_fs_encoding);
|
|
#endif
|
|
SET_ITEM_INT(dev_mode);
|
|
SET_ITEM_INT(allocator);
|
|
return dict;
|
|
|
|
fail:
|
|
Py_DECREF(dict);
|
|
return NULL;
|
|
|
|
#undef SET_ITEM_INT
|
|
}
|
|
|
|
|
|
void
|
|
_PyPreConfig_GetConfig(PyPreConfig *preconfig, const PyConfig *config)
|
|
{
|
|
#define COPY_ATTR(ATTR) \
|
|
if (config->ATTR != -1) { \
|
|
preconfig->ATTR = config->ATTR; \
|
|
}
|
|
|
|
COPY_ATTR(parse_argv);
|
|
COPY_ATTR(isolated);
|
|
COPY_ATTR(use_environment);
|
|
COPY_ATTR(dev_mode);
|
|
|
|
#undef COPY_ATTR
|
|
}
|
|
|
|
|
|
static void
|
|
preconfig_get_global_vars(PyPreConfig *config)
|
|
{
|
|
if (config->_config_init != _PyConfig_INIT_COMPAT) {
|
|
/* Python and Isolated configuration ignore global variables */
|
|
return;
|
|
}
|
|
|
|
#define COPY_FLAG(ATTR, VALUE) \
|
|
if (config->ATTR < 0) { \
|
|
config->ATTR = VALUE; \
|
|
}
|
|
#define COPY_NOT_FLAG(ATTR, VALUE) \
|
|
if (config->ATTR < 0) { \
|
|
config->ATTR = !(VALUE); \
|
|
}
|
|
|
|
_Py_COMP_DIAG_PUSH
|
|
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
|
|
COPY_FLAG(isolated, Py_IsolatedFlag);
|
|
COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
|
|
if (Py_UTF8Mode > 0) {
|
|
config->utf8_mode = Py_UTF8Mode;
|
|
}
|
|
#ifdef MS_WINDOWS
|
|
COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
|
|
#endif
|
|
_Py_COMP_DIAG_POP
|
|
|
|
#undef COPY_FLAG
|
|
#undef COPY_NOT_FLAG
|
|
}
|
|
|
|
|
|
static void
|
|
preconfig_set_global_vars(const PyPreConfig *config)
|
|
{
|
|
#define COPY_FLAG(ATTR, VAR) \
|
|
if (config->ATTR >= 0) { \
|
|
VAR = config->ATTR; \
|
|
}
|
|
#define COPY_NOT_FLAG(ATTR, VAR) \
|
|
if (config->ATTR >= 0) { \
|
|
VAR = !config->ATTR; \
|
|
}
|
|
|
|
_Py_COMP_DIAG_PUSH
|
|
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
|
|
COPY_FLAG(isolated, Py_IsolatedFlag);
|
|
COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
|
|
#ifdef MS_WINDOWS
|
|
COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
|
|
#endif
|
|
COPY_FLAG(utf8_mode, Py_UTF8Mode);
|
|
_Py_COMP_DIAG_POP
|
|
|
|
#undef COPY_FLAG
|
|
#undef COPY_NOT_FLAG
|
|
}
|
|
|
|
|
|
const char*
|
|
_Py_GetEnv(int use_environment, const char *name)
|
|
{
|
|
assert(use_environment >= 0);
|
|
|
|
if (!use_environment) {
|
|
return NULL;
|
|
}
|
|
|
|
const char *var = getenv(name);
|
|
if (var && var[0] != '\0') {
|
|
return var;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
_Py_str_to_int(const char *str, int *result)
|
|
{
|
|
const char *endptr = str;
|
|
errno = 0;
|
|
long value = strtol(str, (char **)&endptr, 10);
|
|
if (*endptr != '\0' || errno == ERANGE) {
|
|
return -1;
|
|
}
|
|
if (value < INT_MIN || value > INT_MAX) {
|
|
return -1;
|
|
}
|
|
|
|
*result = (int)value;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
_Py_get_env_flag(int use_environment, int *flag, const char *name)
|
|
{
|
|
const char *var = _Py_GetEnv(use_environment, name);
|
|
if (!var) {
|
|
return;
|
|
}
|
|
int value;
|
|
if (_Py_str_to_int(var, &value) < 0 || value < 0) {
|
|
/* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */
|
|
value = 1;
|
|
}
|
|
if (*flag < value) {
|
|
*flag = value;
|
|
}
|
|
}
|
|
|
|
|
|
const wchar_t*
|
|
_Py_get_xoption(const PyWideStringList *xoptions, const wchar_t *name)
|
|
{
|
|
for (Py_ssize_t i=0; i < xoptions->length; i++) {
|
|
const wchar_t *option = xoptions->items[i];
|
|
size_t len;
|
|
wchar_t *sep = wcschr(option, L'=');
|
|
if (sep != NULL) {
|
|
len = (sep - option);
|
|
}
|
|
else {
|
|
len = wcslen(option);
|
|
}
|
|
if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') {
|
|
return option;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static PyStatus
|
|
preconfig_init_utf8_mode(PyPreConfig *config, const _PyPreCmdline *cmdline)
|
|
{
|
|
#ifdef MS_WINDOWS
|
|
if (config->legacy_windows_fs_encoding) {
|
|
config->utf8_mode = 0;
|
|
}
|
|
#endif
|
|
|
|
if (config->utf8_mode >= 0) {
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
const wchar_t *xopt;
|
|
xopt = _Py_get_xoption(&cmdline->xoptions, L"utf8");
|
|
if (xopt) {
|
|
wchar_t *sep = wcschr(xopt, L'=');
|
|
if (sep) {
|
|
xopt = sep + 1;
|
|
if (wcscmp(xopt, L"1") == 0) {
|
|
config->utf8_mode = 1;
|
|
}
|
|
else if (wcscmp(xopt, L"0") == 0) {
|
|
config->utf8_mode = 0;
|
|
}
|
|
else {
|
|
return _PyStatus_ERR("invalid -X utf8 option value");
|
|
}
|
|
}
|
|
else {
|
|
config->utf8_mode = 1;
|
|
}
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
const char *opt = _Py_GetEnv(config->use_environment, "PYTHONUTF8");
|
|
if (opt) {
|
|
if (strcmp(opt, "1") == 0) {
|
|
config->utf8_mode = 1;
|
|
}
|
|
else if (strcmp(opt, "0") == 0) {
|
|
config->utf8_mode = 0;
|
|
}
|
|
else {
|
|
return _PyStatus_ERR("invalid PYTHONUTF8 environment "
|
|
"variable value");
|
|
}
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
#ifndef MS_WINDOWS
|
|
if (config->utf8_mode < 0) {
|
|
/* The C locale and the POSIX locale enable the UTF-8 Mode (PEP 540) */
|
|
const char *ctype_loc = setlocale(LC_CTYPE, NULL);
|
|
if (ctype_loc != NULL
|
|
&& (strcmp(ctype_loc, "C") == 0
|
|
|| strcmp(ctype_loc, "POSIX") == 0))
|
|
{
|
|
config->utf8_mode = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (config->utf8_mode < 0) {
|
|
config->utf8_mode = 0;
|
|
}
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
static void
|
|
preconfig_init_coerce_c_locale(PyPreConfig *config)
|
|
{
|
|
if (!config->configure_locale) {
|
|
config->coerce_c_locale = 0;
|
|
config->coerce_c_locale_warn = 0;
|
|
return;
|
|
}
|
|
|
|
const char *env = _Py_GetEnv(config->use_environment, "PYTHONCOERCECLOCALE");
|
|
if (env) {
|
|
if (strcmp(env, "0") == 0) {
|
|
if (config->coerce_c_locale < 0) {
|
|
config->coerce_c_locale = 0;
|
|
}
|
|
}
|
|
else if (strcmp(env, "warn") == 0) {
|
|
if (config->coerce_c_locale_warn < 0) {
|
|
config->coerce_c_locale_warn = 1;
|
|
}
|
|
}
|
|
else {
|
|
if (config->coerce_c_locale < 0) {
|
|
config->coerce_c_locale = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Test if coerce_c_locale equals to -1 or equals to 1:
|
|
PYTHONCOERCECLOCALE=1 doesn't imply that the C locale is always coerced.
|
|
It is only coerced if if the LC_CTYPE locale is "C". */
|
|
if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) {
|
|
/* The C locale enables the C locale coercion (PEP 538) */
|
|
if (_Py_LegacyLocaleDetected(0)) {
|
|
config->coerce_c_locale = 2;
|
|
}
|
|
else {
|
|
config->coerce_c_locale = 0;
|
|
}
|
|
}
|
|
|
|
if (config->coerce_c_locale_warn < 0) {
|
|
config->coerce_c_locale_warn = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static PyStatus
|
|
preconfig_init_allocator(PyPreConfig *config)
|
|
{
|
|
if (config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
|
|
/* bpo-34247. The PYTHONMALLOC environment variable has the priority
|
|
over PYTHONDEV env var and "-X dev" command line option.
|
|
For example, PYTHONMALLOC=malloc PYTHONDEVMODE=1 sets the memory
|
|
allocators to "malloc" (and not to "debug"). */
|
|
const char *envvar = _Py_GetEnv(config->use_environment, "PYTHONMALLOC");
|
|
if (envvar) {
|
|
PyMemAllocatorName name;
|
|
if (_PyMem_GetAllocatorName(envvar, &name) < 0) {
|
|
return _PyStatus_ERR("PYTHONMALLOC: unknown allocator");
|
|
}
|
|
config->allocator = (int)name;
|
|
}
|
|
}
|
|
|
|
if (config->dev_mode && config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
|
|
config->allocator = PYMEM_ALLOCATOR_DEBUG;
|
|
}
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
static PyStatus
|
|
preconfig_read(PyPreConfig *config, _PyPreCmdline *cmdline)
|
|
{
|
|
PyStatus status;
|
|
|
|
status = _PyPreCmdline_Read(cmdline, config);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
|
|
precmdline_set_preconfig(cmdline, config);
|
|
|
|
/* legacy_windows_fs_encoding, coerce_c_locale, utf8_mode */
|
|
#ifdef MS_WINDOWS
|
|
_Py_get_env_flag(config->use_environment,
|
|
&config->legacy_windows_fs_encoding,
|
|
"PYTHONLEGACYWINDOWSFSENCODING");
|
|
#endif
|
|
|
|
preconfig_init_coerce_c_locale(config);
|
|
|
|
status = preconfig_init_utf8_mode(config, cmdline);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
|
|
/* allocator */
|
|
status = preconfig_init_allocator(config);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
|
|
assert(config->coerce_c_locale >= 0);
|
|
assert(config->coerce_c_locale_warn >= 0);
|
|
#ifdef MS_WINDOWS
|
|
assert(config->legacy_windows_fs_encoding >= 0);
|
|
#endif
|
|
assert(config->utf8_mode >= 0);
|
|
assert(config->isolated >= 0);
|
|
assert(config->use_environment >= 0);
|
|
assert(config->dev_mode >= 0);
|
|
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
|
|
/* Read the configuration from:
|
|
|
|
- command line arguments
|
|
- environment variables
|
|
- Py_xxx global configuration variables
|
|
- the LC_CTYPE locale */
|
|
PyStatus
|
|
_PyPreConfig_Read(PyPreConfig *config, const _PyArgv *args)
|
|
{
|
|
PyStatus status;
|
|
|
|
status = _PyRuntime_Initialize();
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
|
|
preconfig_get_global_vars(config);
|
|
|
|
/* Copy LC_CTYPE locale, since it's modified later */
|
|
const char *loc = setlocale(LC_CTYPE, NULL);
|
|
if (loc == NULL) {
|
|
return _PyStatus_ERR("failed to LC_CTYPE locale");
|
|
}
|
|
char *init_ctype_locale = _PyMem_RawStrdup(loc);
|
|
if (init_ctype_locale == NULL) {
|
|
return _PyStatus_NO_MEMORY();
|
|
}
|
|
|
|
/* Save the config to be able to restore it if encodings change */
|
|
PyPreConfig save_config;
|
|
|
|
status = _PyPreConfig_InitFromPreConfig(&save_config, config);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
|
|
/* Set LC_CTYPE to the user preferred locale */
|
|
if (config->configure_locale) {
|
|
_Py_SetLocaleFromEnv(LC_CTYPE);
|
|
}
|
|
|
|
PyPreConfig save_runtime_config;
|
|
preconfig_copy(&save_runtime_config, &_PyRuntime.preconfig);
|
|
|
|
_PyPreCmdline cmdline = _PyPreCmdline_INIT;
|
|
int locale_coerced = 0;
|
|
int loops = 0;
|
|
|
|
while (1) {
|
|
int utf8_mode = config->utf8_mode;
|
|
|
|
/* Watchdog to prevent an infinite loop */
|
|
loops++;
|
|
if (loops == 3) {
|
|
status = _PyStatus_ERR("Encoding changed twice while "
|
|
"reading the configuration");
|
|
goto done;
|
|
}
|
|
|
|
/* bpo-34207: Py_DecodeLocale() and Py_EncodeLocale() depend
|
|
on the utf8_mode and legacy_windows_fs_encoding members
|
|
of _PyRuntime.preconfig. */
|
|
preconfig_copy(&_PyRuntime.preconfig, config);
|
|
|
|
if (args) {
|
|
// Set command line arguments at each iteration. If they are bytes
|
|
// strings, they are decoded from the new encoding.
|
|
status = _PyPreCmdline_SetArgv(&cmdline, args);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
status = preconfig_read(config, &cmdline);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
goto done;
|
|
}
|
|
|
|
/* The legacy C locale assumes ASCII as the default text encoding, which
|
|
* causes problems not only for the CPython runtime, but also other
|
|
* components like GNU readline.
|
|
*
|
|
* Accordingly, when the CLI detects it, it attempts to coerce it to a
|
|
* more capable UTF-8 based alternative.
|
|
*
|
|
* See the documentation of the PYTHONCOERCECLOCALE setting for more
|
|
* details.
|
|
*/
|
|
int encoding_changed = 0;
|
|
if (config->coerce_c_locale && !locale_coerced) {
|
|
locale_coerced = 1;
|
|
_Py_CoerceLegacyLocale(0);
|
|
encoding_changed = 1;
|
|
}
|
|
|
|
if (utf8_mode == -1) {
|
|
if (config->utf8_mode == 1) {
|
|
/* UTF-8 Mode enabled */
|
|
encoding_changed = 1;
|
|
}
|
|
}
|
|
else {
|
|
if (config->utf8_mode != utf8_mode) {
|
|
encoding_changed = 1;
|
|
}
|
|
}
|
|
|
|
if (!encoding_changed) {
|
|
break;
|
|
}
|
|
|
|
/* Reset the configuration before reading again the configuration,
|
|
just keep UTF-8 Mode and coerce C locale value. */
|
|
int new_utf8_mode = config->utf8_mode;
|
|
int new_coerce_c_locale = config->coerce_c_locale;
|
|
preconfig_copy(config, &save_config);
|
|
config->utf8_mode = new_utf8_mode;
|
|
config->coerce_c_locale = new_coerce_c_locale;
|
|
|
|
/* The encoding changed: read again the configuration
|
|
with the new encoding */
|
|
}
|
|
status = _PyStatus_OK();
|
|
|
|
done:
|
|
// Revert side effects
|
|
setlocale(LC_CTYPE, init_ctype_locale);
|
|
PyMem_RawFree(init_ctype_locale);
|
|
preconfig_copy(&_PyRuntime.preconfig, &save_runtime_config);
|
|
_PyPreCmdline_Clear(&cmdline);
|
|
return status;
|
|
}
|
|
|
|
|
|
/* Write the pre-configuration:
|
|
|
|
- set the memory allocators
|
|
- set Py_xxx global configuration variables
|
|
- set the LC_CTYPE locale (coerce C locale, PEP 538) and set the UTF-8 mode
|
|
(PEP 540)
|
|
|
|
The applied configuration is written into _PyRuntime.preconfig.
|
|
If the C locale cannot be coerced, set coerce_c_locale to 0.
|
|
|
|
Do nothing if called after Py_Initialize(): ignore the new
|
|
pre-configuration. */
|
|
PyStatus
|
|
_PyPreConfig_Write(const PyPreConfig *src_config)
|
|
{
|
|
PyPreConfig config;
|
|
|
|
PyStatus status = _PyPreConfig_InitFromPreConfig(&config, src_config);
|
|
if (_PyStatus_EXCEPTION(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (_PyRuntime.core_initialized) {
|
|
/* bpo-34008: Calling this functions after Py_Initialize() ignores
|
|
the new configuration. */
|
|
return _PyStatus_OK();
|
|
}
|
|
|
|
PyMemAllocatorName name = (PyMemAllocatorName)config.allocator;
|
|
if (name != PYMEM_ALLOCATOR_NOT_SET) {
|
|
if (_PyMem_SetupAllocators(name) < 0) {
|
|
return _PyStatus_ERR("Unknown PYTHONMALLOC allocator");
|
|
}
|
|
}
|
|
|
|
preconfig_set_global_vars(&config);
|
|
|
|
if (config.configure_locale) {
|
|
if (config.coerce_c_locale) {
|
|
if (!_Py_CoerceLegacyLocale(config.coerce_c_locale_warn)) {
|
|
/* C locale not coerced */
|
|
config.coerce_c_locale = 0;
|
|
}
|
|
}
|
|
|
|
/* Set LC_CTYPE to the user preferred locale */
|
|
_Py_SetLocaleFromEnv(LC_CTYPE);
|
|
}
|
|
|
|
/* Write the new pre-configuration into _PyRuntime */
|
|
preconfig_copy(&_PyRuntime.preconfig, &config);
|
|
|
|
return _PyStatus_OK();
|
|
}
|