Default: ``0``.
+ .. c:member:: int int_max_str_digits
+
+ Configures the :ref:`integer string conversion length limitation
+ <int_max_str_digits>`. An initial value of ``-1`` means the value will
+ be taken from the command line or environment or otherwise default to
+ 4300 (:data:`sys.int_info.default_max_str_digits`). A value of ``0``
+ disables the limitation. Values greater than zero but less than 640
+ (:data:`sys.int_info.str_digits_check_threshold`) are unsupported and
+ will produce an error.
+
+ Configured by the :option:`-X int_max_str_digits <-X>` command line
+ flag or the :envvar:`PYTHONINTMAXSTRDIGITS` environment varable.
+
+ Default: ``-1`` in Python mode. 4300
+ (:data:`sys.int_info.default_max_str_digits`) in isolated mode.
+
+ .. versionadded:: 3.12
+
.. c:member:: int isolated
If greater than ``0``, enable isolated mode:
wchar_t *check_hash_pycs_mode;
int use_frozen_modules;
int safe_path;
+ int int_max_str_digits;
/* --- Path configuration inputs ------------ */
int pathconfig_warnings;
PyAPI_FUNC(PyObject*) _Py_Get_Getpath_CodeObject(void);
-extern int _Py_global_config_int_max_str_digits; // TODO(gpshead): move this into PyConfig in 3.12 after the backports ship.
-
/* --- Function used for testing ---------------------------------- */
struct types_state types;
struct callable_cache callable_cache;
- int int_max_str_digits;
-
/* The following fields are here to avoid allocation during init.
The data is exposed through PyInterpreterState pointer fields.
These fields should not be accessed directly outside of init.
self.assertEqual(ret, 0)
self.assertEqual(pickle.load(f), {'a': '123x', 'b': '123'})
+ def test_py_config_isoloated_per_interpreter(self):
+ # A config change in one interpreter must not leak to out to others.
+ #
+ # This test could verify ANY config value, it just happens to have been
+ # written around the time of int_max_str_digits. Refactoring is okay.
+ code = """if 1:
+ import sys, _testinternalcapi
+
+ # Any config value would do, this happens to be the one being
+ # double checked at the time this test was written.
+ config = _testinternalcapi.get_config()
+ config['int_max_str_digits'] = 55555
+ _testinternalcapi.set_config(config)
+ sub_value = _testinternalcapi.get_config()['int_max_str_digits']
+ assert sub_value == 55555, sub_value
+ """
+ before_config = _testinternalcapi.get_config()
+ assert before_config['int_max_str_digits'] != 55555
+ self.assertEqual(support.run_in_subinterp(code), 0,
+ 'subinterp code failure, check stderr.')
+ after_config = _testinternalcapi.get_config()
+ self.assertIsNot(
+ before_config, after_config,
+ "Expected get_config() to return a new dict on each call")
+ self.assertEqual(before_config, after_config,
+ "CAUTION: Tests executed after this may be "
+ "running under an altered config.")
+ # try:...finally: calling set_config(before_config) not done
+ # as that results in sys.argv, sys.path, and sys.warnoptions
+ # "being modified by test_capi" per test.regrtest. So if this
+ # test fails, assume that the environment in this process may
+ # be altered and suspect.
+
def test_mutate_exception(self):
"""
Exceptions saved in global module state get shared between
return tuple(int(i) for i in out.split())
res = assert_python_ok('-c', code)
- self.assertEqual(res2int(res), (-1, sys.get_int_max_str_digits()))
+ current_max = sys.get_int_max_str_digits()
+ self.assertEqual(res2int(res), (current_max, current_max))
res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code)
self.assertEqual(res2int(res), (0, 0))
res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code)
'install_signal_handlers': 1,
'use_hash_seed': 0,
'hash_seed': 0,
+ 'int_max_str_digits': sys.int_info.default_max_str_digits,
'faulthandler': 0,
'tracemalloc': 0,
'perf_profiling': 0,
'platlibdir': 'my_platlibdir',
'module_search_paths': self.IGNORE_CONFIG,
'safe_path': 1,
+ 'int_max_str_digits': 31337,
'check_hash_pycs_mode': 'always',
'pathconfig_warnings': 0,
'platlibdir': 'env_platlibdir',
'module_search_paths': self.IGNORE_CONFIG,
'safe_path': 1,
+ 'int_max_str_digits': 4567,
}
self.check_all_configs("test_init_compat_env", config, preconfig,
api=API_COMPAT)
'platlibdir': 'env_platlibdir',
'module_search_paths': self.IGNORE_CONFIG,
'safe_path': 1,
+ 'int_max_str_digits': 4567,
}
self.check_all_configs("test_init_python_env", config, preconfig,
api=API_PYTHON)
with self.subTest(base=base):
self._other_base_helper(base)
+ def test_int_max_str_digits_is_per_interpreter(self):
+ # Changing the limit in one interpreter does not change others.
+ code = """if 1:
+ # Subinterpreters maintain and enforce their own limit
+ import sys
+ sys.set_int_max_str_digits(2323)
+ try:
+ int('3'*3333)
+ except ValueError:
+ pass
+ else:
+ raise AssertionError('Expected a int max str digits ValueError.')
+ """
+ with support.adjust_int_max_str_digits(4000):
+ before_value = sys.get_int_max_str_digits()
+ self.assertEqual(support.run_in_subinterp(code), 0,
+ 'subinterp code failure, check stderr.')
+ after_value = sys.get_int_max_str_digits()
+ self.assertEqual(before_value, after_value)
+
class IntSubclassStrDigitLimitsTests(IntStrDigitLimitsTests):
int_class = IntSubclass
--- /dev/null
+Configuration for the :ref:`integer string conversion length limitation
+<int_max_str_digits>` now lives in the PyConfig C API struct.
if (size_a >= 10 * _PY_LONG_MAX_STR_DIGITS_THRESHOLD
/ (3 * PyLong_SHIFT) + 2) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- int max_str_digits = interp->int_max_str_digits;
+ int max_str_digits = interp->config.int_max_str_digits;
if ((max_str_digits > 0) &&
(max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10)) {
PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR,
}
if (strlen > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- int max_str_digits = interp->int_max_str_digits;
+ int max_str_digits = interp->config.int_max_str_digits;
Py_ssize_t strlen_nosign = strlen - negative;
if ((max_str_digits > 0) && (strlen_nosign > max_str_digits)) {
Py_DECREF(scratch);
* quadratic algorithm. */
if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- int max_str_digits = interp->int_max_str_digits;
+ int max_str_digits = interp->config.int_max_str_digits;
if ((max_str_digits > 0) && (digits > max_str_digits)) {
PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_INT,
max_str_digits, digits);
return _PyStatus_ERR("can't init int info type");
}
}
- interp->int_max_str_digits = _Py_global_config_int_max_str_digits;
- if (interp->int_max_str_digits == -1) {
- interp->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS;
- }
return _PyStatus_OK();
}
config._isolated_interpreter = 1;
+ putenv("PYTHONINTMAXSTRDIGITS=6666");
+ config.int_max_str_digits = 31337;
+
init_from_config_clear(&config);
dump_config();
putenv("PYTHONIOENCODING=iso8859-1:replace");
putenv("PYTHONPLATLIBDIR=env_platlibdir");
putenv("PYTHONSAFEPATH=1");
+ putenv("PYTHONINTMAXSTRDIGITS=4567");
}
assert(config->pathconfig_warnings >= 0);
assert(config->_is_python_build >= 0);
assert(config->safe_path >= 0);
+ assert(config->int_max_str_digits >= 0);
// config->use_frozen_modules is initialized later
// by _PyConfig_InitImportConfig().
return 1;
config->use_frozen_modules = 1;
#endif
config->safe_path = 0;
+ config->int_max_str_digits = -1;
config->_is_python_build = 0;
config->code_debug_ranges = 1;
}
-/* Excluded from public struct PyConfig for backporting reasons. */
-/* default to unconfigured, _PyLong_InitTypes() does the rest */
-int _Py_global_config_int_max_str_digits = -1;
-
static void
config_init_defaults(PyConfig *config)
config->faulthandler = 0;
config->tracemalloc = 0;
config->perf_profiling = 0;
+ config->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS;
config->safe_path = 1;
config->pathconfig_warnings = 0;
#ifdef MS_WINDOWS
COPY_ATTR(safe_path);
COPY_WSTRLIST(orig_argv);
COPY_ATTR(_is_python_build);
+ COPY_ATTR(int_max_str_digits);
#undef COPY_ATTR
#undef COPY_WSTR_ATTR
SET_ITEM_INT(use_frozen_modules);
SET_ITEM_INT(safe_path);
SET_ITEM_INT(_is_python_build);
+ SET_ITEM_INT(int_max_str_digits);
return dict;
} \
CHECK_VALUE(#KEY, config->KEY >= 0); \
} while (0)
+#define GET_INT(KEY) \
+ do { \
+ if (config_dict_get_int(dict, #KEY, &config->KEY) < 0) { \
+ return -1; \
+ } \
+ } while (0)
#define GET_WSTR(KEY) \
do { \
if (config_dict_get_wstr(dict, #KEY, config, &config->KEY) < 0) { \
GET_UINT(use_frozen_modules);
GET_UINT(safe_path);
GET_UINT(_is_python_build);
+ GET_INT(int_max_str_digits);
#undef CHECK_VALUE
#undef GET_UINT
+#undef GET_INT
#undef GET_WSTR
#undef GET_WSTR_OPT
return 0;
const char *env = config_get_env(config, "PYTHONINTMAXSTRDIGITS");
if (env) {
- int valid = 0;
+ bool valid = 0;
if (!_Py_str_to_int(env, &maxdigits)) {
valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD));
}
STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD)
" or 0 for unlimited.");
}
- _Py_global_config_int_max_str_digits = maxdigits;
+ config->int_max_str_digits = maxdigits;
}
const wchar_t *xoption = config_get_xoption(config, L"int_max_str_digits");
if (xoption) {
const wchar_t *sep = wcschr(xoption, L'=');
- int valid = 0;
+ bool valid = 0;
if (sep) {
if (!config_wstr_to_int(sep + 1, &maxdigits)) {
valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD));
#undef _STRINGIFY
#undef STRINGIFY
}
- _Py_global_config_int_max_str_digits = maxdigits;
+ config->int_max_str_digits = maxdigits;
+ }
+ if (config->int_max_str_digits < 0) {
+ config->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS;
}
return _PyStatus_OK();
}
}
}
- if (_Py_global_config_int_max_str_digits < 0) {
+ if (config->int_max_str_digits < 0) {
status = config_init_int_max_str_digits(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/
{
PyInterpreterState *interp = _PyInterpreterState_GET();
- return PyLong_FromSsize_t(interp->int_max_str_digits);
+ return PyLong_FromLong(interp->config.int_max_str_digits);
}
/*[clinic input]
{
PyThreadState *tstate = _PyThreadState_GET();
if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) {
- tstate->interp->int_max_str_digits = maxdigits;
+ tstate->interp->config.int_max_str_digits = maxdigits;
Py_RETURN_NONE;
} else {
PyErr_Format(
SetFlag(preconfig->utf8_mode);
SetFlag(config->warn_default_encoding);
SetFlagObj(PyBool_FromLong(config->safe_path));
- SetFlag(_Py_global_config_int_max_str_digits);
+ SetFlag(config->int_max_str_digits);
#undef SetFlagObj
#undef SetFlag
return 0;