Skip to content

Commit

Permalink
pythongh-107954: Add PyInitConfig C API
Browse files Browse the repository at this point in the history
Add PyInitConfig functions:

* PyInitConfig_Python_New()
* PyInitConfig_Isolated_New()
* PyInitConfig_Free(config)
* PyInitConfig_SetInt(config, key, value)
* PyInitConfig_SetString(config, key, value)
* PyInitConfig_SetList(config, key, length, items)
* PyInitConfig_GetErrorMsg(config)

Add also functions using it:

* Py_InitializeFromInitConfig(config)
* Py_ExitWithInitConfig(config)

Changes:

* Add Include/initconfig.h header.
  • Loading branch information
vstinner committed Oct 1, 2023
1 parent 62405c7 commit 7ea38bf
Show file tree
Hide file tree
Showing 6 changed files with 432 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
#include "sliceobject.h"
#include "cpython/cellobject.h"
#include "iterobject.h"
#include "cpython/initconfig.h"
#include "initconfig.h"
#include "pystate.h"
#include "cpython/genobject.h"
#include "descrobject.h"
Expand Down
13 changes: 2 additions & 11 deletions Include/cpython/initconfig.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#ifndef Py_PYCORECONFIG_H
#define Py_PYCORECONFIG_H
#ifndef Py_LIMITED_API
#ifdef __cplusplus
extern "C" {
#ifndef Py_CPYTHON_INITCONFIG_H
# error "this header file must not be included directly"
#endif

/* --- PyStatus ----------------------------------------------- */
Expand Down Expand Up @@ -252,9 +249,3 @@ PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config,
See also PyConfig.orig_argv. */
PyAPI_FUNC(void) Py_GetArgcArgv(int *argc, wchar_t ***argv);

#ifdef __cplusplus
}
#endif
#endif /* !Py_LIMITED_API */
#endif /* !Py_PYCORECONFIG_H */
64 changes: 64 additions & 0 deletions Include/initconfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef Py_INITCONFIG_H
#define Py_INITCONFIG_H
#ifdef __cplusplus
extern "C" {
#endif

typedef struct PyInitConfig PyInitConfig;

// Create a new initialization configuration.
// It must be freed by PyInitConfig_Free().
// Return NULL on memory allocation failure.
PyAPI_FUNC(PyInitConfig*) PyInitConfig_Python_New(void);
PyAPI_FUNC(PyInitConfig*) PyInitConfig_Isolated_New(void);

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

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

// Set a string configuration option.
// Return 0 on success, or return -1 on error.
PyAPI_FUNC(int) PyInitConfig_SetString(
PyInitConfig *config,
const char *key,
const wchar_t *value);

// Set a string configuration option.
// Return 0 on success, or return -1 on error.
PyAPI_FUNC(int) PyInitConfig_SetList(
PyInitConfig *config,
const char *key,
size_t length,
wchar_t **items);

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

// Get the current error message.
// Return a UTF-8 string allocated by malloc(). It must be released by free().
// Return NULL on memory allocation failure.
PyAPI_FUNC(char*) PyInitConfig_GetErrorMsg(PyInitConfig* config);

// Exit Python and free memory of a initialization configuration.
// The function does not return.
PyAPI_FUNC(void) _Py_NO_RETURN Py_ExitWithInitConfig(PyInitConfig *config);


#ifndef Py_LIMITED_API
# define Py_CPYTHON_INITCONFIG_H
# include "cpython/initconfig.h"
# undef Py_CPYTHON_INITCONFIG_H
#endif

#ifdef __cplusplus
}
#endif
#endif // !Py_INITCONFIG_H
45 changes: 29 additions & 16 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,14 @@ def test_ucnhash_capi_reset(self):
out, err = self.run_embedded_interpreter("test_repeated_init_exec", code)
self.assertEqual(out, '9\n' * INIT_LOOPS)


def config_dev_mode(preconfig, config):
preconfig['allocator'] = PYMEM_ALLOCATOR_DEBUG
config['dev_mode'] = 1
config['warnoptions'] = ['default']
config['faulthandler'] = 1


class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
maxDiff = 4096
UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape')
Expand Down Expand Up @@ -510,7 +518,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'check_hash_pycs_mode': 'default',
'pathconfig_warnings': 1,
'_init_main': 1,
'use_frozen_modules': not support.Py_DEBUG,
'use_frozen_modules': int(not support.Py_DEBUG),
'safe_path': 0,
'_is_python_build': IGNORE_CONFIG,
}
Expand Down Expand Up @@ -990,33 +998,26 @@ def test_init_env_dev_mode_alloc(self):
api=API_COMPAT)

def test_init_dev_mode(self):
preconfig = {
'allocator': PYMEM_ALLOCATOR_DEBUG,
}
preconfig = {}
config = {
'faulthandler': 1,
'dev_mode': 1,
'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'],
'run_filename': os.path.abspath('script.py'),
'dev_mode': 1,
'faulthandler': 1,
'warnoptions': ['default'],
'xoptions': ['dev'],
'safe_path': 1,
}
config_dev_mode(preconfig, config)
self.check_all_configs("test_preinit_parse_argv", config, preconfig,
api=API_PYTHON)

Expand Down Expand Up @@ -1615,16 +1616,15 @@ def test_init_warnoptions(self):
'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)

Expand All @@ -1637,6 +1637,19 @@ def test_init_set_config(self):
self.check_all_configs("test_init_set_config", config,
api=API_ISOLATED)

def test_initconfig_api(self):
preconfig = {}
config = {
'dev_mode': 1,
'pycache_prefix': 'conf_pycache_prefix',
'argv': ['-c'],
'orig_argv': ['./_testembed', '-c', 'pass'],
'run_command': 'pass\n',
}
config_dev_mode(preconfig, config)
self.check_all_configs("test_initconfig_api", config, preconfig,
api=API_PYTHON)

def test_get_argc_argv(self):
self.run_embedded_interpreter("test_get_argc_argv")
# ignore output
Expand Down
43 changes: 43 additions & 0 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,48 @@ static int test_init_set_config(void)
}


static int test_initconfig_api(void)
{
PyInitConfig *config = PyInitConfig_Python_New();
if (config == NULL) {
printf("Init allocation error\n");
return 1;
}

if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
goto error;
}

wchar_t *argv[] = {PROGRAM_NAME, L"-c", L"pass"};
if (PyInitConfig_SetList(config, "argv", Py_ARRAY_LENGTH(argv), argv) < 0) {
goto error;
}

if (PyInitConfig_SetInt(config, "hash_seed", 10) < 0) {
goto error;
}
if (PyInitConfig_SetString(config, "program_name", PROGRAM_NAME) < 0) {
goto error;
}
if (PyInitConfig_SetString(config, "pycache_prefix", L"conf_pycache_prefix") < 0) {
goto error;
}

if (Py_InitializeFromInitConfig(config) < 0) {
Py_ExitWithInitConfig(config);
}
PyInitConfig_Free(config);

dump_config();
Py_Finalize();
return 0;

error:
printf("Init failed:\n");
Py_ExitWithInitConfig(config);
}


static void configure_init_main(PyConfig *config)
{
wchar_t* argv[] = {
Expand Down Expand Up @@ -2131,6 +2173,7 @@ static struct TestCase TestCases[] = {
{"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_run_main", test_run_main},
{"test_run_main_loop", test_run_main_loop},
{"test_get_argc_argv", test_get_argc_argv},
Expand Down
Loading

0 comments on commit 7ea38bf

Please sign in to comment.