mirror of https://github.com/python/cpython
gh-107954, PEP 741: Add PyInitConfig C API (#123502)
Add Doc/c-api/config.rst documentation.
This commit is contained in:
parent
9e079c220b
commit
ef9d54703f
|
@ -7,7 +7,7 @@
|
||||||
Initialization, Finalization, and Threads
|
Initialization, Finalization, and Threads
|
||||||
*****************************************
|
*****************************************
|
||||||
|
|
||||||
See also :ref:`Python Initialization Configuration <init-config>`.
|
See also the :ref:`Python Initialization Configuration <init-config>`.
|
||||||
|
|
||||||
.. _pre-init-safe:
|
.. _pre-init-safe:
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
Python Initialization Configuration
|
Python Initialization Configuration
|
||||||
***********************************
|
***********************************
|
||||||
|
|
||||||
|
PyConfig C API
|
||||||
|
==============
|
||||||
|
|
||||||
.. versionadded:: 3.8
|
.. versionadded:: 3.8
|
||||||
|
|
||||||
Python can be initialized with :c:func:`Py_InitializeFromConfig` and the
|
Python can be initialized with :c:func:`Py_InitializeFromConfig` and the
|
||||||
|
@ -34,7 +37,7 @@ See also :ref:`Initialization, Finalization, and Threads <initialization>`.
|
||||||
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
-------
|
||||||
|
|
||||||
Example of customized Python always running in isolated mode::
|
Example of customized Python always running in isolated mode::
|
||||||
|
|
||||||
|
@ -73,7 +76,7 @@ Example of customized Python always running in isolated mode::
|
||||||
|
|
||||||
|
|
||||||
PyWideStringList
|
PyWideStringList
|
||||||
================
|
----------------
|
||||||
|
|
||||||
.. c:type:: PyWideStringList
|
.. c:type:: PyWideStringList
|
||||||
|
|
||||||
|
@ -116,7 +119,7 @@ PyWideStringList
|
||||||
List items.
|
List items.
|
||||||
|
|
||||||
PyStatus
|
PyStatus
|
||||||
========
|
--------
|
||||||
|
|
||||||
.. c:type:: PyStatus
|
.. c:type:: PyStatus
|
||||||
|
|
||||||
|
@ -210,7 +213,7 @@ Example::
|
||||||
|
|
||||||
|
|
||||||
PyPreConfig
|
PyPreConfig
|
||||||
===========
|
-----------
|
||||||
|
|
||||||
.. c:type:: PyPreConfig
|
.. c:type:: PyPreConfig
|
||||||
|
|
||||||
|
@ -360,7 +363,7 @@ PyPreConfig
|
||||||
.. _c-preinit:
|
.. _c-preinit:
|
||||||
|
|
||||||
Preinitialize Python with PyPreConfig
|
Preinitialize Python with PyPreConfig
|
||||||
=====================================
|
-------------------------------------
|
||||||
|
|
||||||
The preinitialization of Python:
|
The preinitialization of Python:
|
||||||
|
|
||||||
|
@ -440,7 +443,7 @@ the :ref:`Python UTF-8 Mode <utf8-mode>`::
|
||||||
|
|
||||||
|
|
||||||
PyConfig
|
PyConfig
|
||||||
========
|
--------
|
||||||
|
|
||||||
.. c:type:: PyConfig
|
.. c:type:: PyConfig
|
||||||
|
|
||||||
|
@ -1349,7 +1352,7 @@ the :option:`-X` command line option.
|
||||||
|
|
||||||
|
|
||||||
Initialization with PyConfig
|
Initialization with PyConfig
|
||||||
============================
|
----------------------------
|
||||||
|
|
||||||
Function to initialize Python:
|
Function to initialize Python:
|
||||||
|
|
||||||
|
@ -1461,7 +1464,7 @@ initialization::
|
||||||
.. _init-isolated-conf:
|
.. _init-isolated-conf:
|
||||||
|
|
||||||
Isolated Configuration
|
Isolated Configuration
|
||||||
======================
|
----------------------
|
||||||
|
|
||||||
:c:func:`PyPreConfig_InitIsolatedConfig` and
|
:c:func:`PyPreConfig_InitIsolatedConfig` and
|
||||||
:c:func:`PyConfig_InitIsolatedConfig` functions create a configuration to
|
:c:func:`PyConfig_InitIsolatedConfig` functions create a configuration to
|
||||||
|
@ -1481,7 +1484,7 @@ to avoid computing the default path configuration.
|
||||||
.. _init-python-config:
|
.. _init-python-config:
|
||||||
|
|
||||||
Python Configuration
|
Python Configuration
|
||||||
====================
|
--------------------
|
||||||
|
|
||||||
:c:func:`PyPreConfig_InitPythonConfig` and :c:func:`PyConfig_InitPythonConfig`
|
:c:func:`PyPreConfig_InitPythonConfig` and :c:func:`PyConfig_InitPythonConfig`
|
||||||
functions create a configuration to build a customized Python which behaves as
|
functions create a configuration to build a customized Python which behaves as
|
||||||
|
@ -1499,7 +1502,7 @@ and :ref:`Python UTF-8 Mode <utf8-mode>`
|
||||||
.. _init-path-config:
|
.. _init-path-config:
|
||||||
|
|
||||||
Python Path Configuration
|
Python Path Configuration
|
||||||
=========================
|
-------------------------
|
||||||
|
|
||||||
:c:type:`PyConfig` contains multiple fields for the path configuration:
|
:c:type:`PyConfig` contains multiple fields for the path configuration:
|
||||||
|
|
||||||
|
@ -1585,6 +1588,228 @@ The ``__PYVENV_LAUNCHER__`` environment variable is used to set
|
||||||
:c:member:`PyConfig.base_executable`.
|
:c:member:`PyConfig.base_executable`.
|
||||||
|
|
||||||
|
|
||||||
|
PyInitConfig C API
|
||||||
|
==================
|
||||||
|
|
||||||
|
C API to configure the Python initialization (:pep:`741`).
|
||||||
|
|
||||||
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
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*.
|
||||||
|
|
||||||
|
|
||||||
|
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 *config* has an exit code set.
|
||||||
|
* 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``).
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
*\*value* can be set to ``NULL`` if the option is an optional string and the
|
||||||
|
option is unset.
|
||||||
|
|
||||||
|
On success, the string must be released with ``free(value)`` if it's not
|
||||||
|
``NULL``.
|
||||||
|
|
||||||
|
|
||||||
|
.. 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.
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Display the error message
|
||||||
|
const char *err_msg;
|
||||||
|
error:
|
||||||
|
(void)PyInitConfig_GetError(config, &err_msg);
|
||||||
|
printf("PYTHON INIT ERROR: %s\n", err_msg);
|
||||||
|
PyInitConfig_Free(config);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Py_RunMain()
|
Py_RunMain()
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
|
@ -500,7 +500,8 @@ New Features
|
||||||
* Add :c:func:`Py_HashBuffer` to compute and return the hash value of a buffer.
|
* Add :c:func:`Py_HashBuffer` to compute and return the hash value of a buffer.
|
||||||
(Contributed by Antoine Pitrou and Victor Stinner in :gh:`122854`.)
|
(Contributed by Antoine Pitrou and Victor Stinner in :gh:`122854`.)
|
||||||
|
|
||||||
* Add functions to get and set the current runtime Python configuration:
|
* Add functions to get and set the current runtime Python configuration
|
||||||
|
(:pep:`741`):
|
||||||
|
|
||||||
* :c:func:`PyConfig_Get`
|
* :c:func:`PyConfig_Get`
|
||||||
* :c:func:`PyConfig_GetInt`
|
* :c:func:`PyConfig_GetInt`
|
||||||
|
@ -509,6 +510,24 @@ New Features
|
||||||
|
|
||||||
(Contributed by Victor Stinner in :gh:`107954`.)
|
(Contributed by Victor Stinner in :gh:`107954`.)
|
||||||
|
|
||||||
|
* Add functions to configure the Python initialization (:pep:`741`):
|
||||||
|
|
||||||
|
* :c:func:`PyInitConfig_Create`
|
||||||
|
* :c:func:`PyInitConfig_Free`
|
||||||
|
* :c:func:`PyInitConfig_GetError`
|
||||||
|
* :c:func:`PyInitConfig_GetExitCode`
|
||||||
|
* :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`
|
||||||
|
|
||||||
|
(Contributed by Victor Stinner in :gh:`107954`.)
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.14
|
Porting to Python 3.14
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -275,6 +275,47 @@ PyAPI_FUNC(int) PyConfig_Set(const char *name, PyObject *value);
|
||||||
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);
|
||||||
|
|
||||||
|
|
||||||
|
// --- PyInitConfig ---------------------------------------------------------
|
||||||
|
|
||||||
|
typedef struct PyInitConfig PyInitConfig;
|
||||||
|
|
||||||
|
PyAPI_FUNC(PyInitConfig*) PyInitConfig_Create(void);
|
||||||
|
PyAPI_FUNC(void) PyInitConfig_Free(PyInitConfig *config);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_GetError(PyInitConfig* config,
|
||||||
|
const char **err_msg);
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_GetExitCode(PyInitConfig* config,
|
||||||
|
int *exitcode);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_HasOption(PyInitConfig *config,
|
||||||
|
const char *name);
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_GetInt(PyInitConfig *config,
|
||||||
|
const char *name,
|
||||||
|
int64_t *value);
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_GetStr(PyInitConfig *config,
|
||||||
|
const char *name,
|
||||||
|
char **value);
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_GetStrList(PyInitConfig *config,
|
||||||
|
const char *name,
|
||||||
|
size_t *length,
|
||||||
|
char ***items);
|
||||||
|
PyAPI_FUNC(void) PyInitConfig_FreeStrList(size_t length, char **items);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_SetInt(PyInitConfig *config,
|
||||||
|
const char *name,
|
||||||
|
int64_t value);
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_SetStr(PyInitConfig *config,
|
||||||
|
const char *name,
|
||||||
|
const char *value);
|
||||||
|
PyAPI_FUNC(int) PyInitConfig_SetStrList(PyInitConfig *config,
|
||||||
|
const char *name,
|
||||||
|
size_t length,
|
||||||
|
char * const *items);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) Py_InitializeFromInitConfig(PyInitConfig *config);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -494,6 +494,14 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
self.assertEqual(out, '1\n2\n3\n' * INIT_LOOPS)
|
self.assertEqual(out, '1\n2\n3\n' * INIT_LOOPS)
|
||||||
|
|
||||||
|
|
||||||
|
def config_dev_mode(preconfig, config):
|
||||||
|
preconfig['allocator'] = PYMEM_ALLOCATOR_DEBUG
|
||||||
|
preconfig['dev_mode'] = 1
|
||||||
|
config['dev_mode'] = 1
|
||||||
|
config['warnoptions'] = ['default']
|
||||||
|
config['faulthandler'] = 1
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
|
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
|
||||||
class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
maxDiff = 4096
|
maxDiff = 4096
|
||||||
|
@ -1102,23 +1110,20 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
api=API_COMPAT)
|
api=API_COMPAT)
|
||||||
|
|
||||||
def test_init_dev_mode(self):
|
def test_init_dev_mode(self):
|
||||||
preconfig = {
|
preconfig = {}
|
||||||
'allocator': PYMEM_ALLOCATOR_DEBUG,
|
|
||||||
}
|
|
||||||
config = {
|
config = {
|
||||||
'faulthandler': True,
|
'faulthandler': True,
|
||||||
'dev_mode': True,
|
'dev_mode': True,
|
||||||
'warnoptions': ['default'],
|
'warnoptions': ['default'],
|
||||||
}
|
}
|
||||||
|
config_dev_mode(preconfig, config)
|
||||||
self.check_all_configs("test_init_dev_mode", config, preconfig,
|
self.check_all_configs("test_init_dev_mode", config, preconfig,
|
||||||
api=API_PYTHON)
|
api=API_PYTHON)
|
||||||
|
|
||||||
def test_preinit_parse_argv(self):
|
def test_preinit_parse_argv(self):
|
||||||
# Pre-initialize implicitly using argv: make sure that -X dev
|
# Pre-initialize implicitly using argv: make sure that -X dev
|
||||||
# is used to configure the allocation in preinitialization
|
# is used to configure the allocation in preinitialization
|
||||||
preconfig = {
|
preconfig = {}
|
||||||
'allocator': PYMEM_ALLOCATOR_DEBUG,
|
|
||||||
}
|
|
||||||
config = {
|
config = {
|
||||||
'argv': ['script.py'],
|
'argv': ['script.py'],
|
||||||
'orig_argv': ['python3', '-X', 'dev', '-P', 'script.py'],
|
'orig_argv': ['python3', '-X', 'dev', '-P', 'script.py'],
|
||||||
|
@ -1129,6 +1134,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'xoptions': {'dev': True},
|
'xoptions': {'dev': True},
|
||||||
'safe_path': True,
|
'safe_path': True,
|
||||||
}
|
}
|
||||||
|
config_dev_mode(preconfig, config)
|
||||||
self.check_all_configs("test_preinit_parse_argv", config, preconfig,
|
self.check_all_configs("test_preinit_parse_argv", config, preconfig,
|
||||||
api=API_PYTHON)
|
api=API_PYTHON)
|
||||||
|
|
||||||
|
@ -1728,16 +1734,15 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'ignore:::PySys_AddWarnOption2', # PySys_AddWarnOption()
|
'ignore:::PySys_AddWarnOption2', # PySys_AddWarnOption()
|
||||||
'ignore:::PyConfig_BeforeRead', # PyConfig.warnoptions
|
'ignore:::PyConfig_BeforeRead', # PyConfig.warnoptions
|
||||||
'ignore:::PyConfig_AfterRead'] # PyWideStringList_Append()
|
'ignore:::PyConfig_AfterRead'] # PyWideStringList_Append()
|
||||||
preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG)
|
preconfig = {}
|
||||||
config = {
|
config = {
|
||||||
'dev_mode': 1,
|
|
||||||
'faulthandler': 1,
|
|
||||||
'bytes_warning': 1,
|
'bytes_warning': 1,
|
||||||
'warnoptions': warnoptions,
|
|
||||||
'orig_argv': ['python3',
|
'orig_argv': ['python3',
|
||||||
'-Wignore:::cmdline1',
|
'-Wignore:::cmdline1',
|
||||||
'-Wignore:::cmdline2'],
|
'-Wignore:::cmdline2'],
|
||||||
}
|
}
|
||||||
|
config_dev_mode(preconfig, config)
|
||||||
|
config['warnoptions'] = warnoptions
|
||||||
self.check_all_configs("test_init_warnoptions", config, preconfig,
|
self.check_all_configs("test_init_warnoptions", config, preconfig,
|
||||||
api=API_PYTHON)
|
api=API_PYTHON)
|
||||||
|
|
||||||
|
@ -1750,6 +1755,27 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
self.check_all_configs("test_init_set_config", config,
|
self.check_all_configs("test_init_set_config", config,
|
||||||
api=API_ISOLATED)
|
api=API_ISOLATED)
|
||||||
|
|
||||||
|
def test_initconfig_api(self):
|
||||||
|
preconfig = {
|
||||||
|
'configure_locale': True,
|
||||||
|
}
|
||||||
|
config = {
|
||||||
|
'pycache_prefix': 'conf_pycache_prefix',
|
||||||
|
'xoptions': {'faulthandler': True},
|
||||||
|
'hash_seed': 10,
|
||||||
|
'use_hash_seed': True,
|
||||||
|
}
|
||||||
|
config_dev_mode(preconfig, config)
|
||||||
|
config['faulthandler'] = 0
|
||||||
|
self.check_all_configs("test_initconfig_api", config, preconfig,
|
||||||
|
api=API_ISOLATED)
|
||||||
|
|
||||||
|
def test_initconfig_get_api(self):
|
||||||
|
self.run_embedded_interpreter("test_initconfig_get_api")
|
||||||
|
|
||||||
|
def test_initconfig_exit(self):
|
||||||
|
self.run_embedded_interpreter("test_initconfig_exit")
|
||||||
|
|
||||||
def test_get_argc_argv(self):
|
def test_get_argc_argv(self):
|
||||||
self.run_embedded_interpreter("test_get_argc_argv")
|
self.run_embedded_interpreter("test_get_argc_argv")
|
||||||
# ignore output
|
# ignore output
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
Add functions to configure the Python initialization (:pep:`741`):
|
||||||
|
|
||||||
|
* :c:func:`PyInitConfig_Create`
|
||||||
|
* :c:func:`PyInitConfig_Free`
|
||||||
|
* :c:func:`PyInitConfig_GetError`
|
||||||
|
* :c:func:`PyInitConfig_GetExitCode`
|
||||||
|
* :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`
|
||||||
|
|
||||||
|
Patch by Victor Stinner.
|
|
@ -5393,7 +5393,7 @@ _Py_DecodeUTF8_surrogateescape(const char *arg, Py_ssize_t arglen,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* UTF-8 encoder using the surrogateescape error handler .
|
/* UTF-8 encoder.
|
||||||
|
|
||||||
On success, return 0 and write the newly allocated character string (use
|
On success, return 0 and write the newly allocated character string (use
|
||||||
PyMem_Free() to free the memory) into *str.
|
PyMem_Free() to free the memory) into *str.
|
||||||
|
@ -15906,7 +15906,7 @@ encode_wstr_utf8(wchar_t *wstr, char **str, const char *name)
|
||||||
int res;
|
int res;
|
||||||
res = _Py_EncodeUTF8Ex(wstr, str, NULL, NULL, 1, _Py_ERROR_STRICT);
|
res = _Py_EncodeUTF8Ex(wstr, str, NULL, NULL, 1, _Py_ERROR_STRICT);
|
||||||
if (res == -2) {
|
if (res == -2) {
|
||||||
PyErr_Format(PyExc_RuntimeWarning, "cannot decode %s", name);
|
PyErr_Format(PyExc_RuntimeWarning, "cannot encode %s", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
|
|
@ -36,6 +36,7 @@ char **main_argv;
|
||||||
|
|
||||||
/* Use path starting with "./" avoids a search along the PATH */
|
/* Use path starting with "./" avoids a search along the PATH */
|
||||||
#define PROGRAM_NAME L"./_testembed"
|
#define PROGRAM_NAME L"./_testembed"
|
||||||
|
#define PROGRAM_NAME_UTF8 "./_testembed"
|
||||||
|
|
||||||
#define INIT_LOOPS 4
|
#define INIT_LOOPS 4
|
||||||
|
|
||||||
|
@ -1805,6 +1806,141 @@ static int test_init_set_config(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int test_initconfig_api(void)
|
||||||
|
{
|
||||||
|
PyInitConfig *config = PyInitConfig_Create();
|
||||||
|
if (config == NULL) {
|
||||||
|
printf("Init allocation error\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyInitConfig_SetInt(config, "configure_locale", 1) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyInitConfig_SetInt(config, "hash_seed", 10) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a UTF-8 string (program_name)
|
||||||
|
if (PyInitConfig_SetStr(config, "program_name", PROGRAM_NAME_UTF8) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a UTF-8 string (pycache_prefix)
|
||||||
|
if (PyInitConfig_SetStr(config, "pycache_prefix",
|
||||||
|
"conf_pycache_prefix") < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a list of UTF-8 strings (argv)
|
||||||
|
char* xoptions[] = {"faulthandler"};
|
||||||
|
if (PyInitConfig_SetStrList(config, "xoptions",
|
||||||
|
Py_ARRAY_LENGTH(xoptions), xoptions) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (Py_InitializeFromInitConfig(config) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
PyInitConfig_Free(config);
|
||||||
|
|
||||||
|
dump_config();
|
||||||
|
Py_Finalize();
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const char *err_msg;
|
||||||
|
error:
|
||||||
|
(void)PyInitConfig_GetError(config, &err_msg);
|
||||||
|
printf("Python init failed: %s\n", err_msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int test_initconfig_get_api(void)
|
||||||
|
{
|
||||||
|
PyInitConfig *config = PyInitConfig_Create();
|
||||||
|
if (config == NULL) {
|
||||||
|
printf("Init allocation error\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test PyInitConfig_HasOption()
|
||||||
|
assert(PyInitConfig_HasOption(config, "verbose") == 1);
|
||||||
|
assert(PyInitConfig_HasOption(config, "utf8_mode") == 1);
|
||||||
|
assert(PyInitConfig_HasOption(config, "non-existent") == 0);
|
||||||
|
|
||||||
|
// test PyInitConfig_GetInt()
|
||||||
|
int64_t value;
|
||||||
|
assert(PyInitConfig_GetInt(config, "dev_mode", &value) == 0);
|
||||||
|
assert(value == 0);
|
||||||
|
assert(PyInitConfig_SetInt(config, "dev_mode", 1) == 0);
|
||||||
|
assert(PyInitConfig_GetInt(config, "dev_mode", &value) == 0);
|
||||||
|
assert(value == 1);
|
||||||
|
|
||||||
|
// test PyInitConfig_GetInt() on a PyPreConfig option
|
||||||
|
assert(PyInitConfig_GetInt(config, "utf8_mode", &value) == 0);
|
||||||
|
assert(value == 0);
|
||||||
|
assert(PyInitConfig_SetInt(config, "utf8_mode", 1) == 0);
|
||||||
|
assert(PyInitConfig_GetInt(config, "utf8_mode", &value) == 0);
|
||||||
|
assert(value == 1);
|
||||||
|
|
||||||
|
// test PyInitConfig_GetStr()
|
||||||
|
char *str;
|
||||||
|
assert(PyInitConfig_SetStr(config, "program_name", PROGRAM_NAME_UTF8) == 0);
|
||||||
|
assert(PyInitConfig_GetStr(config, "program_name", &str) == 0);
|
||||||
|
assert(strcmp(str, PROGRAM_NAME_UTF8) == 0);
|
||||||
|
free(str);
|
||||||
|
|
||||||
|
// test PyInitConfig_GetStrList() and PyInitConfig_FreeStrList()
|
||||||
|
char* xoptions[] = {"faulthandler"};
|
||||||
|
assert(PyInitConfig_SetStrList(config, "xoptions",
|
||||||
|
Py_ARRAY_LENGTH(xoptions), xoptions) == 0);
|
||||||
|
size_t length;
|
||||||
|
char **items;
|
||||||
|
assert(PyInitConfig_GetStrList(config, "xoptions", &length, &items) == 0);
|
||||||
|
assert(length == 1);
|
||||||
|
assert(strcmp(items[0], "faulthandler") == 0);
|
||||||
|
PyInitConfig_FreeStrList(length, items);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int test_initconfig_exit(void)
|
||||||
|
{
|
||||||
|
PyInitConfig *config = PyInitConfig_Create();
|
||||||
|
if (config == NULL) {
|
||||||
|
printf("Init allocation error\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *argv[] = {PROGRAM_NAME_UTF8, "--help"};
|
||||||
|
assert(PyInitConfig_SetStrList(config, "argv",
|
||||||
|
Py_ARRAY_LENGTH(argv), argv) == 0);
|
||||||
|
|
||||||
|
assert(PyInitConfig_SetInt(config, "parse_argv", 1) == 0);
|
||||||
|
|
||||||
|
assert(Py_InitializeFromInitConfig(config) < 0);
|
||||||
|
|
||||||
|
int exitcode;
|
||||||
|
assert(PyInitConfig_GetExitCode(config, &exitcode) == 1);
|
||||||
|
assert(exitcode == 0);
|
||||||
|
|
||||||
|
const char *err_msg;
|
||||||
|
assert(PyInitConfig_GetError(config, &err_msg) == 1);
|
||||||
|
assert(strcmp(err_msg, "exit code 0") == 0);
|
||||||
|
|
||||||
|
PyInitConfig_Free(config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void configure_init_main(PyConfig *config)
|
static void configure_init_main(PyConfig *config)
|
||||||
{
|
{
|
||||||
wchar_t* argv[] = {
|
wchar_t* argv[] = {
|
||||||
|
@ -2223,6 +2359,9 @@ static struct TestCase TestCases[] = {
|
||||||
{"test_init_is_python_build", test_init_is_python_build},
|
{"test_init_is_python_build", test_init_is_python_build},
|
||||||
{"test_init_warnoptions", test_init_warnoptions},
|
{"test_init_warnoptions", test_init_warnoptions},
|
||||||
{"test_init_set_config", test_init_set_config},
|
{"test_init_set_config", test_init_set_config},
|
||||||
|
{"test_initconfig_api", test_initconfig_api},
|
||||||
|
{"test_initconfig_get_api", test_initconfig_get_api},
|
||||||
|
{"test_initconfig_exit", test_initconfig_exit},
|
||||||
{"test_run_main", test_run_main},
|
{"test_run_main", test_run_main},
|
||||||
{"test_run_main_loop", test_run_main_loop},
|
{"test_run_main_loop", test_run_main_loop},
|
||||||
{"test_get_argc_argv", test_get_argc_argv},
|
{"test_get_argc_argv", test_get_argc_argv},
|
||||||
|
|
|
@ -1114,6 +1114,20 @@ PyConfig_SetBytesString(PyConfig *config, wchar_t **config_str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void*
|
||||||
|
config_get_spec_member(const PyConfig *config, const PyConfigSpec *spec)
|
||||||
|
{
|
||||||
|
return (char *)config + spec->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void*
|
||||||
|
preconfig_get_spec_member(const PyPreConfig *preconfig, const PyConfigSpec *spec)
|
||||||
|
{
|
||||||
|
return (char *)preconfig + spec->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyStatus
|
PyStatus
|
||||||
_PyConfig_Copy(PyConfig *config, const PyConfig *config2)
|
_PyConfig_Copy(PyConfig *config, const PyConfig *config2)
|
||||||
{
|
{
|
||||||
|
@ -1122,8 +1136,8 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
|
||||||
PyStatus status;
|
PyStatus status;
|
||||||
const PyConfigSpec *spec = PYCONFIG_SPEC;
|
const PyConfigSpec *spec = PYCONFIG_SPEC;
|
||||||
for (; spec->name != NULL; spec++) {
|
for (; spec->name != NULL; spec++) {
|
||||||
char *member = (char *)config + spec->offset;
|
void *member = config_get_spec_member(config, spec);
|
||||||
char *member2 = (char *)config2 + spec->offset;
|
const void *member2 = config_get_spec_member((PyConfig*)config2, spec);
|
||||||
switch (spec->type) {
|
switch (spec->type) {
|
||||||
case PyConfig_MEMBER_INT:
|
case PyConfig_MEMBER_INT:
|
||||||
case PyConfig_MEMBER_UINT:
|
case PyConfig_MEMBER_UINT:
|
||||||
|
@ -3405,15 +3419,478 @@ _Py_DumpPathConfig(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- PyConfig_Get() -------------------------------------------------------
|
// --- PyInitConfig API ---------------------------------------------------
|
||||||
|
|
||||||
static void*
|
struct PyInitConfig {
|
||||||
config_spec_get_member(const PyConfigSpec *spec, const PyConfig *config)
|
PyPreConfig preconfig;
|
||||||
|
PyConfig config;
|
||||||
|
PyStatus status;
|
||||||
|
char *err_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyInitConfig*
|
||||||
|
initconfig_alloc(void)
|
||||||
{
|
{
|
||||||
return (char *)config + spec->offset;
|
return calloc(1, sizeof(PyInitConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyInitConfig*
|
||||||
|
PyInitConfig_Create(void)
|
||||||
|
{
|
||||||
|
PyInitConfig *config = initconfig_alloc();
|
||||||
|
if (config == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyPreConfig_InitIsolatedConfig(&config->preconfig);
|
||||||
|
PyConfig_InitIsolatedConfig(&config->config);
|
||||||
|
config->status = _PyStatus_OK();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PyInitConfig_Free(PyInitConfig *config)
|
||||||
|
{
|
||||||
|
free(config->err_msg);
|
||||||
|
free(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_GetError(PyInitConfig* config, const char **perr_msg)
|
||||||
|
{
|
||||||
|
if (_PyStatus_IS_EXIT(config->status)) {
|
||||||
|
char buffer[22]; // len("exit code -2147483648\0")
|
||||||
|
PyOS_snprintf(buffer, sizeof(buffer),
|
||||||
|
"exit code %i",
|
||||||
|
config->status.exitcode);
|
||||||
|
|
||||||
|
if (config->err_msg != NULL) {
|
||||||
|
free(config->err_msg);
|
||||||
|
}
|
||||||
|
config->err_msg = strdup(buffer);
|
||||||
|
if (config->err_msg != NULL) {
|
||||||
|
*perr_msg = config->err_msg;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
config->status = _PyStatus_NO_MEMORY();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_PyStatus_IS_ERROR(config->status) && config->status.err_msg != NULL) {
|
||||||
|
*perr_msg = config->status.err_msg;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*perr_msg = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_GetExitCode(PyInitConfig* config, int *exitcode)
|
||||||
|
{
|
||||||
|
if (_PyStatus_IS_EXIT(config->status)) {
|
||||||
|
*exitcode = config->status.exitcode;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
initconfig_set_error(PyInitConfig *config, const char *err_msg)
|
||||||
|
{
|
||||||
|
config->status = _PyStatus_ERR(err_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const PyConfigSpec*
|
||||||
|
initconfig_find_spec(const PyConfigSpec *spec, const char *name)
|
||||||
|
{
|
||||||
|
for (; spec->name != NULL; spec++) {
|
||||||
|
if (strcmp(name, spec->name) == 0) {
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_HasOption(PyInitConfig *config, const char *name)
|
||||||
|
{
|
||||||
|
const PyConfigSpec *spec = initconfig_find_spec(PYCONFIG_SPEC, name);
|
||||||
|
if (spec == NULL) {
|
||||||
|
spec = initconfig_find_spec(PYPRECONFIG_SPEC, name);
|
||||||
|
}
|
||||||
|
return (spec != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const PyConfigSpec*
|
||||||
|
initconfig_prepare(PyInitConfig *config, const char *name, void **raw_member)
|
||||||
|
{
|
||||||
|
const PyConfigSpec *spec = initconfig_find_spec(PYCONFIG_SPEC, name);
|
||||||
|
if (spec != NULL) {
|
||||||
|
*raw_member = config_get_spec_member(&config->config, spec);
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
spec = initconfig_find_spec(PYPRECONFIG_SPEC, name);
|
||||||
|
if (spec != NULL) {
|
||||||
|
*raw_member = preconfig_get_spec_member(&config->preconfig, spec);
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
initconfig_set_error(config, "unknown config option name");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_GetInt(PyInitConfig *config, const char *name, int64_t *value)
|
||||||
|
{
|
||||||
|
void *raw_member;
|
||||||
|
const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member);
|
||||||
|
if (spec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (spec->type) {
|
||||||
|
case PyConfig_MEMBER_INT:
|
||||||
|
case PyConfig_MEMBER_UINT:
|
||||||
|
case PyConfig_MEMBER_BOOL:
|
||||||
|
{
|
||||||
|
int *member = raw_member;
|
||||||
|
*value = *member;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_ULONG:
|
||||||
|
{
|
||||||
|
unsigned long *member = raw_member;
|
||||||
|
#if SIZEOF_LONG >= 8
|
||||||
|
if ((unsigned long)INT64_MAX < *member) {
|
||||||
|
initconfig_set_error(config,
|
||||||
|
"config option value doesn't fit into int64_t");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
*value = *member;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
initconfig_set_error(config, "config option type is not int");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char*
|
||||||
|
wstr_to_utf8(PyInitConfig *config, wchar_t *wstr)
|
||||||
|
{
|
||||||
|
char *utf8;
|
||||||
|
int res = _Py_EncodeUTF8Ex(wstr, &utf8, NULL, NULL, 1, _Py_ERROR_STRICT);
|
||||||
|
if (res == -2) {
|
||||||
|
initconfig_set_error(config, "encoding error");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (res < 0) {
|
||||||
|
config->status = _PyStatus_NO_MEMORY();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy to use the malloc() memory allocator
|
||||||
|
size_t size = strlen(utf8) + 1;
|
||||||
|
char *str = malloc(size);
|
||||||
|
if (str == NULL) {
|
||||||
|
PyMem_RawFree(utf8);
|
||||||
|
config->status = _PyStatus_NO_MEMORY();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(str, utf8, size);
|
||||||
|
PyMem_RawFree(utf8);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_GetStr(PyInitConfig *config, const char *name, char **value)
|
||||||
|
{
|
||||||
|
void *raw_member;
|
||||||
|
const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member);
|
||||||
|
if (spec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spec->type != PyConfig_MEMBER_WSTR
|
||||||
|
&& spec->type != PyConfig_MEMBER_WSTR_OPT)
|
||||||
|
{
|
||||||
|
initconfig_set_error(config, "config option type is not string");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t **member = raw_member;
|
||||||
|
if (*member == NULL) {
|
||||||
|
*value = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = wstr_to_utf8(config, *member);
|
||||||
|
if (*value == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_GetStrList(PyInitConfig *config, const char *name, size_t *length, char ***items)
|
||||||
|
{
|
||||||
|
void *raw_member;
|
||||||
|
const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member);
|
||||||
|
if (spec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spec->type != PyConfig_MEMBER_WSTR_LIST) {
|
||||||
|
initconfig_set_error(config, "config option type is not string list");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyWideStringList *list = raw_member;
|
||||||
|
*length = list->length;
|
||||||
|
|
||||||
|
*items = malloc(list->length * sizeof(char*));
|
||||||
|
if (*items == NULL) {
|
||||||
|
config->status = _PyStatus_NO_MEMORY();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Py_ssize_t i=0; i < list->length; i++) {
|
||||||
|
(*items)[i] = wstr_to_utf8(config, list->items[i]);
|
||||||
|
if ((*items)[i] == NULL) {
|
||||||
|
PyInitConfig_FreeStrList(i, *items);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PyInitConfig_FreeStrList(size_t length, char **items)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i < length; i++) {
|
||||||
|
free(items[i]);
|
||||||
|
}
|
||||||
|
free(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_SetInt(PyInitConfig *config, const char *name, int64_t value)
|
||||||
|
{
|
||||||
|
void *raw_member;
|
||||||
|
const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member);
|
||||||
|
if (spec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (spec->type) {
|
||||||
|
case PyConfig_MEMBER_INT:
|
||||||
|
{
|
||||||
|
if (value < (int64_t)INT_MIN || (int64_t)INT_MAX < value) {
|
||||||
|
initconfig_set_error(config,
|
||||||
|
"config option value is out of int range");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int int_value = (int)value;
|
||||||
|
|
||||||
|
int *member = raw_member;
|
||||||
|
*member = int_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_UINT:
|
||||||
|
case PyConfig_MEMBER_BOOL:
|
||||||
|
{
|
||||||
|
if (value < 0 || (uint64_t)UINT_MAX < (uint64_t)value) {
|
||||||
|
initconfig_set_error(config,
|
||||||
|
"config option value is out of unsigned int range");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int int_value = (int)value;
|
||||||
|
|
||||||
|
int *member = raw_member;
|
||||||
|
*member = int_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_ULONG:
|
||||||
|
{
|
||||||
|
if (value < 0 || (uint64_t)ULONG_MAX < (uint64_t)value) {
|
||||||
|
initconfig_set_error(config,
|
||||||
|
"config option value is out of unsigned long range");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
unsigned long ulong_value = (unsigned long)value;
|
||||||
|
|
||||||
|
unsigned long *member = raw_member;
|
||||||
|
*member = ulong_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
initconfig_set_error(config, "config option type is not int");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "hash_seed")) {
|
||||||
|
config->config.use_hash_seed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static wchar_t*
|
||||||
|
utf8_to_wstr(PyInitConfig *config, const char *str)
|
||||||
|
{
|
||||||
|
wchar_t *wstr;
|
||||||
|
size_t wlen;
|
||||||
|
int res = _Py_DecodeUTF8Ex(str, strlen(str), &wstr, &wlen, NULL, _Py_ERROR_STRICT);
|
||||||
|
if (res == -2) {
|
||||||
|
initconfig_set_error(config, "decoding error");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (res < 0) {
|
||||||
|
config->status = _PyStatus_NO_MEMORY();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy to use the malloc() memory allocator
|
||||||
|
size_t size = (wlen + 1) * sizeof(wchar_t);
|
||||||
|
wchar_t *wstr2 = malloc(size);
|
||||||
|
if (wstr2 == NULL) {
|
||||||
|
PyMem_RawFree(wstr);
|
||||||
|
config->status = _PyStatus_NO_MEMORY();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(wstr2, wstr, size);
|
||||||
|
PyMem_RawFree(wstr);
|
||||||
|
return wstr2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char* value)
|
||||||
|
{
|
||||||
|
void *raw_member;
|
||||||
|
const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member);
|
||||||
|
if (spec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spec->type != PyConfig_MEMBER_WSTR
|
||||||
|
&& spec->type != PyConfig_MEMBER_WSTR_OPT) {
|
||||||
|
initconfig_set_error(config, "config option type is not string");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == NULL && spec->type != PyConfig_MEMBER_WSTR_OPT) {
|
||||||
|
initconfig_set_error(config, "config option string cannot be NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t **member = raw_member;
|
||||||
|
|
||||||
|
*member = utf8_to_wstr(config, value);
|
||||||
|
if (*member == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
_PyWideStringList_FromUTF8(PyInitConfig *config, PyWideStringList *list,
|
||||||
|
Py_ssize_t length, char * const *items)
|
||||||
|
{
|
||||||
|
PyWideStringList wlist = _PyWideStringList_INIT;
|
||||||
|
size_t size = sizeof(wchar_t*) * length;
|
||||||
|
wlist.items = (wchar_t **)PyMem_RawMalloc(size);
|
||||||
|
if (wlist.items == NULL) {
|
||||||
|
config->status = _PyStatus_NO_MEMORY();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Py_ssize_t i = 0; i < length; i++) {
|
||||||
|
wchar_t *arg = utf8_to_wstr(config, items[i]);
|
||||||
|
if (arg == NULL) {
|
||||||
|
_PyWideStringList_Clear(&wlist);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
wlist.items[i] = arg;
|
||||||
|
wlist.length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_PyWideStringList_Clear(list);
|
||||||
|
*list = wlist;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyInitConfig_SetStrList(PyInitConfig *config, const char *name,
|
||||||
|
size_t length, char * const *items)
|
||||||
|
{
|
||||||
|
void *raw_member;
|
||||||
|
const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member);
|
||||||
|
if (spec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spec->type != PyConfig_MEMBER_WSTR_LIST) {
|
||||||
|
initconfig_set_error(config, "config option type is not strings list");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
PyWideStringList *list = raw_member;
|
||||||
|
return _PyWideStringList_FromUTF8(config, list, length, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
Py_InitializeFromInitConfig(PyInitConfig *config)
|
||||||
|
{
|
||||||
|
_PyPreConfig_GetConfig(&config->preconfig, &config->config);
|
||||||
|
|
||||||
|
config->status = Py_PreInitializeFromArgs(
|
||||||
|
&config->preconfig,
|
||||||
|
config->config.argv.length,
|
||||||
|
config->config.argv.items);
|
||||||
|
if (_PyStatus_EXCEPTION(config->status)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
config->status = Py_InitializeFromConfig(&config->config);
|
||||||
|
if (_PyStatus_EXCEPTION(config->status)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- PyConfig_Get() -------------------------------------------------------
|
||||||
|
|
||||||
static const PyConfigSpec*
|
static const PyConfigSpec*
|
||||||
config_generic_find_spec(const PyConfigSpec *spec, const char *name)
|
config_generic_find_spec(const PyConfigSpec *spec, const char *name)
|
||||||
{
|
{
|
||||||
|
@ -3554,7 +4031,7 @@ config_get(const PyConfig *config, const PyConfigSpec *spec,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *member = config_spec_get_member(spec, config);
|
void *member = config_get_spec_member(config, spec);
|
||||||
switch (spec->type) {
|
switch (spec->type) {
|
||||||
case PyConfig_MEMBER_INT:
|
case PyConfig_MEMBER_INT:
|
||||||
case PyConfig_MEMBER_UINT:
|
case PyConfig_MEMBER_UINT:
|
||||||
|
@ -3761,7 +4238,7 @@ config_set_sys_flag(const PyConfigSpec *spec, int int_value)
|
||||||
assert(spec->type == PyConfig_MEMBER_INT
|
assert(spec->type == PyConfig_MEMBER_INT
|
||||||
|| spec->type == PyConfig_MEMBER_UINT
|
|| spec->type == PyConfig_MEMBER_UINT
|
||||||
|| spec->type == PyConfig_MEMBER_BOOL);
|
|| spec->type == PyConfig_MEMBER_BOOL);
|
||||||
int *member = config_spec_get_member(spec, config);
|
int *member = config_get_spec_member(config, spec);
|
||||||
*member = int_value;
|
*member = int_value;
|
||||||
|
|
||||||
// Set sys.dont_write_bytecode attribute
|
// Set sys.dont_write_bytecode attribute
|
||||||
|
|
Loading…
Reference in New Issue