mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
bpo-36763: Fix Python preinitialization (GH-13432)
* Add _PyPreConfig.parse_argv * Add _PyCoreConfig._config_init field and _PyCoreConfigInitEnum enum type * Initialization functions: reject preconfig=NULL and config=NULL * Add config parameter to _PyCoreConfig_DecodeLocaleErr(): pass config->argv to _Py_PreInitializeFromPyArgv(), to parse config command line arguments in preinitialization. * Add config parameter to _PyCoreConfig_SetString(). It now preinitializes Python. * _PyCoreConfig_SetPyArgv() now also preinitializes Python for wide argv * Fix _Py_PreInitializeFromCoreConfig(): don't pass args to _Py_PreInitializeFromPyArgv() if config.parse_argv=0. * Use "char * const *" and "wchar_t * const *" types for 'argv' parameters and _PyArgv.argv. * Add unit test on preinitialization from argv. * _PyPreConfig.allocator type becomes int * Add _PyPreConfig_InitFromPreConfig() and _PyPreConfig_InitFromCoreConfig() helper functions
This commit is contained in:
parent
6d965b39b7
commit
6d1c46746e
8 changed files with 477 additions and 179 deletions
|
@ -551,6 +551,7 @@ _PyCoreConfig_Init(_PyCoreConfig *config)
|
|||
memset(config, 0, sizeof(*config));
|
||||
|
||||
config->_config_version = _Py_CONFIG_VERSION;
|
||||
config->_config_init = (int)_PyCoreConfig_INIT;
|
||||
config->isolated = -1;
|
||||
config->use_environment = -1;
|
||||
config->dev_mode = -1;
|
||||
|
@ -612,6 +613,7 @@ _PyCoreConfig_InitPythonConfig(_PyCoreConfig *config)
|
|||
{
|
||||
_PyCoreConfig_InitDefaults(config);
|
||||
|
||||
config->_config_init = (int)_PyCoreConfig_INIT_PYTHON;
|
||||
config->configure_c_stdio = 1;
|
||||
config->parse_argv = 1;
|
||||
|
||||
|
@ -624,6 +626,7 @@ _PyCoreConfig_InitIsolatedConfig(_PyCoreConfig *config)
|
|||
{
|
||||
_PyCoreConfig_InitDefaults(config);
|
||||
|
||||
config->_config_init = (int)_PyCoreConfig_INIT_ISOLATED;
|
||||
config->isolated = 1;
|
||||
config->use_environment = 0;
|
||||
config->user_site_directory = 0;
|
||||
|
@ -643,8 +646,14 @@ _PyCoreConfig_InitIsolatedConfig(_PyCoreConfig *config)
|
|||
|
||||
/* Copy str into *config_str (duplicate the string) */
|
||||
_PyInitError
|
||||
_PyCoreConfig_SetString(wchar_t **config_str, const wchar_t *str)
|
||||
_PyCoreConfig_SetString(_PyCoreConfig *config, wchar_t **config_str,
|
||||
const wchar_t *str)
|
||||
{
|
||||
_PyInitError err = _Py_PreInitializeFromCoreConfig(config, NULL);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
wchar_t *str2;
|
||||
if (str != NULL) {
|
||||
str2 = _PyMem_RawWcsdup(str);
|
||||
|
@ -662,10 +671,10 @@ _PyCoreConfig_SetString(wchar_t **config_str, const wchar_t *str)
|
|||
|
||||
|
||||
static _PyInitError
|
||||
_PyCoreConfig_DecodeLocaleErr(wchar_t **config_str, const char *str,
|
||||
const char *decode_err_msg)
|
||||
_PyCoreConfig_DecodeLocaleErr(_PyCoreConfig *config, wchar_t **config_str,
|
||||
const char *str, const char *decode_err_msg)
|
||||
{
|
||||
_PyInitError err = _Py_PreInitialize(NULL);
|
||||
_PyInitError err = _Py_PreInitializeFromCoreConfig(config, NULL);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
@ -692,17 +701,18 @@ _PyCoreConfig_DecodeLocaleErr(wchar_t **config_str, const char *str,
|
|||
}
|
||||
|
||||
|
||||
#define CONFIG_DECODE_LOCALE(config_str, str, NAME) \
|
||||
_PyCoreConfig_DecodeLocaleErr(config_str, str, "cannot decode " NAME)
|
||||
#define CONFIG_DECODE_LOCALE(config, config_str, str, NAME) \
|
||||
_PyCoreConfig_DecodeLocaleErr(config, config_str, str, "cannot decode " NAME)
|
||||
|
||||
|
||||
/* Decode str using Py_DecodeLocale() and set the result into *config_str.
|
||||
Pre-initialize Python if needed to ensure that encodings are properly
|
||||
configured. */
|
||||
_PyInitError
|
||||
_PyCoreConfig_DecodeLocale(wchar_t **config_str, const char *str)
|
||||
_PyCoreConfig_DecodeLocale(_PyCoreConfig *config, wchar_t **config_str,
|
||||
const char *str)
|
||||
{
|
||||
return CONFIG_DECODE_LOCALE(config_str, str, "string");
|
||||
return CONFIG_DECODE_LOCALE(config, config_str, str, "string");
|
||||
}
|
||||
|
||||
|
||||
|
@ -715,7 +725,7 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
|
|||
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
|
||||
#define COPY_WSTR_ATTR(ATTR) \
|
||||
do { \
|
||||
err = _PyCoreConfig_SetString(&config->ATTR, config2->ATTR); \
|
||||
err = _PyCoreConfig_SetString(config, &config->ATTR, config2->ATTR); \
|
||||
if (_Py_INIT_FAILED(err)) { \
|
||||
return err; \
|
||||
} \
|
||||
|
@ -727,6 +737,7 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
COPY_ATTR(_config_init);
|
||||
COPY_ATTR(isolated);
|
||||
COPY_ATTR(use_environment);
|
||||
COPY_ATTR(dev_mode);
|
||||
|
@ -829,6 +840,7 @@ _PyCoreConfig_AsDict(const _PyCoreConfig *config)
|
|||
#define SET_ITEM_WSTRLIST(LIST) \
|
||||
SET_ITEM(#LIST, _PyWstrList_AsList(&config->LIST))
|
||||
|
||||
SET_ITEM_INT(_config_init);
|
||||
SET_ITEM_INT(isolated);
|
||||
SET_ITEM_INT(use_environment);
|
||||
SET_ITEM_INT(dev_mode);
|
||||
|
@ -910,7 +922,7 @@ _PyCoreConfig_GetEnv(const _PyCoreConfig *config, const char *name)
|
|||
Return 0 on success, but *dest can be NULL.
|
||||
Return -1 on memory allocation failure. Return -2 on decoding error. */
|
||||
static _PyInitError
|
||||
_PyCoreConfig_GetEnvDup(const _PyCoreConfig *config,
|
||||
_PyCoreConfig_GetEnvDup(_PyCoreConfig *config,
|
||||
wchar_t **dest,
|
||||
wchar_t *wname, char *name,
|
||||
const char *decode_err_msg)
|
||||
|
@ -930,7 +942,7 @@ _PyCoreConfig_GetEnvDup(const _PyCoreConfig *config,
|
|||
return _Py_INIT_OK();
|
||||
}
|
||||
|
||||
return _PyCoreConfig_SetString(dest, var);
|
||||
return _PyCoreConfig_SetString(config, dest, var);
|
||||
#else
|
||||
const char *var = getenv(name);
|
||||
if (!var || var[0] == '\0') {
|
||||
|
@ -938,7 +950,7 @@ _PyCoreConfig_GetEnvDup(const _PyCoreConfig *config,
|
|||
return _Py_INIT_OK();
|
||||
}
|
||||
|
||||
return _PyCoreConfig_DecodeLocaleErr(dest, var, decode_err_msg);
|
||||
return _PyCoreConfig_DecodeLocaleErr(config, dest, var, decode_err_msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1053,7 +1065,7 @@ config_init_program_name(_PyCoreConfig *config)
|
|||
script. */
|
||||
const char *p = _PyCoreConfig_GetEnv(config, "PYTHONEXECUTABLE");
|
||||
if (p != NULL) {
|
||||
err = CONFIG_DECODE_LOCALE(&config->program_name, p,
|
||||
err = CONFIG_DECODE_LOCALE(config, &config->program_name, p,
|
||||
"PYTHONEXECUTABLE environment variable");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
|
@ -1067,7 +1079,8 @@ config_init_program_name(_PyCoreConfig *config)
|
|||
/* Used by Mac/Tools/pythonw.c to forward
|
||||
* the argv0 of the stub executable
|
||||
*/
|
||||
err = CONFIG_DECODE_LOCALE(&config->program_name, pyvenv_launcher,
|
||||
err = CONFIG_DECODE_LOCALE(config,
|
||||
&config->program_name, pyvenv_launcher,
|
||||
"__PYVENV_LAUNCHER__ environment variable");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
|
@ -1094,7 +1107,8 @@ config_init_program_name(_PyCoreConfig *config)
|
|||
#else
|
||||
const wchar_t *default_program_name = L"python3";
|
||||
#endif
|
||||
err = _PyCoreConfig_SetString(&config->program_name, default_program_name);
|
||||
err = _PyCoreConfig_SetString(config, &config->program_name,
|
||||
default_program_name);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
@ -1109,7 +1123,8 @@ config_init_executable(_PyCoreConfig *config)
|
|||
/* If Py_SetProgramFullPath() was called, use its value */
|
||||
const wchar_t *program_full_path = _Py_path_config.program_full_path;
|
||||
if (program_full_path != NULL) {
|
||||
_PyInitError err = _PyCoreConfig_SetString(&config->executable,
|
||||
_PyInitError err = _PyCoreConfig_SetString(config,
|
||||
&config->executable,
|
||||
program_full_path);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
|
@ -1135,7 +1150,7 @@ config_init_home(_PyCoreConfig *config)
|
|||
/* If Py_SetPythonHome() was called, use its value */
|
||||
wchar_t *home = _Py_path_config.home;
|
||||
if (home) {
|
||||
_PyInitError err = _PyCoreConfig_SetString(&config->home, home);
|
||||
_PyInitError err = _PyCoreConfig_SetString(config, &config->home, home);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
@ -1392,14 +1407,14 @@ config_get_stdio_errors(const _PyCoreConfig *config)
|
|||
|
||||
|
||||
static _PyInitError
|
||||
config_get_locale_encoding(wchar_t **locale_encoding)
|
||||
config_get_locale_encoding(_PyCoreConfig *config, wchar_t **locale_encoding)
|
||||
{
|
||||
#ifdef MS_WINDOWS
|
||||
char encoding[20];
|
||||
PyOS_snprintf(encoding, sizeof(encoding), "cp%u", GetACP());
|
||||
return _PyCoreConfig_DecodeLocale(locale_encoding, encoding);
|
||||
return _PyCoreConfig_DecodeLocale(config, locale_encoding, encoding);
|
||||
#elif defined(_Py_FORCE_UTF8_LOCALE)
|
||||
return _PyCoreConfig_SetString(locale_encoding, L"utf-8");
|
||||
return _PyCoreConfig_SetString(config, locale_encoding, L"utf-8");
|
||||
#else
|
||||
const char *encoding = nl_langinfo(CODESET);
|
||||
if (!encoding || encoding[0] == '\0') {
|
||||
|
@ -1407,7 +1422,8 @@ config_get_locale_encoding(wchar_t **locale_encoding)
|
|||
"nl_langinfo(CODESET) failed");
|
||||
}
|
||||
/* nl_langinfo(CODESET) is decoded by Py_DecodeLocale() */
|
||||
return CONFIG_DECODE_LOCALE(locale_encoding, encoding,
|
||||
return CONFIG_DECODE_LOCALE(config,
|
||||
locale_encoding, encoding,
|
||||
"nl_langinfo(CODESET)");
|
||||
#endif
|
||||
}
|
||||
|
@ -1422,7 +1438,7 @@ config_init_stdio_encoding(_PyCoreConfig *config,
|
|||
/* If Py_SetStandardStreamEncoding() have been called, use these
|
||||
parameters. */
|
||||
if (config->stdio_encoding == NULL && _Py_StandardStreamEncoding != NULL) {
|
||||
err = CONFIG_DECODE_LOCALE(&config->stdio_encoding,
|
||||
err = CONFIG_DECODE_LOCALE(config, &config->stdio_encoding,
|
||||
_Py_StandardStreamEncoding,
|
||||
"_Py_StandardStreamEncoding");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
|
@ -1431,7 +1447,7 @@ config_init_stdio_encoding(_PyCoreConfig *config,
|
|||
}
|
||||
|
||||
if (config->stdio_errors == NULL && _Py_StandardStreamErrors != NULL) {
|
||||
err = CONFIG_DECODE_LOCALE(&config->stdio_errors,
|
||||
err = CONFIG_DECODE_LOCALE(config, &config->stdio_errors,
|
||||
_Py_StandardStreamErrors,
|
||||
"_Py_StandardStreamErrors");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
|
@ -1463,7 +1479,7 @@ config_init_stdio_encoding(_PyCoreConfig *config,
|
|||
/* Does PYTHONIOENCODING contain an encoding? */
|
||||
if (pythonioencoding[0]) {
|
||||
if (config->stdio_encoding == NULL) {
|
||||
err = CONFIG_DECODE_LOCALE(&config->stdio_encoding,
|
||||
err = CONFIG_DECODE_LOCALE(config, &config->stdio_encoding,
|
||||
pythonioencoding,
|
||||
"PYTHONIOENCODING environment variable");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
|
@ -1482,7 +1498,7 @@ config_init_stdio_encoding(_PyCoreConfig *config,
|
|||
}
|
||||
|
||||
if (config->stdio_errors == NULL && errors != NULL) {
|
||||
err = CONFIG_DECODE_LOCALE(&config->stdio_errors,
|
||||
err = CONFIG_DECODE_LOCALE(config, &config->stdio_errors,
|
||||
errors,
|
||||
"PYTHONIOENCODING environment variable");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
|
@ -1497,13 +1513,13 @@ config_init_stdio_encoding(_PyCoreConfig *config,
|
|||
/* UTF-8 Mode uses UTF-8/surrogateescape */
|
||||
if (preconfig->utf8_mode) {
|
||||
if (config->stdio_encoding == NULL) {
|
||||
err = _PyCoreConfig_SetString(&config->stdio_encoding, L"utf-8");
|
||||
err = _PyCoreConfig_SetString(config, &config->stdio_encoding, L"utf-8");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (config->stdio_errors == NULL) {
|
||||
err = _PyCoreConfig_SetString(&config->stdio_errors,
|
||||
err = _PyCoreConfig_SetString(config, &config->stdio_errors,
|
||||
L"surrogateescape");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
|
@ -1513,7 +1529,7 @@ config_init_stdio_encoding(_PyCoreConfig *config,
|
|||
|
||||
/* Choose the default error handler based on the current locale. */
|
||||
if (config->stdio_encoding == NULL) {
|
||||
err = config_get_locale_encoding(&config->stdio_encoding);
|
||||
err = config_get_locale_encoding(config, &config->stdio_encoding);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
@ -1522,7 +1538,7 @@ config_init_stdio_encoding(_PyCoreConfig *config,
|
|||
const wchar_t *errors = config_get_stdio_errors(config);
|
||||
assert(errors != NULL);
|
||||
|
||||
err = _PyCoreConfig_SetString(&config->stdio_errors, errors);
|
||||
err = _PyCoreConfig_SetString(config, &config->stdio_errors, errors);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
@ -1539,34 +1555,35 @@ config_init_fs_encoding(_PyCoreConfig *config, const _PyPreConfig *preconfig)
|
|||
|
||||
if (config->filesystem_encoding == NULL) {
|
||||
#ifdef _Py_FORCE_UTF8_FS_ENCODING
|
||||
err = _PyCoreConfig_SetString(&config->filesystem_encoding, L"utf-8");
|
||||
err = _PyCoreConfig_SetString(config, &config->filesystem_encoding, L"utf-8");
|
||||
#else
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
if (preconfig->legacy_windows_fs_encoding) {
|
||||
/* Legacy Windows filesystem encoding: mbcs/replace */
|
||||
err = _PyCoreConfig_SetString(&config->filesystem_encoding,
|
||||
err = _PyCoreConfig_SetString(config, &config->filesystem_encoding,
|
||||
L"mbcs");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (preconfig->utf8_mode) {
|
||||
err = _PyCoreConfig_SetString(&config->filesystem_encoding,
|
||||
err = _PyCoreConfig_SetString(config, &config->filesystem_encoding,
|
||||
L"utf-8");
|
||||
}
|
||||
#ifndef MS_WINDOWS
|
||||
else if (_Py_GetForceASCII()) {
|
||||
err = _PyCoreConfig_SetString(&config->filesystem_encoding,
|
||||
err = _PyCoreConfig_SetString(config, &config->filesystem_encoding,
|
||||
L"ascii");
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
#ifdef MS_WINDOWS
|
||||
/* Windows defaults to utf-8/surrogatepass (PEP 529). */
|
||||
err = _PyCoreConfig_SetString(&config->filesystem_encoding,
|
||||
err = _PyCoreConfig_SetString(config, &config->filesystem_encoding,
|
||||
L"utf-8");
|
||||
#else
|
||||
err = config_get_locale_encoding(&config->filesystem_encoding);
|
||||
err = config_get_locale_encoding(config,
|
||||
&config->filesystem_encoding);
|
||||
#endif
|
||||
}
|
||||
#endif /* !_Py_FORCE_UTF8_FS_ENCODING */
|
||||
|
@ -1588,7 +1605,7 @@ config_init_fs_encoding(_PyCoreConfig *config, const _PyPreConfig *preconfig)
|
|||
#else
|
||||
errors = L"surrogateescape";
|
||||
#endif
|
||||
err = _PyCoreConfig_SetString(&config->filesystem_errors, errors);
|
||||
err = _PyCoreConfig_SetString(config, &config->filesystem_errors, errors);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
@ -1681,7 +1698,7 @@ config_read(_PyCoreConfig *config)
|
|||
}
|
||||
|
||||
if (config->check_hash_pycs_mode == NULL) {
|
||||
err = _PyCoreConfig_SetString(&config->check_hash_pycs_mode,
|
||||
err = _PyCoreConfig_SetString(config, &config->check_hash_pycs_mode,
|
||||
L"default");
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
|
@ -1832,7 +1849,7 @@ config_parse_cmdline(_PyCoreConfig *config, _PyWstrList *warnoptions,
|
|||
|| wcscmp(_PyOS_optarg, L"never") == 0
|
||||
|| wcscmp(_PyOS_optarg, L"default") == 0)
|
||||
{
|
||||
err = _PyCoreConfig_SetString(&config->check_hash_pycs_mode,
|
||||
err = _PyCoreConfig_SetString(config, &config->check_hash_pycs_mode,
|
||||
_PyOS_optarg);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
|
@ -1966,7 +1983,7 @@ config_parse_cmdline(_PyCoreConfig *config, _PyWstrList *warnoptions,
|
|||
|
||||
/* Get warning options from PYTHONWARNINGS environment variable. */
|
||||
static _PyInitError
|
||||
config_init_env_warnoptions(const _PyCoreConfig *config, _PyWstrList *warnoptions)
|
||||
config_init_env_warnoptions(_PyCoreConfig *config, _PyWstrList *warnoptions)
|
||||
{
|
||||
_PyInitError err;
|
||||
/* CONFIG_GET_ENV_DUP requires dest to be initialized to NULL */
|
||||
|
@ -2136,8 +2153,7 @@ core_read_precmdline(_PyCoreConfig *config, _PyPreCmdline *precmdline)
|
|||
}
|
||||
|
||||
_PyPreConfig preconfig;
|
||||
_PyPreConfig_Init(&preconfig);
|
||||
_PyPreConfig_Copy(&preconfig, &_PyRuntime.preconfig);
|
||||
_PyPreConfig_InitFromPreConfig(&preconfig, &_PyRuntime.preconfig);
|
||||
|
||||
_PyPreConfig_GetCoreConfig(&preconfig, config);
|
||||
|
||||
|
@ -2211,24 +2227,11 @@ done:
|
|||
_PyInitError
|
||||
_PyCoreConfig_SetPyArgv(_PyCoreConfig *config, const _PyArgv *args)
|
||||
{
|
||||
if (args->use_bytes_argv) {
|
||||
_PyInitError err;
|
||||
|
||||
err = _PyRuntime_Initialize();
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
_PyRuntimeState *runtime = &_PyRuntime;
|
||||
|
||||
/* do nothing if Python is already pre-initialized:
|
||||
_PyCoreConfig_Write() will update _PyRuntime.preconfig later */
|
||||
if (!runtime->pre_initialized) {
|
||||
err = _Py_PreInitializeFromCoreConfig(config, args);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
_PyInitError err = _Py_PreInitializeFromCoreConfig(config, args);
|
||||
if (_Py_INIT_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return _PyArgv_AsWstrList(args, &config->argv);
|
||||
}
|
||||
|
||||
|
@ -2236,7 +2239,7 @@ _PyCoreConfig_SetPyArgv(_PyCoreConfig *config, const _PyArgv *args)
|
|||
/* Set config.argv: decode argv using Py_DecodeLocale(). Pre-initialize Python
|
||||
if needed to ensure that encodings are properly configured. */
|
||||
_PyInitError
|
||||
_PyCoreConfig_SetArgv(_PyCoreConfig *config, Py_ssize_t argc, char **argv)
|
||||
_PyCoreConfig_SetArgv(_PyCoreConfig *config, Py_ssize_t argc, char * const *argv)
|
||||
{
|
||||
_PyArgv args = {
|
||||
.argc = argc,
|
||||
|
@ -2248,7 +2251,7 @@ _PyCoreConfig_SetArgv(_PyCoreConfig *config, Py_ssize_t argc, char **argv)
|
|||
|
||||
|
||||
_PyInitError
|
||||
_PyCoreConfig_SetWideArgv(_PyCoreConfig *config, Py_ssize_t argc, wchar_t **argv)
|
||||
_PyCoreConfig_SetWideArgv(_PyCoreConfig *config, Py_ssize_t argc, wchar_t * const *argv)
|
||||
{
|
||||
_PyArgv args = {
|
||||
.argc = argc,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue