* :c:func:`Py_PreInitializeFromArgs`
* :c:func:`Py_PreInitializeFromBytesArgs`
* :c:func:`Py_RunMain`
+* :c:func:`Py_GetArgcArgv`
The preconfiguration (``PyPreConfig`` type) is stored in
``_PyRuntime.preconfig`` and the configuration (``PyConfig`` type) is stored in
:c:func:`Py_RunMain`.
+Py_GetArgcArgv()
+----------------
+
+.. c:function:: void Py_GetArgcArgv(int *argc, wchar_t ***argv)
+
+ Get the original command line arguments, before Python modified them.
+
+
Multi-Phase Initialization Private Provisional API
--------------------------------------------------
/* If non-zero, disallow threads, subprocesses, and fork.
Default: 0. */
int _isolated_interpreter;
+
+ /* Original command line arguments. If _orig_argv is empty and _argv is
+ not equal to [''], PyConfig_Read() copies the configuration 'argv' list
+ into '_orig_argv' list before modifying 'argv' list (if parse_argv
+ is non-zero).
+
+ _PyConfig_Write() initializes Py_GetArgcArgv() to this list. */
+ PyWideStringList _orig_argv;
} PyConfig;
PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config);
PyWideStringList *list,
Py_ssize_t length, wchar_t **items);
+
+/* --- Helper functions --------------------------------------- */
+
+/* Get the original command line arguments, before Python modified them.
+
+ See also PyConfig._orig_argv. */
+PyAPI_FUNC(void) Py_GetArgcArgv(int *argc, wchar_t ***argv);
+
#endif /* !Py_LIMITED_API */
#endif /* !Py_PYCORECONFIG_H */
PyConfig *config,
const PyConfig *config2);
extern PyStatus _PyConfig_InitPathConfig(PyConfig *config);
-extern void _PyConfig_Write(const PyConfig *config,
+extern PyStatus _PyConfig_Write(const PyConfig *config,
struct pyruntimestate *runtime);
extern PyStatus _PyConfig_SetPyArgv(
PyConfig *config,
'program_name': GET_DEFAULT_CONFIG,
'parse_argv': 0,
'argv': [""],
+ '_orig_argv': [],
'xoptions': [],
'warnoptions': [],
'pycache_prefix': 'conf_pycache_prefix',
'program_name': './conf_program_name',
- 'argv': ['-c', 'arg2', ],
+ 'argv': ['-c', 'arg2'],
+ '_orig_argv': ['python3',
+ '-W', 'cmdline_warnoption',
+ '-X', 'cmdline_xoption',
+ '-c', 'pass',
+ 'arg2'],
'parse_argv': 1,
'xoptions': [
'config_xoption1=3',
}
config = {
'argv': ['script.py'],
+ '_orig_argv': ['python3', '-X', 'dev', 'script.py'],
'run_filename': os.path.abspath('script.py'),
'dev_mode': 1,
'faulthandler': 1,
preconfig = {
'isolated': 0,
}
+ argv = ["python3",
+ "-E", "-I",
+ "-X", "dev",
+ "-X", "utf8",
+ "script.py"]
config = {
- 'argv': ["python3", "-E", "-I",
- "-X", "dev", "-X", "utf8", "script.py"],
+ 'argv': argv,
+ '_orig_argv': argv,
'isolated': 0,
}
self.check_all_configs("test_preinit_dont_parse_argv", config, preconfig,
'ignore:::sysadd_warnoption',
'ignore:::config_warnoption',
],
+ '_orig_argv': ['python3',
+ '-W', 'ignore:::cmdline_warnoption',
+ '-X', 'cmdline_xoption'],
}
self.check_all_configs("test_init_sys_add", config, api=API_PYTHON)
'print(json.dumps(_testinternalcapi.get_configs()))')
config = {
'argv': ['-c', 'arg2'],
+ '_orig_argv': ['python3', '-c', code, 'arg2'],
'program_name': './python3',
'run_command': code + '\n',
'parse_argv': 1,
'print(json.dumps(_testinternalcapi.get_configs()))')
config = {
'argv': ['-c', 'arg2'],
+ '_orig_argv': ['python3',
+ '-c', code,
+ 'arg2'],
'program_name': './python3',
'run_command': code + '\n',
'parse_argv': 1,
config = {
'parse_argv': 1,
'argv': ['-c', 'arg1', '-v', 'arg3'],
+ '_orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
'program_name': './argv0',
'run_command': 'pass\n',
'use_environment': 0,
config = {
'parse_argv': 0,
'argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
+ '_orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
'program_name': './argv0',
}
self.check_all_configs("test_init_dont_parse_argv", config, pre_config,
'faulthandler': 1,
'bytes_warning': 1,
'warnoptions': warnoptions,
+ '_orig_argv': ['python3',
+ '-Wignore:::cmdline1',
+ '-Wignore:::cmdline2'],
}
self.check_all_configs("test_init_warnoptions", config, preconfig,
api=API_PYTHON)
+ def test_get_argc_argv(self):
+ self.run_embedded_interpreter("test_get_argc_argv")
+ # ignore output
+
class AuditingTests(EmbeddingTestsMixin, unittest.TestCase):
def test_open_code_hook(self):
--- /dev/null
+Export explicitly the :c:func:`Py_GetArgcArgv` function to the C API and
+document the function. Previously, it was exported implicitly which no
+longer works since Python is built with ``-fvisibility=hidden``.
Py_FinalizeEx=python310.Py_FinalizeEx
Py_GenericAlias=python310.Py_GenericAlias
Py_GenericAliasType=python310.Py_GenericAliasType
+ Py_GetArgcArgv=python310.Py_GetArgcArgv
Py_GetBuildInfo=python310.Py_GetBuildInfo
Py_GetCompiler=python310.Py_GetCompiler
Py_GetCopyright=python310.Py_GetCopyright
return 0;
fail:
+ PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
}
+static int test_get_argc_argv(void)
+{
+ PyConfig config;
+ PyConfig_InitPythonConfig(&config);
+
+ wchar_t *argv[] = {L"python3", L"-c",
+ (L"import sys; "
+ L"print(f'Py_RunMain(): sys.argv={sys.argv}')"),
+ L"arg2"};
+ config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv);
+ config_set_string(&config, &config.program_name, L"./python3");
+
+ // Calling PyConfig_Read() twice must not change Py_GetArgcArgv() result.
+ // The second call is done by Py_InitializeFromConfig().
+ PyStatus status = PyConfig_Read(&config);
+ if (PyStatus_Exception(status)) {
+ PyConfig_Clear(&config);
+ Py_ExitStatusException(status);
+ }
+
+ init_from_config_clear(&config);
+
+ int get_argc;
+ wchar_t **get_argv;
+ Py_GetArgcArgv(&get_argc, &get_argv);
+ printf("argc: %i\n", get_argc);
+ assert(get_argc == Py_ARRAY_LENGTH(argv));
+ for (int i=0; i < get_argc; i++) {
+ printf("argv[%i]: %ls\n", i, get_argv[i]);
+ assert(wcscmp(get_argv[i], argv[i]) == 0);
+ }
+
+ Py_Finalize();
+
+ printf("\n");
+ printf("test ok\n");
+ return 0;
+}
+
+
/* *********************************************************
* List of test cases and the function that implements it.
*
{"test_init_setpythonhome", test_init_setpythonhome},
{"test_init_warnoptions", test_init_warnoptions},
{"test_run_main", test_run_main},
+ {"test_get_argc_argv", test_get_argc_argv},
{"test_open_code_hook", test_open_code_hook},
{"test_audit", test_audit},
res = pyurandom(secret, secret_size, 0, 0);
if (res < 0) {
return _PyStatus_ERR("failed to get random numbers "
- "to initialize Python");
+ "to initialize Python");
}
}
return _PyStatus_OK();
}
-/* Make the *original* argc/argv available to other modules.
- This is rare, but it is needed by the secureware extension. */
void
Py_GetArgcArgv(int *argc, wchar_t ***argv)
{
COPY_ATTR(pathconfig_warnings);
COPY_ATTR(_init_main);
COPY_ATTR(_isolated_interpreter);
+ COPY_WSTRLIST(_orig_argv);
#undef COPY_ATTR
#undef COPY_WSTR_ATTR
SET_ITEM_INT(pathconfig_warnings);
SET_ITEM_INT(_init_main);
SET_ITEM_INT(_isolated_interpreter);
+ SET_ITEM_WSTRLIST(_orig_argv);
return dict;
- set Py_xxx global configuration variables
- initialize C standard streams (stdin, stdout, stderr) */
-void
+PyStatus
_PyConfig_Write(const PyConfig *config, _PyRuntimeState *runtime)
{
config_set_global_vars(config);
preconfig->isolated = config->isolated;
preconfig->use_environment = config->use_environment;
preconfig->dev_mode = config->dev_mode;
+
+ if (_Py_SetArgcArgv(config->_orig_argv.length,
+ config->_orig_argv.items) < 0)
+ {
+ return _PyStatus_NO_MEMORY();
+ }
+ return _PyStatus_OK();
}
PyConfig_Read(PyConfig *config)
{
PyStatus status;
- PyWideStringList orig_argv = _PyWideStringList_INIT;
status = _Py_PreInitializeFromConfig(config, NULL);
if (_PyStatus_EXCEPTION(status)) {
config_get_global_vars(config);
- if (_PyWideStringList_Copy(&orig_argv, &config->argv) < 0) {
- return _PyStatus_NO_MEMORY();
+ if (config->_orig_argv.length == 0
+ && !(config->argv.length == 1
+ && wcscmp(config->argv.items[0], L"") == 0))
+ {
+ if (_PyWideStringList_Copy(&config->_orig_argv, &config->argv) < 0) {
+ return _PyStatus_NO_MEMORY();
+ }
}
_PyPreCmdline precmdline = _PyPreCmdline_INIT;
goto done;
}
- if (_Py_SetArgcArgv(orig_argv.length, orig_argv.items) < 0) {
- status = _PyStatus_NO_MEMORY();
- goto done;
- }
-
/* Check config consistency */
assert(config->isolated >= 0);
assert(config->use_environment >= 0);
assert(config->check_hash_pycs_mode != NULL);
assert(config->_install_importlib >= 0);
assert(config->pathconfig_warnings >= 0);
+ assert(_PyWideStringList_CheckConsistency(&config->_orig_argv));
status = _PyStatus_OK();
done:
- _PyWideStringList_Clear(&orig_argv);
_PyPreCmdline_Clear(&precmdline);
return status;
}
return _PyStatus_ERR("can't make main interpreter");
}
- _PyConfig_Write(config, runtime);
+ status = _PyConfig_Write(config, runtime);
+ if (_PyStatus_EXCEPTION(status)) {
+ return status;
+ }
status = _PyInterpreterState_SetConfig(interp, config);
if (_PyStatus_EXCEPTION(status)) {
return _PyStatus_ERR("main interpreter already initialized");
}
- _PyConfig_Write(config, runtime);
+ PyStatus status = _PyConfig_Write(config, runtime);
+ if (_PyStatus_EXCEPTION(status)) {
+ return status;
+ }
/* Py_Finalize leaves _Py_Finalizing set in order to help daemon
* threads behave a little more gracefully at interpreter shutdown.
*/
_PyRuntimeState_SetFinalizing(runtime, NULL);
- PyStatus status = _Py_HashRandomization_Init(config);
+ status = _Py_HashRandomization_Init(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
PyThreadState **tstate_p,
const PyConfig *config)
{
- _PyConfig_Write(config, runtime);
-
PyStatus status = pycore_init_runtime(runtime, config);
if (_PyStatus_EXCEPTION(status)) {
return status;