Add Doc/c-api/config.rst documentation.
Initialization, Finalization, and Threads
*****************************************
-See also :ref:`Python Initialization Configuration <init-config>`.
+See also the :ref:`Python Initialization Configuration <init-config>`.
.. _pre-init-safe:
Python Initialization Configuration
***********************************
+PyConfig C API
+==============
+
.. versionadded:: 3.8
Python can be initialized with :c:func:`Py_InitializeFromConfig` and the
Example
-=======
+-------
Example of customized Python always running in isolated mode::
PyWideStringList
-================
+----------------
.. c:type:: PyWideStringList
List items.
PyStatus
-========
+--------
.. c:type:: PyStatus
PyPreConfig
-===========
+-----------
.. c:type:: PyPreConfig
.. _c-preinit:
Preinitialize Python with PyPreConfig
-=====================================
+-------------------------------------
The preinitialization of Python:
PyConfig
-========
+--------
.. c:type:: PyConfig
Initialization with PyConfig
-============================
+----------------------------
Function to initialize Python:
.. _init-isolated-conf:
Isolated Configuration
-======================
+----------------------
:c:func:`PyPreConfig_InitIsolatedConfig` and
:c:func:`PyConfig_InitIsolatedConfig` functions create a configuration to
.. _init-python-config:
Python Configuration
-====================
+--------------------
:c:func:`PyPreConfig_InitPythonConfig` and :c:func:`PyConfig_InitPythonConfig`
functions create a configuration to build a customized Python which behaves as
.. _init-path-config:
Python Path Configuration
-=========================
+-------------------------
:c:type:`PyConfig` contains multiple fields for the path configuration:
: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()
============
* 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`.)
-* 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_GetInt`
(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
----------------------
See also PyConfig.orig_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
}
#endif
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")
class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
maxDiff = 4096
api=API_COMPAT)
def test_init_dev_mode(self):
- preconfig = {
- 'allocator': PYMEM_ALLOCATOR_DEBUG,
- }
+ preconfig = {}
config = {
'faulthandler': True,
'dev_mode': True,
'warnoptions': ['default'],
}
+ config_dev_mode(preconfig, config)
self.check_all_configs("test_init_dev_mode", config, preconfig,
api=API_PYTHON)
def test_preinit_parse_argv(self):
# Pre-initialize implicitly using argv: make sure that -X dev
# is used to configure the allocation in preinitialization
- preconfig = {
- 'allocator': PYMEM_ALLOCATOR_DEBUG,
- }
+ preconfig = {}
config = {
'argv': ['script.py'],
'orig_argv': ['python3', '-X', 'dev', '-P', 'script.py'],
'xoptions': {'dev': True},
'safe_path': True,
}
+ config_dev_mode(preconfig, config)
self.check_all_configs("test_preinit_parse_argv", config, preconfig,
api=API_PYTHON)
'ignore:::PySys_AddWarnOption2', # PySys_AddWarnOption()
'ignore:::PyConfig_BeforeRead', # PyConfig.warnoptions
'ignore:::PyConfig_AfterRead'] # PyWideStringList_Append()
- preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG)
+ preconfig = {}
config = {
- 'dev_mode': 1,
- 'faulthandler': 1,
'bytes_warning': 1,
- 'warnoptions': warnoptions,
'orig_argv': ['python3',
'-Wignore:::cmdline1',
'-Wignore:::cmdline2'],
}
+ config_dev_mode(preconfig, config)
+ config['warnoptions'] = warnoptions
self.check_all_configs("test_init_warnoptions", config, preconfig,
api=API_PYTHON)
self.check_all_configs("test_init_set_config", config,
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):
self.run_embedded_interpreter("test_get_argc_argv")
# ignore output
--- /dev/null
+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.
}
-/* UTF-8 encoder using the surrogateescape error handler .
+/* UTF-8 encoder.
On success, return 0 and write the newly allocated character string (use
PyMem_Free() to free the memory) into *str.
int res;
res = _Py_EncodeUTF8Ex(wstr, str, NULL, NULL, 1, _Py_ERROR_STRICT);
if (res == -2) {
- PyErr_Format(PyExc_RuntimeWarning, "cannot decode %s", name);
+ PyErr_Format(PyExc_RuntimeWarning, "cannot encode %s", name);
return -1;
}
if (res < 0) {
/* Use path starting with "./" avoids a search along the PATH */
#define PROGRAM_NAME L"./_testembed"
+#define PROGRAM_NAME_UTF8 "./_testembed"
#define INIT_LOOPS 4
}
+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)
{
wchar_t* argv[] = {
{"test_init_is_python_build", test_init_is_python_build},
{"test_init_warnoptions", test_init_warnoptions},
{"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_loop", test_run_main_loop},
{"test_get_argc_argv", test_get_argc_argv},
}
+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
_PyConfig_Copy(PyConfig *config, const PyConfig *config2)
{
PyStatus status;
const PyConfigSpec *spec = PYCONFIG_SPEC;
for (; spec->name != NULL; spec++) {
- char *member = (char *)config + spec->offset;
- char *member2 = (char *)config2 + spec->offset;
+ void *member = config_get_spec_member(config, spec);
+ const void *member2 = config_get_spec_member((PyConfig*)config2, spec);
switch (spec->type) {
case PyConfig_MEMBER_INT:
case PyConfig_MEMBER_UINT:
}
-// --- PyConfig_Get() -------------------------------------------------------
+// --- PyInitConfig API ---------------------------------------------------
-static void*
-config_spec_get_member(const PyConfigSpec *spec, const PyConfig *config)
+struct PyInitConfig {
+ 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*
config_generic_find_spec(const PyConfigSpec *spec, const char *name)
{
}
}
- void *member = config_spec_get_member(spec, config);
+ void *member = config_get_spec_member(config, spec);
switch (spec->type) {
case PyConfig_MEMBER_INT:
case PyConfig_MEMBER_UINT:
assert(spec->type == PyConfig_MEMBER_INT
|| spec->type == PyConfig_MEMBER_UINT
|| spec->type == PyConfig_MEMBER_BOOL);
- int *member = config_spec_get_member(spec, config);
+ int *member = config_get_spec_member(config, spec);
*member = int_value;
// Set sys.dont_write_bytecode attribute