Skip to content

Commit dc42af8

Browse files
authored
bpo-42260: PyConfig_Read() only parses argv once (GH-23168)
The PyConfig_Read() function now only parses PyConfig.argv arguments once: PyConfig.parse_argv is set to 2 after arguments are parsed. Since Python arguments are strippped from PyConfig.argv, parsing arguments twice would parse the application options as Python options. * Rework the PyConfig documentation. * Fix _testinternalcapi.set_config() error handling. * SetConfigTests no longer needs parse_argv=0 when restoring the old configuration.
1 parent f3cb814 commit dc42af8

File tree

6 files changed

+134
-105
lines changed

6 files changed

+134
-105
lines changed

Doc/c-api/init_config.rst

+104-92
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,68 @@ Python Initialization Configuration
88

99
.. versionadded:: 3.8
1010

11-
Structures:
12-
13-
* :c:type:`PyConfig`
14-
* :c:type:`PyPreConfig`
15-
* :c:type:`PyStatus`
16-
* :c:type:`PyWideStringList`
17-
18-
Functions:
19-
20-
* :c:func:`PyConfig_Clear`
21-
* :c:func:`PyConfig_InitIsolatedConfig`
22-
* :c:func:`PyConfig_InitPythonConfig`
23-
* :c:func:`PyConfig_Read`
24-
* :c:func:`PyConfig_SetArgv`
25-
* :c:func:`PyConfig_SetBytesArgv`
26-
* :c:func:`PyConfig_SetBytesString`
27-
* :c:func:`PyConfig_SetString`
28-
* :c:func:`PyConfig_SetWideStringList`
29-
* :c:func:`PyPreConfig_InitIsolatedConfig`
30-
* :c:func:`PyPreConfig_InitPythonConfig`
31-
* :c:func:`PyStatus_Error`
32-
* :c:func:`PyStatus_Exception`
33-
* :c:func:`PyStatus_Exit`
34-
* :c:func:`PyStatus_IsError`
35-
* :c:func:`PyStatus_IsExit`
36-
* :c:func:`PyStatus_NoMemory`
37-
* :c:func:`PyStatus_Ok`
38-
* :c:func:`PyWideStringList_Append`
39-
* :c:func:`PyWideStringList_Insert`
40-
* :c:func:`Py_ExitStatusException`
41-
* :c:func:`Py_InitializeFromConfig`
42-
* :c:func:`Py_PreInitialize`
43-
* :c:func:`Py_PreInitializeFromArgs`
44-
* :c:func:`Py_PreInitializeFromBytesArgs`
45-
* :c:func:`Py_RunMain`
46-
* :c:func:`Py_GetArgcArgv`
47-
48-
The preconfiguration (``PyPreConfig`` type) is stored in
49-
``_PyRuntime.preconfig`` and the configuration (``PyConfig`` type) is stored in
50-
``PyInterpreterState.config``.
11+
Python can be initialized with :c:func:`Py_InitializeFromConfig` and the
12+
:c:type:`PyConfig` structure. It can be preinitialized with
13+
:c:func:`Py_PreInitialize` and the :c:type:`PyPreConfig` structure.
14+
15+
There are two kinds of configuration:
16+
17+
* The :ref:`Python Configuration <init-python-config>` can be used to build a
18+
customized Python which behaves as the regular Python. For example,
19+
environments variables and command line arguments are used to configure
20+
Python.
21+
22+
* The :ref:`Isolated Configuration <init-isolated-conf>` can be used to embed
23+
Python into an application. It isolates Python from the system. For example,
24+
environments variables are ignored, the LC_CTYPE locale is left unchanged and
25+
no signal handler is registred.
5126

5227
See also :ref:`Initialization, Finalization, and Threads <initialization>`.
5328

5429
.. seealso::
5530
:pep:`587` "Python Initialization Configuration".
5631

32+
Example
33+
=======
34+
35+
Example of customized Python always running in isolated mode::
36+
37+
int main(int argc, char **argv)
38+
{
39+
PyStatus status;
40+
41+
PyConfig config;
42+
PyConfig_InitPythonConfig(&config);
43+
config.isolated = 1;
44+
45+
/* Decode command line arguments.
46+
Implicitly preinitialize Python (in isolated mode). */
47+
status = PyConfig_SetBytesArgv(&config, argc, argv);
48+
if (PyStatus_Exception(status)) {
49+
goto exception;
50+
}
51+
52+
status = Py_InitializeFromConfig(&config);
53+
if (PyStatus_Exception(status)) {
54+
goto exception;
55+
}
56+
PyConfig_Clear(&config);
57+
58+
return Py_RunMain();
59+
60+
exception:
61+
PyConfig_Clear(&config);
62+
if (PyStatus_IsExit(status)) {
63+
return status.exitcode;
64+
}
65+
/* Display the error message and exit the process with
66+
non-zero exit code */
67+
Py_ExitStatusException(status);
68+
}
69+
5770

5871
PyWideStringList
59-
----------------
72+
================
6073

6174
.. c:type:: PyWideStringList
6275
@@ -95,7 +108,7 @@ PyWideStringList
95108
List items.
96109
97110
PyStatus
98-
--------
111+
========
99112
100113
.. c:type:: PyStatus
101114
@@ -187,7 +200,7 @@ Example::
187200
188201
189202
PyPreConfig
190-
-----------
203+
===========
191204
192205
.. c:type:: PyPreConfig
193206
@@ -317,7 +330,7 @@ PyPreConfig
317330
.. _c-preinit:
318331
319332
Preinitialize Python with PyPreConfig
320-
-------------------------------------
333+
=====================================
321334
322335
The preinitialization of Python:
323336
@@ -326,26 +339,35 @@ The preinitialization of Python:
326339
* Set the :ref:`Python UTF-8 Mode <utf8-mode>`
327340
(:c:member:`PyPreConfig.utf8_mode`)
328341
342+
The current preconfiguration (``PyPreConfig`` type) is stored in
343+
``_PyRuntime.preconfig``.
344+
329345
Functions to preinitialize Python:
330346
331347
.. c:function:: PyStatus Py_PreInitialize(const PyPreConfig *preconfig)
332348
333349
Preinitialize Python from *preconfig* preconfiguration.
334350
351+
*preconfig* must not be ``NULL``.
352+
335353
.. c:function:: PyStatus Py_PreInitializeFromBytesArgs(const PyPreConfig *preconfig, int argc, char * const *argv)
336354
337355
Preinitialize Python from *preconfig* preconfiguration.
338356
339357
Parse *argv* command line arguments (bytes strings) if
340358
:c:member:`~PyPreConfig.parse_argv` of *preconfig* is non-zero.
341359
360+
*preconfig* must not be ``NULL``.
361+
342362
.. c:function:: PyStatus Py_PreInitializeFromArgs(const PyPreConfig *preconfig, int argc, wchar_t * const * argv)
343363
344364
Preinitialize Python from *preconfig* preconfiguration.
345365
346366
Parse *argv* command line arguments (wide strings) if
347367
:c:member:`~PyPreConfig.parse_argv` of *preconfig* is non-zero.
348368
369+
*preconfig* must not be ``NULL``.
370+
349371
The caller is responsible to handle exceptions (error or exit) using
350372
:c:func:`PyStatus_Exception` and :c:func:`Py_ExitStatusException`.
351373
@@ -388,7 +410,7 @@ the :ref:`Python UTF-8 Mode <utf8-mode>`::
388410
389411
390412
PyConfig
391-
--------
413+
========
392414
393415
.. c:type:: PyConfig
394416
@@ -449,8 +471,20 @@ PyConfig
449471
450472
Fields which are already initialized are left unchanged.
451473
474+
The :c:func:`PyConfig_Read` function only parses
475+
:c:member:`PyConfig.argv` arguments once: :c:member:`PyConfig.parse_argv`
476+
is set to ``2`` after arguments are parsed. Since Python arguments are
477+
strippped from :c:member:`PyConfig.argv`, parsing arguments twice would
478+
parse the application options as Python options.
479+
452480
:ref:`Preinitialize Python <c-preinit>` if needed.
453481
482+
.. versionchanged:: 3.10
483+
The :c:member:`PyConfig.argv` arguments are now only parsed once,
484+
:c:member:`PyConfig.parse_argv` is set to ``2`` after arguments are
485+
parsed, and arguments are only parsed if
486+
:c:member:`PyConfig.parse_argv` equals ``1``.
487+
454488
.. c:function:: void PyConfig_Clear(PyConfig *config)
455489
456490
Release configuration memory.
@@ -833,7 +867,7 @@ PyConfig
833867
834868
If :c:member:`~PyConfig.orig_argv` list is empty and
835869
:c:member:`~PyConfig.argv` is not a list only containing an empty
836-
string, :c:func:`PyConfig_Read()` copies :c:member:`~PyConfig.argv` into
870+
string, :c:func:`PyConfig_Read` copies :c:member:`~PyConfig.argv` into
837871
:c:member:`~PyConfig.orig_argv` before modifying
838872
:c:member:`~PyConfig.argv` (if :c:member:`~PyConfig.parse_argv` is
839873
non-zero).
@@ -849,12 +883,22 @@ PyConfig
849883
850884
Parse command line arguments?
851885
852-
If non-zero, parse :c:member:`~PyConfig.argv` the same way the regular
886+
If equals to ``1``, parse :c:member:`~PyConfig.argv` the same way the regular
853887
Python parses :ref:`command line arguments <using-on-cmdline>`, and strip
854888
Python arguments from :c:member:`~PyConfig.argv`.
855889
890+
The :c:func:`PyConfig_Read` function only parses
891+
:c:member:`PyConfig.argv` arguments once: :c:member:`PyConfig.parse_argv`
892+
is set to ``2`` after arguments are parsed. Since Python arguments are
893+
strippped from :c:member:`PyConfig.argv`, parsing arguments twice would
894+
parse the application options as Python options.
895+
856896
Default: ``1`` in Python mode, ``0`` in isolated mode.
857897
898+
.. versionchanged:: 3.10
899+
The :c:member:`PyConfig.argv` arguments are now only parsed if
900+
:c:member:`PyConfig.parse_argv` equals to ``1``.
901+
858902
.. c:member:: int parser_debug
859903
860904
Parser debug mode. If greater than 0, turn on parser debugging output (for expert only, depending
@@ -1108,7 +1152,7 @@ the :option:`-X` command line option.
11081152
11091153
11101154
Initialization with PyConfig
1111-
----------------------------
1155+
============================
11121156
11131157
Function to initialize Python:
11141158
@@ -1123,6 +1167,9 @@ If :c:func:`PyImport_FrozenModules`, :c:func:`PyImport_AppendInittab` or
11231167
:c:func:`PyImport_ExtendInittab` are used, they must be set or called after
11241168
Python preinitialization and before the Python initialization.
11251169
1170+
The current configuration (``PyConfig`` type) is stored in
1171+
``PyInterpreterState.config``.
1172+
11261173
Example setting the program name::
11271174
11281175
void init_python(void)
@@ -1136,17 +1183,17 @@ Example setting the program name::
11361183
status = PyConfig_SetString(&config, &config.program_name,
11371184
L"/path/to/my_program");
11381185
if (PyStatus_Exception(status)) {
1139-
goto fail;
1186+
goto exception;
11401187
}
11411188
11421189
status = Py_InitializeFromConfig(&config);
11431190
if (PyStatus_Exception(status)) {
1144-
goto fail;
1191+
goto exception;
11451192
}
11461193
PyConfig_Clear(&config);
11471194
return;
11481195
1149-
fail:
1196+
exception:
11501197
PyConfig_Clear(&config);
11511198
Py_ExitStatusException(status);
11521199
}
@@ -1202,7 +1249,7 @@ configuration, and then override some parameters::
12021249
.. _init-isolated-conf:
12031250
12041251
Isolated Configuration
1205-
----------------------
1252+
======================
12061253
12071254
:c:func:`PyPreConfig_InitIsolatedConfig` and
12081255
:c:func:`PyConfig_InitIsolatedConfig` functions create a configuration to
@@ -1223,7 +1270,7 @@ configuration.
12231270
.. _init-python-config:
12241271
12251272
Python Configuration
1226-
--------------------
1273+
====================
12271274
12281275
:c:func:`PyPreConfig_InitPythonConfig` and :c:func:`PyConfig_InitPythonConfig`
12291276
functions create a configuration to build a customized Python which behaves as
@@ -1237,46 +1284,11 @@ and :ref:`Python UTF-8 Mode <utf8-mode>`
12371284
(:pep:`540`) depending on the LC_CTYPE locale, :envvar:`PYTHONUTF8` and
12381285
:envvar:`PYTHONCOERCECLOCALE` environment variables.
12391286
1240-
Example of customized Python always running in isolated mode::
1241-
1242-
int main(int argc, char **argv)
1243-
{
1244-
PyStatus status;
1245-
1246-
PyConfig config;
1247-
PyConfig_InitPythonConfig(&config);
1248-
config.isolated = 1;
1249-
1250-
/* Decode command line arguments.
1251-
Implicitly preinitialize Python (in isolated mode). */
1252-
status = PyConfig_SetBytesArgv(&config, argc, argv);
1253-
if (PyStatus_Exception(status)) {
1254-
goto fail;
1255-
}
1256-
1257-
status = Py_InitializeFromConfig(&config);
1258-
if (PyStatus_Exception(status)) {
1259-
goto fail;
1260-
}
1261-
PyConfig_Clear(&config);
1262-
1263-
return Py_RunMain();
1264-
1265-
fail:
1266-
PyConfig_Clear(&config);
1267-
if (PyStatus_IsExit(status)) {
1268-
return status.exitcode;
1269-
}
1270-
/* Display the error message and exit the process with
1271-
non-zero exit code */
1272-
Py_ExitStatusException(status);
1273-
}
1274-
12751287
12761288
.. _init-path-config:
12771289
12781290
Path Configuration
1279-
------------------
1291+
==================
12801292
12811293
:c:type:`PyConfig` contains multiple fields for the path configuration:
12821294
@@ -1356,7 +1368,7 @@ The ``__PYVENV_LAUNCHER__`` environment variable is used to set
13561368
13571369
13581370
Py_RunMain()
1359-
------------
1371+
============
13601372
13611373
.. c:function:: int Py_RunMain(void)
13621374
@@ -1376,7 +1388,7 @@ customized Python always running in isolated mode using
13761388
13771389
13781390
Py_GetArgcArgv()
1379-
----------------
1391+
================
13801392
13811393
.. c:function:: void Py_GetArgcArgv(int *argc, wchar_t ***argv)
13821394
@@ -1386,7 +1398,7 @@ Py_GetArgcArgv()
13861398
13871399
13881400
Multi-Phase Initialization Private Provisional API
1389-
--------------------------------------------------
1401+
==================================================
13901402
13911403
This section is a private provisional API introducing multi-phase
13921404
initialization, the core feature of the :pep:`432`:

Lib/test/_test_embed_set_config.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def setUp(self):
2020
self.sys_copy = dict(sys.__dict__)
2121

2222
def tearDown(self):
23-
self.set_config(parse_argv=0)
23+
_testinternalcapi.set_config(self.old_config)
2424
sys.__dict__.clear()
2525
sys.__dict__.update(self.sys_copy)
2626

@@ -234,6 +234,12 @@ def test_argv(self):
234234
self.assertEqual(sys.argv, ['python_program', 'args'])
235235
self.assertEqual(sys.orig_argv, ['orig', 'orig_args'])
236236

237+
self.set_config(parse_argv=0,
238+
argv=[],
239+
orig_argv=[])
240+
self.assertEqual(sys.argv, [''])
241+
self.assertEqual(sys.orig_argv, [])
242+
237243
def test_pycache_prefix(self):
238244
self.check(pycache_prefix=None)
239245
self.check(pycache_prefix="pycache_prefix")

0 commit comments

Comments
 (0)