bpo-34485: Add _Py_ClearStandardStreamEncoding() (GH-8982)

* Move Py_SetStandardStreamEncoding() from pylifecycle.c
  to coreconfig.c
* Add _Py_ClearStandardStreamEncoding() private function.
* pymain_free() now calls _Py_ClearStandardStreamEncoding().
* Add assertions add the end of _PyCoreConfig_Read()
* _PyCoreConfig_Copy(): rename COPY_STR_ATTR() macro
  to COPY_WSTR_ATTR().
* Fix get_stdio_errors() indentation.
This commit is contained in:
Victor Stinner 2018-08-29 01:29:06 +02:00 committed by GitHub
parent 2c8ddcf4f1
commit 124b9eb4e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 97 deletions

View File

@ -19,7 +19,13 @@ PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void);
*/
PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding,
const char *errors);
#endif
#ifdef Py_BUILD_CORE
PyAPI_FUNC(void) _Py_ClearStandardStreamEncoding(void);
#endif
#ifndef Py_LIMITED_API
/* PEP 432 Multi-phase initialization API (Private while provisional!) */
PyAPI_FUNC(_PyInitError) _Py_InitializeCore(
PyInterpreterState **interp,

View File

@ -466,6 +466,7 @@ pymain_free(_PyMain *pymain)
remain valid after Py_Finalize(), since
Py_Initialize()-Py_Finalize() can be called multiple times. */
_PyPathConfig_ClearGlobal();
_Py_ClearStandardStreamEncoding();
/* Force the allocator used by pymain_read_conf() */
PyMemAllocatorEx old_alloc;
@ -1262,7 +1263,6 @@ pymain_read_conf_impl(_PyMain *pymain, _PyCoreConfig *config,
return -1;
}
assert(config->use_environment >= 0);
if (config->use_environment) {
err = cmdline_init_env_warnoptions(pymain, config, cmdline);
if (_Py_INIT_FAILED(err)) {

View File

@ -85,6 +85,88 @@ _Py_wstrlist_copy(int len, wchar_t **list)
}
/* Helper to allow an embedding application to override the normal
* mechanism that attempts to figure out an appropriate IO encoding
*/
char *_Py_StandardStreamEncoding = NULL;
char *_Py_StandardStreamErrors = NULL;
int
Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
{
if (Py_IsInitialized()) {
/* This is too late to have any effect */
return -1;
}
int res = 0;
/* Py_SetStandardStreamEncoding() can be called before Py_Initialize(),
but Py_Initialize() can change the allocator. Use a known allocator
to be able to release the memory later. */
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
/* Can't call PyErr_NoMemory() on errors, as Python hasn't been
* initialised yet.
*
* However, the raw memory allocators are initialised appropriately
* as C static variables, so _PyMem_RawStrdup is OK even though
* Py_Initialize hasn't been called yet.
*/
if (encoding) {
_Py_StandardStreamEncoding = _PyMem_RawStrdup(encoding);
if (!_Py_StandardStreamEncoding) {
res = -2;
goto done;
}
}
if (errors) {
_Py_StandardStreamErrors = _PyMem_RawStrdup(errors);
if (!_Py_StandardStreamErrors) {
if (_Py_StandardStreamEncoding) {
PyMem_RawFree(_Py_StandardStreamEncoding);
}
res = -3;
goto done;
}
}
#ifdef MS_WINDOWS
if (_Py_StandardStreamEncoding) {
/* Overriding the stream encoding implies legacy streams */
Py_LegacyWindowsStdioFlag = 1;
}
#endif
done:
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
return res;
}
void
_Py_ClearStandardStreamEncoding(void)
{
/* Use the same allocator than Py_SetStandardStreamEncoding() */
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
/* We won't need them anymore. */
if (_Py_StandardStreamEncoding) {
PyMem_RawFree(_Py_StandardStreamEncoding);
_Py_StandardStreamEncoding = NULL;
}
if (_Py_StandardStreamErrors) {
PyMem_RawFree(_Py_StandardStreamErrors);
_Py_StandardStreamErrors = NULL;
}
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
}
/* Free memory allocated in config, but don't clear all attributes */
void
_PyCoreConfig_Clear(_PyCoreConfig *config)
@ -134,7 +216,7 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
_PyCoreConfig_Clear(config);
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
#define COPY_STR_ATTR(ATTR) \
#define COPY_WSTR_ATTR(ATTR) \
do { \
if (config2->ATTR != NULL) { \
config->ATTR = _PyMem_RawWcsdup(config2->ATTR); \
@ -173,25 +255,25 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
COPY_ATTR(coerce_c_locale_warn);
COPY_ATTR(utf8_mode);
COPY_STR_ATTR(pycache_prefix);
COPY_STR_ATTR(module_search_path_env);
COPY_STR_ATTR(home);
COPY_STR_ATTR(program_name);
COPY_STR_ATTR(program);
COPY_WSTR_ATTR(pycache_prefix);
COPY_WSTR_ATTR(module_search_path_env);
COPY_WSTR_ATTR(home);
COPY_WSTR_ATTR(program_name);
COPY_WSTR_ATTR(program);
COPY_WSTRLIST(argc, argv);
COPY_WSTRLIST(nwarnoption, warnoptions);
COPY_WSTRLIST(nxoption, xoptions);
COPY_WSTRLIST(nmodule_search_path, module_search_paths);
COPY_STR_ATTR(executable);
COPY_STR_ATTR(prefix);
COPY_STR_ATTR(base_prefix);
COPY_STR_ATTR(exec_prefix);
COPY_WSTR_ATTR(executable);
COPY_WSTR_ATTR(prefix);
COPY_WSTR_ATTR(base_prefix);
COPY_WSTR_ATTR(exec_prefix);
#ifdef MS_WINDOWS
COPY_STR_ATTR(dll_path);
COPY_WSTR_ATTR(dll_path);
#endif
COPY_STR_ATTR(base_exec_prefix);
COPY_WSTR_ATTR(base_exec_prefix);
COPY_ATTR(isolated);
COPY_ATTR(site_import);
@ -213,7 +295,7 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
COPY_ATTR(_frozen);
#undef COPY_ATTR
#undef COPY_STR_ATTR
#undef COPY_WSTR_ATTR
#undef COPY_WSTRLIST
return 0;
}
@ -627,8 +709,6 @@ get_env_flag(_PyCoreConfig *config, int *flag, const char *name)
static _PyInitError
config_read_env_vars(_PyCoreConfig *config)
{
assert(config->use_environment > 0);
/* Get environment variables */
get_env_flag(config, &config->parser_debug, "PYTHONDEBUG");
get_env_flag(config, &config->verbose, "PYTHONVERBOSE");
@ -870,6 +950,7 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
_PyInitError err;
_PyCoreConfig_GetGlobalConfig(config);
assert(config->use_environment >= 0);
if (config->isolated > 0) {
config->use_environment = 0;
@ -882,7 +963,6 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
}
#endif
assert(config->use_environment >= 0);
if (config->use_environment) {
err = config_read_env_vars(config);
if (_Py_INIT_FAILED(err)) {
@ -960,12 +1040,12 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
if (config->utf8_mode < 0) {
config->utf8_mode = 0;
}
if (config->_frozen < 0) {
config->_frozen = 0;
}
if (config->argc < 0) {
config->argc = 0;
}
assert(config->coerce_c_locale >= 0);
assert(config->use_environment >= 0);
return _Py_INIT_OK();
}

View File

@ -138,66 +138,6 @@ Py_IsInitialized(void)
return _PyRuntime.initialized;
}
/* Helper to allow an embedding application to override the normal
* mechanism that attempts to figure out an appropriate IO encoding
*/
static char *_Py_StandardStreamEncoding = NULL;
static char *_Py_StandardStreamErrors = NULL;
int
Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
{
if (Py_IsInitialized()) {
/* This is too late to have any effect */
return -1;
}
int res = 0;
/* Py_SetStandardStreamEncoding() can be called before Py_Initialize(),
but Py_Initialize() can change the allocator. Use a known allocator
to be able to release the memory later. */
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
/* Can't call PyErr_NoMemory() on errors, as Python hasn't been
* initialised yet.
*
* However, the raw memory allocators are initialised appropriately
* as C static variables, so _PyMem_RawStrdup is OK even though
* Py_Initialize hasn't been called yet.
*/
if (encoding) {
_Py_StandardStreamEncoding = _PyMem_RawStrdup(encoding);
if (!_Py_StandardStreamEncoding) {
res = -2;
goto done;
}
}
if (errors) {
_Py_StandardStreamErrors = _PyMem_RawStrdup(errors);
if (!_Py_StandardStreamErrors) {
if (_Py_StandardStreamEncoding) {
PyMem_RawFree(_Py_StandardStreamEncoding);
}
res = -3;
goto done;
}
}
#ifdef MS_WINDOWS
if (_Py_StandardStreamEncoding) {
/* Overriding the stream encoding implies legacy streams */
Py_LegacyWindowsStdioFlag = 1;
}
#endif
done:
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
return res;
}
/* Global initializations. Can be undone by Py_FinalizeEx(). Don't
call this twice without an intervening Py_FinalizeEx() call. When
@ -1803,6 +1743,8 @@ init_sys_streams(PyInterpreterState *interp)
char *locale_encoding = NULL;
char *codec_name = NULL;
_PyInitError res = _Py_INIT_OK();
extern char *_Py_StandardStreamEncoding;
extern char *_Py_StandardStreamErrors;
/* Hack to avoid a nasty recursion issue when Python is invoked
in verbose mode: pre-import the Latin-1 and UTF-8 codecs */
@ -1951,22 +1893,8 @@ init_sys_streams(PyInterpreterState *interp)
error:
res = _Py_INIT_ERR("can't initialize sys standard streams");
/* Use the same allocator than Py_SetStandardStreamEncoding() */
PyMemAllocatorEx old_alloc;
done:
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
/* We won't need them anymore. */
if (_Py_StandardStreamEncoding) {
PyMem_RawFree(_Py_StandardStreamEncoding);
_Py_StandardStreamEncoding = NULL;
}
if (_Py_StandardStreamErrors) {
PyMem_RawFree(_Py_StandardStreamErrors);
_Py_StandardStreamErrors = NULL;
}
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
_Py_ClearStandardStreamEncoding();
PyMem_RawFree(locale_encoding);
PyMem_RawFree(codec_name);