Skip to content

Commit c745977

Browse files
committed
pythongh-107954, PEP 741: Add PyInitConfig C API
Add Doc/c-api/config.rst documentation.
1 parent 3d60dfb commit c745977

File tree

13 files changed

+952
-42
lines changed

13 files changed

+952
-42
lines changed

Doc/c-api/config.rst

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
.. highlight:: c
2+
3+
.. _config-c-api:
4+
5+
********************
6+
Python Configuration
7+
********************
8+
9+
.. versionadded:: 3.14
10+
11+
C API to configure the Python initialization (:pep:`741`).
12+
13+
See also :ref:`Python Initialization Configuration <init-config>`.
14+
15+
16+
Initialize Python
17+
=================
18+
19+
Create Config
20+
-------------
21+
22+
.. c:struct:: PyInitConfig
23+
24+
Opaque structure to configure the Python initialization.
25+
26+
27+
.. c:function:: PyInitConfig* PyInitConfig_Create(void)
28+
29+
Create a new initialization configuration using :ref:`Isolated Configuration
30+
<init-isolated-conf>` default values.
31+
32+
It must be freed by :c:func:`PyInitConfig_Free`.
33+
34+
Return ``NULL`` on memory allocation failure.
35+
36+
37+
.. c:function:: void PyInitConfig_Free(PyInitConfig *config)
38+
39+
Free memory of the initialization configuration *config*.
40+
41+
42+
Get Options
43+
-----------
44+
45+
The configuration option *name* parameter must be a non-NULL
46+
null-terminated UTF-8 encoded string.
47+
48+
.. c:function:: int PyInitConfig_HasOption(PyInitConfig *config, const char *name)
49+
50+
Test if the configuration has an option called *name*.
51+
52+
Return ``1`` if the option exists, or return ``0`` otherwise.
53+
54+
55+
.. c:function:: int PyInitConfig_GetInt(PyInitConfig *config, const char *name, int64_t *value)
56+
57+
Get an integer configuration option.
58+
59+
* Set *\*value*, and return ``0`` on success.
60+
* Set an error in *config* and return ``-1`` on error.
61+
62+
63+
.. c:function:: int PyInitConfig_GetStr(PyInitConfig *config, const char *name, char **value)
64+
65+
Get a string configuration option as a null-terminated UTF-8
66+
encoded string.
67+
68+
* Set *\*value*, and return ``0`` on success.
69+
* Set an error in *config* and return ``-1`` on error.
70+
71+
On success, the string must be released with ``free(value)``.
72+
73+
74+
.. c:function:: int PyInitConfig_GetStrList(PyInitConfig *config, const char *name, size_t *length, char ***items)
75+
76+
Get a string list configuration option as an array of
77+
null-terminated UTF-8 encoded strings.
78+
79+
* Set *\*length* and *\*value*, and return ``0`` on success.
80+
* Set an error in *config* and return ``-1`` on error.
81+
82+
On success, the string list must be released with
83+
``PyInitConfig_FreeStrList(length, items)``.
84+
85+
86+
.. c:function:: void PyInitConfig_FreeStrList(size_t length, char **items)
87+
88+
Free memory of a string list created by
89+
``PyInitConfig_GetStrList()``.
90+
91+
92+
Set Options
93+
-----------
94+
95+
The configuration option *name* parameter must be a non-NULL null-terminated
96+
UTF-8 encoded string.
97+
98+
Some configuration options have side effects on other options. This logic is
99+
only implemented when ``Py_InitializeFromInitConfig()`` is called, not by the
100+
"Set" functions below. For example, setting ``dev_mode`` to ``1`` does not set
101+
``faulthandler`` to ``1``.
102+
103+
.. c:function:: int PyInitConfig_SetInt(PyInitConfig *config, const char *name, int64_t value)
104+
105+
Set an integer configuration option.
106+
107+
* Return ``0`` on success.
108+
* Set an error in *config* and return ``-1`` on error.
109+
110+
111+
.. c:function:: int PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char *value)
112+
113+
Set a string configuration option from a null-terminated UTF-8
114+
encoded string. The string is copied.
115+
116+
* Return ``0`` on success.
117+
* Set an error in *config* and return ``-1`` on error.
118+
119+
120+
.. c:function:: int PyInitConfig_SetStrList(PyInitConfig *config, const char *name, size_t length, char * const *items)
121+
122+
Set a string list configuration option from an array of
123+
null-terminated UTF-8 encoded strings. The string list is copied.
124+
125+
* Return ``0`` on success.
126+
* Set an error in *config* and return ``-1`` on error.
127+
128+
129+
Initialize Python
130+
-----------------
131+
132+
.. c:function:: int Py_InitializeFromInitConfig(PyInitConfig *config)
133+
134+
Initialize Python from the initialization configuration.
135+
136+
* Return ``0`` on success.
137+
* Set an error in *config* and return ``-1`` on error.
138+
* Set an exit code in *config* and return ``-1`` if Python wants to
139+
exit.
140+
141+
See ``PyInitConfig_GetExitcode()`` for the exit code case.
142+
143+
144+
Error Handling
145+
--------------
146+
147+
.. c:function:: int PyInitConfig_GetError(PyInitConfig* config, const char **err_msg)
148+
149+
Get the *config* error message.
150+
151+
* Set *\*err_msg* and return ``1`` if an error is set.
152+
* Set *\*err_msg* to ``NULL`` and return ``0`` otherwise.
153+
154+
An error message is an UTF-8 encoded string.
155+
156+
If *config* has an exit code, format the exit code as an error
157+
message.
158+
159+
The error message remains valid until another ``PyInitConfig``
160+
function is called with *config*. The caller doesn't have to free the
161+
error message.
162+
163+
164+
.. c:function:: int PyInitConfig_GetExitCode(PyInitConfig* config, int *exitcode)
165+
166+
Get the *config* exit code.
167+
168+
* Set *\*exitcode* and return ``1`` if Python wants to exit.
169+
* Return ``0`` if *config* has no exit code set.
170+
171+
Only the ``Py_InitializeFromInitConfig()`` function can set an exit
172+
code if the ``parse_argv`` option is non-zero.
173+
174+
An exit code can be set when parsing the command line failed (exit
175+
code ``2``) or when a command line option asks to display the command
176+
line help (exit code ``0``).
177+
178+
179+
Example
180+
=======
181+
182+
Example initializing Python, set configuration options of various types,
183+
return ``-1`` on error:
184+
185+
.. code-block:: c
186+
187+
int init_python(void)
188+
{
189+
PyInitConfig *config = PyInitConfig_Create();
190+
if (config == NULL) {
191+
printf("PYTHON INIT ERROR: memory allocation failed\n");
192+
return -1;
193+
}
194+
195+
// Set an integer (dev mode)
196+
if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
197+
goto error;
198+
}
199+
200+
// Set a list of UTF-8 strings (argv)
201+
char *argv[] = {"my_program", "-c", "pass"};
202+
if (PyInitConfig_SetStrList(config, "argv",
203+
Py_ARRAY_LENGTH(argv), argv) < 0) {
204+
goto error;
205+
}
206+
207+
// Set a UTF-8 string (program name)
208+
if (PyInitConfig_SetStr(config, "program_name", L"my_program") < 0) {
209+
goto error;
210+
}
211+
212+
// Initialize Python with the configuration
213+
if (Py_InitializeFromInitConfig(config) < 0) {
214+
goto error;
215+
}
216+
PyInitConfig_Free(config);
217+
return 0;
218+
219+
error:
220+
// Display the error message
221+
const char *err_msg;
222+
(void)PyInitConfig_GetError(config, &err_msg);
223+
printf("PYTHON INIT ERROR: %s\n", err_msg);
224+
PyInitConfig_Free(config);
225+
226+
return -1;
227+
}

Doc/c-api/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ document the API functions in detail.
2222
concrete.rst
2323
init.rst
2424
init_config.rst
25+
config.rst
2526
memory.rst
2627
objimpl.rst
2728
apiabiversion.rst

Doc/c-api/init.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
Initialization, Finalization, and Threads
88
*****************************************
99

10-
See also :ref:`Python Initialization Configuration <init-config>`.
10+
See also the :ref:`Python Initialization Configuration <init-config>`
11+
and the :ref:`Python Configuration <config-c-api>`
1112

1213
.. _pre-init-safe:
1314

Doc/c-api/init_config.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ There are two kinds of configuration:
2727
The :c:func:`Py_RunMain` function can be used to write a customized Python
2828
program.
2929

30-
See also :ref:`Initialization, Finalization, and Threads <initialization>`.
30+
See also :ref:`Initialization, Finalization, and Threads <initialization>`
31+
and the :ref:`Python Configuration <config-c-api>`.
3132

3233
.. seealso::
3334
:pep:`587` "Python Initialization Configuration".

Doc/whatsnew/3.14.rst

+18
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,24 @@ New Features
489489
similar to ``sep.join(iterable)`` in Python.
490490
(Contributed by Victor Stinner in :gh:`121645`.)
491491

492+
* Add functions to configure the Python initialization:
493+
494+
* :c:func:`PyInitConfig_Create`
495+
* :c:func:`PyInitConfig_Free`
496+
* :c:func:`PyInitConfig_HasOption`
497+
* :c:func:`PyInitConfig_GetInt`
498+
* :c:func:`PyInitConfig_GetStr`
499+
* :c:func:`PyInitConfig_GetStrList`
500+
* :c:func:`PyInitConfig_FreeStrList`
501+
* :c:func:`PyInitConfig_SetInt`
502+
* :c:func:`PyInitConfig_SetStr`
503+
* :c:func:`PyInitConfig_SetStrList`
504+
* :c:func:`Py_InitializeFromInitConfig`
505+
* :c:func:`PyInitConfig_GetError`
506+
* :c:func:`PyInitConfig_GetExitCode`
507+
508+
(Contributed by Victor Stinner in :gh:`107954`.)
509+
492510

493511
Porting to Python 3.14
494512
----------------------

Include/cpython/initconfig.h

+60
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,66 @@ PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config,
267267
See also PyConfig.orig_argv. */
268268
PyAPI_FUNC(void) Py_GetArgcArgv(int *argc, wchar_t ***argv);
269269

270+
271+
// --- PyInitConfig ---------------------------------------------------------
272+
273+
typedef struct PyInitConfig PyInitConfig;
274+
275+
// Create a new initialization configuration using the Isolated configuration
276+
// defaults.
277+
// It must be freed by PyInitConfig_Free().
278+
// Return NULL on memory allocation failure.
279+
PyAPI_FUNC(PyInitConfig*) PyInitConfig_Create(void);
280+
281+
// Free memory of a initialization configuration.
282+
PyAPI_FUNC(void) PyInitConfig_Free(PyInitConfig *config);
283+
284+
// Set an integer configuration option.
285+
// Return 0 on success, or return -1 on error.
286+
PyAPI_FUNC(int) PyInitConfig_SetInt(
287+
PyInitConfig *config,
288+
const char *name,
289+
int64_t value);
290+
291+
// Set a string configuration option from a bytes string.
292+
//
293+
// The bytes string is decoded by Py_DecodeLocale(). Preinitialize Python if
294+
// needed to ensure that encodings are properly configured.
295+
//
296+
// Return 0 on success, or return -1 on error.
297+
PyAPI_FUNC(int) PyInitConfig_SetStr(
298+
PyInitConfig *config,
299+
const char *name,
300+
const char *value);
301+
302+
// Set a string list configuration option from bytes strings.
303+
//
304+
// The bytes strings are decoded by Py_DecodeLocale(). Preinitialize Python if
305+
// needed to ensure that encodings are properly configured.
306+
//
307+
// Return 0 on success, or return -1 on error.
308+
PyAPI_FUNC(int) PyInitConfig_SetStrList(
309+
PyInitConfig *config,
310+
const char *name,
311+
size_t length,
312+
char * const *items);
313+
314+
// Initialize Python from the initialization configuration.
315+
// Return 0 on success.
316+
// Return -1 if Python wants to exit and on error
317+
PyAPI_FUNC(int) Py_InitializeFromInitConfig(PyInitConfig *config);
318+
319+
// Get the error message.
320+
// Set *err_msg and return 1 if an error is set.
321+
// Set *err_msg to NULL and return 0 otherwise.
322+
PyAPI_FUNC(int) PyInitConfig_GetError(PyInitConfig* config, const char **err_msg);
323+
324+
// Get the exit code.
325+
// Set '*exitcode' and return 1 if an exit code is set.
326+
// Return 0 otherwise.
327+
PyAPI_FUNC(int) PyInitConfig_GetExitCode(PyInitConfig* config, int *exitcode);
328+
329+
270330
#ifdef __cplusplus
271331
}
272332
#endif

Include/internal/pycore_initconfig.h

+4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ extern int _PyWideStringList_Copy(PyWideStringList *list,
6262
extern PyStatus _PyWideStringList_Extend(PyWideStringList *list,
6363
const PyWideStringList *list2);
6464
extern PyObject* _PyWideStringList_AsList(const PyWideStringList *list);
65+
extern PyStatus _PyWideStringList_FromBytes(
66+
PyWideStringList *list,
67+
Py_ssize_t length,
68+
char * const *items);
6569

6670

6771
/* --- _PyArgv ---------------------------------------------------- */

0 commit comments

Comments
 (0)