Skip to content

Commit 124b9eb

Browse files
authored
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.
1 parent 2c8ddcf commit 124b9eb

File tree

4 files changed

+111
-97
lines changed

4 files changed

+111
-97
lines changed

Include/pylifecycle.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void);
1919
*/
2020
PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding,
2121
const char *errors);
22+
#endif
23+
#ifdef Py_BUILD_CORE
24+
PyAPI_FUNC(void) _Py_ClearStandardStreamEncoding(void);
25+
#endif
2226

27+
28+
#ifndef Py_LIMITED_API
2329
/* PEP 432 Multi-phase initialization API (Private while provisional!) */
2430
PyAPI_FUNC(_PyInitError) _Py_InitializeCore(
2531
PyInterpreterState **interp,

Modules/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ pymain_free(_PyMain *pymain)
466466
remain valid after Py_Finalize(), since
467467
Py_Initialize()-Py_Finalize() can be called multiple times. */
468468
_PyPathConfig_ClearGlobal();
469+
_Py_ClearStandardStreamEncoding();
469470

470471
/* Force the allocator used by pymain_read_conf() */
471472
PyMemAllocatorEx old_alloc;
@@ -1262,7 +1263,6 @@ pymain_read_conf_impl(_PyMain *pymain, _PyCoreConfig *config,
12621263
return -1;
12631264
}
12641265

1265-
assert(config->use_environment >= 0);
12661266
if (config->use_environment) {
12671267
err = cmdline_init_env_warnoptions(pymain, config, cmdline);
12681268
if (_Py_INIT_FAILED(err)) {

Python/coreconfig.c

Lines changed: 99 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,88 @@ _Py_wstrlist_copy(int len, wchar_t **list)
8585
}
8686

8787

88+
/* Helper to allow an embedding application to override the normal
89+
* mechanism that attempts to figure out an appropriate IO encoding
90+
*/
91+
92+
char *_Py_StandardStreamEncoding = NULL;
93+
char *_Py_StandardStreamErrors = NULL;
94+
95+
int
96+
Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
97+
{
98+
if (Py_IsInitialized()) {
99+
/* This is too late to have any effect */
100+
return -1;
101+
}
102+
103+
int res = 0;
104+
105+
/* Py_SetStandardStreamEncoding() can be called before Py_Initialize(),
106+
but Py_Initialize() can change the allocator. Use a known allocator
107+
to be able to release the memory later. */
108+
PyMemAllocatorEx old_alloc;
109+
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
110+
111+
/* Can't call PyErr_NoMemory() on errors, as Python hasn't been
112+
* initialised yet.
113+
*
114+
* However, the raw memory allocators are initialised appropriately
115+
* as C static variables, so _PyMem_RawStrdup is OK even though
116+
* Py_Initialize hasn't been called yet.
117+
*/
118+
if (encoding) {
119+
_Py_StandardStreamEncoding = _PyMem_RawStrdup(encoding);
120+
if (!_Py_StandardStreamEncoding) {
121+
res = -2;
122+
goto done;
123+
}
124+
}
125+
if (errors) {
126+
_Py_StandardStreamErrors = _PyMem_RawStrdup(errors);
127+
if (!_Py_StandardStreamErrors) {
128+
if (_Py_StandardStreamEncoding) {
129+
PyMem_RawFree(_Py_StandardStreamEncoding);
130+
}
131+
res = -3;
132+
goto done;
133+
}
134+
}
135+
#ifdef MS_WINDOWS
136+
if (_Py_StandardStreamEncoding) {
137+
/* Overriding the stream encoding implies legacy streams */
138+
Py_LegacyWindowsStdioFlag = 1;
139+
}
140+
#endif
141+
142+
done:
143+
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
144+
145+
return res;
146+
}
147+
148+
149+
void
150+
_Py_ClearStandardStreamEncoding(void)
151+
{
152+
/* Use the same allocator than Py_SetStandardStreamEncoding() */
153+
PyMemAllocatorEx old_alloc;
154+
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
155+
156+
/* We won't need them anymore. */
157+
if (_Py_StandardStreamEncoding) {
158+
PyMem_RawFree(_Py_StandardStreamEncoding);
159+
_Py_StandardStreamEncoding = NULL;
160+
}
161+
if (_Py_StandardStreamErrors) {
162+
PyMem_RawFree(_Py_StandardStreamErrors);
163+
_Py_StandardStreamErrors = NULL;
164+
}
165+
166+
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
167+
}
168+
169+
88170
/* Free memory allocated in config, but don't clear all attributes */
89171
void
90172
_PyCoreConfig_Clear(_PyCoreConfig *config)
@@ -134,7 +216,7 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
134216
_PyCoreConfig_Clear(config);
135217

136218
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
137-
#define COPY_STR_ATTR(ATTR) \
219+
#define COPY_WSTR_ATTR(ATTR) \
138220
do { \
139221
if (config2->ATTR != NULL) { \
140222
config->ATTR = _PyMem_RawWcsdup(config2->ATTR); \
@@ -173,25 +255,25 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
173255
COPY_ATTR(coerce_c_locale_warn);
174256
COPY_ATTR(utf8_mode);
175257

176-
COPY_STR_ATTR(pycache_prefix);
177-
COPY_STR_ATTR(module_search_path_env);
178-
COPY_STR_ATTR(home);
179-
COPY_STR_ATTR(program_name);
180-
COPY_STR_ATTR(program);
258+
COPY_WSTR_ATTR(pycache_prefix);
259+
COPY_WSTR_ATTR(module_search_path_env);
260+
COPY_WSTR_ATTR(home);
261+
COPY_WSTR_ATTR(program_name);
262+
COPY_WSTR_ATTR(program);
181263

182264
COPY_WSTRLIST(argc, argv);
183265
COPY_WSTRLIST(nwarnoption, warnoptions);
184266
COPY_WSTRLIST(nxoption, xoptions);
185267
COPY_WSTRLIST(nmodule_search_path, module_search_paths);
186268

187-
COPY_STR_ATTR(executable);
188-
COPY_STR_ATTR(prefix);
189-
COPY_STR_ATTR(base_prefix);
190-
COPY_STR_ATTR(exec_prefix);
269+
COPY_WSTR_ATTR(executable);
270+
COPY_WSTR_ATTR(prefix);
271+
COPY_WSTR_ATTR(base_prefix);
272+
COPY_WSTR_ATTR(exec_prefix);
191273
#ifdef MS_WINDOWS
192-
COPY_STR_ATTR(dll_path);
274+
COPY_WSTR_ATTR(dll_path);
193275
#endif
194-
COPY_STR_ATTR(base_exec_prefix);
276+
COPY_WSTR_ATTR(base_exec_prefix);
195277

196278
COPY_ATTR(isolated);
197279
COPY_ATTR(site_import);
@@ -213,7 +295,7 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
213295
COPY_ATTR(_frozen);
214296

215297
#undef COPY_ATTR
216-
#undef COPY_STR_ATTR
298+
#undef COPY_WSTR_ATTR
217299
#undef COPY_WSTRLIST
218300
return 0;
219301
}
@@ -627,8 +709,6 @@ get_env_flag(_PyCoreConfig *config, int *flag, const char *name)
627709
static _PyInitError
628710
config_read_env_vars(_PyCoreConfig *config)
629711
{
630-
assert(config->use_environment > 0);
631-
632712
/* Get environment variables */
633713
get_env_flag(config, &config->parser_debug, "PYTHONDEBUG");
634714
get_env_flag(config, &config->verbose, "PYTHONVERBOSE");
@@ -870,6 +950,7 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
870950
_PyInitError err;
871951

872952
_PyCoreConfig_GetGlobalConfig(config);
953+
assert(config->use_environment >= 0);
873954

874955
if (config->isolated > 0) {
875956
config->use_environment = 0;
@@ -882,7 +963,6 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
882963
}
883964
#endif
884965

885-
assert(config->use_environment >= 0);
886966
if (config->use_environment) {
887967
err = config_read_env_vars(config);
888968
if (_Py_INIT_FAILED(err)) {
@@ -960,12 +1040,12 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
9601040
if (config->utf8_mode < 0) {
9611041
config->utf8_mode = 0;
9621042
}
963-
if (config->_frozen < 0) {
964-
config->_frozen = 0;
965-
}
9661043
if (config->argc < 0) {
9671044
config->argc = 0;
9681045
}
9691046

1047+
assert(config->coerce_c_locale >= 0);
1048+
assert(config->use_environment >= 0);
1049+
9701050
return _Py_INIT_OK();
9711051
}

Python/pylifecycle.c

Lines changed: 5 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -138,66 +138,6 @@ Py_IsInitialized(void)
138138
return _PyRuntime.initialized;
139139
}
140140

141-
/* Helper to allow an embedding application to override the normal
142-
* mechanism that attempts to figure out an appropriate IO encoding
143-
*/
144-
145-
static char *_Py_StandardStreamEncoding = NULL;
146-
static char *_Py_StandardStreamErrors = NULL;
147-
148-
int
149-
Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
150-
{
151-
if (Py_IsInitialized()) {
152-
/* This is too late to have any effect */
153-
return -1;
154-
}
155-
156-
int res = 0;
157-
158-
/* Py_SetStandardStreamEncoding() can be called before Py_Initialize(),
159-
but Py_Initialize() can change the allocator. Use a known allocator
160-
to be able to release the memory later. */
161-
PyMemAllocatorEx old_alloc;
162-
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
163-
164-
/* Can't call PyErr_NoMemory() on errors, as Python hasn't been
165-
* initialised yet.
166-
*
167-
* However, the raw memory allocators are initialised appropriately
168-
* as C static variables, so _PyMem_RawStrdup is OK even though
169-
* Py_Initialize hasn't been called yet.
170-
*/
171-
if (encoding) {
172-
_Py_StandardStreamEncoding = _PyMem_RawStrdup(encoding);
173-
if (!_Py_StandardStreamEncoding) {
174-
res = -2;
175-
goto done;
176-
}
177-
}
178-
if (errors) {
179-
_Py_StandardStreamErrors = _PyMem_RawStrdup(errors);
180-
if (!_Py_StandardStreamErrors) {
181-
if (_Py_StandardStreamEncoding) {
182-
PyMem_RawFree(_Py_StandardStreamEncoding);
183-
}
184-
res = -3;
185-
goto done;
186-
}
187-
}
188-
#ifdef MS_WINDOWS
189-
if (_Py_StandardStreamEncoding) {
190-
/* Overriding the stream encoding implies legacy streams */
191-
Py_LegacyWindowsStdioFlag = 1;
192-
}
193-
#endif
194-
195-
done:
196-
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
197-
198-
return res;
199-
}
200-
201141

202142
/* Global initializations. Can be undone by Py_FinalizeEx(). Don't
203143
call this twice without an intervening Py_FinalizeEx() call. When
@@ -419,9 +359,9 @@ get_stdio_errors(void)
419359
}
420360
}
421361
#endif
422-
}
362+
}
423363

424-
return "strict";
364+
return "strict";
425365
}
426366

427367
#ifdef PY_COERCE_C_LOCALE
@@ -1803,6 +1743,8 @@ init_sys_streams(PyInterpreterState *interp)
18031743
char *locale_encoding = NULL;
18041744
char *codec_name = NULL;
18051745
_PyInitError res = _Py_INIT_OK();
1746+
extern char *_Py_StandardStreamEncoding;
1747+
extern char *_Py_StandardStreamErrors;
18061748

18071749
/* Hack to avoid a nasty recursion issue when Python is invoked
18081750
in verbose mode: pre-import the Latin-1 and UTF-8 codecs */
@@ -1951,22 +1893,8 @@ init_sys_streams(PyInterpreterState *interp)
19511893
error:
19521894
res = _Py_INIT_ERR("can't initialize sys standard streams");
19531895

1954-
/* Use the same allocator than Py_SetStandardStreamEncoding() */
1955-
PyMemAllocatorEx old_alloc;
19561896
done:
1957-
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
1958-
1959-
/* We won't need them anymore. */
1960-
if (_Py_StandardStreamEncoding) {
1961-
PyMem_RawFree(_Py_StandardStreamEncoding);
1962-
_Py_StandardStreamEncoding = NULL;
1963-
}
1964-
if (_Py_StandardStreamErrors) {
1965-
PyMem_RawFree(_Py_StandardStreamErrors);
1966-
_Py_StandardStreamErrors = NULL;
1967-
}
1968-
1969-
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
1897+
_Py_ClearStandardStreamEncoding();
19701898

19711899
PyMem_RawFree(locale_encoding);
19721900
PyMem_RawFree(codec_name);

0 commit comments

Comments
 (0)