(root)/
Python-3.12.0/
Python/
preconfig.c
       1  #include "Python.h"
       2  #include "pycore_fileutils.h"     // DECODE_LOCALE_ERR
       3  #include "pycore_getopt.h"        // _PyOS_GetOpt()
       4  #include "pycore_initconfig.h"    // _PyArgv
       5  #include "pycore_pymem.h"         // _PyMem_GetAllocatorName()
       6  #include "pycore_runtime.h"       // _PyRuntime_Initialize()
       7  
       8  #include <locale.h>               // setlocale()
       9  #include <stdlib.h>               // getenv()
      10  
      11  
      12  /* Forward declarations */
      13  static void
      14  preconfig_copy(PyPreConfig *config, const PyPreConfig *config2);
      15  
      16  
      17  /* --- File system encoding/errors -------------------------------- */
      18  
      19  const char *Py_FileSystemDefaultEncoding = NULL;
      20  int Py_HasFileSystemDefaultEncoding = 0;
      21  const char *Py_FileSystemDefaultEncodeErrors = NULL;
      22  int _Py_HasFileSystemDefaultEncodeErrors = 0;
      23  
      24  void
      25  _Py_ClearFileSystemEncoding(void)
      26  {
      27  _Py_COMP_DIAG_PUSH
      28  _Py_COMP_DIAG_IGNORE_DEPR_DECLS
      29      if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) {
      30          PyMem_RawFree((char*)Py_FileSystemDefaultEncoding);
      31          Py_FileSystemDefaultEncoding = NULL;
      32      }
      33      if (!_Py_HasFileSystemDefaultEncodeErrors && Py_FileSystemDefaultEncodeErrors) {
      34          PyMem_RawFree((char*)Py_FileSystemDefaultEncodeErrors);
      35          Py_FileSystemDefaultEncodeErrors = NULL;
      36      }
      37  _Py_COMP_DIAG_POP
      38  }
      39  
      40  
      41  /* Set Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors
      42     global configuration variables to PyConfig.filesystem_encoding and
      43     PyConfig.filesystem_errors (encoded to UTF-8).
      44  
      45     Function called by _PyUnicode_InitEncodings(). */
      46  int
      47  _Py_SetFileSystemEncoding(const char *encoding, const char *errors)
      48  {
      49      char *encoding2 = _PyMem_RawStrdup(encoding);
      50      if (encoding2 == NULL) {
      51          return -1;
      52      }
      53  
      54      char *errors2 = _PyMem_RawStrdup(errors);
      55      if (errors2 == NULL) {
      56          PyMem_RawFree(encoding2);
      57          return -1;
      58      }
      59  
      60      _Py_ClearFileSystemEncoding();
      61  
      62  _Py_COMP_DIAG_PUSH
      63  _Py_COMP_DIAG_IGNORE_DEPR_DECLS
      64      Py_FileSystemDefaultEncoding = encoding2;
      65      Py_HasFileSystemDefaultEncoding = 0;
      66  
      67      Py_FileSystemDefaultEncodeErrors = errors2;
      68      _Py_HasFileSystemDefaultEncodeErrors = 0;
      69  _Py_COMP_DIAG_POP
      70      return 0;
      71  }
      72  
      73  
      74  /* --- _PyArgv ---------------------------------------------------- */
      75  
      76  /* Decode bytes_argv using Py_DecodeLocale() */
      77  PyStatus
      78  _PyArgv_AsWstrList(const _PyArgv *args, PyWideStringList *list)
      79  {
      80      PyWideStringList wargv = _PyWideStringList_INIT;
      81      if (args->use_bytes_argv) {
      82          size_t size = sizeof(wchar_t*) * args->argc;
      83          wargv.items = (wchar_t **)PyMem_RawMalloc(size);
      84          if (wargv.items == NULL) {
      85              return _PyStatus_NO_MEMORY();
      86          }
      87  
      88          for (Py_ssize_t i = 0; i < args->argc; i++) {
      89              size_t len;
      90              wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
      91              if (arg == NULL) {
      92                  _PyWideStringList_Clear(&wargv);
      93                  return DECODE_LOCALE_ERR("command line arguments", len);
      94              }
      95              wargv.items[i] = arg;
      96              wargv.length++;
      97          }
      98  
      99          _PyWideStringList_Clear(list);
     100          *list = wargv;
     101      }
     102      else {
     103          wargv.length = args->argc;
     104          wargv.items = (wchar_t **)args->wchar_argv;
     105          if (_PyWideStringList_Copy(list, &wargv) < 0) {
     106              return _PyStatus_NO_MEMORY();
     107          }
     108      }
     109      return _PyStatus_OK();
     110  }
     111  
     112  
     113  /* --- _PyPreCmdline ------------------------------------------------- */
     114  
     115  void
     116  _PyPreCmdline_Clear(_PyPreCmdline *cmdline)
     117  {
     118      _PyWideStringList_Clear(&cmdline->argv);
     119      _PyWideStringList_Clear(&cmdline->xoptions);
     120  }
     121  
     122  
     123  PyStatus
     124  _PyPreCmdline_SetArgv(_PyPreCmdline *cmdline, const _PyArgv *args)
     125  {
     126      return _PyArgv_AsWstrList(args, &cmdline->argv);
     127  }
     128  
     129  
     130  static void
     131  precmdline_get_preconfig(_PyPreCmdline *cmdline, const PyPreConfig *config)
     132  {
     133  #define COPY_ATTR(ATTR) \
     134      if (config->ATTR != -1) { \
     135          cmdline->ATTR = config->ATTR; \
     136      }
     137  
     138      COPY_ATTR(isolated);
     139      COPY_ATTR(use_environment);
     140      COPY_ATTR(dev_mode);
     141  
     142  #undef COPY_ATTR
     143  }
     144  
     145  
     146  static void
     147  precmdline_set_preconfig(const _PyPreCmdline *cmdline, PyPreConfig *config)
     148  {
     149  #define COPY_ATTR(ATTR) \
     150      config->ATTR = cmdline->ATTR
     151  
     152      COPY_ATTR(isolated);
     153      COPY_ATTR(use_environment);
     154      COPY_ATTR(dev_mode);
     155  
     156  #undef COPY_ATTR
     157  }
     158  
     159  
     160  PyStatus
     161  _PyPreCmdline_SetConfig(const _PyPreCmdline *cmdline, PyConfig *config)
     162  {
     163  #define COPY_ATTR(ATTR) \
     164      config->ATTR = cmdline->ATTR
     165  
     166      PyStatus status = _PyWideStringList_Extend(&config->xoptions, &cmdline->xoptions);
     167      if (_PyStatus_EXCEPTION(status)) {
     168          return status;
     169      }
     170  
     171      COPY_ATTR(isolated);
     172      COPY_ATTR(use_environment);
     173      COPY_ATTR(dev_mode);
     174      COPY_ATTR(warn_default_encoding);
     175      return _PyStatus_OK();
     176  
     177  #undef COPY_ATTR
     178  }
     179  
     180  
     181  /* Parse the command line arguments */
     182  static PyStatus
     183  precmdline_parse_cmdline(_PyPreCmdline *cmdline)
     184  {
     185      const PyWideStringList *argv = &cmdline->argv;
     186  
     187      _PyOS_ResetGetOpt();
     188      /* Don't log parsing errors into stderr here: PyConfig_Read()
     189         is responsible for that */
     190      _PyOS_opterr = 0;
     191      do {
     192          int longindex = -1;
     193          int c = _PyOS_GetOpt(argv->length, argv->items, &longindex);
     194  
     195          if (c == EOF || c == 'c' || c == 'm') {
     196              break;
     197          }
     198  
     199          switch (c) {
     200          case 'E':
     201              cmdline->use_environment = 0;
     202              break;
     203  
     204          case 'I':
     205              cmdline->isolated = 1;
     206              break;
     207  
     208          case 'X':
     209          {
     210              PyStatus status = PyWideStringList_Append(&cmdline->xoptions,
     211                                                        _PyOS_optarg);
     212              if (_PyStatus_EXCEPTION(status)) {
     213                  return status;
     214              }
     215              break;
     216          }
     217  
     218          default:
     219              /* ignore other argument:
     220                 handled by PyConfig_Read() */
     221              break;
     222          }
     223      } while (1);
     224  
     225      return _PyStatus_OK();
     226  }
     227  
     228  
     229  PyStatus
     230  _PyPreCmdline_Read(_PyPreCmdline *cmdline, const PyPreConfig *preconfig)
     231  {
     232      precmdline_get_preconfig(cmdline, preconfig);
     233  
     234      if (preconfig->parse_argv) {
     235          PyStatus status = precmdline_parse_cmdline(cmdline);
     236          if (_PyStatus_EXCEPTION(status)) {
     237              return status;
     238          }
     239      }
     240  
     241      /* isolated, use_environment */
     242      if (cmdline->isolated < 0) {
     243          cmdline->isolated = 0;
     244      }
     245      if (cmdline->isolated > 0) {
     246          cmdline->use_environment = 0;
     247      }
     248      if (cmdline->use_environment < 0) {
     249          cmdline->use_environment = 0;
     250      }
     251  
     252      /* dev_mode */
     253      if ((cmdline->dev_mode < 0)
     254          && (_Py_get_xoption(&cmdline->xoptions, L"dev")
     255              || _Py_GetEnv(cmdline->use_environment, "PYTHONDEVMODE")))
     256      {
     257          cmdline->dev_mode = 1;
     258      }
     259      if (cmdline->dev_mode < 0) {
     260          cmdline->dev_mode = 0;
     261      }
     262  
     263      // warn_default_encoding
     264      if (_Py_get_xoption(&cmdline->xoptions, L"warn_default_encoding")
     265              || _Py_GetEnv(cmdline->use_environment, "PYTHONWARNDEFAULTENCODING"))
     266      {
     267          cmdline->warn_default_encoding = 1;
     268      }
     269  
     270      assert(cmdline->use_environment >= 0);
     271      assert(cmdline->isolated >= 0);
     272      assert(cmdline->dev_mode >= 0);
     273      assert(cmdline->warn_default_encoding >= 0);
     274  
     275      return _PyStatus_OK();
     276  }
     277  
     278  
     279  /* --- PyPreConfig ----------------------------------------------- */
     280  
     281  
     282  void
     283  _PyPreConfig_InitCompatConfig(PyPreConfig *config)
     284  {
     285      memset(config, 0, sizeof(*config));
     286  
     287      config->_config_init = (int)_PyConfig_INIT_COMPAT;
     288      config->parse_argv = 0;
     289      config->isolated = -1;
     290      config->use_environment = -1;
     291      config->configure_locale = 1;
     292  
     293      /* bpo-36443: C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
     294         are disabled by default using the Compat configuration.
     295  
     296         Py_UTF8Mode=1 enables the UTF-8 mode. PYTHONUTF8 environment variable
     297         is ignored (even if use_environment=1). */
     298      config->utf8_mode = 0;
     299      config->coerce_c_locale = 0;
     300      config->coerce_c_locale_warn = 0;
     301  
     302      config->dev_mode = -1;
     303      config->allocator = PYMEM_ALLOCATOR_NOT_SET;
     304  #ifdef MS_WINDOWS
     305      config->legacy_windows_fs_encoding = -1;
     306  #endif
     307  }
     308  
     309  
     310  void
     311  PyPreConfig_InitPythonConfig(PyPreConfig *config)
     312  {
     313      _PyPreConfig_InitCompatConfig(config);
     314  
     315      config->_config_init = (int)_PyConfig_INIT_PYTHON;
     316      config->isolated = 0;
     317      config->parse_argv = 1;
     318      config->use_environment = 1;
     319      /* Set to -1 to enable C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
     320         depending on the LC_CTYPE locale, PYTHONUTF8 and PYTHONCOERCECLOCALE
     321         environment variables. */
     322      config->coerce_c_locale = -1;
     323      config->coerce_c_locale_warn = -1;
     324      config->utf8_mode = -1;
     325  #ifdef MS_WINDOWS
     326      config->legacy_windows_fs_encoding = 0;
     327  #endif
     328  }
     329  
     330  
     331  void
     332  PyPreConfig_InitIsolatedConfig(PyPreConfig *config)
     333  {
     334      _PyPreConfig_InitCompatConfig(config);
     335  
     336      config->_config_init = (int)_PyConfig_INIT_ISOLATED;
     337      config->configure_locale = 0;
     338      config->isolated = 1;
     339      config->use_environment = 0;
     340      config->utf8_mode = 0;
     341      config->dev_mode = 0;
     342  #ifdef MS_WINDOWS
     343      config->legacy_windows_fs_encoding = 0;
     344  #endif
     345  }
     346  
     347  
     348  PyStatus
     349  _PyPreConfig_InitFromPreConfig(PyPreConfig *config,
     350                                 const PyPreConfig *config2)
     351  {
     352      PyPreConfig_InitPythonConfig(config);
     353      preconfig_copy(config, config2);
     354      return _PyStatus_OK();
     355  }
     356  
     357  
     358  void
     359  _PyPreConfig_InitFromConfig(PyPreConfig *preconfig, const PyConfig *config)
     360  {
     361      _PyConfigInitEnum config_init = (_PyConfigInitEnum)config->_config_init;
     362      switch (config_init) {
     363      case _PyConfig_INIT_PYTHON:
     364          PyPreConfig_InitPythonConfig(preconfig);
     365          break;
     366      case _PyConfig_INIT_ISOLATED:
     367          PyPreConfig_InitIsolatedConfig(preconfig);
     368          break;
     369      case _PyConfig_INIT_COMPAT:
     370      default:
     371          _PyPreConfig_InitCompatConfig(preconfig);
     372      }
     373  
     374      _PyPreConfig_GetConfig(preconfig, config);
     375  }
     376  
     377  
     378  static void
     379  preconfig_copy(PyPreConfig *config, const PyPreConfig *config2)
     380  {
     381  #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
     382  
     383      COPY_ATTR(_config_init);
     384      COPY_ATTR(parse_argv);
     385      COPY_ATTR(isolated);
     386      COPY_ATTR(use_environment);
     387      COPY_ATTR(configure_locale);
     388      COPY_ATTR(dev_mode);
     389      COPY_ATTR(coerce_c_locale);
     390      COPY_ATTR(coerce_c_locale_warn);
     391      COPY_ATTR(utf8_mode);
     392      COPY_ATTR(allocator);
     393  #ifdef MS_WINDOWS
     394      COPY_ATTR(legacy_windows_fs_encoding);
     395  #endif
     396  
     397  #undef COPY_ATTR
     398  }
     399  
     400  
     401  PyObject*
     402  _PyPreConfig_AsDict(const PyPreConfig *config)
     403  {
     404      PyObject *dict;
     405  
     406      dict = PyDict_New();
     407      if (dict == NULL) {
     408          return NULL;
     409      }
     410  
     411  #define SET_ITEM_INT(ATTR) \
     412          do { \
     413              PyObject *obj = PyLong_FromLong(config->ATTR); \
     414              if (obj == NULL) { \
     415                  goto fail; \
     416              } \
     417              int res = PyDict_SetItemString(dict, #ATTR, obj); \
     418              Py_DECREF(obj); \
     419              if (res < 0) { \
     420                  goto fail; \
     421              } \
     422          } while (0)
     423  
     424      SET_ITEM_INT(_config_init);
     425      SET_ITEM_INT(parse_argv);
     426      SET_ITEM_INT(isolated);
     427      SET_ITEM_INT(use_environment);
     428      SET_ITEM_INT(configure_locale);
     429      SET_ITEM_INT(coerce_c_locale);
     430      SET_ITEM_INT(coerce_c_locale_warn);
     431      SET_ITEM_INT(utf8_mode);
     432  #ifdef MS_WINDOWS
     433      SET_ITEM_INT(legacy_windows_fs_encoding);
     434  #endif
     435      SET_ITEM_INT(dev_mode);
     436      SET_ITEM_INT(allocator);
     437      return dict;
     438  
     439  fail:
     440      Py_DECREF(dict);
     441      return NULL;
     442  
     443  #undef SET_ITEM_INT
     444  }
     445  
     446  
     447  void
     448  _PyPreConfig_GetConfig(PyPreConfig *preconfig, const PyConfig *config)
     449  {
     450  #define COPY_ATTR(ATTR) \
     451      if (config->ATTR != -1) { \
     452          preconfig->ATTR = config->ATTR; \
     453      }
     454  
     455      COPY_ATTR(parse_argv);
     456      COPY_ATTR(isolated);
     457      COPY_ATTR(use_environment);
     458      COPY_ATTR(dev_mode);
     459  
     460  #undef COPY_ATTR
     461  }
     462  
     463  
     464  static void
     465  preconfig_get_global_vars(PyPreConfig *config)
     466  {
     467      if (config->_config_init != _PyConfig_INIT_COMPAT) {
     468          /* Python and Isolated configuration ignore global variables */
     469          return;
     470      }
     471  
     472  #define COPY_FLAG(ATTR, VALUE) \
     473      if (config->ATTR < 0) { \
     474          config->ATTR = VALUE; \
     475      }
     476  #define COPY_NOT_FLAG(ATTR, VALUE) \
     477      if (config->ATTR < 0) { \
     478          config->ATTR = !(VALUE); \
     479      }
     480  
     481  _Py_COMP_DIAG_PUSH
     482  _Py_COMP_DIAG_IGNORE_DEPR_DECLS
     483      COPY_FLAG(isolated, Py_IsolatedFlag);
     484      COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
     485      if (Py_UTF8Mode > 0) {
     486          config->utf8_mode = Py_UTF8Mode;
     487      }
     488  #ifdef MS_WINDOWS
     489      COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
     490  #endif
     491  _Py_COMP_DIAG_POP
     492  
     493  #undef COPY_FLAG
     494  #undef COPY_NOT_FLAG
     495  }
     496  
     497  
     498  static void
     499  preconfig_set_global_vars(const PyPreConfig *config)
     500  {
     501  #define COPY_FLAG(ATTR, VAR) \
     502      if (config->ATTR >= 0) { \
     503          VAR = config->ATTR; \
     504      }
     505  #define COPY_NOT_FLAG(ATTR, VAR) \
     506      if (config->ATTR >= 0) { \
     507          VAR = !config->ATTR; \
     508      }
     509  
     510  _Py_COMP_DIAG_PUSH
     511  _Py_COMP_DIAG_IGNORE_DEPR_DECLS
     512      COPY_FLAG(isolated, Py_IsolatedFlag);
     513      COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
     514  #ifdef MS_WINDOWS
     515      COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
     516  #endif
     517      COPY_FLAG(utf8_mode, Py_UTF8Mode);
     518  _Py_COMP_DIAG_POP
     519  
     520  #undef COPY_FLAG
     521  #undef COPY_NOT_FLAG
     522  }
     523  
     524  
     525  const char*
     526  _Py_GetEnv(int use_environment, const char *name)
     527  {
     528      assert(use_environment >= 0);
     529  
     530      if (!use_environment) {
     531          return NULL;
     532      }
     533  
     534      const char *var = getenv(name);
     535      if (var && var[0] != '\0') {
     536          return var;
     537      }
     538      else {
     539          return NULL;
     540      }
     541  }
     542  
     543  
     544  int
     545  _Py_str_to_int(const char *str, int *result)
     546  {
     547      const char *endptr = str;
     548      errno = 0;
     549      long value = strtol(str, (char **)&endptr, 10);
     550      if (*endptr != '\0' || errno == ERANGE) {
     551          return -1;
     552      }
     553      if (value < INT_MIN || value > INT_MAX) {
     554          return -1;
     555      }
     556  
     557      *result = (int)value;
     558      return 0;
     559  }
     560  
     561  
     562  void
     563  _Py_get_env_flag(int use_environment, int *flag, const char *name)
     564  {
     565      const char *var = _Py_GetEnv(use_environment, name);
     566      if (!var) {
     567          return;
     568      }
     569      int value;
     570      if (_Py_str_to_int(var, &value) < 0 || value < 0) {
     571          /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */
     572          value = 1;
     573      }
     574      if (*flag < value) {
     575          *flag = value;
     576      }
     577  }
     578  
     579  
     580  const wchar_t*
     581  _Py_get_xoption(const PyWideStringList *xoptions, const wchar_t *name)
     582  {
     583      for (Py_ssize_t i=0; i < xoptions->length; i++) {
     584          const wchar_t *option = xoptions->items[i];
     585          size_t len;
     586          wchar_t *sep = wcschr(option, L'=');
     587          if (sep != NULL) {
     588              len = (sep - option);
     589          }
     590          else {
     591              len = wcslen(option);
     592          }
     593          if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') {
     594              return option;
     595          }
     596      }
     597      return NULL;
     598  }
     599  
     600  
     601  static PyStatus
     602  preconfig_init_utf8_mode(PyPreConfig *config, const _PyPreCmdline *cmdline)
     603  {
     604  #ifdef MS_WINDOWS
     605      if (config->legacy_windows_fs_encoding) {
     606          config->utf8_mode = 0;
     607      }
     608  #endif
     609  
     610      if (config->utf8_mode >= 0) {
     611          return _PyStatus_OK();
     612      }
     613  
     614      const wchar_t *xopt;
     615      xopt = _Py_get_xoption(&cmdline->xoptions, L"utf8");
     616      if (xopt) {
     617          wchar_t *sep = wcschr(xopt, L'=');
     618          if (sep) {
     619              xopt = sep + 1;
     620              if (wcscmp(xopt, L"1") == 0) {
     621                  config->utf8_mode = 1;
     622              }
     623              else if (wcscmp(xopt, L"0") == 0) {
     624                  config->utf8_mode = 0;
     625              }
     626              else {
     627                  return _PyStatus_ERR("invalid -X utf8 option value");
     628              }
     629          }
     630          else {
     631              config->utf8_mode = 1;
     632          }
     633          return _PyStatus_OK();
     634      }
     635  
     636      const char *opt = _Py_GetEnv(config->use_environment, "PYTHONUTF8");
     637      if (opt) {
     638          if (strcmp(opt, "1") == 0) {
     639              config->utf8_mode = 1;
     640          }
     641          else if (strcmp(opt, "0") == 0) {
     642              config->utf8_mode = 0;
     643          }
     644          else {
     645              return _PyStatus_ERR("invalid PYTHONUTF8 environment "
     646                                  "variable value");
     647          }
     648          return _PyStatus_OK();
     649      }
     650  
     651  
     652  #ifndef MS_WINDOWS
     653      if (config->utf8_mode < 0) {
     654          /* The C locale and the POSIX locale enable the UTF-8 Mode (PEP 540) */
     655          const char *ctype_loc = setlocale(LC_CTYPE, NULL);
     656          if (ctype_loc != NULL
     657             && (strcmp(ctype_loc, "C") == 0
     658                 || strcmp(ctype_loc, "POSIX") == 0))
     659          {
     660              config->utf8_mode = 1;
     661          }
     662      }
     663  #endif
     664  
     665      if (config->utf8_mode < 0) {
     666          config->utf8_mode = 0;
     667      }
     668      return _PyStatus_OK();
     669  }
     670  
     671  
     672  static void
     673  preconfig_init_coerce_c_locale(PyPreConfig *config)
     674  {
     675      if (!config->configure_locale) {
     676          config->coerce_c_locale = 0;
     677          config->coerce_c_locale_warn = 0;
     678          return;
     679      }
     680  
     681      const char *env = _Py_GetEnv(config->use_environment, "PYTHONCOERCECLOCALE");
     682      if (env) {
     683          if (strcmp(env, "0") == 0) {
     684              if (config->coerce_c_locale < 0) {
     685                  config->coerce_c_locale = 0;
     686              }
     687          }
     688          else if (strcmp(env, "warn") == 0) {
     689              if (config->coerce_c_locale_warn < 0) {
     690                  config->coerce_c_locale_warn = 1;
     691              }
     692          }
     693          else {
     694              if (config->coerce_c_locale < 0) {
     695                  config->coerce_c_locale = 1;
     696              }
     697          }
     698      }
     699  
     700      /* Test if coerce_c_locale equals to -1 or equals to 1:
     701         PYTHONCOERCECLOCALE=1 doesn't imply that the C locale is always coerced.
     702         It is only coerced if if the LC_CTYPE locale is "C". */
     703      if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) {
     704          /* The C locale enables the C locale coercion (PEP 538) */
     705          if (_Py_LegacyLocaleDetected(0)) {
     706              config->coerce_c_locale = 2;
     707          }
     708          else {
     709              config->coerce_c_locale = 0;
     710          }
     711      }
     712  
     713      if (config->coerce_c_locale_warn < 0) {
     714          config->coerce_c_locale_warn = 0;
     715      }
     716  }
     717  
     718  
     719  static PyStatus
     720  preconfig_init_allocator(PyPreConfig *config)
     721  {
     722      if (config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
     723          /* bpo-34247. The PYTHONMALLOC environment variable has the priority
     724             over PYTHONDEV env var and "-X dev" command line option.
     725             For example, PYTHONMALLOC=malloc PYTHONDEVMODE=1 sets the memory
     726             allocators to "malloc" (and not to "debug"). */
     727          const char *envvar = _Py_GetEnv(config->use_environment, "PYTHONMALLOC");
     728          if (envvar) {
     729              PyMemAllocatorName name;
     730              if (_PyMem_GetAllocatorName(envvar, &name) < 0) {
     731                  return _PyStatus_ERR("PYTHONMALLOC: unknown allocator");
     732              }
     733              config->allocator = (int)name;
     734          }
     735      }
     736  
     737      if (config->dev_mode && config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
     738          config->allocator = PYMEM_ALLOCATOR_DEBUG;
     739      }
     740      return _PyStatus_OK();
     741  }
     742  
     743  
     744  static PyStatus
     745  preconfig_read(PyPreConfig *config, _PyPreCmdline *cmdline)
     746  {
     747      PyStatus status;
     748  
     749      status = _PyPreCmdline_Read(cmdline, config);
     750      if (_PyStatus_EXCEPTION(status)) {
     751          return status;
     752      }
     753  
     754      precmdline_set_preconfig(cmdline, config);
     755  
     756      /* legacy_windows_fs_encoding, coerce_c_locale, utf8_mode */
     757  #ifdef MS_WINDOWS
     758      _Py_get_env_flag(config->use_environment,
     759                       &config->legacy_windows_fs_encoding,
     760                       "PYTHONLEGACYWINDOWSFSENCODING");
     761  #endif
     762  
     763      preconfig_init_coerce_c_locale(config);
     764  
     765      status = preconfig_init_utf8_mode(config, cmdline);
     766      if (_PyStatus_EXCEPTION(status)) {
     767          return status;
     768      }
     769  
     770      /* allocator */
     771      status = preconfig_init_allocator(config);
     772      if (_PyStatus_EXCEPTION(status)) {
     773          return status;
     774      }
     775  
     776      assert(config->coerce_c_locale >= 0);
     777      assert(config->coerce_c_locale_warn >= 0);
     778  #ifdef MS_WINDOWS
     779      assert(config->legacy_windows_fs_encoding >= 0);
     780  #endif
     781      assert(config->utf8_mode >= 0);
     782      assert(config->isolated >= 0);
     783      assert(config->use_environment >= 0);
     784      assert(config->dev_mode >= 0);
     785  
     786      return _PyStatus_OK();
     787  }
     788  
     789  
     790  /* Read the configuration from:
     791  
     792     - command line arguments
     793     - environment variables
     794     - Py_xxx global configuration variables
     795     - the LC_CTYPE locale */
     796  PyStatus
     797  _PyPreConfig_Read(PyPreConfig *config, const _PyArgv *args)
     798  {
     799      PyStatus status;
     800  
     801      status = _PyRuntime_Initialize();
     802      if (_PyStatus_EXCEPTION(status)) {
     803          return status;
     804      }
     805  
     806      preconfig_get_global_vars(config);
     807  
     808      /* Copy LC_CTYPE locale, since it's modified later */
     809      const char *loc = setlocale(LC_CTYPE, NULL);
     810      if (loc == NULL) {
     811          return _PyStatus_ERR("failed to LC_CTYPE locale");
     812      }
     813      char *init_ctype_locale = _PyMem_RawStrdup(loc);
     814      if (init_ctype_locale == NULL) {
     815          return _PyStatus_NO_MEMORY();
     816      }
     817  
     818      /* Save the config to be able to restore it if encodings change */
     819      PyPreConfig save_config;
     820  
     821      status = _PyPreConfig_InitFromPreConfig(&save_config, config);
     822      if (_PyStatus_EXCEPTION(status)) {
     823          return status;
     824      }
     825  
     826      /* Set LC_CTYPE to the user preferred locale */
     827      if (config->configure_locale) {
     828          _Py_SetLocaleFromEnv(LC_CTYPE);
     829      }
     830  
     831      PyPreConfig save_runtime_config;
     832      preconfig_copy(&save_runtime_config, &_PyRuntime.preconfig);
     833  
     834      _PyPreCmdline cmdline = _PyPreCmdline_INIT;
     835      int locale_coerced = 0;
     836      int loops = 0;
     837  
     838      while (1) {
     839          int utf8_mode = config->utf8_mode;
     840  
     841          /* Watchdog to prevent an infinite loop */
     842          loops++;
     843          if (loops == 3) {
     844              status = _PyStatus_ERR("Encoding changed twice while "
     845                                     "reading the configuration");
     846              goto done;
     847          }
     848  
     849          /* bpo-34207: Py_DecodeLocale() and Py_EncodeLocale() depend
     850             on the utf8_mode and legacy_windows_fs_encoding members
     851             of _PyRuntime.preconfig. */
     852          preconfig_copy(&_PyRuntime.preconfig, config);
     853  
     854          if (args) {
     855              // Set command line arguments at each iteration. If they are bytes
     856              // strings, they are decoded from the new encoding.
     857              status = _PyPreCmdline_SetArgv(&cmdline, args);
     858              if (_PyStatus_EXCEPTION(status)) {
     859                  goto done;
     860              }
     861          }
     862  
     863          status = preconfig_read(config, &cmdline);
     864          if (_PyStatus_EXCEPTION(status)) {
     865              goto done;
     866          }
     867  
     868          /* The legacy C locale assumes ASCII as the default text encoding, which
     869           * causes problems not only for the CPython runtime, but also other
     870           * components like GNU readline.
     871           *
     872           * Accordingly, when the CLI detects it, it attempts to coerce it to a
     873           * more capable UTF-8 based alternative.
     874           *
     875           * See the documentation of the PYTHONCOERCECLOCALE setting for more
     876           * details.
     877           */
     878          int encoding_changed = 0;
     879          if (config->coerce_c_locale && !locale_coerced) {
     880              locale_coerced = 1;
     881              _Py_CoerceLegacyLocale(0);
     882              encoding_changed = 1;
     883          }
     884  
     885          if (utf8_mode == -1) {
     886              if (config->utf8_mode == 1) {
     887                  /* UTF-8 Mode enabled */
     888                  encoding_changed = 1;
     889              }
     890          }
     891          else {
     892              if (config->utf8_mode != utf8_mode) {
     893                  encoding_changed = 1;
     894              }
     895          }
     896  
     897          if (!encoding_changed) {
     898              break;
     899          }
     900  
     901          /* Reset the configuration before reading again the configuration,
     902             just keep UTF-8 Mode and coerce C locale value. */
     903          int new_utf8_mode = config->utf8_mode;
     904          int new_coerce_c_locale = config->coerce_c_locale;
     905          preconfig_copy(config, &save_config);
     906          config->utf8_mode = new_utf8_mode;
     907          config->coerce_c_locale = new_coerce_c_locale;
     908  
     909          /* The encoding changed: read again the configuration
     910             with the new encoding */
     911      }
     912      status = _PyStatus_OK();
     913  
     914  done:
     915      // Revert side effects
     916      setlocale(LC_CTYPE, init_ctype_locale);
     917      PyMem_RawFree(init_ctype_locale);
     918      preconfig_copy(&_PyRuntime.preconfig, &save_runtime_config);
     919      _PyPreCmdline_Clear(&cmdline);
     920      return status;
     921  }
     922  
     923  
     924  /* Write the pre-configuration:
     925  
     926     - set the memory allocators
     927     - set Py_xxx global configuration variables
     928     - set the LC_CTYPE locale (coerce C locale, PEP 538) and set the UTF-8 mode
     929       (PEP 540)
     930  
     931     The applied configuration is written into _PyRuntime.preconfig.
     932     If the C locale cannot be coerced, set coerce_c_locale to 0.
     933  
     934     Do nothing if called after Py_Initialize(): ignore the new
     935     pre-configuration. */
     936  PyStatus
     937  _PyPreConfig_Write(const PyPreConfig *src_config)
     938  {
     939      PyPreConfig config;
     940  
     941      PyStatus status = _PyPreConfig_InitFromPreConfig(&config, src_config);
     942      if (_PyStatus_EXCEPTION(status)) {
     943          return status;
     944      }
     945  
     946      if (_PyRuntime.core_initialized) {
     947          /* bpo-34008: Calling this functions after Py_Initialize() ignores
     948             the new configuration. */
     949          return _PyStatus_OK();
     950      }
     951  
     952      PyMemAllocatorName name = (PyMemAllocatorName)config.allocator;
     953      if (name != PYMEM_ALLOCATOR_NOT_SET) {
     954          if (_PyMem_SetupAllocators(name) < 0) {
     955              return _PyStatus_ERR("Unknown PYTHONMALLOC allocator");
     956          }
     957      }
     958  
     959      preconfig_set_global_vars(&config);
     960  
     961      if (config.configure_locale) {
     962          if (config.coerce_c_locale) {
     963              if (!_Py_CoerceLegacyLocale(config.coerce_c_locale_warn)) {
     964                  /* C locale not coerced */
     965                  config.coerce_c_locale = 0;
     966              }
     967          }
     968  
     969          /* Set LC_CTYPE to the user preferred locale */
     970          _Py_SetLocaleFromEnv(LC_CTYPE);
     971      }
     972  
     973      /* Write the new pre-configuration into _PyRuntime */
     974      preconfig_copy(&_PyRuntime.preconfig, &config);
     975  
     976      return _PyStatus_OK();
     977  }