Skip to content

Commit

Permalink
pythongh-96512: Move int_max_str_digits setting to PyConfig
Browse files Browse the repository at this point in the history
It had to live as a global outside of PyConfig for stable ABI reasons in
the pre-3.12 backports.

This removes the `_Py_global_config_int_max_str_digits` and gets rid of
the equivalent field in the internal `struct _is PyInterpreterState` as
code can just use the existing nested config struct within that.
  • Loading branch information
gpshead committed Sep 20, 2022
1 parent e61ca22 commit cf20758
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 29 deletions.
17 changes: 17 additions & 0 deletions Doc/c-api/init_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,23 @@ PyConfig
Default: ``0``.
.. c:member:: int int_max_str_digits
Configures the :ref:`integer string conversion length limitation
<int_max_str_digits>`. ``-1`` means that
:data:`sys.int_info.default_max_str_digits` will be used. No other
negative value is accepted. ``0`` disables the limitation. Values
greater than zero but less than ``_PY_LONG_MAX_STR_DIGITS_THRESHOLD``
(640) also known as :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``.
.. versionadded:: 3.12
.. c:member:: int isolated
If greater than ``0``, enable isolated mode:
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ typedef struct PyConfig {
wchar_t *check_hash_pycs_mode;
int use_frozen_modules;
int safe_path;
int int_max_str_digits;

/* --- Path configuration inputs ------------ */
int pathconfig_warnings;
Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ extern void _Py_DumpPathConfig(PyThreadState *tstate);

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 ---------------------------------- */

Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,6 @@ struct _is {
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.
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'install_signal_handlers': 1,
'use_hash_seed': 0,
'hash_seed': 0,
'int_max_str_digits': 4300,
'faulthandler': 0,
'tracemalloc': 0,
'perf_profiling': 0,
Expand Down Expand Up @@ -876,6 +877,7 @@ def test_init_from_config(self):
'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,
Expand Down
10 changes: 3 additions & 7 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1767,7 +1767,7 @@ long_to_decimal_string_internal(PyObject *aa,
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,
Expand Down Expand Up @@ -1837,7 +1837,7 @@ long_to_decimal_string_internal(PyObject *aa,
}
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);
Expand Down Expand Up @@ -2514,7 +2514,7 @@ digit beyond the first.
/* Limit the size to avoid excessive computation attacks. */
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);
Expand Down Expand Up @@ -6196,10 +6196,6 @@ _PyLong_InitTypes(PyInterpreterState *interp)
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();
}
Expand Down
3 changes: 3 additions & 0 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,9 @@ static int test_init_from_config(void)

config._isolated_interpreter = 1;

putenv("PYTHONINTMAXSTRDIGITS=6666");
config.int_max_str_digits = 31337;

init_from_config_clear(&config);

dump_config();
Expand Down
8 changes: 4 additions & 4 deletions Python/clinic/sysmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 22 additions & 8 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,7 @@ config_check_consistency(const PyConfig *config)
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;
Expand Down Expand Up @@ -789,14 +790,11 @@ _PyConfig_InitCompatConfig(PyConfig *config)
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)
Expand Down Expand Up @@ -1021,6 +1019,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
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
Expand Down Expand Up @@ -1128,6 +1127,7 @@ _PyConfig_AsDict(const PyConfig *config)
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;

Expand Down Expand Up @@ -1317,6 +1317,12 @@ _PyConfig_FromDict(PyConfig *config, PyObject *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) { \
Expand Down Expand Up @@ -1415,9 +1421,11 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
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;
Expand Down Expand Up @@ -1779,7 +1787,7 @@ static PyStatus
config_init_int_max_str_digits(PyConfig *config)
{
int maxdigits;
int valid = 0;
bool valid = 0;

const char *env = config_get_env(config, "PYTHONINTMAXSTRDIGITS");
if (env) {
Expand All @@ -1794,7 +1802,7 @@ config_init_int_max_str_digits(PyConfig *config)
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");
Expand All @@ -1813,7 +1821,13 @@ config_init_int_max_str_digits(PyConfig *config)
#undef _STRINGIFY
#undef STRINGIFY
}
_Py_global_config_int_max_str_digits = maxdigits;
config->int_max_str_digits = maxdigits;
}
if (config->int_max_str_digits < -1) {
return _PyStatus_ERR("invalid value: PyConfig.int_max_str_digits < -1.");
}
if (config->int_max_str_digits == -1) {
config->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS;
}
return _PyStatus_OK();
}
Expand Down Expand Up @@ -1881,7 +1895,7 @@ config_read_complex_options(PyConfig *config)
}
}

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;
Expand Down
18 changes: 12 additions & 6 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1717,24 +1717,29 @@ sys_get_int_max_str_digits_impl(PyObject *module)
/*[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]
sys.set_int_max_str_digits
maxdigits: int
maxdigits: long
Set the maximum string digits limit for non-binary int<->str conversions.
[clinic start generated code]*/

static PyObject *
sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits)
/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/
sys_set_int_max_str_digits_impl(PyObject *module, long maxdigits)
/*[clinic end generated code: output=40ea5d33a82c5c44 input=657c61a1822f6bcf]*/
{
PyThreadState *tstate = _PyThreadState_GET();
if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) {
tstate->interp->int_max_str_digits = maxdigits;
if (maxdigits > INT_MAX) {
/* Silently cap our range; already effectively unlimited as no
* computation this large can finish. */
maxdigits = INT_MAX;
}
tstate->interp->config.int_max_str_digits = maxdigits;
Py_RETURN_NONE;
} else {
PyErr_Format(
Expand Down Expand Up @@ -2810,7 +2815,8 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
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 == _PY_LONG_DEFAULT_MAX_STR_DIGITS ?
-1 : config->int_max_str_digits);
#undef SetFlagObj
#undef SetFlag
return 0;
Expand Down

0 comments on commit cf20758

Please sign in to comment.