mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	bpo-34485: Add _PyCoreConfig.stdio_encoding (GH-8881)
* Add stdio_encoding and stdio_errors fields to _PyCoreConfig. * Add unit tests on stdio_encoding and stdio_errors.
This commit is contained in:
		
							parent
							
								
									177d921c8c
								
							
						
					
					
						commit
						dfe0dc7453
					
				
					 6 changed files with 265 additions and 135 deletions
				
			
		|  | @ -203,6 +203,18 @@ typedef struct { | ||||||
|        If set to -1 (default), it is set to !Py_UnbufferedStdioFlag. */ |        If set to -1 (default), it is set to !Py_UnbufferedStdioFlag. */ | ||||||
|     int buffered_stdio; |     int buffered_stdio; | ||||||
| 
 | 
 | ||||||
|  |     /* Encoding of sys.stdin, sys.stdout and sys.stderr.
 | ||||||
|  |        Value set from PYTHONIOENCODING environment variable and | ||||||
|  |        Py_SetStandardStreamEncoding() function. | ||||||
|  |        See also 'stdio_errors' attribute. */ | ||||||
|  |     char *stdio_encoding; | ||||||
|  | 
 | ||||||
|  |     /* Error handler of sys.stdin and sys.stdout.
 | ||||||
|  |        Value set from PYTHONIOENCODING environment variable and | ||||||
|  |        Py_SetStandardStreamEncoding() function. | ||||||
|  |        See also 'stdio_encoding' attribute. */ | ||||||
|  |     char *stdio_errors; | ||||||
|  | 
 | ||||||
| #ifdef MS_WINDOWS | #ifdef MS_WINDOWS | ||||||
|     /* If greater than 1, use the "mbcs" encoding instead of the UTF-8
 |     /* If greater than 1, use the "mbcs" encoding instead of the UTF-8
 | ||||||
|        encoding for the filesystem encoding. |        encoding for the filesystem encoding. | ||||||
|  |  | ||||||
|  | @ -179,6 +179,9 @@ PyAPI_FUNC(void) _Py_CoerceLegacyLocale(const _PyCoreConfig *config); | ||||||
| PyAPI_FUNC(int) _Py_LegacyLocaleDetected(void); | PyAPI_FUNC(int) _Py_LegacyLocaleDetected(void); | ||||||
| PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category); | PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category); | ||||||
| #endif | #endif | ||||||
|  | #ifdef Py_BUILD_CORE | ||||||
|  | PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -288,13 +288,29 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): | ||||||
|         'quiet': 0, |         'quiet': 0, | ||||||
|         'user_site_directory': 1, |         'user_site_directory': 1, | ||||||
|         'buffered_stdio': 1, |         'buffered_stdio': 1, | ||||||
|  |         # None means that check_config() gets the expected encoding at runtime | ||||||
|  |         'stdio_encoding': None, | ||||||
|  |         'stdio_errors': None, | ||||||
| 
 | 
 | ||||||
|         '_install_importlib': 1, |         '_install_importlib': 1, | ||||||
|         '_check_hash_pycs_mode': 'default', |         '_check_hash_pycs_mode': 'default', | ||||||
|         '_frozen': 0, |         '_frozen': 0, | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     def get_stdio_encoding(self, env): | ||||||
|  |         code = 'import sys; print(sys.stdout.encoding, sys.stdout.errors)' | ||||||
|  |         args = (sys.executable, '-c', code) | ||||||
|  |         proc = subprocess.run(args, env=env, text=True, | ||||||
|  |                               stdout=subprocess.PIPE, | ||||||
|  |                               stderr=subprocess.STDOUT) | ||||||
|  |         if proc.returncode: | ||||||
|  |             raise Exception(f"failed to get the stdio encoding: stdout={proc.stdout!r}") | ||||||
|  |         out = proc.stdout.rstrip() | ||||||
|  |         return out.split() | ||||||
|  | 
 | ||||||
|     def check_config(self, testname, expected): |     def check_config(self, testname, expected): | ||||||
|  |         expected = dict(self.DEFAULT_CONFIG, **expected) | ||||||
|  | 
 | ||||||
|         env = dict(os.environ) |         env = dict(os.environ) | ||||||
|         for key in list(env): |         for key in list(env): | ||||||
|             if key.startswith('PYTHON'): |             if key.startswith('PYTHON'): | ||||||
|  | @ -303,13 +319,19 @@ def check_config(self, testname, expected): | ||||||
|         # on the current locale |         # on the current locale | ||||||
|         env['PYTHONCOERCECLOCALE'] = '0' |         env['PYTHONCOERCECLOCALE'] = '0' | ||||||
|         env['PYTHONUTF8'] = '0' |         env['PYTHONUTF8'] = '0' | ||||||
|         out, err = self.run_embedded_interpreter(testname, env=env) |  | ||||||
|         # Ignore err |  | ||||||
| 
 | 
 | ||||||
|         expected = dict(self.DEFAULT_CONFIG, **expected) |         if expected['stdio_encoding'] is None or expected['stdio_errors'] is None: | ||||||
|  |             res = self.get_stdio_encoding(env) | ||||||
|  |             if expected['stdio_encoding'] is None: | ||||||
|  |                 expected['stdio_encoding'] = res[0] | ||||||
|  |             if expected['stdio_errors'] is None: | ||||||
|  |                 expected['stdio_errors'] = res[1] | ||||||
|         for key, value in expected.items(): |         for key, value in expected.items(): | ||||||
|             expected[key] = str(value) |             expected[key] = str(value) | ||||||
| 
 | 
 | ||||||
|  |         out, err = self.run_embedded_interpreter(testname, env=env) | ||||||
|  |         # Ignore err | ||||||
|  | 
 | ||||||
|         config = {} |         config = {} | ||||||
|         for line in out.splitlines(): |         for line in out.splitlines(): | ||||||
|             key, value = line.split(' = ', 1) |             key, value = line.split(' = ', 1) | ||||||
|  | @ -331,7 +353,11 @@ def test_init_global_config(self): | ||||||
|             'verbose': 1, |             'verbose': 1, | ||||||
|             'quiet': 1, |             'quiet': 1, | ||||||
|             'buffered_stdio': 0, |             'buffered_stdio': 0, | ||||||
|  | 
 | ||||||
|             'utf8_mode': 1, |             'utf8_mode': 1, | ||||||
|  |             'stdio_encoding': 'utf-8', | ||||||
|  |             'stdio_errors': 'surrogateescape', | ||||||
|  | 
 | ||||||
|             'user_site_directory': 0, |             'user_site_directory': 0, | ||||||
|             '_frozen': 1, |             '_frozen': 1, | ||||||
|         } |         } | ||||||
|  | @ -350,6 +376,8 @@ def test_init_from_config(self): | ||||||
|             'malloc_stats': 1, |             'malloc_stats': 1, | ||||||
| 
 | 
 | ||||||
|             'utf8_mode': 1, |             'utf8_mode': 1, | ||||||
|  |             'stdio_encoding': 'iso8859-1', | ||||||
|  |             'stdio_errors': 'replace', | ||||||
| 
 | 
 | ||||||
|             'pycache_prefix': 'conf_pycache_prefix', |             'pycache_prefix': 'conf_pycache_prefix', | ||||||
|             'program_name': './conf_program_name', |             'program_name': './conf_program_name', | ||||||
|  | @ -387,6 +415,8 @@ def test_init_env(self): | ||||||
|             'write_bytecode': 0, |             'write_bytecode': 0, | ||||||
|             'verbose': 1, |             'verbose': 1, | ||||||
|             'buffered_stdio': 0, |             'buffered_stdio': 0, | ||||||
|  |             'stdio_encoding': 'iso8859-1', | ||||||
|  |             'stdio_errors': 'replace', | ||||||
|             'user_site_directory': 0, |             'user_site_directory': 0, | ||||||
|             'faulthandler': 1, |             'faulthandler': 1, | ||||||
|             'dev_mode': 1, |             'dev_mode': 1, | ||||||
|  |  | ||||||
|  | @ -374,6 +374,8 @@ dump_config(void) | ||||||
|     printf("user_site_directory = %i\n", config->user_site_directory); |     printf("user_site_directory = %i\n", config->user_site_directory); | ||||||
|     printf("buffered_stdio = %i\n", config->buffered_stdio); |     printf("buffered_stdio = %i\n", config->buffered_stdio); | ||||||
|     ASSERT_EQUAL(config->buffered_stdio, !Py_UnbufferedStdioFlag); |     ASSERT_EQUAL(config->buffered_stdio, !Py_UnbufferedStdioFlag); | ||||||
|  |     printf("stdio_encoding = %s\n", config->stdio_encoding); | ||||||
|  |     printf("stdio_errors = %s\n", config->stdio_errors); | ||||||
| 
 | 
 | ||||||
|     /* FIXME: test legacy_windows_fs_encoding */ |     /* FIXME: test legacy_windows_fs_encoding */ | ||||||
|     /* FIXME: test legacy_windows_stdio */ |     /* FIXME: test legacy_windows_stdio */ | ||||||
|  | @ -532,6 +534,11 @@ static int test_init_from_config(void) | ||||||
|     Py_UnbufferedStdioFlag = 0; |     Py_UnbufferedStdioFlag = 0; | ||||||
|     config.buffered_stdio = 0; |     config.buffered_stdio = 0; | ||||||
| 
 | 
 | ||||||
|  |     putenv("PYTHONIOENCODING=cp424"); | ||||||
|  |     Py_SetStandardStreamEncoding("ascii", "ignore"); | ||||||
|  |     config.stdio_encoding = "iso8859-1"; | ||||||
|  |     config.stdio_errors = "replace"; | ||||||
|  | 
 | ||||||
|     putenv("PYTHONNOUSERSITE="); |     putenv("PYTHONNOUSERSITE="); | ||||||
|     Py_NoUserSiteDirectory = 0; |     Py_NoUserSiteDirectory = 0; | ||||||
|     config.user_site_directory = 0; |     config.user_site_directory = 0; | ||||||
|  | @ -569,6 +576,7 @@ static void test_init_env_putenvs(void) | ||||||
|     putenv("PYTHONNOUSERSITE=1"); |     putenv("PYTHONNOUSERSITE=1"); | ||||||
|     putenv("PYTHONFAULTHANDLER=1"); |     putenv("PYTHONFAULTHANDLER=1"); | ||||||
|     putenv("PYTHONDEVMODE=1"); |     putenv("PYTHONDEVMODE=1"); | ||||||
|  |     putenv("PYTHONIOENCODING=iso8859-1:replace"); | ||||||
|     /* FIXME: test PYTHONWARNINGS */ |     /* FIXME: test PYTHONWARNINGS */ | ||||||
|     /* FIXME: test PYTHONEXECUTABLE */ |     /* FIXME: test PYTHONEXECUTABLE */ | ||||||
|     /* FIXME: test PYTHONHOME */ |     /* FIXME: test PYTHONHOME */ | ||||||
|  |  | ||||||
|  | @ -1,6 +1,9 @@ | ||||||
| #include "Python.h" | #include "Python.h" | ||||||
| #include "internal/pystate.h" | #include "internal/pystate.h" | ||||||
| #include <locale.h> | #include <locale.h> | ||||||
|  | #ifdef HAVE_LANGINFO_H | ||||||
|  | #  include <langinfo.h> | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #define DECODE_LOCALE_ERR(NAME, LEN) \ | #define DECODE_LOCALE_ERR(NAME, LEN) \ | ||||||
|  | @ -89,8 +92,8 @@ _Py_wstrlist_copy(int len, wchar_t **list) | ||||||
|  * mechanism that attempts to figure out an appropriate IO encoding |  * mechanism that attempts to figure out an appropriate IO encoding | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| char *_Py_StandardStreamEncoding = NULL; | static char *_Py_StandardStreamEncoding = NULL; | ||||||
| char *_Py_StandardStreamErrors = NULL; | static char *_Py_StandardStreamErrors = NULL; | ||||||
| 
 | 
 | ||||||
| int | int | ||||||
| Py_SetStandardStreamEncoding(const char *encoding, const char *errors) | Py_SetStandardStreamEncoding(const char *encoding, const char *errors) | ||||||
|  | @ -205,6 +208,9 @@ _PyCoreConfig_Clear(_PyCoreConfig *config) | ||||||
|     CLEAR(config->dll_path); |     CLEAR(config->dll_path); | ||||||
| #endif | #endif | ||||||
|     CLEAR(config->base_exec_prefix); |     CLEAR(config->base_exec_prefix); | ||||||
|  | 
 | ||||||
|  |     CLEAR(config->stdio_encoding); | ||||||
|  |     CLEAR(config->stdio_errors); | ||||||
| #undef CLEAR | #undef CLEAR | ||||||
| #undef CLEAR_WSTRLIST | #undef CLEAR_WSTRLIST | ||||||
| } | } | ||||||
|  | @ -216,6 +222,15 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2) | ||||||
|     _PyCoreConfig_Clear(config); |     _PyCoreConfig_Clear(config); | ||||||
| 
 | 
 | ||||||
| #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR | #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR | ||||||
|  | #define COPY_STR_ATTR(ATTR) \ | ||||||
|  |     do { \ | ||||||
|  |         if (config2->ATTR != NULL) { \ | ||||||
|  |             config->ATTR = _PyMem_RawStrdup(config2->ATTR); \ | ||||||
|  |             if (config->ATTR == NULL) { \ | ||||||
|  |                 return -1; \ | ||||||
|  |             } \ | ||||||
|  |         } \ | ||||||
|  |     } while (0) | ||||||
| #define COPY_WSTR_ATTR(ATTR) \ | #define COPY_WSTR_ATTR(ATTR) \ | ||||||
|     do { \ |     do { \ | ||||||
|         if (config2->ATTR != NULL) { \ |         if (config2->ATTR != NULL) { \ | ||||||
|  | @ -287,6 +302,8 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2) | ||||||
|     COPY_ATTR(quiet); |     COPY_ATTR(quiet); | ||||||
|     COPY_ATTR(user_site_directory); |     COPY_ATTR(user_site_directory); | ||||||
|     COPY_ATTR(buffered_stdio); |     COPY_ATTR(buffered_stdio); | ||||||
|  |     COPY_STR_ATTR(stdio_encoding); | ||||||
|  |     COPY_STR_ATTR(stdio_errors); | ||||||
| #ifdef MS_WINDOWS | #ifdef MS_WINDOWS | ||||||
|     COPY_ATTR(legacy_windows_fs_encoding); |     COPY_ATTR(legacy_windows_fs_encoding); | ||||||
|     COPY_ATTR(legacy_windows_stdio); |     COPY_ATTR(legacy_windows_stdio); | ||||||
|  | @ -932,6 +949,161 @@ config_init_locale(_PyCoreConfig *config) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static const char * | ||||||
|  | get_stdio_errors(const _PyCoreConfig *config) | ||||||
|  | { | ||||||
|  | #ifndef MS_WINDOWS | ||||||
|  |     const char *loc = setlocale(LC_CTYPE, NULL); | ||||||
|  |     if (loc != NULL) { | ||||||
|  |         /* surrogateescape is the default in the legacy C and POSIX locales */ | ||||||
|  |         if (strcmp(loc, "C") == 0 || strcmp(loc, "POSIX") == 0) { | ||||||
|  |             return "surrogateescape"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | #ifdef PY_COERCE_C_LOCALE | ||||||
|  |         /* surrogateescape is the default in locale coercion target locales */ | ||||||
|  |         if (_Py_IsLocaleCoercionTarget(loc)) { | ||||||
|  |             return "surrogateescape"; | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return "strict"; | ||||||
|  | #else | ||||||
|  |     /* On Windows, always use surrogateescape by default */ | ||||||
|  |     return "surrogateescape"; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | _PyInitError | ||||||
|  | _Py_get_locale_encoding(char **locale_encoding) | ||||||
|  | { | ||||||
|  | #ifdef MS_WINDOWS | ||||||
|  |     char encoding[20]; | ||||||
|  |     PyOS_snprintf(encoding, sizeof(encoding), "cp%d", GetACP()); | ||||||
|  | #elif defined(__ANDROID__) | ||||||
|  |     const char *encoding = "UTF-8"; | ||||||
|  | #else | ||||||
|  |     const char *encoding = nl_langinfo(CODESET); | ||||||
|  |     if (!encoding || encoding[0] == '\0') { | ||||||
|  |         return _Py_INIT_USER_ERR("failed to get the locale encoding: " | ||||||
|  |                                  "nl_langinfo(CODESET) failed"); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |     *locale_encoding = _PyMem_RawStrdup(encoding); | ||||||
|  |     if (*locale_encoding == NULL) { | ||||||
|  |         return _Py_INIT_NO_MEMORY(); | ||||||
|  |     } | ||||||
|  |     return _Py_INIT_OK(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static _PyInitError | ||||||
|  | config_init_stdio_encoding(_PyCoreConfig *config) | ||||||
|  | { | ||||||
|  |     /* If Py_SetStandardStreamEncoding() have been called, use these
 | ||||||
|  |         parameters. */ | ||||||
|  |     if (config->stdio_encoding == NULL && _Py_StandardStreamEncoding != NULL) { | ||||||
|  |         config->stdio_encoding = _PyMem_RawStrdup(_Py_StandardStreamEncoding); | ||||||
|  |         if (config->stdio_encoding == NULL) { | ||||||
|  |             return _Py_INIT_NO_MEMORY(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (config->stdio_errors == NULL && _Py_StandardStreamErrors != NULL) { | ||||||
|  |         config->stdio_errors = _PyMem_RawStrdup(_Py_StandardStreamErrors); | ||||||
|  |         if (config->stdio_errors == NULL) { | ||||||
|  |             return _Py_INIT_NO_MEMORY(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (config->stdio_encoding != NULL && config->stdio_errors != NULL) { | ||||||
|  |         return _Py_INIT_OK(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* PYTHONIOENCODING environment variable */ | ||||||
|  |     const char *opt = _PyCoreConfig_GetEnv(config, "PYTHONIOENCODING"); | ||||||
|  |     if (opt) { | ||||||
|  |         char *pythonioencoding = _PyMem_RawStrdup(opt); | ||||||
|  |         if (pythonioencoding == NULL) { | ||||||
|  |             return _Py_INIT_NO_MEMORY(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         char *err = strchr(pythonioencoding, ':'); | ||||||
|  |         if (err) { | ||||||
|  |             *err = '\0'; | ||||||
|  |             err++; | ||||||
|  |             if (!err[0]) { | ||||||
|  |                 err = NULL; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* Does PYTHONIOENCODING contain an encoding? */ | ||||||
|  |         if (pythonioencoding[0]) { | ||||||
|  |             if (config->stdio_encoding == NULL) { | ||||||
|  |                 config->stdio_encoding = _PyMem_RawStrdup(pythonioencoding); | ||||||
|  |                 if (config->stdio_encoding == NULL) { | ||||||
|  |                     PyMem_RawFree(pythonioencoding); | ||||||
|  |                     return _Py_INIT_NO_MEMORY(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             /* If the encoding is set but not the error handler,
 | ||||||
|  |                use "strict" error handler by default. | ||||||
|  |                PYTHONIOENCODING=latin1 behaves as | ||||||
|  |                PYTHONIOENCODING=latin1:strict. */ | ||||||
|  |             if (!err) { | ||||||
|  |                 err = "strict"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (config->stdio_errors == NULL && err != NULL) { | ||||||
|  |             config->stdio_errors = _PyMem_RawStrdup(err); | ||||||
|  |             if (config->stdio_errors == NULL) { | ||||||
|  |                 PyMem_RawFree(pythonioencoding); | ||||||
|  |                 return _Py_INIT_NO_MEMORY(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         PyMem_RawFree(pythonioencoding); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* UTF-8 Mode uses UTF-8/surrogateescape */ | ||||||
|  |     if (config->utf8_mode) { | ||||||
|  |         if (config->stdio_encoding == NULL) { | ||||||
|  |             config->stdio_encoding = _PyMem_RawStrdup("utf-8"); | ||||||
|  |             if (config->stdio_encoding == NULL) { | ||||||
|  |                 return _Py_INIT_NO_MEMORY(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (config->stdio_errors == NULL) { | ||||||
|  |             config->stdio_errors = _PyMem_RawStrdup("surrogateescape"); | ||||||
|  |             if (config->stdio_errors == NULL) { | ||||||
|  |                 return _Py_INIT_NO_MEMORY(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Choose the default error handler based on the current locale. */ | ||||||
|  |     if (config->stdio_encoding == NULL) { | ||||||
|  |         _PyInitError err = _Py_get_locale_encoding(&config->stdio_encoding); | ||||||
|  |         if (_Py_INIT_FAILED(err)) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (config->stdio_errors == NULL) { | ||||||
|  |         const char *errors = get_stdio_errors(config); | ||||||
|  |         config->stdio_errors = _PyMem_RawStrdup(errors); | ||||||
|  |         if (config->stdio_errors == NULL) { | ||||||
|  |             return _Py_INIT_NO_MEMORY(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return _Py_INIT_OK(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Read configuration settings from standard locations
 | /* Read configuration settings from standard locations
 | ||||||
|  * |  * | ||||||
|  * This function doesn't make any changes to the interpreter state - it |  * This function doesn't make any changes to the interpreter state - it | ||||||
|  | @ -1044,6 +1216,11 @@ _PyCoreConfig_Read(_PyCoreConfig *config) | ||||||
|         config->argc = 0; |         config->argc = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     err = config_init_stdio_encoding(config); | ||||||
|  |     if (_Py_INIT_FAILED(err)) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     assert(config->coerce_c_locale >= 0); |     assert(config->coerce_c_locale >= 0); | ||||||
|     assert(config->use_environment >= 0); |     assert(config->use_environment >= 0); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -184,27 +184,6 @@ get_codec_name(const char *encoding) | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static _PyInitError |  | ||||||
| get_locale_encoding(char **locale_encoding) |  | ||||||
| { |  | ||||||
| #ifdef MS_WINDOWS |  | ||||||
|     char encoding[20]; |  | ||||||
|     PyOS_snprintf(encoding, sizeof(encoding), "cp%d", GetACP()); |  | ||||||
| #elif defined(__ANDROID__) |  | ||||||
|     const char *encoding = "UTF-8"; |  | ||||||
| #else |  | ||||||
|     const char *encoding = nl_langinfo(CODESET); |  | ||||||
|     if (!encoding || encoding[0] == '\0') { |  | ||||||
|         return _Py_INIT_USER_ERR("failed to get the locale encoding: " |  | ||||||
|                                  "nl_langinfo(CODESET) failed"); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|     *locale_encoding = _PyMem_RawStrdup(encoding); |  | ||||||
|     if (*locale_encoding == NULL) { |  | ||||||
|         return _Py_INIT_NO_MEMORY(); |  | ||||||
|     } |  | ||||||
|     return _Py_INIT_OK(); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static _PyInitError | static _PyInitError | ||||||
| initimport(PyInterpreterState *interp, PyObject *sysmod) | initimport(PyInterpreterState *interp, PyObject *sysmod) | ||||||
|  | @ -340,35 +319,20 @@ static _LocaleCoercionTarget _TARGET_LOCALES[] = { | ||||||
|     {NULL} |     {NULL} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const char * | 
 | ||||||
| get_stdio_errors(void) | int | ||||||
|  | _Py_IsLocaleCoercionTarget(const char *ctype_loc) | ||||||
| { | { | ||||||
| #ifndef MS_WINDOWS |     const _LocaleCoercionTarget *target = NULL; | ||||||
|     const char *ctype_loc = setlocale(LC_CTYPE, NULL); |     for (target = _TARGET_LOCALES; target->locale_name; target++) { | ||||||
|     if (ctype_loc != NULL) { |         if (strcmp(ctype_loc, target->locale_name) == 0) { | ||||||
|         /* surrogateescape is the default in the legacy C and POSIX locales */ |             return 1; | ||||||
|         if (strcmp(ctype_loc, "C") == 0 || strcmp(ctype_loc, "POSIX") == 0) { |  | ||||||
|             return "surrogateescape"; |  | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
| #ifdef PY_COERCE_C_LOCALE |  | ||||||
|         /* surrogateescape is the default in locale coercion target locales */ |  | ||||||
|         const _LocaleCoercionTarget *target = NULL; |  | ||||||
|         for (target = _TARGET_LOCALES; target->locale_name; target++) { |  | ||||||
|             if (strcmp(ctype_loc, target->locale_name) == 0) { |  | ||||||
|                 return "surrogateescape"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|     } |     } | ||||||
| 
 |     return 0; | ||||||
|     return "strict"; |  | ||||||
| #else |  | ||||||
|     /* On Windows, always use surrogateescape by default */ |  | ||||||
|     return "surrogateescape"; |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| #ifdef PY_COERCE_C_LOCALE | #ifdef PY_COERCE_C_LOCALE | ||||||
| static const char C_LOCALE_COERCION_WARNING[] = | static const char C_LOCALE_COERCION_WARNING[] = | ||||||
|     "Python detected LC_CTYPE=C: LC_CTYPE coerced to %.20s (set another locale " |     "Python detected LC_CTYPE=C: LC_CTYPE coerced to %.20s (set another locale " | ||||||
|  | @ -1533,8 +1497,10 @@ initfsencoding(PyInterpreterState *interp) | ||||||
|             Py_HasFileSystemDefaultEncoding = 1; |             Py_HasFileSystemDefaultEncoding = 1; | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|  |             extern _PyInitError _Py_get_locale_encoding(char **locale_encoding); | ||||||
|  | 
 | ||||||
|             char *locale_encoding; |             char *locale_encoding; | ||||||
|             _PyInitError err = get_locale_encoding(&locale_encoding); |             _PyInitError err = _Py_get_locale_encoding(&locale_encoding); | ||||||
|             if (_Py_INIT_FAILED(err)) { |             if (_Py_INIT_FAILED(err)) { | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  | @ -1740,13 +1706,16 @@ init_sys_streams(PyInterpreterState *interp) | ||||||
|     PyObject *std = NULL; |     PyObject *std = NULL; | ||||||
|     int fd; |     int fd; | ||||||
|     PyObject * encoding_attr; |     PyObject * encoding_attr; | ||||||
|     char *pythonioencoding = NULL; |  | ||||||
|     const char *encoding, *errors; |  | ||||||
|     char *locale_encoding = NULL; |  | ||||||
|     char *codec_name = NULL; |  | ||||||
|     _PyInitError res = _Py_INIT_OK(); |     _PyInitError res = _Py_INIT_OK(); | ||||||
|     extern char *_Py_StandardStreamEncoding; |     _PyCoreConfig *config = &interp->core_config; | ||||||
|     extern char *_Py_StandardStreamErrors; | 
 | ||||||
|  |     char *codec_name = get_codec_name(config->stdio_encoding); | ||||||
|  |     if (codec_name == NULL) { | ||||||
|  |         return _Py_INIT_ERR("failed to get the Python codec name " | ||||||
|  |                             "of the stdio encoding"); | ||||||
|  |     } | ||||||
|  |     PyMem_RawFree(config->stdio_encoding); | ||||||
|  |     config->stdio_encoding = codec_name; | ||||||
| 
 | 
 | ||||||
|     /* Hack to avoid a nasty recursion issue when Python is invoked
 |     /* Hack to avoid a nasty recursion issue when Python is invoked
 | ||||||
|        in verbose mode: pre-import the Latin-1 and UTF-8 codecs */ |        in verbose mode: pre-import the Latin-1 and UTF-8 codecs */ | ||||||
|  | @ -1778,85 +1747,15 @@ init_sys_streams(PyInterpreterState *interp) | ||||||
|     } |     } | ||||||
|     Py_DECREF(wrapper); |     Py_DECREF(wrapper); | ||||||
| 
 | 
 | ||||||
|     encoding = _Py_StandardStreamEncoding; |  | ||||||
|     errors = _Py_StandardStreamErrors; |  | ||||||
|     if (!encoding || !errors) { |  | ||||||
|         char *opt = Py_GETENV("PYTHONIOENCODING"); |  | ||||||
|         if (opt && opt[0] != '\0') { |  | ||||||
|             char *err; |  | ||||||
|             pythonioencoding = _PyMem_Strdup(opt); |  | ||||||
|             if (pythonioencoding == NULL) { |  | ||||||
|                 PyErr_NoMemory(); |  | ||||||
|                 goto error; |  | ||||||
|             } |  | ||||||
|             err = strchr(pythonioencoding, ':'); |  | ||||||
|             if (err) { |  | ||||||
|                 *err = '\0'; |  | ||||||
|                 err++; |  | ||||||
|                 if (!err[0]) { |  | ||||||
|                     err = NULL; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             /* Does PYTHONIOENCODING contain an encoding? */ |  | ||||||
|             if (pythonioencoding[0]) { |  | ||||||
|                 if (!encoding) { |  | ||||||
|                     encoding = pythonioencoding; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 /* If the encoding is set but not the error handler,
 |  | ||||||
|                    use "strict" error handler by default. |  | ||||||
|                    PYTHONIOENCODING=latin1 behaves as |  | ||||||
|                    PYTHONIOENCODING=latin1:strict. */ |  | ||||||
|                 if (!err) { |  | ||||||
|                     err = "strict"; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!errors && err != NULL) { |  | ||||||
|                 errors = err; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (interp->core_config.utf8_mode) { |  | ||||||
|             if (!encoding) { |  | ||||||
|                 encoding = "utf-8"; |  | ||||||
|             } |  | ||||||
|             if (!errors) { |  | ||||||
|                 errors = "surrogateescape"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!errors) { |  | ||||||
|             /* Choose the default error handler based on the current locale */ |  | ||||||
|             errors = get_stdio_errors(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (encoding == NULL) { |  | ||||||
|         _PyInitError err = get_locale_encoding(&locale_encoding); |  | ||||||
|         if (_Py_INIT_FAILED(err)) { |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|         encoding = locale_encoding; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     codec_name = get_codec_name(encoding); |  | ||||||
|     if (codec_name == NULL) { |  | ||||||
|         PyErr_SetString(PyExc_RuntimeError, |  | ||||||
|                         "failed to get the Python codec name " |  | ||||||
|                         "of stdio encoding"); |  | ||||||
|         goto error; |  | ||||||
|     } |  | ||||||
|     encoding = codec_name; |  | ||||||
| 
 |  | ||||||
|     /* Set sys.stdin */ |     /* Set sys.stdin */ | ||||||
|     fd = fileno(stdin); |     fd = fileno(stdin); | ||||||
|     /* Under some conditions stdin, stdout and stderr may not be connected
 |     /* Under some conditions stdin, stdout and stderr may not be connected
 | ||||||
|      * and fileno() may point to an invalid file descriptor. For example |      * and fileno() may point to an invalid file descriptor. For example | ||||||
|      * GUI apps don't have valid standard streams by default. |      * GUI apps don't have valid standard streams by default. | ||||||
|      */ |      */ | ||||||
|     std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors); |     std = create_stdio(iomod, fd, 0, "<stdin>", | ||||||
|  |                        config->stdio_encoding, | ||||||
|  |                        config->stdio_errors); | ||||||
|     if (std == NULL) |     if (std == NULL) | ||||||
|         goto error; |         goto error; | ||||||
|     PySys_SetObject("__stdin__", std); |     PySys_SetObject("__stdin__", std); | ||||||
|  | @ -1865,7 +1764,9 @@ init_sys_streams(PyInterpreterState *interp) | ||||||
| 
 | 
 | ||||||
|     /* Set sys.stdout */ |     /* Set sys.stdout */ | ||||||
|     fd = fileno(stdout); |     fd = fileno(stdout); | ||||||
|     std = create_stdio(iomod, fd, 1, "<stdout>", encoding, errors); |     std = create_stdio(iomod, fd, 1, "<stdout>", | ||||||
|  |                        config->stdio_encoding, | ||||||
|  |                        config->stdio_errors); | ||||||
|     if (std == NULL) |     if (std == NULL) | ||||||
|         goto error; |         goto error; | ||||||
|     PySys_SetObject("__stdout__", std); |     PySys_SetObject("__stdout__", std); | ||||||
|  | @ -1875,7 +1776,9 @@ init_sys_streams(PyInterpreterState *interp) | ||||||
| #if 1 /* Disable this if you have trouble debugging bootstrap stuff */ | #if 1 /* Disable this if you have trouble debugging bootstrap stuff */ | ||||||
|     /* Set sys.stderr, replaces the preliminary stderr */ |     /* Set sys.stderr, replaces the preliminary stderr */ | ||||||
|     fd = fileno(stderr); |     fd = fileno(stderr); | ||||||
|     std = create_stdio(iomod, fd, 1, "<stderr>", encoding, "backslashreplace"); |     std = create_stdio(iomod, fd, 1, "<stderr>", | ||||||
|  |                        config->stdio_encoding, | ||||||
|  |                        "backslashreplace"); | ||||||
|     if (std == NULL) |     if (std == NULL) | ||||||
|         goto error; |         goto error; | ||||||
| 
 | 
 | ||||||
|  | @ -1911,9 +1814,6 @@ init_sys_streams(PyInterpreterState *interp) | ||||||
| done: | done: | ||||||
|     _Py_ClearStandardStreamEncoding(); |     _Py_ClearStandardStreamEncoding(); | ||||||
| 
 | 
 | ||||||
|     PyMem_RawFree(locale_encoding); |  | ||||||
|     PyMem_RawFree(codec_name); |  | ||||||
|     PyMem_Free(pythonioencoding); |  | ||||||
|     Py_XDECREF(bimod); |     Py_XDECREF(bimod); | ||||||
|     Py_XDECREF(iomod); |     Py_XDECREF(iomod); | ||||||
|     return res; |     return res; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Victor Stinner
						Victor Stinner