Skip to content

Commit

Permalink
pythongh-107954, PEP 741: Add PyInitConfig C API
Browse files Browse the repository at this point in the history
Add Doc/c-api/config.rst documentation.
  • Loading branch information
vstinner committed Aug 30, 2024
1 parent 3d60dfb commit c745977
Show file tree
Hide file tree
Showing 13 changed files with 952 additions and 42 deletions.
227 changes: 227 additions & 0 deletions Doc/c-api/config.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
.. highlight:: c

.. _config-c-api:

********************
Python Configuration
********************

.. versionadded:: 3.14

C API to configure the Python initialization (:pep:`741`).

See also :ref:`Python Initialization Configuration <init-config>`.


Initialize Python
=================

Create Config
-------------

.. c:struct:: PyInitConfig
Opaque structure to configure the Python initialization.


.. c:function:: PyInitConfig* PyInitConfig_Create(void)
Create a new initialization configuration using :ref:`Isolated Configuration
<init-isolated-conf>` default values.
It must be freed by :c:func:`PyInitConfig_Free`.
Return ``NULL`` on memory allocation failure.
.. c:function:: void PyInitConfig_Free(PyInitConfig *config)
Free memory of the initialization configuration *config*.
Get Options
-----------
The configuration option *name* parameter must be a non-NULL
null-terminated UTF-8 encoded string.
.. c:function:: int PyInitConfig_HasOption(PyInitConfig *config, const char *name)
Test if the configuration has an option called *name*.
Return ``1`` if the option exists, or return ``0`` otherwise.
.. c:function:: int PyInitConfig_GetInt(PyInitConfig *config, const char *name, int64_t *value)
Get an integer configuration option.
* Set *\*value*, and return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
.. c:function:: int PyInitConfig_GetStr(PyInitConfig *config, const char *name, char **value)
Get a string configuration option as a null-terminated UTF-8
encoded string.
* Set *\*value*, and return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
On success, the string must be released with ``free(value)``.
.. c:function:: int PyInitConfig_GetStrList(PyInitConfig *config, const char *name, size_t *length, char ***items)
Get a string list configuration option as an array of
null-terminated UTF-8 encoded strings.
* Set *\*length* and *\*value*, and return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
On success, the string list must be released with
``PyInitConfig_FreeStrList(length, items)``.
.. c:function:: void PyInitConfig_FreeStrList(size_t length, char **items)
Free memory of a string list created by
``PyInitConfig_GetStrList()``.
Set Options
-----------
The configuration option *name* parameter must be a non-NULL null-terminated
UTF-8 encoded string.
Some configuration options have side effects on other options. This logic is
only implemented when ``Py_InitializeFromInitConfig()`` is called, not by the
"Set" functions below. For example, setting ``dev_mode`` to ``1`` does not set
``faulthandler`` to ``1``.
.. c:function:: int PyInitConfig_SetInt(PyInitConfig *config, const char *name, int64_t value)
Set an integer configuration option.
* Return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
.. c:function:: int PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char *value)
Set a string configuration option from a null-terminated UTF-8
encoded string. The string is copied.
* Return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
.. c:function:: int PyInitConfig_SetStrList(PyInitConfig *config, const char *name, size_t length, char * const *items)
Set a string list configuration option from an array of
null-terminated UTF-8 encoded strings. The string list is copied.
* Return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
Initialize Python
-----------------
.. c:function:: int Py_InitializeFromInitConfig(PyInitConfig *config)
Initialize Python from the initialization configuration.
* Return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
* Set an exit code in *config* and return ``-1`` if Python wants to
exit.
See ``PyInitConfig_GetExitcode()`` for the exit code case.
Error Handling
--------------
.. c:function:: int PyInitConfig_GetError(PyInitConfig* config, const char **err_msg)
Get the *config* error message.
* Set *\*err_msg* and return ``1`` if an error is set.
* Set *\*err_msg* to ``NULL`` and return ``0`` otherwise.
An error message is an UTF-8 encoded string.
If *config* has an exit code, format the exit code as an error
message.
The error message remains valid until another ``PyInitConfig``
function is called with *config*. The caller doesn't have to free the
error message.
.. c:function:: int PyInitConfig_GetExitCode(PyInitConfig* config, int *exitcode)
Get the *config* exit code.
* Set *\*exitcode* and return ``1`` if Python wants to exit.
* Return ``0`` if *config* has no exit code set.
Only the ``Py_InitializeFromInitConfig()`` function can set an exit
code if the ``parse_argv`` option is non-zero.
An exit code can be set when parsing the command line failed (exit
code ``2``) or when a command line option asks to display the command
line help (exit code ``0``).
Example
=======
Example initializing Python, set configuration options of various types,
return ``-1`` on error:
.. code-block:: c
int init_python(void)
{
PyInitConfig *config = PyInitConfig_Create();
if (config == NULL) {
printf("PYTHON INIT ERROR: memory allocation failed\n");
return -1;
}
// Set an integer (dev mode)
if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
goto error;
}
// Set a list of UTF-8 strings (argv)
char *argv[] = {"my_program", "-c", "pass"};
if (PyInitConfig_SetStrList(config, "argv",
Py_ARRAY_LENGTH(argv), argv) < 0) {
goto error;
}
// Set a UTF-8 string (program name)
if (PyInitConfig_SetStr(config, "program_name", L"my_program") < 0) {
goto error;
}
// Initialize Python with the configuration
if (Py_InitializeFromInitConfig(config) < 0) {
goto error;
}
PyInitConfig_Free(config);
return 0;
error:
// Display the error message
const char *err_msg;
(void)PyInitConfig_GetError(config, &err_msg);
printf("PYTHON INIT ERROR: %s\n", err_msg);
PyInitConfig_Free(config);
return -1;
}
1 change: 1 addition & 0 deletions Doc/c-api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ document the API functions in detail.
concrete.rst
init.rst
init_config.rst
config.rst
memory.rst
objimpl.rst
apiabiversion.rst
Expand Down
3 changes: 2 additions & 1 deletion Doc/c-api/init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
Initialization, Finalization, and Threads
*****************************************

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

.. _pre-init-safe:

Expand Down
3 changes: 2 additions & 1 deletion Doc/c-api/init_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ There are two kinds of configuration:
The :c:func:`Py_RunMain` function can be used to write a customized Python
program.

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

.. seealso::
:pep:`587` "Python Initialization Configuration".
Expand Down
18 changes: 18 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,24 @@ New Features
similar to ``sep.join(iterable)`` in Python.
(Contributed by Victor Stinner in :gh:`121645`.)

* Add functions to configure the Python initialization:

* :c:func:`PyInitConfig_Create`
* :c:func:`PyInitConfig_Free`
* :c:func:`PyInitConfig_HasOption`
* :c:func:`PyInitConfig_GetInt`
* :c:func:`PyInitConfig_GetStr`
* :c:func:`PyInitConfig_GetStrList`
* :c:func:`PyInitConfig_FreeStrList`
* :c:func:`PyInitConfig_SetInt`
* :c:func:`PyInitConfig_SetStr`
* :c:func:`PyInitConfig_SetStrList`
* :c:func:`Py_InitializeFromInitConfig`
* :c:func:`PyInitConfig_GetError`
* :c:func:`PyInitConfig_GetExitCode`

(Contributed by Victor Stinner in :gh:`107954`.)


Porting to Python 3.14
----------------------
Expand Down
60 changes: 60 additions & 0 deletions Include/cpython/initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,66 @@ PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config,
See also PyConfig.orig_argv. */
PyAPI_FUNC(void) Py_GetArgcArgv(int *argc, wchar_t ***argv);


// --- PyInitConfig ---------------------------------------------------------

typedef struct PyInitConfig PyInitConfig;

// Create a new initialization configuration using the Isolated configuration
// defaults.
// It must be freed by PyInitConfig_Free().
// Return NULL on memory allocation failure.
PyAPI_FUNC(PyInitConfig*) PyInitConfig_Create(void);

// Free memory of a initialization configuration.
PyAPI_FUNC(void) PyInitConfig_Free(PyInitConfig *config);

// Set an integer configuration option.
// Return 0 on success, or return -1 on error.
PyAPI_FUNC(int) PyInitConfig_SetInt(
PyInitConfig *config,
const char *name,
int64_t value);

// Set a string configuration option from a bytes string.
//
// The bytes string is decoded by Py_DecodeLocale(). Preinitialize Python if
// needed to ensure that encodings are properly configured.
//
// Return 0 on success, or return -1 on error.
PyAPI_FUNC(int) PyInitConfig_SetStr(
PyInitConfig *config,
const char *name,
const char *value);

// Set a string list configuration option from bytes strings.
//
// The bytes strings are decoded by Py_DecodeLocale(). Preinitialize Python if
// needed to ensure that encodings are properly configured.
//
// Return 0 on success, or return -1 on error.
PyAPI_FUNC(int) PyInitConfig_SetStrList(
PyInitConfig *config,
const char *name,
size_t length,
char * const *items);

// Initialize Python from the initialization configuration.
// Return 0 on success.
// Return -1 if Python wants to exit and on error
PyAPI_FUNC(int) Py_InitializeFromInitConfig(PyInitConfig *config);

// Get the error message.
// Set *err_msg and return 1 if an error is set.
// Set *err_msg to NULL and return 0 otherwise.
PyAPI_FUNC(int) PyInitConfig_GetError(PyInitConfig* config, const char **err_msg);

// Get the exit code.
// Set '*exitcode' and return 1 if an exit code is set.
// Return 0 otherwise.
PyAPI_FUNC(int) PyInitConfig_GetExitCode(PyInitConfig* config, int *exitcode);


#ifdef __cplusplus
}
#endif
Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ extern int _PyWideStringList_Copy(PyWideStringList *list,
extern PyStatus _PyWideStringList_Extend(PyWideStringList *list,
const PyWideStringList *list2);
extern PyObject* _PyWideStringList_AsList(const PyWideStringList *list);
extern PyStatus _PyWideStringList_FromBytes(
PyWideStringList *list,
Py_ssize_t length,
char * const *items);


/* --- _PyArgv ---------------------------------------------------- */
Expand Down
Loading

0 comments on commit c745977

Please sign in to comment.