Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Lib/test/test_capi/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from test.support import import_helper

_testcapi = import_helper.import_module('_testcapi')
_testinternalcapi = import_helper.import_module('_testinternalcapi')


# Is the Py_STATS macro defined?
Expand Down Expand Up @@ -377,6 +378,46 @@ def expect_bool_not(value):
finally:
config_set(name, old_value)

def test_config_set_global_vars(self):
# Test PyConfig_Set() with global configuration variables
config_get = _testcapi.config_get
config_set = _testcapi.config_set
get_configs = _testinternalcapi.get_configs
new_values = (0, 1, 5)

for name, global_name, not_value in (
('bytes_warning', 'Py_BytesWarningFlag', False),
('inspect', 'Py_InspectFlag', False),
('interactive', 'Py_InteractiveFlag', False),
('optimization_level', 'Py_OptimizeFlag', False),
('parser_debug', 'Py_DebugFlag', False),
('quiet', 'Py_QuietFlag', False),
('use_environment', 'Py_IgnoreEnvironmentFlag', True),
('verbose', 'Py_VerboseFlag', False),
('write_bytecode', 'Py_DontWriteBytecodeFlag', True),

# Read-only variables
#('buffered_stdio', 'Py_UnbufferedStdioFlag', True),
#('isolated', 'Py_IsolatedFlag', False),
#('pathconfig_warnings', 'Py_FrozenFlag', True),
#('site_import', 'Py_NoSiteFlag', True),
#('user_site_directory', 'Py_NoUserSiteDirectory', True),
# Windows only
#('legacy_windows_stdio', 'Py_LegacyWindowsStdioFlag', False)
):
with self.subTest(name=name):
old_value = config_get(name)
try:
for value in new_values:
config_set(name, value)
global_config = get_configs()['global_config']
expected = value
if not_value:
expected = int(not value)
self.assertEqual(global_config[global_name], expected)
finally:
config_set(name, old_value)

def test_config_set_cpu_count(self):
config_get = _testcapi.config_get
config_set = _testcapi.config_set
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:c:func:`PyConfig_Set()` now also set global configuration variables. For
example, ``PyConfig_Set("inspect", value)`` now also sets
:c:var:`Py_InspectFlag`. Patch by Victor Stinner.
16 changes: 10 additions & 6 deletions Modules/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,15 @@ pymain_run_interactive_hook(int *exitcode)
static void
pymain_set_inspect(PyConfig *config, int inspect)
{
config->inspect = inspect;
_Py_COMP_DIAG_PUSH
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
Py_InspectFlag = inspect;
_Py_COMP_DIAG_POP
PyObject *value = PyLong_FromLong(inspect);
if (value == NULL || PyConfig_Set("inspect", value) < 0) {
fprintf(stderr, "Could not set the inspect flag\n");
PyErr_Print();
}
else {
assert(config->inspect == inspect);
}
Py_XDECREF(value);
}


Expand Down Expand Up @@ -615,7 +619,7 @@ pymain_run_python(int *exitcode)
{
PyObject *main_importer_path = NULL;
PyInterpreterState *interp = _PyInterpreterState_GET();
/* pymain_run_stdin() modify the config */
/* pymain_repl() and pymain_run_stdin() modify the config */
PyConfig *config = (PyConfig*)_PyInterpreterState_GetConfig(interp);

/* ensure path config is written into global variables */
Expand Down
188 changes: 111 additions & 77 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,118 +84,137 @@ typedef struct {
config_sys_flag_setter flag_setter;
} PyConfigSysSpec;

typedef struct {
int *ptr;
int not;
} PyConfigGlobalVar;

typedef struct {
const char *name;
size_t offset;
PyConfigMemberType type;
PyConfigMemberVisibility visibility;
PyConfigSysSpec sys;
PyConfigGlobalVar global_var;
} PyConfigSpec;

#define SPEC(MEMBER, TYPE, VISIBILITY, sys) \
#define SPEC(MEMBER, TYPE, VISIBILITY, sys, global_var) \
{#MEMBER, offsetof(PyConfig, MEMBER), \
PyConfig_MEMBER_##TYPE, PyConfig_MEMBER_##VISIBILITY, sys}
PyConfig_MEMBER_##TYPE, PyConfig_MEMBER_##VISIBILITY, sys, global_var}

#define SYS_ATTR(name) {name, -1, NULL}
#define SYS_FLAG_SETTER(index, setter) {NULL, index, setter}
#define SYS_FLAG(index) SYS_FLAG_SETTER(index, NULL)
#define NO_SYS SYS_ATTR(NULL)

#define GLOBAL(ptr, not) {ptr, not}
#define NO_GLOBAL GLOBAL(NULL, 0)

// Ignore deprecations on global variables such as Py_IsolatedFlag
_Py_COMP_DIAG_PUSH
_Py_COMP_DIAG_IGNORE_DEPR_DECLS

// Update _test_embed_set_config when adding new members
static const PyConfigSpec PYCONFIG_SPEC[] = {
// --- Public options -----------

SPEC(argv, WSTR_LIST, PUBLIC, SYS_ATTR("argv")),
SPEC(base_exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_exec_prefix")),
SPEC(base_executable, WSTR_OPT, PUBLIC, SYS_ATTR("_base_executable")),
SPEC(base_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_prefix")),
SPEC(bytes_warning, UINT, PUBLIC, SYS_FLAG(9)),
SPEC(cpu_count, INT, PUBLIC, NO_SYS),
SPEC(exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("exec_prefix")),
SPEC(executable, WSTR_OPT, PUBLIC, SYS_ATTR("executable")),
SPEC(inspect, BOOL, PUBLIC, SYS_FLAG(1)),
SPEC(int_max_str_digits, UINT, PUBLIC, NO_SYS),
SPEC(interactive, BOOL, PUBLIC, SYS_FLAG(2)),
SPEC(module_search_paths, WSTR_LIST, PUBLIC, SYS_ATTR("path")),
SPEC(optimization_level, UINT, PUBLIC, SYS_FLAG(3)),
SPEC(parser_debug, BOOL, PUBLIC, SYS_FLAG(0)),
SPEC(platlibdir, WSTR, PUBLIC, SYS_ATTR("platlibdir")),
SPEC(prefix, WSTR_OPT, PUBLIC, SYS_ATTR("prefix")),
SPEC(pycache_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("pycache_prefix")),
SPEC(quiet, BOOL, PUBLIC, SYS_FLAG(10)),
SPEC(stdlib_dir, WSTR_OPT, PUBLIC, SYS_ATTR("_stdlib_dir")),
SPEC(use_environment, BOOL, PUBLIC, SYS_FLAG_SETTER(7, config_sys_flag_not)),
SPEC(verbose, UINT, PUBLIC, SYS_FLAG(8)),
SPEC(warnoptions, WSTR_LIST, PUBLIC, SYS_ATTR("warnoptions")),
SPEC(write_bytecode, BOOL, PUBLIC, SYS_FLAG_SETTER(4, config_sys_flag_not)),
SPEC(xoptions, WSTR_LIST, PUBLIC, SYS_ATTR("_xoptions")),
SPEC(argv, WSTR_LIST, PUBLIC, SYS_ATTR("argv"), NO_GLOBAL),
SPEC(base_exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_exec_prefix"), NO_GLOBAL),
SPEC(base_executable, WSTR_OPT, PUBLIC, SYS_ATTR("_base_executable"), NO_GLOBAL),
SPEC(base_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_prefix"), NO_GLOBAL),
SPEC(bytes_warning, UINT, PUBLIC, SYS_FLAG(9), GLOBAL(&Py_BytesWarningFlag, 0)),
SPEC(cpu_count, INT, PUBLIC, NO_SYS, NO_GLOBAL),
SPEC(exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("exec_prefix"), NO_GLOBAL),
SPEC(executable, WSTR_OPT, PUBLIC, SYS_ATTR("executable"), NO_GLOBAL),
SPEC(inspect, BOOL, PUBLIC, SYS_FLAG(1), GLOBAL(&Py_InspectFlag, 0)),
SPEC(int_max_str_digits, UINT, PUBLIC, NO_SYS, NO_GLOBAL),
SPEC(interactive, BOOL, PUBLIC, SYS_FLAG(2), GLOBAL(&Py_InteractiveFlag, 0)),
SPEC(module_search_paths, WSTR_LIST, PUBLIC, SYS_ATTR("path"), NO_GLOBAL),
SPEC(optimization_level, UINT, PUBLIC, SYS_FLAG(3), GLOBAL(&Py_OptimizeFlag, 0)),
SPEC(parser_debug, BOOL, PUBLIC, SYS_FLAG(0), GLOBAL(&Py_DebugFlag, 0)),
SPEC(platlibdir, WSTR, PUBLIC, SYS_ATTR("platlibdir"), NO_GLOBAL),
SPEC(prefix, WSTR_OPT, PUBLIC, SYS_ATTR("prefix"), NO_GLOBAL),
SPEC(pycache_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("pycache_prefix"), NO_GLOBAL),
SPEC(quiet, BOOL, PUBLIC, SYS_FLAG(10), GLOBAL(&Py_QuietFlag, 0)),
SPEC(stdlib_dir, WSTR_OPT, PUBLIC, SYS_ATTR("_stdlib_dir"), NO_GLOBAL),
SPEC(use_environment, BOOL, PUBLIC,
SYS_FLAG_SETTER(7, config_sys_flag_not), GLOBAL(&Py_IgnoreEnvironmentFlag, 1)),
SPEC(verbose, UINT, PUBLIC, SYS_FLAG(8), GLOBAL(&Py_VerboseFlag, 0)),
SPEC(warnoptions, WSTR_LIST, PUBLIC, SYS_ATTR("warnoptions"), NO_GLOBAL),
SPEC(write_bytecode, BOOL, PUBLIC, SYS_FLAG_SETTER(4, config_sys_flag_not),
GLOBAL(&Py_DontWriteBytecodeFlag, 1)),
SPEC(xoptions, WSTR_LIST, PUBLIC, SYS_ATTR("_xoptions"), NO_GLOBAL),

// --- Read-only options -----------

#ifdef Py_STATS
SPEC(_pystats, BOOL, READ_ONLY, NO_SYS),
SPEC(_pystats, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
#endif
SPEC(buffered_stdio, BOOL, READ_ONLY, NO_SYS),
SPEC(check_hash_pycs_mode, WSTR, READ_ONLY, NO_SYS),
SPEC(code_debug_ranges, BOOL, READ_ONLY, NO_SYS),
SPEC(configure_c_stdio, BOOL, READ_ONLY, NO_SYS),
SPEC(dev_mode, BOOL, READ_ONLY, NO_SYS), // sys.flags.dev_mode
SPEC(dump_refs, BOOL, READ_ONLY, NO_SYS),
SPEC(dump_refs_file, WSTR_OPT, READ_ONLY, NO_SYS),
SPEC(buffered_stdio, BOOL, READ_ONLY, NO_SYS,
GLOBAL(&Py_UnbufferedStdioFlag, 1)),
SPEC(check_hash_pycs_mode, WSTR, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(code_debug_ranges, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(configure_c_stdio, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(dev_mode, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL), // sys.flags.dev_mode
SPEC(dump_refs, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(dump_refs_file, WSTR_OPT, READ_ONLY, NO_SYS, NO_GLOBAL),
#ifdef Py_GIL_DISABLED
SPEC(enable_gil, INT, READ_ONLY, NO_SYS),
SPEC(tlbc_enabled, INT, READ_ONLY, NO_SYS),
SPEC(enable_gil, INT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(tlbc_enabled, INT, READ_ONLY, NO_SYS, NO_GLOBAL),
#endif
SPEC(faulthandler, BOOL, READ_ONLY, NO_SYS),
SPEC(filesystem_encoding, WSTR, READ_ONLY, NO_SYS),
SPEC(filesystem_errors, WSTR, READ_ONLY, NO_SYS),
SPEC(hash_seed, ULONG, READ_ONLY, NO_SYS),
SPEC(home, WSTR_OPT, READ_ONLY, NO_SYS),
SPEC(thread_inherit_context, INT, READ_ONLY, NO_SYS),
SPEC(context_aware_warnings, INT, READ_ONLY, NO_SYS),
SPEC(import_time, UINT, READ_ONLY, NO_SYS),
SPEC(install_signal_handlers, BOOL, READ_ONLY, NO_SYS),
SPEC(isolated, BOOL, READ_ONLY, NO_SYS), // sys.flags.isolated
SPEC(faulthandler, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(filesystem_encoding, WSTR, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(filesystem_errors, WSTR, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(hash_seed, ULONG, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(home, WSTR_OPT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(thread_inherit_context, INT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(context_aware_warnings, INT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(import_time, UINT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(install_signal_handlers, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(isolated, BOOL, READ_ONLY, NO_SYS, GLOBAL(&Py_IsolatedFlag, 0)), // sys.flags.isolated
#ifdef MS_WINDOWS
SPEC(legacy_windows_stdio, BOOL, READ_ONLY, NO_SYS),
SPEC(legacy_windows_stdio, BOOL, READ_ONLY, NO_SYS,
GLOBAL(&Py_LegacyWindowsStdioFlag, 0)),
#endif
SPEC(malloc_stats, BOOL, READ_ONLY, NO_SYS),
SPEC(orig_argv, WSTR_LIST, READ_ONLY, SYS_ATTR("orig_argv")),
SPEC(parse_argv, BOOL, READ_ONLY, NO_SYS),
SPEC(pathconfig_warnings, BOOL, READ_ONLY, NO_SYS),
SPEC(perf_profiling, UINT, READ_ONLY, NO_SYS),
SPEC(remote_debug, BOOL, READ_ONLY, NO_SYS),
SPEC(program_name, WSTR, READ_ONLY, NO_SYS),
SPEC(run_command, WSTR_OPT, READ_ONLY, NO_SYS),
SPEC(run_filename, WSTR_OPT, READ_ONLY, NO_SYS),
SPEC(run_module, WSTR_OPT, READ_ONLY, NO_SYS),
SPEC(malloc_stats, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(orig_argv, WSTR_LIST, READ_ONLY, SYS_ATTR("orig_argv"), NO_GLOBAL),
SPEC(parse_argv, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(pathconfig_warnings, BOOL, READ_ONLY, NO_SYS,
GLOBAL(&Py_FrozenFlag, 1)),
SPEC(perf_profiling, UINT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(remote_debug, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(program_name, WSTR, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(run_command, WSTR_OPT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(run_filename, WSTR_OPT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(run_module, WSTR_OPT, READ_ONLY, NO_SYS, NO_GLOBAL),
#ifdef Py_DEBUG
SPEC(run_presite, WSTR_OPT, READ_ONLY, NO_SYS),
SPEC(run_presite, WSTR_OPT, READ_ONLY, NO_SYS, NO_GLOBAL),
#endif
SPEC(safe_path, BOOL, READ_ONLY, NO_SYS),
SPEC(show_ref_count, BOOL, READ_ONLY, NO_SYS),
SPEC(site_import, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_site
SPEC(skip_source_first_line, BOOL, READ_ONLY, NO_SYS),
SPEC(stdio_encoding, WSTR, READ_ONLY, NO_SYS),
SPEC(stdio_errors, WSTR, READ_ONLY, NO_SYS),
SPEC(tracemalloc, UINT, READ_ONLY, NO_SYS),
SPEC(use_frozen_modules, BOOL, READ_ONLY, NO_SYS),
SPEC(use_hash_seed, BOOL, READ_ONLY, NO_SYS),
SPEC(safe_path, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(show_ref_count, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(site_import, BOOL, READ_ONLY, NO_SYS, GLOBAL(&Py_NoSiteFlag, 1)), // sys.flags.no_site
SPEC(skip_source_first_line, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(stdio_encoding, WSTR, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(stdio_errors, WSTR, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(tracemalloc, UINT, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(use_frozen_modules, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
SPEC(use_hash_seed, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
#ifdef __APPLE__
SPEC(use_system_logger, BOOL, READ_ONLY, NO_SYS),
SPEC(use_system_logger, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),
#endif
SPEC(user_site_directory, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_user_site
SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS),
SPEC(user_site_directory, BOOL, READ_ONLY, NO_SYS,
GLOBAL(&Py_NoUserSiteDirectory, 1)), // sys.flags.no_user_site
SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS, NO_GLOBAL),

// --- Init-only options -----------

SPEC(_config_init, UINT, INIT_ONLY, NO_SYS),
SPEC(_init_main, BOOL, INIT_ONLY, NO_SYS),
SPEC(_install_importlib, BOOL, INIT_ONLY, NO_SYS),
SPEC(_is_python_build, BOOL, INIT_ONLY, NO_SYS),
SPEC(module_search_paths_set, BOOL, INIT_ONLY, NO_SYS),
SPEC(pythonpath_env, WSTR_OPT, INIT_ONLY, NO_SYS),
SPEC(sys_path_0, WSTR_OPT, INIT_ONLY, NO_SYS),
SPEC(_config_init, UINT, INIT_ONLY, NO_SYS, NO_GLOBAL),
SPEC(_init_main, BOOL, INIT_ONLY, NO_SYS, NO_GLOBAL),
SPEC(_install_importlib, BOOL, INIT_ONLY, NO_SYS, NO_GLOBAL),
SPEC(_is_python_build, BOOL, INIT_ONLY, NO_SYS, NO_GLOBAL),
SPEC(module_search_paths_set, BOOL, INIT_ONLY, NO_SYS, NO_GLOBAL),
SPEC(pythonpath_env, WSTR_OPT, INIT_ONLY, NO_SYS, NO_GLOBAL),
SPEC(sys_path_0, WSTR_OPT, INIT_ONLY, NO_SYS, NO_GLOBAL),

// Array terminator
{NULL, 0, 0, 0, NO_SYS},
Expand Down Expand Up @@ -231,11 +250,16 @@ static const PyConfigSpec PYPRECONFIG_SPEC[] = {
{NULL, 0, 0, 0, NO_SYS},
};

// End of ignoring deprecations on global variables
_Py_COMP_DIAG_POP

#undef SPEC
#undef SYS_ATTR
#undef SYS_FLAG_SETTER
#undef SYS_FLAG
#undef NO_SYS
#undef GLOBAL
#undef NO_GLOBAL


// Forward declarations
Expand Down Expand Up @@ -4729,6 +4753,16 @@ PyConfig_Set(const char *name, PyObject *value)
Py_UNREACHABLE();
}

// Set the global variable
if (spec->global_var.ptr != NULL) {
assert(has_int_value);
int value = int_value;
if (spec->global_var.not) {
value = !value;
}
*spec->global_var.ptr = value;
}

if (spec->sys.attr != NULL) {
// Set the sys attribute, but don't set PyInterpreterState.config
// to keep the code simple.
Expand Down
Loading