mirror of https://github.com/python/cpython
gh-107954, PEP 741: Add PyConfig_Get()/Set() functions (#123472)
Add PyConfig_Get(), PyConfig_GetInt(), PyConfig_Set() and PyConfig_Names() functions to get and set the current runtime Python configuration. Add visibility and "sys spec" to config and preconfig specifications. _PyConfig_AsDict() now converts PyConfig.xoptions as a dictionary. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
db42934270
commit
33b790978d
|
@ -1605,6 +1605,75 @@ customized Python always running in isolated mode using
|
||||||
:c:func:`Py_RunMain`.
|
:c:func:`Py_RunMain`.
|
||||||
|
|
||||||
|
|
||||||
|
Runtime Python configuration API
|
||||||
|
================================
|
||||||
|
|
||||||
|
The configuration option *name* parameter must be a non-NULL null-terminated
|
||||||
|
UTF-8 encoded string.
|
||||||
|
|
||||||
|
Some options are read from the :mod:`sys` attributes. For example, the option
|
||||||
|
``"argv"`` is read from :data:`sys.argv`.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: PyObject* PyConfig_Get(const char *name)
|
||||||
|
|
||||||
|
Get the current runtime value of a configuration option as a Python object.
|
||||||
|
|
||||||
|
* Return a new reference on success.
|
||||||
|
* Set an exception and return ``NULL`` on error.
|
||||||
|
|
||||||
|
The object type depends on the configuration option. It can be:
|
||||||
|
|
||||||
|
* ``bool``
|
||||||
|
* ``int``
|
||||||
|
* ``str``
|
||||||
|
* ``list[str]``
|
||||||
|
* ``dict[str, str]``
|
||||||
|
|
||||||
|
The caller must hold the GIL. The function cannot be called before
|
||||||
|
Python initialization nor after Python finalization.
|
||||||
|
|
||||||
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyConfig_GetInt(const char *name, int *value)
|
||||||
|
|
||||||
|
Similar to :c:func:`PyConfig_Get`, but get the value as a C int.
|
||||||
|
|
||||||
|
* Return ``0`` on success.
|
||||||
|
* Set an exception and return ``-1`` on error.
|
||||||
|
|
||||||
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: PyObject* PyConfig_Names(void)
|
||||||
|
|
||||||
|
Get all configuration option names as a ``frozenset``.
|
||||||
|
|
||||||
|
* Return a new reference on success.
|
||||||
|
* Set an exception and return ``NULL`` on error.
|
||||||
|
|
||||||
|
The caller must hold the GIL. The function cannot be called before
|
||||||
|
Python initialization nor after Python finalization.
|
||||||
|
|
||||||
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyConfig_Set(const char *name, PyObject *value)
|
||||||
|
|
||||||
|
Set the current runtime value of a configuration option.
|
||||||
|
|
||||||
|
* Raise a :exc:`ValueError` if there is no option *name*.
|
||||||
|
* Raise a :exc:`ValueError` if *value* is an invalid value.
|
||||||
|
* Raise a :exc:`ValueError` if the option is read-only (cannot be set).
|
||||||
|
* Raise a :exc:`TypeError` if *value* has not the proper type.
|
||||||
|
|
||||||
|
The caller must hold the GIL. The function cannot be called before
|
||||||
|
Python initialization nor after Python finalization.
|
||||||
|
|
||||||
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
|
||||||
Py_GetArgcArgv()
|
Py_GetArgcArgv()
|
||||||
================
|
================
|
||||||
|
|
||||||
|
|
|
@ -492,6 +492,15 @@ 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:
|
||||||
|
|
||||||
|
* :c:func:`PyConfig_Get`
|
||||||
|
* :c:func:`PyConfig_GetInt`
|
||||||
|
* :c:func:`PyConfig_Set`
|
||||||
|
* :c:func:`PyConfig_Names`
|
||||||
|
|
||||||
|
(Contributed by Victor Stinner in :gh:`107954`.)
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.14
|
Porting to Python 3.14
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -260,6 +260,14 @@ PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config,
|
||||||
Py_ssize_t length, wchar_t **items);
|
Py_ssize_t length, wchar_t **items);
|
||||||
|
|
||||||
|
|
||||||
|
/* --- PyConfig_Get() ----------------------------------------- */
|
||||||
|
|
||||||
|
PyAPI_FUNC(PyObject*) PyConfig_Get(const char *name);
|
||||||
|
PyAPI_FUNC(int) PyConfig_GetInt(const char *name, int *value);
|
||||||
|
PyAPI_FUNC(PyObject*) PyConfig_Names(void);
|
||||||
|
PyAPI_FUNC(int) PyConfig_Set(const char *name, PyObject *value);
|
||||||
|
|
||||||
|
|
||||||
/* --- Helper functions --------------------------------------- */
|
/* --- Helper functions --------------------------------------- */
|
||||||
|
|
||||||
/* Get the original command line arguments, before Python modified them.
|
/* Get the original command line arguments, before Python modified them.
|
||||||
|
|
|
@ -181,7 +181,7 @@ extern PyStatus _PyConfig_Write(const PyConfig *config,
|
||||||
extern PyStatus _PyConfig_SetPyArgv(
|
extern PyStatus _PyConfig_SetPyArgv(
|
||||||
PyConfig *config,
|
PyConfig *config,
|
||||||
const _PyArgv *args);
|
const _PyArgv *args);
|
||||||
|
extern PyObject* _PyConfig_CreateXOptionsDict(const PyConfig *config);
|
||||||
|
|
||||||
extern void _Py_DumpPathConfig(PyThreadState *tstate);
|
extern void _Py_DumpPathConfig(PyThreadState *tstate);
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@ extern int _PySys_SetAttr(PyObject *, PyObject *);
|
||||||
extern int _PySys_ClearAttrString(PyInterpreterState *interp,
|
extern int _PySys_ClearAttrString(PyInterpreterState *interp,
|
||||||
const char *name, int verbose);
|
const char *name, int verbose);
|
||||||
|
|
||||||
|
extern int _PySys_SetFlagObj(Py_ssize_t pos, PyObject *new_value);
|
||||||
|
extern int _PySys_SetIntMaxStrDigits(int maxdigits);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -137,7 +137,8 @@ class SetConfigTests(unittest.TestCase):
|
||||||
'warnoptions',
|
'warnoptions',
|
||||||
'module_search_paths',
|
'module_search_paths',
|
||||||
):
|
):
|
||||||
value_tests.append((key, invalid_wstrlist))
|
if key != 'xoptions':
|
||||||
|
value_tests.append((key, invalid_wstrlist))
|
||||||
type_tests.append((key, 123))
|
type_tests.append((key, 123))
|
||||||
type_tests.append((key, "abc"))
|
type_tests.append((key, "abc"))
|
||||||
type_tests.append((key, [123]))
|
type_tests.append((key, [123]))
|
||||||
|
@ -160,14 +161,14 @@ class SetConfigTests(unittest.TestCase):
|
||||||
def test_flags(self):
|
def test_flags(self):
|
||||||
bool_options = set(BOOL_OPTIONS)
|
bool_options = set(BOOL_OPTIONS)
|
||||||
for sys_attr, key, value in (
|
for sys_attr, key, value in (
|
||||||
("debug", "parser_debug", 1),
|
("debug", "parser_debug", 2),
|
||||||
("inspect", "inspect", 2),
|
("inspect", "inspect", 3),
|
||||||
("interactive", "interactive", 3),
|
("interactive", "interactive", 4),
|
||||||
("optimize", "optimization_level", 4),
|
("optimize", "optimization_level", 5),
|
||||||
("verbose", "verbose", 1),
|
("verbose", "verbose", 6),
|
||||||
("bytes_warning", "bytes_warning", 10),
|
("bytes_warning", "bytes_warning", 7),
|
||||||
("quiet", "quiet", 11),
|
("quiet", "quiet", 8),
|
||||||
("isolated", "isolated", 12),
|
("isolated", "isolated", 9),
|
||||||
):
|
):
|
||||||
with self.subTest(sys=sys_attr, key=key, value=value):
|
with self.subTest(sys=sys_attr, key=key, value=value):
|
||||||
self.set_config(**{key: value, 'parse_argv': 0})
|
self.set_config(**{key: value, 'parse_argv': 0})
|
||||||
|
@ -228,9 +229,9 @@ class SetConfigTests(unittest.TestCase):
|
||||||
self.check(warnoptions=[])
|
self.check(warnoptions=[])
|
||||||
self.check(warnoptions=["default", "ignore"])
|
self.check(warnoptions=["default", "ignore"])
|
||||||
|
|
||||||
self.set_config(xoptions=[])
|
self.set_config(xoptions={})
|
||||||
self.assertEqual(sys._xoptions, {})
|
self.assertEqual(sys._xoptions, {})
|
||||||
self.set_config(xoptions=["dev", "tracemalloc=5"])
|
self.set_config(xoptions={"dev": True, "tracemalloc": "5"})
|
||||||
self.assertEqual(sys._xoptions, {"dev": True, "tracemalloc": "5"})
|
self.assertEqual(sys._xoptions, {"dev": True, "tracemalloc": "5"})
|
||||||
|
|
||||||
def test_pathconfig(self):
|
def test_pathconfig(self):
|
||||||
|
|
|
@ -0,0 +1,379 @@
|
||||||
|
"""
|
||||||
|
Tests PyConfig_Get() and PyConfig_Set() C API (PEP 741).
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
import types
|
||||||
|
import unittest
|
||||||
|
from test import support
|
||||||
|
from test.support import import_helper
|
||||||
|
|
||||||
|
_testcapi = import_helper.import_module('_testcapi')
|
||||||
|
|
||||||
|
|
||||||
|
# Is the Py_STATS macro defined?
|
||||||
|
Py_STATS = hasattr(sys, '_stats_on')
|
||||||
|
|
||||||
|
|
||||||
|
class CAPITests(unittest.TestCase):
|
||||||
|
def test_config_get(self):
|
||||||
|
# Test PyConfig_Get()
|
||||||
|
config_get = _testcapi.config_get
|
||||||
|
config_names = _testcapi.config_names
|
||||||
|
|
||||||
|
TEST_VALUE = {
|
||||||
|
str: "TEST_MARKER_STR",
|
||||||
|
str | None: "TEST_MARKER_OPT_STR",
|
||||||
|
list[str]: ("TEST_MARKER_STR_TUPLE",),
|
||||||
|
dict[str, str | bool]: {"x": "value", "y": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
# read config options and check their type
|
||||||
|
options = [
|
||||||
|
("allocator", int, None),
|
||||||
|
("argv", list[str], "argv"),
|
||||||
|
("base_exec_prefix", str | None, "base_exec_prefix"),
|
||||||
|
("base_executable", str | None, "_base_executable"),
|
||||||
|
("base_prefix", str | None, "base_prefix"),
|
||||||
|
("buffered_stdio", bool, None),
|
||||||
|
("bytes_warning", int, None),
|
||||||
|
("check_hash_pycs_mode", str, None),
|
||||||
|
("code_debug_ranges", bool, None),
|
||||||
|
("configure_c_stdio", bool, None),
|
||||||
|
("coerce_c_locale", bool, None),
|
||||||
|
("coerce_c_locale_warn", bool, None),
|
||||||
|
("configure_locale", bool, None),
|
||||||
|
("cpu_count", int, None),
|
||||||
|
("dev_mode", bool, None),
|
||||||
|
("dump_refs", bool, None),
|
||||||
|
("dump_refs_file", str | None, None),
|
||||||
|
("exec_prefix", str | None, "exec_prefix"),
|
||||||
|
("executable", str | None, "executable"),
|
||||||
|
("faulthandler", bool, None),
|
||||||
|
("filesystem_encoding", str, None),
|
||||||
|
("filesystem_errors", str, None),
|
||||||
|
("hash_seed", int, None),
|
||||||
|
("home", str | None, None),
|
||||||
|
("import_time", bool, None),
|
||||||
|
("inspect", bool, None),
|
||||||
|
("install_signal_handlers", bool, None),
|
||||||
|
("int_max_str_digits", int, None),
|
||||||
|
("interactive", bool, None),
|
||||||
|
("isolated", bool, None),
|
||||||
|
("malloc_stats", bool, None),
|
||||||
|
("module_search_paths", list[str], "path"),
|
||||||
|
("optimization_level", int, None),
|
||||||
|
("orig_argv", list[str], "orig_argv"),
|
||||||
|
("parser_debug", bool, None),
|
||||||
|
("parse_argv", bool, None),
|
||||||
|
("pathconfig_warnings", bool, None),
|
||||||
|
("perf_profiling", bool, None),
|
||||||
|
("platlibdir", str, "platlibdir"),
|
||||||
|
("prefix", str | None, "prefix"),
|
||||||
|
("program_name", str, None),
|
||||||
|
("pycache_prefix", str | None, "pycache_prefix"),
|
||||||
|
("quiet", bool, None),
|
||||||
|
("run_command", str | None, None),
|
||||||
|
("run_filename", str | None, None),
|
||||||
|
("run_module", str | None, None),
|
||||||
|
("safe_path", bool, None),
|
||||||
|
("show_ref_count", bool, None),
|
||||||
|
("site_import", bool, None),
|
||||||
|
("skip_source_first_line", bool, None),
|
||||||
|
("stdio_encoding", str, None),
|
||||||
|
("stdio_errors", str, None),
|
||||||
|
("stdlib_dir", str | None, "_stdlib_dir"),
|
||||||
|
("tracemalloc", int, None),
|
||||||
|
("use_environment", bool, None),
|
||||||
|
("use_frozen_modules", bool, None),
|
||||||
|
("use_hash_seed", bool, None),
|
||||||
|
("user_site_directory", bool, None),
|
||||||
|
("utf8_mode", bool, None),
|
||||||
|
("verbose", int, None),
|
||||||
|
("warn_default_encoding", bool, None),
|
||||||
|
("warnoptions", list[str], "warnoptions"),
|
||||||
|
("write_bytecode", bool, None),
|
||||||
|
("xoptions", dict[str, str | bool], "_xoptions"),
|
||||||
|
]
|
||||||
|
if support.Py_DEBUG:
|
||||||
|
options.append(("run_presite", str | None, None))
|
||||||
|
if sysconfig.get_config_var('Py_GIL_DISABLED'):
|
||||||
|
options.append(("enable_gil", int, None))
|
||||||
|
if support.MS_WINDOWS:
|
||||||
|
options.extend((
|
||||||
|
("legacy_windows_stdio", bool, None),
|
||||||
|
("legacy_windows_fs_encoding", bool, None),
|
||||||
|
))
|
||||||
|
if Py_STATS:
|
||||||
|
options.extend((
|
||||||
|
("_pystats", bool, None),
|
||||||
|
))
|
||||||
|
|
||||||
|
for name, option_type, sys_attr in options:
|
||||||
|
with self.subTest(name=name, option_type=option_type,
|
||||||
|
sys_attr=sys_attr):
|
||||||
|
value = config_get(name)
|
||||||
|
if isinstance(option_type, types.GenericAlias):
|
||||||
|
self.assertIsInstance(value, option_type.__origin__)
|
||||||
|
if option_type.__origin__ == dict:
|
||||||
|
key_type = option_type.__args__[0]
|
||||||
|
value_type = option_type.__args__[1]
|
||||||
|
for item in value.items():
|
||||||
|
self.assertIsInstance(item[0], key_type)
|
||||||
|
self.assertIsInstance(item[1], value_type)
|
||||||
|
else:
|
||||||
|
item_type = option_type.__args__[0]
|
||||||
|
for item in value:
|
||||||
|
self.assertIsInstance(item, item_type)
|
||||||
|
else:
|
||||||
|
self.assertIsInstance(value, option_type)
|
||||||
|
|
||||||
|
if sys_attr is not None:
|
||||||
|
expected = getattr(sys, sys_attr)
|
||||||
|
self.assertEqual(expected, value)
|
||||||
|
|
||||||
|
override = TEST_VALUE[option_type]
|
||||||
|
with support.swap_attr(sys, sys_attr, override):
|
||||||
|
self.assertEqual(config_get(name), override)
|
||||||
|
|
||||||
|
# check that the test checks all options
|
||||||
|
self.assertEqual(sorted(name for name, option_type, sys_attr in options),
|
||||||
|
sorted(config_names()))
|
||||||
|
|
||||||
|
def test_config_get_sys_flags(self):
|
||||||
|
# Test PyConfig_Get()
|
||||||
|
config_get = _testcapi.config_get
|
||||||
|
|
||||||
|
# compare config options with sys.flags
|
||||||
|
for flag, name, negate in (
|
||||||
|
("debug", "parser_debug", False),
|
||||||
|
("inspect", "inspect", False),
|
||||||
|
("interactive", "interactive", False),
|
||||||
|
("optimize", "optimization_level", False),
|
||||||
|
("dont_write_bytecode", "write_bytecode", True),
|
||||||
|
("no_user_site", "user_site_directory", True),
|
||||||
|
("no_site", "site_import", True),
|
||||||
|
("ignore_environment", "use_environment", True),
|
||||||
|
("verbose", "verbose", False),
|
||||||
|
("bytes_warning", "bytes_warning", False),
|
||||||
|
("quiet", "quiet", False),
|
||||||
|
# "hash_randomization" is tested below
|
||||||
|
("isolated", "isolated", False),
|
||||||
|
("dev_mode", "dev_mode", False),
|
||||||
|
("utf8_mode", "utf8_mode", False),
|
||||||
|
("warn_default_encoding", "warn_default_encoding", False),
|
||||||
|
("safe_path", "safe_path", False),
|
||||||
|
("int_max_str_digits", "int_max_str_digits", False),
|
||||||
|
# "gil" is tested below
|
||||||
|
):
|
||||||
|
with self.subTest(flag=flag, name=name, negate=negate):
|
||||||
|
value = config_get(name)
|
||||||
|
if negate:
|
||||||
|
value = not value
|
||||||
|
self.assertEqual(getattr(sys.flags, flag), value)
|
||||||
|
|
||||||
|
self.assertEqual(sys.flags.hash_randomization,
|
||||||
|
config_get('use_hash_seed') == 0
|
||||||
|
or config_get('hash_seed') != 0)
|
||||||
|
|
||||||
|
if sysconfig.get_config_var('Py_GIL_DISABLED'):
|
||||||
|
value = config_get('enable_gil')
|
||||||
|
expected = (value if value != -1 else None)
|
||||||
|
self.assertEqual(sys.flags.gil, expected)
|
||||||
|
|
||||||
|
def test_config_get_non_existent(self):
|
||||||
|
# Test PyConfig_Get() on non-existent option name
|
||||||
|
config_get = _testcapi.config_get
|
||||||
|
nonexistent_key = 'NONEXISTENT_KEY'
|
||||||
|
err_msg = f'unknown config option name: {nonexistent_key}'
|
||||||
|
with self.assertRaisesRegex(ValueError, err_msg):
|
||||||
|
config_get(nonexistent_key)
|
||||||
|
|
||||||
|
def test_config_get_write_bytecode(self):
|
||||||
|
# PyConfig_Get("write_bytecode") gets sys.dont_write_bytecode
|
||||||
|
# as an integer
|
||||||
|
config_get = _testcapi.config_get
|
||||||
|
with support.swap_attr(sys, "dont_write_bytecode", 0):
|
||||||
|
self.assertEqual(config_get('write_bytecode'), 1)
|
||||||
|
with support.swap_attr(sys, "dont_write_bytecode", "yes"):
|
||||||
|
self.assertEqual(config_get('write_bytecode'), 0)
|
||||||
|
with support.swap_attr(sys, "dont_write_bytecode", []):
|
||||||
|
self.assertEqual(config_get('write_bytecode'), 1)
|
||||||
|
|
||||||
|
def test_config_getint(self):
|
||||||
|
# Test PyConfig_GetInt()
|
||||||
|
config_getint = _testcapi.config_getint
|
||||||
|
|
||||||
|
# PyConfig_MEMBER_INT type
|
||||||
|
self.assertEqual(config_getint('verbose'), sys.flags.verbose)
|
||||||
|
|
||||||
|
# PyConfig_MEMBER_UINT type
|
||||||
|
self.assertEqual(config_getint('isolated'), sys.flags.isolated)
|
||||||
|
|
||||||
|
# PyConfig_MEMBER_ULONG type
|
||||||
|
self.assertIsInstance(config_getint('hash_seed'), int)
|
||||||
|
|
||||||
|
# PyPreConfig member
|
||||||
|
self.assertIsInstance(config_getint('allocator'), int)
|
||||||
|
|
||||||
|
# platlibdir type is str
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
config_getint('platlibdir')
|
||||||
|
|
||||||
|
def test_get_config_names(self):
|
||||||
|
names = _testcapi.config_names()
|
||||||
|
self.assertIsInstance(names, frozenset)
|
||||||
|
for name in names:
|
||||||
|
self.assertIsInstance(name, str)
|
||||||
|
|
||||||
|
def test_config_set_sys_attr(self):
|
||||||
|
# Test PyConfig_Set() with sys attributes
|
||||||
|
config_get = _testcapi.config_get
|
||||||
|
config_set = _testcapi.config_set
|
||||||
|
|
||||||
|
# mutable configuration option mapped to sys attributes
|
||||||
|
for name, sys_attr, option_type in (
|
||||||
|
('argv', 'argv', list[str]),
|
||||||
|
('base_exec_prefix', 'base_exec_prefix', str | None),
|
||||||
|
('base_executable', '_base_executable', str | None),
|
||||||
|
('base_prefix', 'base_prefix', str | None),
|
||||||
|
('exec_prefix', 'exec_prefix', str | None),
|
||||||
|
('executable', 'executable', str | None),
|
||||||
|
('module_search_paths', 'path', list[str]),
|
||||||
|
('platlibdir', 'platlibdir', str),
|
||||||
|
('prefix', 'prefix', str | None),
|
||||||
|
('pycache_prefix', 'pycache_prefix', str | None),
|
||||||
|
('stdlib_dir', '_stdlib_dir', str | None),
|
||||||
|
('warnoptions', 'warnoptions', list[str]),
|
||||||
|
('xoptions', '_xoptions', dict[str, str | bool]),
|
||||||
|
):
|
||||||
|
with self.subTest(name=name):
|
||||||
|
if option_type == str:
|
||||||
|
test_values = ('TEST_REPLACE',)
|
||||||
|
invalid_types = (1, None)
|
||||||
|
elif option_type == str | None:
|
||||||
|
test_values = ('TEST_REPLACE', None)
|
||||||
|
invalid_types = (123,)
|
||||||
|
elif option_type == list[str]:
|
||||||
|
test_values = (['TEST_REPLACE'], [])
|
||||||
|
invalid_types = ('text', 123, [123])
|
||||||
|
else: # option_type == dict[str, str | bool]:
|
||||||
|
test_values = ({"x": "value", "y": True},)
|
||||||
|
invalid_types = ('text', 123, ['option'],
|
||||||
|
{123: 'value'},
|
||||||
|
{'key': b'bytes'})
|
||||||
|
|
||||||
|
old_opt_value = config_get(name)
|
||||||
|
old_sys_value = getattr(sys, sys_attr)
|
||||||
|
try:
|
||||||
|
for value in test_values:
|
||||||
|
config_set(name, value)
|
||||||
|
self.assertEqual(config_get(name), value)
|
||||||
|
self.assertEqual(getattr(sys, sys_attr), value)
|
||||||
|
|
||||||
|
for value in invalid_types:
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
config_set(name, value)
|
||||||
|
finally:
|
||||||
|
setattr(sys, sys_attr, old_sys_value)
|
||||||
|
config_set(name, old_opt_value)
|
||||||
|
|
||||||
|
def test_config_set_sys_flag(self):
|
||||||
|
# Test PyConfig_Set() with sys.flags
|
||||||
|
config_get = _testcapi.config_get
|
||||||
|
config_set = _testcapi.config_set
|
||||||
|
|
||||||
|
# mutable configuration option mapped to sys.flags
|
||||||
|
class unsigned_int(int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def expect_int(value):
|
||||||
|
value = int(value)
|
||||||
|
return (value, value)
|
||||||
|
|
||||||
|
def expect_bool(value):
|
||||||
|
value = int(bool(value))
|
||||||
|
return (value, value)
|
||||||
|
|
||||||
|
def expect_bool_not(value):
|
||||||
|
value = bool(value)
|
||||||
|
return (int(value), int(not value))
|
||||||
|
|
||||||
|
for name, sys_flag, option_type, expect_func in (
|
||||||
|
# (some flags cannot be set, see comments below.)
|
||||||
|
('parser_debug', 'debug', bool, expect_bool),
|
||||||
|
('inspect', 'inspect', bool, expect_bool),
|
||||||
|
('interactive', 'interactive', bool, expect_bool),
|
||||||
|
('optimization_level', 'optimize', unsigned_int, expect_int),
|
||||||
|
('write_bytecode', 'dont_write_bytecode', bool, expect_bool_not),
|
||||||
|
# user_site_directory
|
||||||
|
# site_import
|
||||||
|
('use_environment', 'ignore_environment', bool, expect_bool_not),
|
||||||
|
('verbose', 'verbose', unsigned_int, expect_int),
|
||||||
|
('bytes_warning', 'bytes_warning', unsigned_int, expect_int),
|
||||||
|
('quiet', 'quiet', bool, expect_bool),
|
||||||
|
# hash_randomization
|
||||||
|
# isolated
|
||||||
|
# dev_mode
|
||||||
|
# utf8_mode
|
||||||
|
# warn_default_encoding
|
||||||
|
# safe_path
|
||||||
|
('int_max_str_digits', 'int_max_str_digits', unsigned_int, expect_int),
|
||||||
|
# gil
|
||||||
|
):
|
||||||
|
if name == "int_max_str_digits":
|
||||||
|
new_values = (0, 5_000, 999_999)
|
||||||
|
invalid_values = (-1, 40) # value must 0 or >= 4300
|
||||||
|
invalid_types = (1.0, "abc")
|
||||||
|
elif option_type == int:
|
||||||
|
new_values = (False, True, 0, 1, 5, -5)
|
||||||
|
invalid_values = ()
|
||||||
|
invalid_types = (1.0, "abc")
|
||||||
|
else:
|
||||||
|
new_values = (False, True, 0, 1, 5)
|
||||||
|
invalid_values = (-5,)
|
||||||
|
invalid_types = (1.0, "abc")
|
||||||
|
|
||||||
|
with self.subTest(name=name):
|
||||||
|
old_value = config_get(name)
|
||||||
|
try:
|
||||||
|
for value in new_values:
|
||||||
|
expected, expect_flag = expect_func(value)
|
||||||
|
|
||||||
|
config_set(name, value)
|
||||||
|
self.assertEqual(config_get(name), expected)
|
||||||
|
self.assertEqual(getattr(sys.flags, sys_flag), expect_flag)
|
||||||
|
if name == "write_bytecode":
|
||||||
|
self.assertEqual(getattr(sys, "dont_write_bytecode"),
|
||||||
|
expect_flag)
|
||||||
|
if name == "int_max_str_digits":
|
||||||
|
self.assertEqual(sys.get_int_max_str_digits(),
|
||||||
|
expect_flag)
|
||||||
|
|
||||||
|
for value in invalid_values:
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
config_set(name, value)
|
||||||
|
|
||||||
|
for value in invalid_types:
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
config_set(name, value)
|
||||||
|
finally:
|
||||||
|
config_set(name, old_value)
|
||||||
|
|
||||||
|
def test_config_set_read_only(self):
|
||||||
|
# Test PyConfig_Set() on read-only options
|
||||||
|
config_set = _testcapi.config_set
|
||||||
|
for name, value in (
|
||||||
|
("allocator", 0), # PyPreConfig member
|
||||||
|
("cpu_count", 8),
|
||||||
|
("dev_mode", True),
|
||||||
|
("filesystem_encoding", "utf-8"),
|
||||||
|
):
|
||||||
|
with self.subTest(name=name, value=value):
|
||||||
|
with self.assertRaisesRegex(ValueError, r"read-only"):
|
||||||
|
config_set(name, value)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
|
@ -508,30 +508,30 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
PRE_CONFIG_COMPAT = {
|
PRE_CONFIG_COMPAT = {
|
||||||
'_config_init': API_COMPAT,
|
'_config_init': API_COMPAT,
|
||||||
'allocator': PYMEM_ALLOCATOR_NOT_SET,
|
'allocator': PYMEM_ALLOCATOR_NOT_SET,
|
||||||
'parse_argv': 0,
|
'parse_argv': False,
|
||||||
'configure_locale': 1,
|
'configure_locale': True,
|
||||||
'coerce_c_locale': 0,
|
'coerce_c_locale': False,
|
||||||
'coerce_c_locale_warn': 0,
|
'coerce_c_locale_warn': False,
|
||||||
'utf8_mode': 0,
|
'utf8_mode': False,
|
||||||
}
|
}
|
||||||
if MS_WINDOWS:
|
if MS_WINDOWS:
|
||||||
PRE_CONFIG_COMPAT.update({
|
PRE_CONFIG_COMPAT.update({
|
||||||
'legacy_windows_fs_encoding': 0,
|
'legacy_windows_fs_encoding': False,
|
||||||
})
|
})
|
||||||
PRE_CONFIG_PYTHON = dict(PRE_CONFIG_COMPAT,
|
PRE_CONFIG_PYTHON = dict(PRE_CONFIG_COMPAT,
|
||||||
_config_init=API_PYTHON,
|
_config_init=API_PYTHON,
|
||||||
parse_argv=1,
|
parse_argv=True,
|
||||||
coerce_c_locale=GET_DEFAULT_CONFIG,
|
coerce_c_locale=GET_DEFAULT_CONFIG,
|
||||||
utf8_mode=GET_DEFAULT_CONFIG,
|
utf8_mode=GET_DEFAULT_CONFIG,
|
||||||
)
|
)
|
||||||
PRE_CONFIG_ISOLATED = dict(PRE_CONFIG_COMPAT,
|
PRE_CONFIG_ISOLATED = dict(PRE_CONFIG_COMPAT,
|
||||||
_config_init=API_ISOLATED,
|
_config_init=API_ISOLATED,
|
||||||
configure_locale=0,
|
configure_locale=False,
|
||||||
isolated=1,
|
isolated=True,
|
||||||
use_environment=0,
|
use_environment=False,
|
||||||
utf8_mode=0,
|
utf8_mode=False,
|
||||||
dev_mode=0,
|
dev_mode=False,
|
||||||
coerce_c_locale=0,
|
coerce_c_locale=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
COPY_PRE_CONFIG = [
|
COPY_PRE_CONFIG = [
|
||||||
|
@ -570,7 +570,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'argv': [""],
|
'argv': [""],
|
||||||
'orig_argv': [],
|
'orig_argv': [],
|
||||||
|
|
||||||
'xoptions': [],
|
'xoptions': {},
|
||||||
'warnoptions': [],
|
'warnoptions': [],
|
||||||
|
|
||||||
'pythonpath_env': None,
|
'pythonpath_env': None,
|
||||||
|
@ -619,14 +619,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'_is_python_build': IGNORE_CONFIG,
|
'_is_python_build': IGNORE_CONFIG,
|
||||||
}
|
}
|
||||||
if Py_STATS:
|
if Py_STATS:
|
||||||
CONFIG_COMPAT['_pystats'] = 0
|
CONFIG_COMPAT['_pystats'] = False
|
||||||
if support.Py_DEBUG:
|
if support.Py_DEBUG:
|
||||||
CONFIG_COMPAT['run_presite'] = None
|
CONFIG_COMPAT['run_presite'] = None
|
||||||
if support.Py_GIL_DISABLED:
|
if support.Py_GIL_DISABLED:
|
||||||
CONFIG_COMPAT['enable_gil'] = -1
|
CONFIG_COMPAT['enable_gil'] = -1
|
||||||
if MS_WINDOWS:
|
if MS_WINDOWS:
|
||||||
CONFIG_COMPAT.update({
|
CONFIG_COMPAT.update({
|
||||||
'legacy_windows_stdio': 0,
|
'legacy_windows_stdio': False,
|
||||||
})
|
})
|
||||||
|
|
||||||
CONFIG_PYTHON = dict(CONFIG_COMPAT,
|
CONFIG_PYTHON = dict(CONFIG_COMPAT,
|
||||||
|
@ -644,12 +644,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
install_signal_handlers=False,
|
install_signal_handlers=False,
|
||||||
use_hash_seed=False,
|
use_hash_seed=False,
|
||||||
faulthandler=False,
|
faulthandler=False,
|
||||||
tracemalloc=0,
|
tracemalloc=False,
|
||||||
perf_profiling=False,
|
perf_profiling=False,
|
||||||
pathconfig_warnings=False,
|
pathconfig_warnings=False,
|
||||||
)
|
)
|
||||||
if MS_WINDOWS:
|
if MS_WINDOWS:
|
||||||
CONFIG_ISOLATED['legacy_windows_stdio'] = 0
|
CONFIG_ISOLATED['legacy_windows_stdio'] = False
|
||||||
|
|
||||||
# global config
|
# global config
|
||||||
DEFAULT_GLOBAL_CONFIG = {
|
DEFAULT_GLOBAL_CONFIG = {
|
||||||
|
@ -928,23 +928,23 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
|
|
||||||
def test_init_global_config(self):
|
def test_init_global_config(self):
|
||||||
preconfig = {
|
preconfig = {
|
||||||
'utf8_mode': 1,
|
'utf8_mode': True,
|
||||||
}
|
}
|
||||||
config = {
|
config = {
|
||||||
'program_name': './globalvar',
|
'program_name': './globalvar',
|
||||||
'site_import': 0,
|
'site_import': False,
|
||||||
'bytes_warning': 1,
|
'bytes_warning': True,
|
||||||
'warnoptions': ['default::BytesWarning'],
|
'warnoptions': ['default::BytesWarning'],
|
||||||
'inspect': 1,
|
'inspect': True,
|
||||||
'interactive': 1,
|
'interactive': True,
|
||||||
'optimization_level': 2,
|
'optimization_level': 2,
|
||||||
'write_bytecode': 0,
|
'write_bytecode': False,
|
||||||
'verbose': 1,
|
'verbose': True,
|
||||||
'quiet': 1,
|
'quiet': True,
|
||||||
'buffered_stdio': 0,
|
'buffered_stdio': False,
|
||||||
|
|
||||||
'user_site_directory': 0,
|
'user_site_directory': False,
|
||||||
'pathconfig_warnings': 0,
|
'pathconfig_warnings': False,
|
||||||
}
|
}
|
||||||
self.check_all_configs("test_init_global_config", config, preconfig,
|
self.check_all_configs("test_init_global_config", config, preconfig,
|
||||||
api=API_COMPAT)
|
api=API_COMPAT)
|
||||||
|
@ -952,7 +952,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
def test_init_from_config(self):
|
def test_init_from_config(self):
|
||||||
preconfig = {
|
preconfig = {
|
||||||
'allocator': ALLOCATOR_FOR_CONFIG,
|
'allocator': ALLOCATOR_FOR_CONFIG,
|
||||||
'utf8_mode': 1,
|
'utf8_mode': True,
|
||||||
}
|
}
|
||||||
config = {
|
config = {
|
||||||
'install_signal_handlers': False,
|
'install_signal_handlers': False,
|
||||||
|
@ -977,12 +977,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'-c', 'pass',
|
'-c', 'pass',
|
||||||
'arg2'],
|
'arg2'],
|
||||||
'parse_argv': True,
|
'parse_argv': True,
|
||||||
'xoptions': [
|
'xoptions': {
|
||||||
'config_xoption1=3',
|
'config_xoption1': '3',
|
||||||
'config_xoption2=',
|
'config_xoption2': '',
|
||||||
'config_xoption3',
|
'config_xoption3': True,
|
||||||
'cmdline_xoption',
|
'cmdline_xoption': True,
|
||||||
],
|
},
|
||||||
'warnoptions': [
|
'warnoptions': [
|
||||||
'cmdline_warnoption',
|
'cmdline_warnoption',
|
||||||
'default::BytesWarning',
|
'default::BytesWarning',
|
||||||
|
@ -1126,7 +1126,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'dev_mode': True,
|
'dev_mode': True,
|
||||||
'faulthandler': True,
|
'faulthandler': True,
|
||||||
'warnoptions': ['default'],
|
'warnoptions': ['default'],
|
||||||
'xoptions': ['dev'],
|
'xoptions': {'dev': True},
|
||||||
'safe_path': True,
|
'safe_path': True,
|
||||||
}
|
}
|
||||||
self.check_all_configs("test_preinit_parse_argv", config, preconfig,
|
self.check_all_configs("test_preinit_parse_argv", config, preconfig,
|
||||||
|
@ -1135,7 +1135,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
def test_preinit_dont_parse_argv(self):
|
def test_preinit_dont_parse_argv(self):
|
||||||
# -X dev must be ignored by isolated preconfiguration
|
# -X dev must be ignored by isolated preconfiguration
|
||||||
preconfig = {
|
preconfig = {
|
||||||
'isolated': 0,
|
'isolated': False,
|
||||||
}
|
}
|
||||||
argv = ["python3",
|
argv = ["python3",
|
||||||
"-E", "-I", "-P",
|
"-E", "-I", "-P",
|
||||||
|
@ -1145,7 +1145,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
config = {
|
config = {
|
||||||
'argv': argv,
|
'argv': argv,
|
||||||
'orig_argv': argv,
|
'orig_argv': argv,
|
||||||
'isolated': 0,
|
'isolated': False,
|
||||||
}
|
}
|
||||||
self.check_all_configs("test_preinit_dont_parse_argv", config, preconfig,
|
self.check_all_configs("test_preinit_dont_parse_argv", config, preconfig,
|
||||||
api=API_ISOLATED)
|
api=API_ISOLATED)
|
||||||
|
@ -1218,12 +1218,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
def test_init_sys_add(self):
|
def test_init_sys_add(self):
|
||||||
config = {
|
config = {
|
||||||
'faulthandler': 1,
|
'faulthandler': 1,
|
||||||
'xoptions': [
|
'xoptions': {
|
||||||
'config_xoption',
|
'config_xoption': True,
|
||||||
'cmdline_xoption',
|
'cmdline_xoption': True,
|
||||||
'sysadd_xoption',
|
'sysadd_xoption': True,
|
||||||
'faulthandler',
|
'faulthandler': True,
|
||||||
],
|
},
|
||||||
'warnoptions': [
|
'warnoptions': [
|
||||||
'ignore:::cmdline_warnoption',
|
'ignore:::cmdline_warnoption',
|
||||||
'ignore:::sysadd_warnoption',
|
'ignore:::sysadd_warnoption',
|
||||||
|
@ -1259,7 +1259,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'program_name': './python3',
|
'program_name': './python3',
|
||||||
'run_command': code + '\n',
|
'run_command': code + '\n',
|
||||||
'parse_argv': True,
|
'parse_argv': True,
|
||||||
'_init_main': 0,
|
'_init_main': False,
|
||||||
'sys_path_0': '',
|
'sys_path_0': '',
|
||||||
}
|
}
|
||||||
self.check_all_configs("test_init_main", config,
|
self.check_all_configs("test_init_main", config,
|
||||||
|
@ -1637,12 +1637,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
config['base_prefix'] = pyvenv_home
|
config['base_prefix'] = pyvenv_home
|
||||||
config['prefix'] = pyvenv_home
|
config['prefix'] = pyvenv_home
|
||||||
config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib')
|
config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib')
|
||||||
config['use_frozen_modules'] = int(not support.Py_DEBUG)
|
config['use_frozen_modules'] = bool(not support.Py_DEBUG)
|
||||||
else:
|
else:
|
||||||
# cannot reliably assume stdlib_dir here because it
|
# cannot reliably assume stdlib_dir here because it
|
||||||
# depends too much on our build. But it ought to be found
|
# depends too much on our build. But it ought to be found
|
||||||
config['stdlib_dir'] = self.IGNORE_CONFIG
|
config['stdlib_dir'] = self.IGNORE_CONFIG
|
||||||
config['use_frozen_modules'] = int(not support.Py_DEBUG)
|
config['use_frozen_modules'] = bool(not support.Py_DEBUG)
|
||||||
|
|
||||||
env = self.copy_paths_by_env(config)
|
env = self.copy_paths_by_env(config)
|
||||||
self.check_all_configs("test_init_compat_config", config,
|
self.check_all_configs("test_init_compat_config", config,
|
||||||
|
@ -1706,7 +1706,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
|
|
||||||
config = _testinternalcapi.get_configs()['config']
|
config = _testinternalcapi.get_configs()['config']
|
||||||
|
|
||||||
self.assertEqual(Py_GetPath().split(os.path.pathsep),
|
self.assertEqual(tuple(Py_GetPath().split(os.path.pathsep)),
|
||||||
config['module_search_paths'])
|
config['module_search_paths'])
|
||||||
self.assertEqual(Py_GetPrefix(), config['prefix'])
|
self.assertEqual(Py_GetPrefix(), config['prefix'])
|
||||||
self.assertEqual(Py_GetExecPrefix(), config['exec_prefix'])
|
self.assertEqual(Py_GetExecPrefix(), config['exec_prefix'])
|
||||||
|
@ -1763,6 +1763,10 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
}
|
}
|
||||||
for raw, expected in tests:
|
for raw, expected in tests:
|
||||||
optval = f'frozen_modules{raw}'
|
optval = f'frozen_modules{raw}'
|
||||||
|
if raw.startswith('='):
|
||||||
|
xoption_value = raw[1:]
|
||||||
|
else:
|
||||||
|
xoption_value = True
|
||||||
config = {
|
config = {
|
||||||
'parse_argv': True,
|
'parse_argv': True,
|
||||||
'argv': ['-c'],
|
'argv': ['-c'],
|
||||||
|
@ -1770,7 +1774,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'program_name': './argv0',
|
'program_name': './argv0',
|
||||||
'run_command': 'pass\n',
|
'run_command': 'pass\n',
|
||||||
'use_environment': True,
|
'use_environment': True,
|
||||||
'xoptions': [optval],
|
'xoptions': {'frozen_modules': xoption_value},
|
||||||
'use_frozen_modules': expected,
|
'use_frozen_modules': expected,
|
||||||
}
|
}
|
||||||
env = {'TESTFROZEN': raw[1:]} if raw else None
|
env = {'TESTFROZEN': raw[1:]} if raw else None
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
Add functions to get and set the current runtime Python configuration:
|
||||||
|
|
||||||
|
* :c:func:`PyConfig_Get`
|
||||||
|
* :c:func:`PyConfig_GetInt`
|
||||||
|
* :c:func:`PyConfig_Set`
|
||||||
|
* :c:func:`PyConfig_Names`
|
||||||
|
|
||||||
|
Patch by Victor Stinner.
|
|
@ -162,7 +162,7 @@
|
||||||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
||||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
|
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
|
||||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c
|
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c
|
||||||
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c
|
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c
|
||||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||||
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
|
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include "parts.h"
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_testcapi_config_get(PyObject *module, PyObject *name_obj)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
if (PyArg_Parse(name_obj, "s", &name) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyConfig_Get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_testcapi_config_getint(PyObject *module, PyObject *name_obj)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
if (PyArg_Parse(name_obj, "s", &name) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value;
|
||||||
|
if (PyConfig_GetInt(name, &value) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyLong_FromLong(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_testcapi_config_names(PyObject *module, PyObject* Py_UNUSED(args))
|
||||||
|
{
|
||||||
|
return PyConfig_Names();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_testcapi_config_set(PyObject *module, PyObject *args)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
PyObject *value;
|
||||||
|
if (PyArg_ParseTuple(args, "sO", &name, &value) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = PyConfig_Set(name, value);
|
||||||
|
if (res < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyMethodDef test_methods[] = {
|
||||||
|
{"config_get", _testcapi_config_get, METH_O},
|
||||||
|
{"config_getint", _testcapi_config_getint, METH_O},
|
||||||
|
{"config_names", _testcapi_config_names, METH_NOARGS},
|
||||||
|
{"config_set", _testcapi_config_set, METH_VARARGS},
|
||||||
|
{NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyTestCapi_Init_Config(PyObject *mod)
|
||||||
|
{
|
||||||
|
return PyModule_AddFunctions(mod, test_methods);
|
||||||
|
}
|
|
@ -60,5 +60,6 @@ int _PyTestCapi_Init_Hash(PyObject *module);
|
||||||
int _PyTestCapi_Init_Time(PyObject *module);
|
int _PyTestCapi_Init_Time(PyObject *module);
|
||||||
int _PyTestCapi_Init_Monitoring(PyObject *module);
|
int _PyTestCapi_Init_Monitoring(PyObject *module);
|
||||||
int _PyTestCapi_Init_Object(PyObject *module);
|
int _PyTestCapi_Init_Object(PyObject *module);
|
||||||
|
int _PyTestCapi_Init_Config(PyObject *mod);
|
||||||
|
|
||||||
#endif // Py_TESTCAPI_PARTS_H
|
#endif // Py_TESTCAPI_PARTS_H
|
||||||
|
|
|
@ -4172,6 +4172,9 @@ PyInit__testcapi(void)
|
||||||
if (_PyTestCapi_Init_Object(m) < 0) {
|
if (_PyTestCapi_Init_Object(m) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (_PyTestCapi_Init_Config(m) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
PyState_AddModule(m, &_testcapimodule);
|
PyState_AddModule(m, &_testcapimodule);
|
||||||
return m;
|
return m;
|
||||||
|
|
|
@ -126,6 +126,7 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\run.c" />
|
<ClCompile Include="..\Modules\_testcapi\run.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\monitoring.c" />
|
<ClCompile Include="..\Modules\_testcapi\monitoring.c" />
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\config.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="..\PC\python_nt.rc" />
|
<ResourceCompile Include="..\PC\python_nt.rc" />
|
||||||
|
|
|
@ -111,6 +111,9 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\monitoring.c">
|
<ClCompile Include="..\Modules\_testcapi\monitoring.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\config.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="..\PC\python_nt.rc">
|
<ResourceCompile Include="..\PC\python_nt.rc">
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "pycore_pymem.h" // _PyMem_SetDefaultAllocator()
|
#include "pycore_pymem.h" // _PyMem_SetDefaultAllocator()
|
||||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||||
#include "pycore_pystats.h" // _Py_StatsOn()
|
#include "pycore_pystats.h" // _Py_StatsOn()
|
||||||
|
#include "pycore_sysmodule.h" // _PySys_SetIntMaxStrDigits()
|
||||||
|
|
||||||
#include "osdefs.h" // DELIM
|
#include "osdefs.h" // DELIM
|
||||||
|
|
||||||
|
@ -26,6 +27,22 @@
|
||||||
|
|
||||||
#include "config_common.h"
|
#include "config_common.h"
|
||||||
|
|
||||||
|
/* --- PyConfig setters ------------------------------------------- */
|
||||||
|
|
||||||
|
typedef PyObject* (*config_sys_flag_setter) (int value);
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
config_sys_flag_long(int value)
|
||||||
|
{
|
||||||
|
return PyLong_FromLong(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
config_sys_flag_not(int value)
|
||||||
|
{
|
||||||
|
value = (!value);
|
||||||
|
return config_sys_flag_long(value);
|
||||||
|
}
|
||||||
|
|
||||||
/* --- PyConfig spec ---------------------------------------------- */
|
/* --- PyConfig spec ---------------------------------------------- */
|
||||||
|
|
||||||
|
@ -40,99 +57,174 @@ typedef enum {
|
||||||
PyConfig_MEMBER_WSTR_LIST = 12,
|
PyConfig_MEMBER_WSTR_LIST = 12,
|
||||||
} PyConfigMemberType;
|
} PyConfigMemberType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// Option which cannot be get or set by PyConfig_Get() and PyConfig_Set()
|
||||||
|
PyConfig_MEMBER_INIT_ONLY = 0,
|
||||||
|
|
||||||
|
// Option which cannot be set by PyConfig_Set()
|
||||||
|
PyConfig_MEMBER_READ_ONLY = 1,
|
||||||
|
|
||||||
|
// Public option: can be get and set by PyConfig_Get() and PyConfig_Set()
|
||||||
|
PyConfig_MEMBER_PUBLIC = 2,
|
||||||
|
} PyConfigMemberVisibility;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *attr;
|
||||||
|
int flag_index;
|
||||||
|
config_sys_flag_setter flag_setter;
|
||||||
|
} PyConfigSysSpec;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
PyConfigMemberType type;
|
PyConfigMemberType type;
|
||||||
|
PyConfigMemberVisibility visibility;
|
||||||
|
PyConfigSysSpec sys;
|
||||||
} PyConfigSpec;
|
} PyConfigSpec;
|
||||||
|
|
||||||
#define SPEC(MEMBER, TYPE) \
|
#define SPEC(MEMBER, TYPE, VISIBILITY, sys) \
|
||||||
{#MEMBER, offsetof(PyConfig, MEMBER), PyConfig_MEMBER_##TYPE}
|
{#MEMBER, offsetof(PyConfig, MEMBER), \
|
||||||
|
PyConfig_MEMBER_##TYPE, PyConfig_MEMBER_##VISIBILITY, sys}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
// Update _test_embed_set_config when adding new members
|
// Update _test_embed_set_config when adding new members
|
||||||
static const PyConfigSpec PYCONFIG_SPEC[] = {
|
static const PyConfigSpec PYCONFIG_SPEC[] = {
|
||||||
SPEC(_config_init, UINT),
|
// --- Public options -----------
|
||||||
SPEC(isolated, BOOL),
|
|
||||||
SPEC(use_environment, BOOL),
|
SPEC(argv, WSTR_LIST, PUBLIC, SYS_ATTR("argv")),
|
||||||
SPEC(dev_mode, BOOL),
|
SPEC(base_exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_exec_prefix")),
|
||||||
SPEC(install_signal_handlers, BOOL),
|
SPEC(base_executable, WSTR_OPT, PUBLIC, SYS_ATTR("_base_executable")),
|
||||||
SPEC(use_hash_seed, BOOL),
|
SPEC(base_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_prefix")),
|
||||||
SPEC(hash_seed, ULONG),
|
SPEC(bytes_warning, UINT, PUBLIC, SYS_FLAG(9)),
|
||||||
SPEC(faulthandler, BOOL),
|
SPEC(exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("exec_prefix")),
|
||||||
SPEC(tracemalloc, UINT),
|
SPEC(executable, WSTR_OPT, PUBLIC, SYS_ATTR("executable")),
|
||||||
SPEC(perf_profiling, UINT),
|
SPEC(inspect, BOOL, PUBLIC, SYS_FLAG(1)),
|
||||||
SPEC(import_time, BOOL),
|
SPEC(int_max_str_digits, UINT, PUBLIC, NO_SYS),
|
||||||
SPEC(code_debug_ranges, BOOL),
|
SPEC(interactive, BOOL, PUBLIC, SYS_FLAG(2)),
|
||||||
SPEC(show_ref_count, BOOL),
|
SPEC(module_search_paths, WSTR_LIST, PUBLIC, SYS_ATTR("path")),
|
||||||
SPEC(dump_refs, BOOL),
|
SPEC(optimization_level, UINT, PUBLIC, SYS_FLAG(3)),
|
||||||
SPEC(dump_refs_file, WSTR_OPT),
|
SPEC(parser_debug, BOOL, PUBLIC, SYS_FLAG(0)),
|
||||||
SPEC(malloc_stats, BOOL),
|
SPEC(platlibdir, WSTR, PUBLIC, SYS_ATTR("platlibdir")),
|
||||||
SPEC(filesystem_encoding, WSTR),
|
SPEC(prefix, WSTR_OPT, PUBLIC, SYS_ATTR("prefix")),
|
||||||
SPEC(filesystem_errors, WSTR),
|
SPEC(pycache_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("pycache_prefix")),
|
||||||
SPEC(pycache_prefix, WSTR_OPT),
|
SPEC(quiet, BOOL, PUBLIC, SYS_FLAG(10)),
|
||||||
SPEC(parse_argv, BOOL),
|
SPEC(stdlib_dir, WSTR_OPT, PUBLIC, SYS_ATTR("_stdlib_dir")),
|
||||||
SPEC(orig_argv, WSTR_LIST),
|
SPEC(use_environment, BOOL, PUBLIC, SYS_FLAG_SETTER(7, config_sys_flag_not)),
|
||||||
SPEC(argv, WSTR_LIST),
|
SPEC(verbose, UINT, PUBLIC, SYS_FLAG(8)),
|
||||||
SPEC(xoptions, WSTR_LIST),
|
SPEC(warnoptions, WSTR_LIST, PUBLIC, SYS_ATTR("warnoptions")),
|
||||||
SPEC(warnoptions, WSTR_LIST),
|
SPEC(write_bytecode, BOOL, PUBLIC, SYS_FLAG_SETTER(4, config_sys_flag_not)),
|
||||||
SPEC(site_import, BOOL),
|
SPEC(xoptions, WSTR_LIST, PUBLIC, SYS_ATTR("_xoptions")),
|
||||||
SPEC(bytes_warning, UINT),
|
|
||||||
SPEC(warn_default_encoding, BOOL),
|
// --- Read-only options -----------
|
||||||
SPEC(inspect, BOOL),
|
|
||||||
SPEC(interactive, BOOL),
|
|
||||||
SPEC(optimization_level, UINT),
|
|
||||||
SPEC(parser_debug, BOOL),
|
|
||||||
SPEC(write_bytecode, BOOL),
|
|
||||||
SPEC(verbose, UINT),
|
|
||||||
SPEC(quiet, BOOL),
|
|
||||||
SPEC(user_site_directory, BOOL),
|
|
||||||
SPEC(configure_c_stdio, BOOL),
|
|
||||||
SPEC(buffered_stdio, BOOL),
|
|
||||||
SPEC(stdio_encoding, WSTR),
|
|
||||||
SPEC(stdio_errors, WSTR),
|
|
||||||
#ifdef MS_WINDOWS
|
|
||||||
SPEC(legacy_windows_stdio, BOOL),
|
|
||||||
#endif
|
|
||||||
SPEC(check_hash_pycs_mode, WSTR),
|
|
||||||
SPEC(use_frozen_modules, BOOL),
|
|
||||||
SPEC(safe_path, BOOL),
|
|
||||||
SPEC(int_max_str_digits, INT),
|
|
||||||
SPEC(cpu_count, INT),
|
|
||||||
#ifdef Py_GIL_DISABLED
|
|
||||||
SPEC(enable_gil, INT),
|
|
||||||
#endif
|
|
||||||
SPEC(pathconfig_warnings, BOOL),
|
|
||||||
SPEC(program_name, WSTR),
|
|
||||||
SPEC(pythonpath_env, WSTR_OPT),
|
|
||||||
SPEC(home, WSTR_OPT),
|
|
||||||
SPEC(platlibdir, WSTR),
|
|
||||||
SPEC(sys_path_0, WSTR_OPT),
|
|
||||||
SPEC(module_search_paths_set, BOOL),
|
|
||||||
SPEC(module_search_paths, WSTR_LIST),
|
|
||||||
SPEC(stdlib_dir, WSTR_OPT),
|
|
||||||
SPEC(executable, WSTR_OPT),
|
|
||||||
SPEC(base_executable, WSTR_OPT),
|
|
||||||
SPEC(prefix, WSTR_OPT),
|
|
||||||
SPEC(base_prefix, WSTR_OPT),
|
|
||||||
SPEC(exec_prefix, WSTR_OPT),
|
|
||||||
SPEC(base_exec_prefix, WSTR_OPT),
|
|
||||||
SPEC(skip_source_first_line, BOOL),
|
|
||||||
SPEC(run_command, WSTR_OPT),
|
|
||||||
SPEC(run_module, WSTR_OPT),
|
|
||||||
SPEC(run_filename, WSTR_OPT),
|
|
||||||
SPEC(_install_importlib, BOOL),
|
|
||||||
SPEC(_init_main, BOOL),
|
|
||||||
SPEC(_is_python_build, BOOL),
|
|
||||||
#ifdef Py_STATS
|
#ifdef Py_STATS
|
||||||
SPEC(_pystats, BOOL),
|
SPEC(_pystats, BOOL, READ_ONLY, NO_SYS),
|
||||||
#endif
|
#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(cpu_count, INT, 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),
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
SPEC(enable_gil, INT, READ_ONLY, NO_SYS),
|
||||||
|
#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(import_time, BOOL, READ_ONLY, NO_SYS),
|
||||||
|
SPEC(install_signal_handlers, BOOL, READ_ONLY, NO_SYS),
|
||||||
|
SPEC(isolated, BOOL, READ_ONLY, NO_SYS), // sys.flags.isolated
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
SPEC(legacy_windows_stdio, BOOL, READ_ONLY, NO_SYS),
|
||||||
|
#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, 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),
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
SPEC(run_presite, WSTR_OPT),
|
SPEC(run_presite, WSTR_OPT, READ_ONLY, NO_SYS),
|
||||||
#endif
|
#endif
|
||||||
{NULL, 0, 0},
|
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(user_site_directory, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_user_site
|
||||||
|
SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS),
|
||||||
|
|
||||||
|
// --- 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),
|
||||||
|
|
||||||
|
// Array terminator
|
||||||
|
{NULL, 0, 0, 0, NO_SYS},
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef SPEC
|
#undef SPEC
|
||||||
|
#define SPEC(MEMBER, TYPE, VISIBILITY) \
|
||||||
|
{#MEMBER, offsetof(PyPreConfig, MEMBER), PyConfig_MEMBER_##TYPE, \
|
||||||
|
PyConfig_MEMBER_##VISIBILITY, NO_SYS}
|
||||||
|
|
||||||
|
static const PyConfigSpec PYPRECONFIG_SPEC[] = {
|
||||||
|
// --- Read-only options -----------
|
||||||
|
|
||||||
|
SPEC(allocator, INT, READ_ONLY),
|
||||||
|
SPEC(coerce_c_locale, BOOL, READ_ONLY),
|
||||||
|
SPEC(coerce_c_locale_warn, BOOL, READ_ONLY),
|
||||||
|
SPEC(configure_locale, BOOL, READ_ONLY),
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
SPEC(legacy_windows_fs_encoding, BOOL, READ_ONLY),
|
||||||
|
#endif
|
||||||
|
SPEC(utf8_mode, BOOL, READ_ONLY),
|
||||||
|
|
||||||
|
// --- Init-only options -----------
|
||||||
|
// Members already present in PYCONFIG_SPEC
|
||||||
|
|
||||||
|
SPEC(_config_init, INT, INIT_ONLY),
|
||||||
|
SPEC(dev_mode, BOOL, INIT_ONLY),
|
||||||
|
SPEC(isolated, BOOL, INIT_ONLY),
|
||||||
|
SPEC(parse_argv, BOOL, INIT_ONLY),
|
||||||
|
SPEC(use_environment, BOOL, INIT_ONLY),
|
||||||
|
|
||||||
|
// Array terminator
|
||||||
|
{NULL, 0, 0, 0, NO_SYS},
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef SPEC
|
||||||
|
#undef SYS_ATTR
|
||||||
|
#undef SYS_FLAG_SETTER
|
||||||
|
#undef SYS_FLAG
|
||||||
|
#undef NO_SYS
|
||||||
|
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
static PyObject*
|
||||||
|
config_get(const PyConfig *config, const PyConfigSpec *spec,
|
||||||
|
int use_sys);
|
||||||
|
|
||||||
|
|
||||||
/* --- Command line options --------------------------------------- */
|
/* --- Command line options --------------------------------------- */
|
||||||
|
@ -656,6 +748,28 @@ _PyWideStringList_AsList(const PyWideStringList *list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
_PyWideStringList_AsTuple(const PyWideStringList *list)
|
||||||
|
{
|
||||||
|
assert(_PyWideStringList_CheckConsistency(list));
|
||||||
|
|
||||||
|
PyObject *tuple = PyTuple_New(list->length);
|
||||||
|
if (tuple == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Py_ssize_t i = 0; i < list->length; i++) {
|
||||||
|
PyObject *item = PyUnicode_FromWideChar(list->items[i], -1);
|
||||||
|
if (item == NULL) {
|
||||||
|
Py_DECREF(tuple);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyTuple_SET_ITEM(tuple, i, item);
|
||||||
|
}
|
||||||
|
return tuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- Py_GetArgcArgv() ------------------------------------------- */
|
/* --- Py_GetArgcArgv() ------------------------------------------- */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1059,54 +1173,12 @@ _PyConfig_AsDict(const PyConfig *config)
|
||||||
|
|
||||||
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;
|
PyObject *obj = config_get(config, spec, 0);
|
||||||
PyObject *obj;
|
|
||||||
switch (spec->type) {
|
|
||||||
case PyConfig_MEMBER_INT:
|
|
||||||
case PyConfig_MEMBER_UINT:
|
|
||||||
{
|
|
||||||
int value = *(int*)member;
|
|
||||||
obj = PyLong_FromLong(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PyConfig_MEMBER_BOOL:
|
|
||||||
{
|
|
||||||
int value = *(int*)member;
|
|
||||||
obj = PyBool_FromLong(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PyConfig_MEMBER_ULONG:
|
|
||||||
{
|
|
||||||
unsigned long value = *(unsigned long*)member;
|
|
||||||
obj = PyLong_FromUnsignedLong(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PyConfig_MEMBER_WSTR:
|
|
||||||
case PyConfig_MEMBER_WSTR_OPT:
|
|
||||||
{
|
|
||||||
const wchar_t *wstr = *(const wchar_t**)member;
|
|
||||||
if (wstr != NULL) {
|
|
||||||
obj = PyUnicode_FromWideChar(wstr, -1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
obj = Py_NewRef(Py_None);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PyConfig_MEMBER_WSTR_LIST:
|
|
||||||
{
|
|
||||||
const PyWideStringList *list = (const PyWideStringList*)member;
|
|
||||||
obj = _PyWideStringList_AsList(list);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Py_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj == NULL) {
|
if (obj == NULL) {
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int res = PyDict_SetItemString(dict, spec->name, obj);
|
int res = PyDict_SetItemString(dict, spec->name, obj);
|
||||||
Py_DECREF(obj);
|
Py_DECREF(obj);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
@ -1218,15 +1290,17 @@ config_dict_get_wstrlist(PyObject *dict, const char *name, PyConfig *config,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PyList_CheckExact(list)) {
|
int is_list = PyList_CheckExact(list);
|
||||||
|
if (!is_list && !PyTuple_CheckExact(list)) {
|
||||||
Py_DECREF(list);
|
Py_DECREF(list);
|
||||||
config_dict_invalid_type(name);
|
config_dict_invalid_type(name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyWideStringList wstrlist = _PyWideStringList_INIT;
|
PyWideStringList wstrlist = _PyWideStringList_INIT;
|
||||||
for (Py_ssize_t i=0; i < PyList_GET_SIZE(list); i++) {
|
Py_ssize_t len = is_list ? PyList_GET_SIZE(list) : PyTuple_GET_SIZE(list);
|
||||||
PyObject *item = PyList_GET_ITEM(list, i);
|
for (Py_ssize_t i=0; i < len; i++) {
|
||||||
|
PyObject *item = is_list ? PyList_GET_ITEM(list, i) : PyTuple_GET_ITEM(list, i);
|
||||||
|
|
||||||
if (item == Py_None) {
|
if (item == Py_None) {
|
||||||
config_dict_invalid_value(name);
|
config_dict_invalid_value(name);
|
||||||
|
@ -1263,6 +1337,66 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
config_dict_get_xoptions(PyObject *dict, const char *name, PyConfig *config,
|
||||||
|
PyWideStringList *result)
|
||||||
|
{
|
||||||
|
PyObject *xoptions = config_dict_get(dict, name);
|
||||||
|
if (xoptions == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyDict_CheckExact(xoptions)) {
|
||||||
|
Py_DECREF(xoptions);
|
||||||
|
config_dict_invalid_type(name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
PyObject *key, *value;
|
||||||
|
PyWideStringList wstrlist = _PyWideStringList_INIT;
|
||||||
|
while (PyDict_Next(xoptions, &pos, &key, &value)) {
|
||||||
|
PyObject *item;
|
||||||
|
|
||||||
|
if (value != Py_True) {
|
||||||
|
item = PyUnicode_FromFormat("%S=%S", key, value);
|
||||||
|
if (item == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item = Py_NewRef(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t *wstr = PyUnicode_AsWideCharString(item, NULL);
|
||||||
|
Py_DECREF(item);
|
||||||
|
if (wstr == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyStatus status = PyWideStringList_Append(&wstrlist, wstr);
|
||||||
|
PyMem_Free(wstr);
|
||||||
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_PyWideStringList_Copy(result, &wstrlist) < 0) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
_PyWideStringList_Clear(&wstrlist);
|
||||||
|
Py_DECREF(xoptions);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
_PyWideStringList_Clear(&wstrlist);
|
||||||
|
Py_DECREF(xoptions);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyConfig_FromDict(PyConfig *config, PyObject *dict)
|
_PyConfig_FromDict(PyConfig *config, PyObject *dict)
|
||||||
{
|
{
|
||||||
|
@ -1324,9 +1458,17 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
|
||||||
}
|
}
|
||||||
case PyConfig_MEMBER_WSTR_LIST:
|
case PyConfig_MEMBER_WSTR_LIST:
|
||||||
{
|
{
|
||||||
if (config_dict_get_wstrlist(dict, spec->name, config,
|
if (strcmp(spec->name, "xoptions") == 0) {
|
||||||
(PyWideStringList*)member) < 0) {
|
if (config_dict_get_xoptions(dict, spec->name, config,
|
||||||
return -1;
|
(PyWideStringList*)member) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (config_dict_get_wstrlist(dict, spec->name, config,
|
||||||
|
(PyWideStringList*)member) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3261,3 +3403,505 @@ _Py_DumpPathConfig(PyThreadState *tstate)
|
||||||
|
|
||||||
_PyErr_SetRaisedException(tstate, exc);
|
_PyErr_SetRaisedException(tstate, exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- PyConfig_Get() -------------------------------------------------------
|
||||||
|
|
||||||
|
static void*
|
||||||
|
config_spec_get_member(const PyConfigSpec *spec, const PyConfig *config)
|
||||||
|
{
|
||||||
|
return (char *)config + spec->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const PyConfigSpec*
|
||||||
|
config_generic_find_spec(const PyConfigSpec *spec, const char *name)
|
||||||
|
{
|
||||||
|
for (; spec->name != NULL; spec++) {
|
||||||
|
if (spec->visibility == PyConfig_MEMBER_INIT_ONLY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strcmp(name, spec->name) == 0) {
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const PyConfigSpec*
|
||||||
|
config_find_spec(const char *name)
|
||||||
|
{
|
||||||
|
return config_generic_find_spec(PYCONFIG_SPEC, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const PyConfigSpec*
|
||||||
|
preconfig_find_spec(const char *name)
|
||||||
|
{
|
||||||
|
return config_generic_find_spec(PYPRECONFIG_SPEC, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
config_add_xoption(PyObject *dict, const wchar_t *str)
|
||||||
|
{
|
||||||
|
PyObject *name = NULL, *value = NULL;
|
||||||
|
|
||||||
|
const wchar_t *name_end = wcschr(str, L'=');
|
||||||
|
if (!name_end) {
|
||||||
|
name = PyUnicode_FromWideChar(str, -1);
|
||||||
|
if (name == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
value = Py_NewRef(Py_True);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = PyUnicode_FromWideChar(str, name_end - str);
|
||||||
|
if (name == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
value = PyUnicode_FromWideChar(name_end + 1, -1);
|
||||||
|
if (value == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (PyDict_SetItem(dict, name, value) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
Py_DECREF(name);
|
||||||
|
Py_DECREF(value);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(name);
|
||||||
|
Py_XDECREF(value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
_PyConfig_CreateXOptionsDict(const PyConfig *config)
|
||||||
|
{
|
||||||
|
PyObject *dict = PyDict_New();
|
||||||
|
if (dict == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t nxoption = config->xoptions.length;
|
||||||
|
wchar_t **xoptions = config->xoptions.items;
|
||||||
|
for (Py_ssize_t i=0; i < nxoption; i++) {
|
||||||
|
const wchar_t *option = xoptions[i];
|
||||||
|
if (config_add_xoption(dict, option) < 0) {
|
||||||
|
Py_DECREF(dict);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
config_get_sys(const char *name)
|
||||||
|
{
|
||||||
|
PyObject *value = PySys_GetObject(name);
|
||||||
|
if (value == NULL) {
|
||||||
|
PyErr_Format(PyExc_RuntimeError, "lost sys.%s", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return Py_NewRef(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
config_get_sys_write_bytecode(const PyConfig *config, int *value)
|
||||||
|
{
|
||||||
|
PyObject *attr = config_get_sys("dont_write_bytecode");
|
||||||
|
if (attr == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_true = PyObject_IsTrue(attr);
|
||||||
|
Py_DECREF(attr);
|
||||||
|
if (is_true < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*value = (!is_true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
config_get(const PyConfig *config, const PyConfigSpec *spec,
|
||||||
|
int use_sys)
|
||||||
|
{
|
||||||
|
if (use_sys) {
|
||||||
|
if (spec->sys.attr != NULL) {
|
||||||
|
return config_get_sys(spec->sys.attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(spec->name, "write_bytecode") == 0) {
|
||||||
|
int value;
|
||||||
|
if (config_get_sys_write_bytecode(config, &value) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyBool_FromLong(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(spec->name, "int_max_str_digits") == 0) {
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
return PyLong_FromLong(interp->long_state.max_str_digits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *member = config_spec_get_member(spec, config);
|
||||||
|
switch (spec->type) {
|
||||||
|
case PyConfig_MEMBER_INT:
|
||||||
|
case PyConfig_MEMBER_UINT:
|
||||||
|
{
|
||||||
|
int value = *(int *)member;
|
||||||
|
return PyLong_FromLong(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_BOOL:
|
||||||
|
{
|
||||||
|
int value = *(int *)member;
|
||||||
|
return PyBool_FromLong(value != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_ULONG:
|
||||||
|
{
|
||||||
|
unsigned long value = *(unsigned long *)member;
|
||||||
|
return PyLong_FromUnsignedLong(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_WSTR:
|
||||||
|
case PyConfig_MEMBER_WSTR_OPT:
|
||||||
|
{
|
||||||
|
wchar_t *wstr = *(wchar_t **)member;
|
||||||
|
if (wstr != NULL) {
|
||||||
|
return PyUnicode_FromWideChar(wstr, -1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Py_NewRef(Py_None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_WSTR_LIST:
|
||||||
|
{
|
||||||
|
if (strcmp(spec->name, "xoptions") == 0) {
|
||||||
|
return _PyConfig_CreateXOptionsDict(config);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const PyWideStringList *list = (const PyWideStringList *)member;
|
||||||
|
return _PyWideStringList_AsTuple(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Py_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
preconfig_get(const PyPreConfig *preconfig, const PyConfigSpec *spec)
|
||||||
|
{
|
||||||
|
// The type of all PYPRECONFIG_SPEC members is INT or BOOL.
|
||||||
|
assert(spec->type == PyConfig_MEMBER_INT
|
||||||
|
|| spec->type == PyConfig_MEMBER_BOOL);
|
||||||
|
|
||||||
|
char *member = (char *)preconfig + spec->offset;
|
||||||
|
int value = *(int *)member;
|
||||||
|
|
||||||
|
if (spec->type == PyConfig_MEMBER_BOOL) {
|
||||||
|
return PyBool_FromLong(value != 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return PyLong_FromLong(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
config_unknown_name_error(const char *name)
|
||||||
|
{
|
||||||
|
PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
PyConfig_Get(const char *name)
|
||||||
|
{
|
||||||
|
const PyConfigSpec *spec = config_find_spec(name);
|
||||||
|
if (spec != NULL) {
|
||||||
|
const PyConfig *config = _Py_GetConfig();
|
||||||
|
return config_get(config, spec, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
spec = preconfig_find_spec(name);
|
||||||
|
if (spec != NULL) {
|
||||||
|
const PyPreConfig *preconfig = &_PyRuntime.preconfig;
|
||||||
|
return preconfig_get(preconfig, spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
config_unknown_name_error(name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyConfig_GetInt(const char *name, int *value)
|
||||||
|
{
|
||||||
|
assert(!PyErr_Occurred());
|
||||||
|
|
||||||
|
PyObject *obj = PyConfig_Get(name);
|
||||||
|
if (obj == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyLong_Check(obj)) {
|
||||||
|
Py_DECREF(obj);
|
||||||
|
PyErr_Format(PyExc_TypeError, "config option %s is not an int", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int as_int = PyLong_AsInt(obj);
|
||||||
|
Py_DECREF(obj);
|
||||||
|
if (as_int == -1 && PyErr_Occurred()) {
|
||||||
|
PyErr_Format(PyExc_OverflowError,
|
||||||
|
"config option %s value does not fit into a C int", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = as_int;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
config_names_add(PyObject *names, const PyConfigSpec *spec)
|
||||||
|
{
|
||||||
|
for (; spec->name != NULL; spec++) {
|
||||||
|
if (spec->visibility == PyConfig_MEMBER_INIT_ONLY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PyObject *name = PyUnicode_FromString(spec->name);
|
||||||
|
if (name == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int res = PyList_Append(names, name);
|
||||||
|
Py_DECREF(name);
|
||||||
|
if (res < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
PyConfig_Names(void)
|
||||||
|
{
|
||||||
|
PyObject *names = PyList_New(0);
|
||||||
|
if (names == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_names_add(names, PYCONFIG_SPEC) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (config_names_add(names, PYPRECONFIG_SPEC) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *frozen = PyFrozenSet_New(names);
|
||||||
|
Py_DECREF(names);
|
||||||
|
return frozen;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(names);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- PyConfig_Set() -------------------------------------------------------
|
||||||
|
|
||||||
|
static int
|
||||||
|
config_set_sys_flag(const PyConfigSpec *spec, int int_value)
|
||||||
|
{
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
PyConfig *config = &interp->config;
|
||||||
|
|
||||||
|
if (spec->type == PyConfig_MEMBER_BOOL) {
|
||||||
|
if (int_value != 0) {
|
||||||
|
// convert values < 0 and values > 1 to 1
|
||||||
|
int_value = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *value;
|
||||||
|
if (spec->sys.flag_setter) {
|
||||||
|
value = spec->sys.flag_setter(int_value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = config_sys_flag_long(int_value);
|
||||||
|
}
|
||||||
|
if (value == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sys.flags.FLAG
|
||||||
|
Py_ssize_t pos = spec->sys.flag_index;
|
||||||
|
if (_PySys_SetFlagObj(pos, value) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set PyConfig.ATTR
|
||||||
|
assert(spec->type == PyConfig_MEMBER_INT
|
||||||
|
|| spec->type == PyConfig_MEMBER_UINT
|
||||||
|
|| spec->type == PyConfig_MEMBER_BOOL);
|
||||||
|
int *member = config_spec_get_member(spec, config);
|
||||||
|
*member = int_value;
|
||||||
|
|
||||||
|
// Set sys.dont_write_bytecode attribute
|
||||||
|
if (strcmp(spec->name, "write_bytecode") == 0) {
|
||||||
|
if (PySys_SetObject("dont_write_bytecode", value) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(value);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_DECREF(value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyConfig_Set(const char *name, PyObject *value)
|
||||||
|
{
|
||||||
|
const PyConfigSpec *spec = config_find_spec(name);
|
||||||
|
if (spec == NULL) {
|
||||||
|
spec = preconfig_find_spec(name);
|
||||||
|
if (spec == NULL) {
|
||||||
|
config_unknown_name_error(name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(spec->visibility != PyConfig_MEMBER_PUBLIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spec->visibility != PyConfig_MEMBER_PUBLIC) {
|
||||||
|
PyErr_Format(PyExc_ValueError, "cannot set read-only option %s",
|
||||||
|
name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int int_value = 0;
|
||||||
|
int has_int_value = 0;
|
||||||
|
|
||||||
|
switch (spec->type) {
|
||||||
|
case PyConfig_MEMBER_INT:
|
||||||
|
case PyConfig_MEMBER_UINT:
|
||||||
|
case PyConfig_MEMBER_BOOL:
|
||||||
|
if (!PyLong_Check(value)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected int or bool, got %T", value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int_value = PyLong_AsInt(value);
|
||||||
|
if (int_value == -1 && PyErr_Occurred()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (int_value < 0 && spec->type != PyConfig_MEMBER_INT) {
|
||||||
|
PyErr_Format(PyExc_ValueError, "value must be >= 0");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
has_int_value = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_ULONG:
|
||||||
|
// not implemented: only hash_seed uses this type, and it's read-only
|
||||||
|
goto cannot_set;
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_WSTR:
|
||||||
|
if (!PyUnicode_CheckExact(value)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected str, got %T", value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_WSTR_OPT:
|
||||||
|
if (value != Py_None && !PyUnicode_CheckExact(value)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected str or None, got %T", value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PyConfig_MEMBER_WSTR_LIST:
|
||||||
|
if (strcmp(spec->name, "xoptions") != 0) {
|
||||||
|
if (!PyList_Check(value)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected list[str], got %T",
|
||||||
|
value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (Py_ssize_t i=0; i < PyList_GET_SIZE(value); i++) {
|
||||||
|
PyObject *item = PyList_GET_ITEM(value, i);
|
||||||
|
if (!PyUnicode_Check(item)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"expected str, list item %zd has type %T",
|
||||||
|
i, item);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// xoptions type is dict[str, str]
|
||||||
|
if (!PyDict_Check(value)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"expected dict[str, str | bool], got %T",
|
||||||
|
value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
PyObject *key, *item;
|
||||||
|
while (PyDict_Next(value, &pos, &key, &item)) {
|
||||||
|
if (!PyUnicode_Check(key)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"expected str, "
|
||||||
|
"got dict key type %T", key);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!PyUnicode_Check(item) && !PyBool_Check(item)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"expected str or bool, "
|
||||||
|
"got dict value type %T", key);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Py_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (spec->sys.attr != NULL) {
|
||||||
|
// Set the sys attribute, but don't set PyInterpreterState.config
|
||||||
|
// to keep the code simple.
|
||||||
|
return PySys_SetObject(spec->sys.attr, value);
|
||||||
|
}
|
||||||
|
else if (spec->sys.flag_index >= 0 && has_int_value) {
|
||||||
|
return config_set_sys_flag(spec, int_value);
|
||||||
|
}
|
||||||
|
else if (strcmp(spec->name, "int_max_str_digits") == 0 && has_int_value) {
|
||||||
|
return _PySys_SetIntMaxStrDigits(int_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
cannot_set:
|
||||||
|
PyErr_Format(PyExc_ValueError, "cannot set option %s", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
|
@ -1845,6 +1845,7 @@ sys_get_int_max_str_digits_impl(PyObject *module)
|
||||||
return PyLong_FromLong(interp->long_state.max_str_digits);
|
return PyLong_FromLong(interp->long_state.max_str_digits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
sys.set_int_max_str_digits
|
sys.set_int_max_str_digits
|
||||||
|
|
||||||
|
@ -1857,16 +1858,10 @@ static PyObject *
|
||||||
sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits)
|
sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits)
|
||||||
/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/
|
/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/
|
||||||
{
|
{
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
if (_PySys_SetIntMaxStrDigits(maxdigits) < 0) {
|
||||||
if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) {
|
|
||||||
tstate->interp->long_state.max_str_digits = maxdigits;
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
} else {
|
|
||||||
PyErr_Format(
|
|
||||||
PyExc_ValueError, "maxdigits must be 0 or larger than %d",
|
|
||||||
_PY_LONG_MAX_STR_DIGITS_THRESHOLD);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
|
@ -3120,6 +3115,8 @@ static PyStructSequence_Field flags_fields[] = {
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SYS_FLAGS_INT_MAX_STR_DIGITS 17
|
||||||
|
|
||||||
static PyStructSequence_Desc flags_desc = {
|
static PyStructSequence_Desc flags_desc = {
|
||||||
"sys.flags", /* name */
|
"sys.flags", /* name */
|
||||||
flags__doc__, /* doc */
|
flags__doc__, /* doc */
|
||||||
|
@ -3127,6 +3124,48 @@ static PyStructSequence_Desc flags_desc = {
|
||||||
18
|
18
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value)
|
||||||
|
{
|
||||||
|
assert(pos >= 0 && pos < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1));
|
||||||
|
|
||||||
|
PyObject *old_value = PyStructSequence_GET_ITEM(flags, pos);
|
||||||
|
PyStructSequence_SET_ITEM(flags, pos, Py_NewRef(value));
|
||||||
|
Py_XDECREF(old_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
_PySys_SetFlagObj(Py_ssize_t pos, PyObject *value)
|
||||||
|
{
|
||||||
|
PyObject *flags = Py_XNewRef(PySys_GetObject("flags"));
|
||||||
|
if (flags == NULL) {
|
||||||
|
if (!PyErr_Occurred()) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "lost sys.flags");
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_set_flag(flags, pos, value);
|
||||||
|
Py_DECREF(flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
_PySys_SetFlagInt(Py_ssize_t pos, int value)
|
||||||
|
{
|
||||||
|
PyObject *obj = PyLong_FromLong(value);
|
||||||
|
if (obj == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = _PySys_SetFlagObj(pos, obj);
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
|
set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
|
||||||
{
|
{
|
||||||
|
@ -3142,8 +3181,8 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
|
||||||
if (value == NULL) { \
|
if (value == NULL) { \
|
||||||
return -1; \
|
return -1; \
|
||||||
} \
|
} \
|
||||||
Py_XDECREF(PyStructSequence_GET_ITEM(flags, pos)); \
|
sys_set_flag(flags, pos, value); \
|
||||||
PyStructSequence_SET_ITEM(flags, pos, value); \
|
Py_DECREF(value); \
|
||||||
pos++; \
|
pos++; \
|
||||||
} while (0)
|
} while (0)
|
||||||
#define SetFlag(expr) SetFlagObj(PyLong_FromLong(expr))
|
#define SetFlag(expr) SetFlagObj(PyLong_FromLong(expr))
|
||||||
|
@ -3599,64 +3638,6 @@ err_occurred:
|
||||||
return _PyStatus_ERR("can't initialize sys module");
|
return _PyStatus_ERR("can't initialize sys module");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
sys_add_xoption(PyObject *opts, const wchar_t *s)
|
|
||||||
{
|
|
||||||
PyObject *name, *value = NULL;
|
|
||||||
|
|
||||||
const wchar_t *name_end = wcschr(s, L'=');
|
|
||||||
if (!name_end) {
|
|
||||||
name = PyUnicode_FromWideChar(s, -1);
|
|
||||||
if (name == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
value = Py_NewRef(Py_True);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
name = PyUnicode_FromWideChar(s, name_end - s);
|
|
||||||
if (name == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
value = PyUnicode_FromWideChar(name_end + 1, -1);
|
|
||||||
if (value == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (PyDict_SetItem(opts, name, value) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
Py_DECREF(name);
|
|
||||||
Py_DECREF(value);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
Py_XDECREF(name);
|
|
||||||
Py_XDECREF(value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
sys_create_xoptions_dict(const PyConfig *config)
|
|
||||||
{
|
|
||||||
Py_ssize_t nxoption = config->xoptions.length;
|
|
||||||
wchar_t * const * xoptions = config->xoptions.items;
|
|
||||||
PyObject *dict = PyDict_New();
|
|
||||||
if (dict == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Py_ssize_t i=0; i < nxoption; i++) {
|
|
||||||
const wchar_t *option = xoptions[i];
|
|
||||||
if (sys_add_xoption(dict, option) < 0) {
|
|
||||||
Py_DECREF(dict);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Update sys attributes for a new PyConfig configuration.
|
// Update sys attributes for a new PyConfig configuration.
|
||||||
// This function also adds attributes that _PySys_InitCore() didn't add.
|
// This function also adds attributes that _PySys_InitCore() didn't add.
|
||||||
|
@ -3703,7 +3684,7 @@ _PySys_UpdateConfig(PyThreadState *tstate)
|
||||||
COPY_LIST("orig_argv", config->orig_argv);
|
COPY_LIST("orig_argv", config->orig_argv);
|
||||||
COPY_LIST("warnoptions", config->warnoptions);
|
COPY_LIST("warnoptions", config->warnoptions);
|
||||||
|
|
||||||
SET_SYS("_xoptions", sys_create_xoptions_dict(config));
|
SET_SYS("_xoptions", _PyConfig_CreateXOptionsDict(config));
|
||||||
|
|
||||||
const wchar_t *stdlibdir = _Py_GetStdlibDir();
|
const wchar_t *stdlibdir = _Py_GetStdlibDir();
|
||||||
if (stdlibdir != NULL) {
|
if (stdlibdir != NULL) {
|
||||||
|
@ -4129,3 +4110,28 @@ PySys_FormatStderr(const char *format, ...)
|
||||||
sys_format(&_Py_ID(stderr), stderr, format, va);
|
sys_format(&_Py_ID(stderr), stderr, format, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
_PySys_SetIntMaxStrDigits(int maxdigits)
|
||||||
|
{
|
||||||
|
if (maxdigits != 0 && maxdigits < _PY_LONG_MAX_STR_DIGITS_THRESHOLD) {
|
||||||
|
PyErr_Format(
|
||||||
|
PyExc_ValueError, "maxdigits must be 0 or larger than %d",
|
||||||
|
_PY_LONG_MAX_STR_DIGITS_THRESHOLD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sys.flags.int_max_str_digits
|
||||||
|
const Py_ssize_t pos = SYS_FLAGS_INT_MAX_STR_DIGITS;
|
||||||
|
if (_PySys_SetFlagInt(pos, maxdigits) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set PyInterpreterState.long_state.max_str_digits
|
||||||
|
// and PyInterpreterState.config.int_max_str_digits.
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
interp->long_state.max_str_digits = maxdigits;
|
||||||
|
interp->config.int_max_str_digits = maxdigits;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -329,6 +329,7 @@ MAX_SIZES = {
|
||||||
_abs('Python/parking_lot.c'): (40_000, 1000),
|
_abs('Python/parking_lot.c'): (40_000, 1000),
|
||||||
_abs('Python/pylifecycle.c'): (500_000, 5000),
|
_abs('Python/pylifecycle.c'): (500_000, 5000),
|
||||||
_abs('Python/pystate.c'): (500_000, 5000),
|
_abs('Python/pystate.c'): (500_000, 5000),
|
||||||
|
_abs('Python/initconfig.c'): (50_000, 500),
|
||||||
|
|
||||||
# Generated files:
|
# Generated files:
|
||||||
_abs('Include/internal/pycore_opcode.h'): (10_000, 1000),
|
_abs('Include/internal/pycore_opcode.h'): (10_000, 1000),
|
||||||
|
|
|
@ -90,6 +90,7 @@ Python/initconfig.c - _Py_StandardStreamErrors -
|
||||||
|
|
||||||
# Internal constant list
|
# Internal constant list
|
||||||
Python/initconfig.c - PYCONFIG_SPEC -
|
Python/initconfig.c - PYCONFIG_SPEC -
|
||||||
|
Python/initconfig.c - PYPRECONFIG_SPEC -
|
||||||
|
|
||||||
|
|
||||||
##-----------------------
|
##-----------------------
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Reference in New Issue