bpo-23427: Add sys.orig_argv attribute (GH-20729)
Add sys.orig_argv attribute: the list of the original command line arguments passed to the Python executable. Rename also PyConfig._orig_argv to PyConfig.orig_argv and document it.
This commit is contained in:
parent
2fb5f038f2
commit
dd8a93e23b
|
@ -424,6 +424,8 @@ PyConfig
|
||||||
:c:member:`~PyConfig.argv` is empty, an empty string is added to ensure
|
:c:member:`~PyConfig.argv` is empty, an empty string is added to ensure
|
||||||
that :data:`sys.argv` always exists and is never empty.
|
that :data:`sys.argv` always exists and is never empty.
|
||||||
|
|
||||||
|
See also the :c:member:`~PyConfig.orig_argv` member.
|
||||||
|
|
||||||
.. c:member:: wchar_t* base_exec_prefix
|
.. c:member:: wchar_t* base_exec_prefix
|
||||||
|
|
||||||
:data:`sys.base_exec_prefix`.
|
:data:`sys.base_exec_prefix`.
|
||||||
|
@ -586,6 +588,23 @@ PyConfig
|
||||||
* 1: Remove assertions, set ``__debug__`` to ``False``
|
* 1: Remove assertions, set ``__debug__`` to ``False``
|
||||||
* 2: Strip docstrings
|
* 2: Strip docstrings
|
||||||
|
|
||||||
|
.. c:member:: PyWideStringList orig_argv
|
||||||
|
|
||||||
|
The list of the original command line arguments passed to the Python
|
||||||
|
executable.
|
||||||
|
|
||||||
|
If :c:member:`~PyConfig.orig_argv` list is empty and
|
||||||
|
:c:member:`~PyConfig.argv` is not a list only containing an empty
|
||||||
|
string, :c:func:`PyConfig_Read()` copies :c:member:`~PyConfig.argv` into
|
||||||
|
:c:member:`~PyConfig.orig_argv` before modifying
|
||||||
|
:c:member:`~PyConfig.argv` (if :c:member:`~PyConfig.parse_argv` is
|
||||||
|
non-zero).
|
||||||
|
|
||||||
|
See also the :c:member:`~PyConfig.argv` member and the
|
||||||
|
:c:func:`Py_GetArgcArgv` function.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
.. c:member:: int parse_argv
|
.. c:member:: int parse_argv
|
||||||
|
|
||||||
If non-zero, parse :c:member:`~PyConfig.argv` the same way the regular
|
If non-zero, parse :c:member:`~PyConfig.argv` the same way the regular
|
||||||
|
@ -982,6 +1001,8 @@ Py_GetArgcArgv()
|
||||||
|
|
||||||
Get the original command line arguments, before Python modified them.
|
Get the original command line arguments, before Python modified them.
|
||||||
|
|
||||||
|
See also :c:member:`PyConfig.orig_argv` member.
|
||||||
|
|
||||||
|
|
||||||
Multi-Phase Initialization Private Provisional API
|
Multi-Phase Initialization Private Provisional API
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
|
@ -66,6 +66,8 @@ always available.
|
||||||
To loop over the standard input, or the list of files given on the
|
To loop over the standard input, or the list of files given on the
|
||||||
command line, see the :mod:`fileinput` module.
|
command line, see the :mod:`fileinput` module.
|
||||||
|
|
||||||
|
See also :data:`sys.orig_argv`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
On Unix, command line arguments are passed by bytes from OS. Python decodes
|
On Unix, command line arguments are passed by bytes from OS. Python decodes
|
||||||
them with filesystem encoding and "surrogateescape" error handler.
|
them with filesystem encoding and "surrogateescape" error handler.
|
||||||
|
@ -1037,6 +1039,16 @@ always available.
|
||||||
deleting essential items from the dictionary may cause Python to fail.
|
deleting essential items from the dictionary may cause Python to fail.
|
||||||
|
|
||||||
|
|
||||||
|
.. data:: orig_argv
|
||||||
|
|
||||||
|
The list of the original command line arguments passed to the Python
|
||||||
|
executable.
|
||||||
|
|
||||||
|
See also :data:`sys.argv`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
|
||||||
.. data:: path
|
.. data:: path
|
||||||
|
|
||||||
.. index:: triple: module; search; path
|
.. index:: triple: module; search; path
|
||||||
|
|
|
@ -110,6 +110,13 @@ Added the *root_dir* and *dir_fd* parameters in :func:`~glob.glob` and
|
||||||
:func:`~glob.iglob` which allow to specify the root directory for searching.
|
:func:`~glob.iglob` which allow to specify the root directory for searching.
|
||||||
(Contributed by Serhiy Storchaka in :issue:`38144`.)
|
(Contributed by Serhiy Storchaka in :issue:`38144`.)
|
||||||
|
|
||||||
|
sys
|
||||||
|
---
|
||||||
|
|
||||||
|
Add :data:`sys.orig_argv` attribute: the list of the original command line
|
||||||
|
arguments passed to the Python executable.
|
||||||
|
(Contributed by Victor Stinner in :issue:`23427`.)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
=============
|
=============
|
||||||
|
@ -150,10 +157,14 @@ C API Changes
|
||||||
New Features
|
New Features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The result of :c:func:`PyNumber_Index` now always has exact type :class:`int`.
|
* The result of :c:func:`PyNumber_Index` now always has exact type :class:`int`.
|
||||||
Previously, the result could have been an instance of a subclass of ``int``.
|
Previously, the result could have been an instance of a subclass of ``int``.
|
||||||
(Contributed by Serhiy Storchaka in :issue:`40792`.)
|
(Contributed by Serhiy Storchaka in :issue:`40792`.)
|
||||||
|
|
||||||
|
* Add a new :c:member:`~PyConfig.orig_argv` member to the :c:type:`PyConfig`
|
||||||
|
structure: the list of the original command line arguments passed to the
|
||||||
|
Python executable.
|
||||||
|
(Contributed by Victor Stinner in :issue:`23427`.)
|
||||||
|
|
||||||
Porting to Python 3.10
|
Porting to Python 3.10
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -408,13 +408,15 @@ typedef struct {
|
||||||
Default: 0. */
|
Default: 0. */
|
||||||
int _isolated_interpreter;
|
int _isolated_interpreter;
|
||||||
|
|
||||||
/* Original command line arguments. If _orig_argv is empty and _argv is
|
/* The list of the original command line arguments passed to the Python
|
||||||
not equal to [''], PyConfig_Read() copies the configuration 'argv' list
|
executable.
|
||||||
into '_orig_argv' list before modifying 'argv' list (if parse_argv
|
|
||||||
is non-zero).
|
If 'orig_argv' list is empty and 'argv' is not a list only containing an
|
||||||
|
empty string, PyConfig_Read() copies 'argv' into 'orig_argv' before
|
||||||
|
modifying 'argv' (if 'parse_argv is non-zero).
|
||||||
|
|
||||||
_PyConfig_Write() initializes Py_GetArgcArgv() to this list. */
|
_PyConfig_Write() initializes Py_GetArgcArgv() to this list. */
|
||||||
PyWideStringList _orig_argv;
|
PyWideStringList orig_argv;
|
||||||
} PyConfig;
|
} PyConfig;
|
||||||
|
|
||||||
PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config);
|
PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config);
|
||||||
|
@ -445,7 +447,7 @@ PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config,
|
||||||
|
|
||||||
/* Get the original command line arguments, before Python modified them.
|
/* Get the original command line arguments, before Python modified them.
|
||||||
|
|
||||||
See also PyConfig._orig_argv. */
|
See also PyConfig.orig_argv. */
|
||||||
PyAPI_FUNC(void) Py_GetArgcArgv(int *argc, wchar_t ***argv);
|
PyAPI_FUNC(void) Py_GetArgcArgv(int *argc, wchar_t ***argv);
|
||||||
|
|
||||||
#endif /* !Py_LIMITED_API */
|
#endif /* !Py_LIMITED_API */
|
||||||
|
|
|
@ -365,7 +365,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'program_name': GET_DEFAULT_CONFIG,
|
'program_name': GET_DEFAULT_CONFIG,
|
||||||
'parse_argv': 0,
|
'parse_argv': 0,
|
||||||
'argv': [""],
|
'argv': [""],
|
||||||
'_orig_argv': [],
|
'orig_argv': [],
|
||||||
|
|
||||||
'xoptions': [],
|
'xoptions': [],
|
||||||
'warnoptions': [],
|
'warnoptions': [],
|
||||||
|
@ -739,7 +739,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'pycache_prefix': 'conf_pycache_prefix',
|
'pycache_prefix': 'conf_pycache_prefix',
|
||||||
'program_name': './conf_program_name',
|
'program_name': './conf_program_name',
|
||||||
'argv': ['-c', 'arg2'],
|
'argv': ['-c', 'arg2'],
|
||||||
'_orig_argv': ['python3',
|
'orig_argv': ['python3',
|
||||||
'-W', 'cmdline_warnoption',
|
'-W', 'cmdline_warnoption',
|
||||||
'-X', 'cmdline_xoption',
|
'-X', 'cmdline_xoption',
|
||||||
'-c', 'pass',
|
'-c', 'pass',
|
||||||
|
@ -874,7 +874,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
}
|
}
|
||||||
config = {
|
config = {
|
||||||
'argv': ['script.py'],
|
'argv': ['script.py'],
|
||||||
'_orig_argv': ['python3', '-X', 'dev', 'script.py'],
|
'orig_argv': ['python3', '-X', 'dev', 'script.py'],
|
||||||
'run_filename': os.path.abspath('script.py'),
|
'run_filename': os.path.abspath('script.py'),
|
||||||
'dev_mode': 1,
|
'dev_mode': 1,
|
||||||
'faulthandler': 1,
|
'faulthandler': 1,
|
||||||
|
@ -896,7 +896,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
"script.py"]
|
"script.py"]
|
||||||
config = {
|
config = {
|
||||||
'argv': argv,
|
'argv': argv,
|
||||||
'_orig_argv': argv,
|
'orig_argv': argv,
|
||||||
'isolated': 0,
|
'isolated': 0,
|
||||||
}
|
}
|
||||||
self.check_all_configs("test_preinit_dont_parse_argv", config, preconfig,
|
self.check_all_configs("test_preinit_dont_parse_argv", config, preconfig,
|
||||||
|
@ -975,7 +975,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'ignore:::sysadd_warnoption',
|
'ignore:::sysadd_warnoption',
|
||||||
'ignore:::config_warnoption',
|
'ignore:::config_warnoption',
|
||||||
],
|
],
|
||||||
'_orig_argv': ['python3',
|
'orig_argv': ['python3',
|
||||||
'-W', 'ignore:::cmdline_warnoption',
|
'-W', 'ignore:::cmdline_warnoption',
|
||||||
'-X', 'cmdline_xoption'],
|
'-X', 'cmdline_xoption'],
|
||||||
}
|
}
|
||||||
|
@ -986,7 +986,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'print(json.dumps(_testinternalcapi.get_configs()))')
|
'print(json.dumps(_testinternalcapi.get_configs()))')
|
||||||
config = {
|
config = {
|
||||||
'argv': ['-c', 'arg2'],
|
'argv': ['-c', 'arg2'],
|
||||||
'_orig_argv': ['python3', '-c', code, 'arg2'],
|
'orig_argv': ['python3', '-c', code, 'arg2'],
|
||||||
'program_name': './python3',
|
'program_name': './python3',
|
||||||
'run_command': code + '\n',
|
'run_command': code + '\n',
|
||||||
'parse_argv': 1,
|
'parse_argv': 1,
|
||||||
|
@ -998,7 +998,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'print(json.dumps(_testinternalcapi.get_configs()))')
|
'print(json.dumps(_testinternalcapi.get_configs()))')
|
||||||
config = {
|
config = {
|
||||||
'argv': ['-c', 'arg2'],
|
'argv': ['-c', 'arg2'],
|
||||||
'_orig_argv': ['python3',
|
'orig_argv': ['python3',
|
||||||
'-c', code,
|
'-c', code,
|
||||||
'arg2'],
|
'arg2'],
|
||||||
'program_name': './python3',
|
'program_name': './python3',
|
||||||
|
@ -1014,7 +1014,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
config = {
|
config = {
|
||||||
'parse_argv': 1,
|
'parse_argv': 1,
|
||||||
'argv': ['-c', 'arg1', '-v', 'arg3'],
|
'argv': ['-c', 'arg1', '-v', 'arg3'],
|
||||||
'_orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
|
'orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
|
||||||
'program_name': './argv0',
|
'program_name': './argv0',
|
||||||
'run_command': 'pass\n',
|
'run_command': 'pass\n',
|
||||||
'use_environment': 0,
|
'use_environment': 0,
|
||||||
|
@ -1028,7 +1028,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
config = {
|
config = {
|
||||||
'parse_argv': 0,
|
'parse_argv': 0,
|
||||||
'argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
|
'argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
|
||||||
'_orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
|
'orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
|
||||||
'program_name': './argv0',
|
'program_name': './argv0',
|
||||||
}
|
}
|
||||||
self.check_all_configs("test_init_dont_parse_argv", config, pre_config,
|
self.check_all_configs("test_init_dont_parse_argv", config, pre_config,
|
||||||
|
@ -1316,7 +1316,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'faulthandler': 1,
|
'faulthandler': 1,
|
||||||
'bytes_warning': 1,
|
'bytes_warning': 1,
|
||||||
'warnoptions': warnoptions,
|
'warnoptions': warnoptions,
|
||||||
'_orig_argv': ['python3',
|
'orig_argv': ['python3',
|
||||||
'-Wignore:::cmdline1',
|
'-Wignore:::cmdline1',
|
||||||
'-Wignore:::cmdline2'],
|
'-Wignore:::cmdline2'],
|
||||||
}
|
}
|
||||||
|
|
|
@ -434,6 +434,11 @@ class SysModuleTest(unittest.TestCase):
|
||||||
def test_attributes(self):
|
def test_attributes(self):
|
||||||
self.assertIsInstance(sys.api_version, int)
|
self.assertIsInstance(sys.api_version, int)
|
||||||
self.assertIsInstance(sys.argv, list)
|
self.assertIsInstance(sys.argv, list)
|
||||||
|
for arg in sys.argv:
|
||||||
|
self.assertIsInstance(arg, str)
|
||||||
|
self.assertIsInstance(sys.orig_argv, list)
|
||||||
|
for arg in sys.orig_argv:
|
||||||
|
self.assertIsInstance(arg, str)
|
||||||
self.assertIn(sys.byteorder, ("little", "big"))
|
self.assertIn(sys.byteorder, ("little", "big"))
|
||||||
self.assertIsInstance(sys.builtin_module_names, tuple)
|
self.assertIsInstance(sys.builtin_module_names, tuple)
|
||||||
self.assertIsInstance(sys.copyright, str)
|
self.assertIsInstance(sys.copyright, str)
|
||||||
|
@ -930,6 +935,21 @@ class SysModuleTest(unittest.TestCase):
|
||||||
out = out.decode('ascii', 'replace').rstrip()
|
out = out.decode('ascii', 'replace').rstrip()
|
||||||
self.assertEqual(out, 'mbcs replace')
|
self.assertEqual(out, 'mbcs replace')
|
||||||
|
|
||||||
|
def test_orig_argv(self):
|
||||||
|
code = textwrap.dedent('''
|
||||||
|
import sys
|
||||||
|
print(sys.argv)
|
||||||
|
print(sys.orig_argv)
|
||||||
|
''')
|
||||||
|
args = [sys.executable, '-I', '-X', 'utf8', '-c', code, 'arg']
|
||||||
|
proc = subprocess.run(args, check=True, capture_output=True, text=True)
|
||||||
|
expected = [
|
||||||
|
repr(['-c', 'arg']), # sys.argv
|
||||||
|
repr(args), # sys.orig_argv
|
||||||
|
]
|
||||||
|
self.assertEqual(proc.stdout.rstrip().splitlines(), expected,
|
||||||
|
proc)
|
||||||
|
|
||||||
|
|
||||||
@test.support.cpython_only
|
@test.support.cpython_only
|
||||||
class UnraisableHookTest(unittest.TestCase):
|
class UnraisableHookTest(unittest.TestCase):
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add :data:`sys.orig_argv` attribute: the list of the original command line
|
||||||
|
arguments passed to the Python executable.
|
|
@ -601,7 +601,7 @@ PyConfig_Clear(PyConfig *config)
|
||||||
CLEAR(config->run_filename);
|
CLEAR(config->run_filename);
|
||||||
CLEAR(config->check_hash_pycs_mode);
|
CLEAR(config->check_hash_pycs_mode);
|
||||||
|
|
||||||
_PyWideStringList_Clear(&config->_orig_argv);
|
_PyWideStringList_Clear(&config->orig_argv);
|
||||||
#undef CLEAR
|
#undef CLEAR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,7 +856,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
|
||||||
COPY_ATTR(pathconfig_warnings);
|
COPY_ATTR(pathconfig_warnings);
|
||||||
COPY_ATTR(_init_main);
|
COPY_ATTR(_init_main);
|
||||||
COPY_ATTR(_isolated_interpreter);
|
COPY_ATTR(_isolated_interpreter);
|
||||||
COPY_WSTRLIST(_orig_argv);
|
COPY_WSTRLIST(orig_argv);
|
||||||
|
|
||||||
#undef COPY_ATTR
|
#undef COPY_ATTR
|
||||||
#undef COPY_WSTR_ATTR
|
#undef COPY_WSTR_ATTR
|
||||||
|
@ -957,7 +957,7 @@ config_as_dict(const PyConfig *config)
|
||||||
SET_ITEM_INT(pathconfig_warnings);
|
SET_ITEM_INT(pathconfig_warnings);
|
||||||
SET_ITEM_INT(_init_main);
|
SET_ITEM_INT(_init_main);
|
||||||
SET_ITEM_INT(_isolated_interpreter);
|
SET_ITEM_INT(_isolated_interpreter);
|
||||||
SET_ITEM_WSTRLIST(_orig_argv);
|
SET_ITEM_WSTRLIST(orig_argv);
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
|
|
||||||
|
@ -1864,8 +1864,8 @@ _PyConfig_Write(const PyConfig *config, _PyRuntimeState *runtime)
|
||||||
preconfig->use_environment = config->use_environment;
|
preconfig->use_environment = config->use_environment;
|
||||||
preconfig->dev_mode = config->dev_mode;
|
preconfig->dev_mode = config->dev_mode;
|
||||||
|
|
||||||
if (_Py_SetArgcArgv(config->_orig_argv.length,
|
if (_Py_SetArgcArgv(config->orig_argv.length,
|
||||||
config->_orig_argv.items) < 0)
|
config->orig_argv.items) < 0)
|
||||||
{
|
{
|
||||||
return _PyStatus_NO_MEMORY();
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
}
|
||||||
|
@ -2501,11 +2501,11 @@ PyConfig_Read(PyConfig *config)
|
||||||
|
|
||||||
config_get_global_vars(config);
|
config_get_global_vars(config);
|
||||||
|
|
||||||
if (config->_orig_argv.length == 0
|
if (config->orig_argv.length == 0
|
||||||
&& !(config->argv.length == 1
|
&& !(config->argv.length == 1
|
||||||
&& wcscmp(config->argv.items[0], L"") == 0))
|
&& wcscmp(config->argv.items[0], L"") == 0))
|
||||||
{
|
{
|
||||||
if (_PyWideStringList_Copy(&config->_orig_argv, &config->argv) < 0) {
|
if (_PyWideStringList_Copy(&config->orig_argv, &config->argv) < 0) {
|
||||||
return _PyStatus_NO_MEMORY();
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2589,7 +2589,7 @@ PyConfig_Read(PyConfig *config)
|
||||||
assert(config->check_hash_pycs_mode != NULL);
|
assert(config->check_hash_pycs_mode != NULL);
|
||||||
assert(config->_install_importlib >= 0);
|
assert(config->_install_importlib >= 0);
|
||||||
assert(config->pathconfig_warnings >= 0);
|
assert(config->pathconfig_warnings >= 0);
|
||||||
assert(_PyWideStringList_CheckConsistency(&config->_orig_argv));
|
assert(_PyWideStringList_CheckConsistency(&config->orig_argv));
|
||||||
|
|
||||||
status = _PyStatus_OK();
|
status = _PyStatus_OK();
|
||||||
|
|
||||||
|
|
|
@ -2931,6 +2931,7 @@ _PySys_InitMain(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
COPY_LIST("argv", config->argv);
|
COPY_LIST("argv", config->argv);
|
||||||
|
COPY_LIST("orig_argv", config->orig_argv);
|
||||||
COPY_LIST("warnoptions", config->warnoptions);
|
COPY_LIST("warnoptions", config->warnoptions);
|
||||||
|
|
||||||
PyObject *xoptions = sys_create_xoptions_dict(config);
|
PyObject *xoptions = sys_create_xoptions_dict(config);
|
||||||
|
|
Loading…
Reference in New Issue