PEP 741: Python Configuration C API (#3625)
This commit is contained in:
parent
f31f53dcba
commit
c3af863741
|
@ -0,0 +1,524 @@
|
||||||
|
PEP: 741
|
||||||
|
Title: Python Configuration C API
|
||||||
|
Author: Victor Stinner <vstinner@python.org>
|
||||||
|
Status: Draft
|
||||||
|
Type: Standards Track
|
||||||
|
Created: 18-Jan-2024
|
||||||
|
Python-Version: 3.13
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
========
|
||||||
|
|
||||||
|
Add a C API to the limited C API to configure the Python
|
||||||
|
preinitialization and initialization, and to get the current
|
||||||
|
configuration. It can be used with the stable ABI.
|
||||||
|
|
||||||
|
Add ``sys.get_config(name)`` function to get the current value of a
|
||||||
|
configuration option.
|
||||||
|
|
||||||
|
Allow setting custom configuration options, not used by Python but by
|
||||||
|
third-party code. Options are referred to by their name as a string.
|
||||||
|
|
||||||
|
:pep:`587` "Python Initialization Configuration" unified all the ways to
|
||||||
|
configure the Python **initialization**. This PEP unifies also the
|
||||||
|
configuration of the Python **preinitialization** and the Python
|
||||||
|
**initialization** in a single API.
|
||||||
|
|
||||||
|
|
||||||
|
Rationale
|
||||||
|
=========
|
||||||
|
|
||||||
|
PyConfig is not part of the limited C API
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
When the first versions of :pep:`587` "Python Initialization Configuration"
|
||||||
|
were discussed, there was a private field ``_config_version`` (``int``):
|
||||||
|
the configuration version, used for ABI compatibility. It was decided
|
||||||
|
that if an application embeds Python, it sticks to a Python version
|
||||||
|
anyway, and so there is no need to bother with the ABI compatibility.
|
||||||
|
|
||||||
|
The final PyConfig API of :pep:`587` is excluded from the limited C API
|
||||||
|
since its main ``PyConfig`` structure is not versioned. Python cannot
|
||||||
|
guarantee ABI backward and forward compatibility, it's incompatible with
|
||||||
|
the stable ABI.
|
||||||
|
|
||||||
|
Since PyConfig was added to Python 3.8, the limited C API and the stable
|
||||||
|
ABI are getting more popular. For example, Rust bindings such as the
|
||||||
|
`PyO3 project <https://pyo3.rs/>`_ target the limited C API to embed
|
||||||
|
Python in Rust. In practice, PyO3 can use non-limited C API for specific
|
||||||
|
needs, but using them avoids the stable ABI advantages.
|
||||||
|
|
||||||
|
Deprecated legacy API
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The legacy configuration API has been deprecated since Python 3.8:
|
||||||
|
|
||||||
|
* Set the initialization configuration such as ``Py_SetPath()``:
|
||||||
|
deprecated in Python 3.11.
|
||||||
|
* Global configuration variables such as ``Py_VerboseFlag``:
|
||||||
|
deprecated in Python 3.12.
|
||||||
|
* Get the current configuration such as ``Py_GetPath()``:
|
||||||
|
deprecated in Python 3.13.
|
||||||
|
|
||||||
|
Get the current configuration
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
:pep:`587` has no API to **get** the **current** configuration, only to
|
||||||
|
**configure** the Python **initialization**.
|
||||||
|
|
||||||
|
For example, the global configuration variable
|
||||||
|
``Py_UnbufferedStdioFlag`` was deprecated in Python 3.12 and using
|
||||||
|
``PyConfig.buffered_stdio`` is recommended instead. It only works to
|
||||||
|
configure Python, there is no public API to get
|
||||||
|
``PyConfig.buffered_stdio``.
|
||||||
|
|
||||||
|
Users of the limited C API are asking for a public API to get the
|
||||||
|
current configuration.
|
||||||
|
|
||||||
|
Security fix
|
||||||
|
------------
|
||||||
|
|
||||||
|
To fix `CVE-2020-10735
|
||||||
|
<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735>`_,
|
||||||
|
a denial-of-service when converting very a large string to an integer (in base
|
||||||
|
10), it was discussed to add a new ``PyConfig`` member to stable
|
||||||
|
branches which affects the ABI.
|
||||||
|
|
||||||
|
Gregory P. Smith proposed a different API using text based configuration
|
||||||
|
file to not be limited by ``PyConfig`` members: `FR: Allow private
|
||||||
|
runtime config to enable extending without breaking the PyConfig ABI
|
||||||
|
<https://discuss.python.org/t/fr-allow-private-runtime-config-to-enable-extending-without-breaking-the-pyconfig-abi/18004>`__
|
||||||
|
(August 2022).
|
||||||
|
|
||||||
|
In the end, it was decided to not add a new ``PyConfig`` member to
|
||||||
|
stable branches, but only add a new ``PyConfig.int_max_str_digits``
|
||||||
|
member to the development branch (which became Python 3.12). A dedicated
|
||||||
|
private global variable (unrelated to ``PyConfig``) is used in stable
|
||||||
|
branches.
|
||||||
|
|
||||||
|
Redundancy between PyPreConfig and PyConfig
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
The Python preinitialization uses the ``PyPreConfig`` structure and the
|
||||||
|
Python initialization uses the ``PyConfig`` structure. Both structures
|
||||||
|
have four duplicated members: ``dev_mode``, ``parse_argv``, ``isolated``
|
||||||
|
and ``use_environment``.
|
||||||
|
|
||||||
|
The redundancy is caused by the fact that the two structures are
|
||||||
|
separated, whereas some ``PyConfig`` members are needed by the
|
||||||
|
preinitialization.
|
||||||
|
|
||||||
|
|
||||||
|
Specification
|
||||||
|
=============
|
||||||
|
|
||||||
|
C API:
|
||||||
|
|
||||||
|
* ``PyInitConfig`` structure
|
||||||
|
* ``PyInitConfig_Python_New()``
|
||||||
|
* ``PyInitConfig_Isolated_New()``
|
||||||
|
* ``PyInitConfig_Free(config)``
|
||||||
|
* ``PyInitConfig_SetInt(config, name, value)``
|
||||||
|
* ``PyInitConfig_SetStr(config, name, value)``
|
||||||
|
* ``PyInitConfig_SetWStr(config, name, value)``
|
||||||
|
* ``PyInitConfig_SetStrList(config, name, length, items)``
|
||||||
|
* ``PyInitConfig_SetWStrList(config, name, length, items)``
|
||||||
|
* ``Py_InitializeFromInitConfig(config)``
|
||||||
|
* ``PyInitConfig_Exception(config)``
|
||||||
|
* ``PyInitConfig_GetError(config, &err_msg)``
|
||||||
|
* ``PyInitConfig_GetExitCode(config, &exitcode)``
|
||||||
|
* ``Py_ExitWithInitConfig(config)``
|
||||||
|
* ``PyConfig_Get(name)``
|
||||||
|
* ``PyConfig_GetInt(name, &value)``
|
||||||
|
|
||||||
|
Python API:
|
||||||
|
|
||||||
|
* ``sys.get_config(name)``
|
||||||
|
|
||||||
|
The C API uses null-terminated UTF-8 encoded strings to refer to a
|
||||||
|
configuration option.
|
||||||
|
|
||||||
|
All C API functions are added to the limited C API version 3.13.
|
||||||
|
|
||||||
|
The ``PyInitConfig`` structure is implemented by combining the three
|
||||||
|
structures of the ``PyConfig`` API:
|
||||||
|
|
||||||
|
* ``PyPreConfig preconfig``
|
||||||
|
* ``PyConfig config``
|
||||||
|
* ``PyStatus status``
|
||||||
|
|
||||||
|
The ``PyStatus`` status is no longer separated, but part of the unified
|
||||||
|
``PyInitConfig`` structure, which makes the API easier to use.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Options
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Configuration options are named after ``PyPreConfig`` and
|
||||||
|
``PyConfig`` structure members such as ``"verbose"``
|
||||||
|
(``PyConfig.verbose``), ``"buffered_stdio"``
|
||||||
|
(``PyConfig.buffered_stdio``), or ``"allocator"``
|
||||||
|
(``PyPreConfig.allocator``).
|
||||||
|
|
||||||
|
The type of configuration options depends on the option. For example,
|
||||||
|
the ``"verbose"`` option type is an integer, whereas
|
||||||
|
``"module_search_paths"`` option type is an array of wide strings.
|
||||||
|
|
||||||
|
See the `PyConfig documentation
|
||||||
|
<https://docs.python.org/dev/c-api/init_config.html#pyconfig>`_
|
||||||
|
and the `PyPreConfig documentation
|
||||||
|
<https://docs.python.org/dev/c-api/init_config.html#pypreconfig>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Configure the Python initialization
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
``PyInitConfig`` structure:
|
||||||
|
Opaque structure to configure the Python preinitialization and the
|
||||||
|
Python initialization.
|
||||||
|
|
||||||
|
``PyInitConfig* PyInitConfig_Python_New(void)``:
|
||||||
|
Create a new initialization configuration using default values
|
||||||
|
of the `Python Configuration
|
||||||
|
<https://docs.python.org/dev/c-api/init_config.html#python-configuration>`_.
|
||||||
|
|
||||||
|
It must be freed with ``PyInitConfig_Free()``.
|
||||||
|
|
||||||
|
Return ``NULL`` on memory allocation failure.
|
||||||
|
|
||||||
|
``PyInitConfig* PyInitConfig_Isolated_New(void)``:
|
||||||
|
Similar to ``PyInitConfig_Python_New()``, but use default values
|
||||||
|
of the `Isolated Configuration
|
||||||
|
<https://docs.python.org/dev/c-api/init_config.html#isolated-configuration>`_.
|
||||||
|
|
||||||
|
``void PyInitConfig_Free(PyInitConfig *config)``:
|
||||||
|
Free memory of an initialization configuration.
|
||||||
|
|
||||||
|
|
||||||
|
``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.
|
||||||
|
|
||||||
|
``int PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char *value)``:
|
||||||
|
Set a string configuration option from a null-terminated bytes
|
||||||
|
string.
|
||||||
|
|
||||||
|
The bytes string is decoded by ``Py_DecodeLocale()``. If Python is
|
||||||
|
not yet preinitialized, this function preinitializes it to ensure
|
||||||
|
that encodings are properly configured.
|
||||||
|
|
||||||
|
* Return ``0`` on success.
|
||||||
|
* Set an error in *config* and return ``-1`` on error.
|
||||||
|
|
||||||
|
``int PyInitConfig_SetWStr(PyInitConfig *config, const char *name, const wchar_t *value)``:
|
||||||
|
Set a string configuration option from a null-terminated wide
|
||||||
|
string.
|
||||||
|
|
||||||
|
If Python is not yet preinitialized, this function preinitializes
|
||||||
|
it.
|
||||||
|
|
||||||
|
* Return ``0`` on success.
|
||||||
|
* Set an error in *config* and return ``-1`` on error.
|
||||||
|
|
||||||
|
``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 bytes strings.
|
||||||
|
|
||||||
|
The bytes string is decoded by :c:func:`Py_DecodeLocale`. If Python
|
||||||
|
is not yet preinitialized, this function preinitializes it to ensure
|
||||||
|
that encodings are properly configured.
|
||||||
|
|
||||||
|
* Return ``0`` on success.
|
||||||
|
* Set an error in *config* and return ``-1`` on error.
|
||||||
|
|
||||||
|
``int PyInitConfig_SetWStrList(PyInitConfig *config, const char *name, size_t length, wchar_t * const *items)``:
|
||||||
|
Set a string list configuration option from an error of
|
||||||
|
null-terminated wide strings.
|
||||||
|
|
||||||
|
If Python is not yet preinitialized, this function preinitializes
|
||||||
|
it.
|
||||||
|
|
||||||
|
* Return ``0`` on success.
|
||||||
|
* Set an error in *config* and return ``-1`` on error.
|
||||||
|
|
||||||
|
``int Py_PreInitializeFromInitConfig(PyInitConfig *config)``:
|
||||||
|
Preinitialize Python from the initialization configuration.
|
||||||
|
|
||||||
|
* Return ``0`` on success.
|
||||||
|
* Set an error in *config* and return ``-1`` on error.
|
||||||
|
|
||||||
|
``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`` on exit.
|
||||||
|
|
||||||
|
Error handling
|
||||||
|
--------------
|
||||||
|
|
||||||
|
``int PyInitConfig_Exception(PyInitConfig* config)``:
|
||||||
|
Check if an exception is set in *config*:
|
||||||
|
|
||||||
|
* Return non-zero if an error was set or if an exit code was set.
|
||||||
|
* Return zero otherwise.
|
||||||
|
|
||||||
|
``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.
|
||||||
|
|
||||||
|
The error message remains valid until a ``PyInitConfig`` function is
|
||||||
|
called with *config*. The caller doesn't have to free the error
|
||||||
|
message.
|
||||||
|
|
||||||
|
``int PyInitConfig_GetExitCode(PyInitConfig* config, int *exitcode)``:
|
||||||
|
Get the *config* exit code.
|
||||||
|
|
||||||
|
* Set *\*exitcode* and return ``1`` if an exit code is set.
|
||||||
|
* Return ``0`` otherwise.
|
||||||
|
|
||||||
|
|
||||||
|
``void Py_ExitWithInitConfig(PyInitConfig *config)``:
|
||||||
|
Exit Python and free memory of an initialization configuration.
|
||||||
|
|
||||||
|
If an error message is set, display the error message.
|
||||||
|
|
||||||
|
If an exit code is set, use it to exit the process.
|
||||||
|
|
||||||
|
The function does not return.
|
||||||
|
|
||||||
|
Get current configuration
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
``PyObject* PyConfig_Get(const char *name)``:
|
||||||
|
Get the current value of a configuration option as an object.
|
||||||
|
|
||||||
|
* Return a new reference on success.
|
||||||
|
* Set an exception and return ``NULL`` on error.
|
||||||
|
|
||||||
|
The object type depends on the option.
|
||||||
|
|
||||||
|
``int PyConfig_GetInt(const char *name, int *value)``:
|
||||||
|
Similar to ``PyConfig_Get()``, but get the value as an integer.
|
||||||
|
|
||||||
|
* Set ``*value`` and return ``0`` success.
|
||||||
|
* Set an exception and return ``-1`` on error.
|
||||||
|
|
||||||
|
sys.get_config()
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Add ``sys.get_config(name: str)`` function which calls
|
||||||
|
``PyConfig_Get()``:
|
||||||
|
|
||||||
|
* Return the configuration option value on success.
|
||||||
|
* Raise an exception on error.
|
||||||
|
|
||||||
|
Custom configuration options
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
It is possible to set custom configuration options, not used by Python
|
||||||
|
but only by third-party code, by calling:
|
||||||
|
``PyInitConfig_SetInt(config, "allow_custom_options", 1)``. In this
|
||||||
|
case, setting custom configuration options is accepted, rather than
|
||||||
|
failing with an "unknown option" error. By default, setting custom
|
||||||
|
configuration options is not allowed.
|
||||||
|
|
||||||
|
Custom configuration options are set with the ``PyInitConfig`` API, such
|
||||||
|
as ``PyInitConfig_SetInt()``, and can be got later with the
|
||||||
|
``PyConfig_Get()`` API.
|
||||||
|
|
||||||
|
To avoid conflicts with future Python configuration options, it is
|
||||||
|
recommended to use a prefix separated by a colon. For example, an
|
||||||
|
application called ``myapp`` can use the ``"myapp:verbose"`` option name
|
||||||
|
instead of ``"verbose"`` name, to avoid conflict with the Python
|
||||||
|
``verbose`` option.
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
|
Initialize Python
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Example setting some configuration options of different types to
|
||||||
|
initialize Python.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
void init_python(void)
|
||||||
|
{
|
||||||
|
PyInitConfig *config = PyInitConfig_Python_New();
|
||||||
|
if (config == NULL) {
|
||||||
|
printf("Init allocation error\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an integer (dev_mode)
|
||||||
|
if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a list of wide strings (argv)
|
||||||
|
wchar_t *argv[] = {L"my_program"", L"-c", L"pass"};
|
||||||
|
if (PyInitConfig_SetWStrList(config, "argv",
|
||||||
|
Py_ARRAY_LENGTH(argv), argv) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a wide string (program_name)
|
||||||
|
if (PyInitConfig_SetWStr(config, "program_name", L"my_program") < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a list of bytes strings (xoptions).
|
||||||
|
// Preinitialize implicitly Python to decode the bytes string.
|
||||||
|
char* xoptions[] = {"faulthandler"};
|
||||||
|
if (PyInitConfig_SetStrList(config, "xoptions",
|
||||||
|
Py_ARRAY_LENGTH(xoptions), xoptions) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Python with the configuration
|
||||||
|
if (Py_InitializeFromInitConfig(config) < 0) {
|
||||||
|
Py_ExitWithInitConfig(config);
|
||||||
|
}
|
||||||
|
PyInitConfig_Free(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Get the verbose option
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Example getting the configuration option ``verbose``:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
int get_verbose(void)
|
||||||
|
{
|
||||||
|
int verbose;
|
||||||
|
if (PyConfig_GetInt("verbose", &verbose) < 0) {
|
||||||
|
// Silently ignore the error
|
||||||
|
PyErr_Clear();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return verbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
On error, the function silently ignores the error and returns ``-1``.
|
||||||
|
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
==============
|
||||||
|
|
||||||
|
* Issue: `No limited C API to customize Python initialization
|
||||||
|
<https://github.com/python/cpython/issues/107954>`_
|
||||||
|
* PR: `Add PyInitConfig C API
|
||||||
|
<https://github.com/python/cpython/pull/110176>`_
|
||||||
|
* PR: `Add PyConfig_Get() function
|
||||||
|
<https://github.com/python/cpython/pull/112609>`_
|
||||||
|
|
||||||
|
|
||||||
|
Backwards Compatibility
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Changes are fully backward compatible. Only new APIs are added.
|
||||||
|
Existing API such as the ``PyConfig`` C API are left unchanged.
|
||||||
|
|
||||||
|
|
||||||
|
Discussions
|
||||||
|
===========
|
||||||
|
|
||||||
|
* `FR: Allow private runtime config to enable extending without breaking
|
||||||
|
the PyConfig ABI
|
||||||
|
<https://discuss.python.org/t/fr-allow-private-runtime-config-to-enable-extending-without-breaking-the-pyconfig-abi/18004>`__
|
||||||
|
(August 2022).
|
||||||
|
|
||||||
|
|
||||||
|
Rejected Ideas
|
||||||
|
==============
|
||||||
|
|
||||||
|
Configuration as text
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
It was proposed to provide the configuration as text to make the API
|
||||||
|
compatible with the stable ABI and to allow custom options.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
# integer
|
||||||
|
bytes_warning = 2
|
||||||
|
|
||||||
|
# string
|
||||||
|
filesystem_encoding = "utf8" # comment
|
||||||
|
|
||||||
|
# list of strings
|
||||||
|
argv = ['python', '-c', 'code']
|
||||||
|
|
||||||
|
The API would take the configuration as a string, not as a file. Example
|
||||||
|
with a hypothetical ``PyInit_SetConfig()`` function:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
void stable_abi_init_demo(int set_path)
|
||||||
|
{
|
||||||
|
PyInit_SetConfig(
|
||||||
|
"isolated = 1\n"
|
||||||
|
"argv = ['python', '-c', 'code']\n"
|
||||||
|
"filesystem_encoding = 'utf-8'\n"
|
||||||
|
);
|
||||||
|
if (set_path) {
|
||||||
|
PyInit_SetConfig("pythonpath = '/my/path'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The example ignores error handling to make it easier to read.
|
||||||
|
|
||||||
|
The problem is that generating such configuration text requires adding
|
||||||
|
quotes to strings and to escape quotes in strings. Formatting an array
|
||||||
|
of strings becomes non-trivial.
|
||||||
|
|
||||||
|
Providing an API to format a string or an array of strings is not really
|
||||||
|
worth it, whereas Python can provide directly an API to set a
|
||||||
|
configuration option where the value is passed directly as a string or
|
||||||
|
an array of strings. It avoids giving special meaning to some
|
||||||
|
characters, such as newline characters, which would have to be escaped.
|
||||||
|
|
||||||
|
|
||||||
|
Refer to an option with an integer
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Using strings to refer to a configuration option requires comparing
|
||||||
|
strings which can be slower than comparing integers.
|
||||||
|
|
||||||
|
Use integers, similar to type "slots" such as ``Py_tp_doc``, to refer to
|
||||||
|
a configuration option. The ``const char *name`` parameter is replaced
|
||||||
|
with ``int option``.
|
||||||
|
|
||||||
|
Accepting custom options is more likely to cause conflicts when using
|
||||||
|
integers, since it's harder to maintain "namespaces" (ranges) for
|
||||||
|
integer options. Using strings, a simple prefix with a colon separator
|
||||||
|
can be used.
|
||||||
|
|
||||||
|
Integers also requires maintaining a list of integer constants and so
|
||||||
|
make the C API and the Python API larger.
|
||||||
|
|
||||||
|
Python 3.13 only has around 62 configuration options, and so performance
|
||||||
|
is not really a blocker issue. If better performance is needed later, a
|
||||||
|
hash table can be used to get an option by its name.
|
||||||
|
|
||||||
|
If getting a configuration option is used in hot code, the value can be
|
||||||
|
read once and cached. By the way, most configuration options cannot be
|
||||||
|
changed at runtime.
|
||||||
|
|
||||||
|
|
||||||
|
Copyright
|
||||||
|
=========
|
||||||
|
|
||||||
|
This document is placed in the public domain or under the
|
||||||
|
CC0-1.0-Universal license, whichever is more permissive.
|
Loading…
Reference in New Issue