diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index be092364047852..1be0bfccd72c6a 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5493,7 +5493,8 @@ When an operation would exceed the limit, a :exc:`ValueError` is raised: >>> _ = int('2' * 5432) Traceback (most recent call last): ... - ValueError: Exceeds the limit (4300) for integer string conversion: value has 5432 digits; use sys.set_int_max_str_digits() to increase the limit. + ValueError: Exceeds the limit (4300) for integer string conversion: value has 5432 digits + Use sys.set_int_max_str_digits() to increase the limit >>> i = int('2' * 4300) >>> len(str(i)) 4300 @@ -5501,7 +5502,8 @@ When an operation would exceed the limit, a :exc:`ValueError` is raised: >>> len(str(i_squared)) Traceback (most recent call last): ... - ValueError: Exceeds the limit (4300) for integer string conversion: value has 8599 digits; use sys.set_int_max_str_digits() to increase the limit. + ValueError: Exceeds the limit (4300) for integer string conversion: value has 8599 digits + Use sys.set_int_max_str_digits() to increase the limit >>> len(hex(i_squared)) 7144 >>> assert int(hex(i_squared), base=16) == i*i # Hexadecimal is unlimited. diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index 66f37942ef916a..98d8921b63c075 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -94,6 +94,11 @@ PyAPI_FUNC(PyObject *) _PyExc_PrepReraiseStar( PyAPI_FUNC(int) _PyErr_CheckSignalsTstate(PyThreadState *tstate); +// Call add_note(note) on the current exception. +// Return -1 on error, or 0 on success. +// Save and restore the current exception. +PyAPI_FUNC(int) _PyErr_AddNote(const char *note); + PyAPI_FUNC(void) _Py_DumpExtensionModules(int fd, PyInterpreterState *interp); extern PyObject* _Py_Offer_Suggestions(PyObject* exception); diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 3703fdcda4dbe9..3178e0b96e8b5a 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -11,6 +11,7 @@ #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_initconfig.h" #include "pycore_object.h" +#include "pycore_pyerrors.h" // _PyErr_Restore() #include "structmember.h" // PyMemberDef #include "osdefs.h" // SEP @@ -3833,3 +3834,38 @@ _PyErr_TrySetFromCause(const char *format, ...) PyErr_Restore(new_exc, new_val, new_tb); return new_val; } + + +int +_PyErr_AddNote(const char *note) +{ + int err = -1; + PyObject *str = NULL, *res = NULL; + + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *exc_type, *exc_value, *exc_tb; + _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb); + + if (exc_value == NULL) { + goto exit; + } + + str = PyUnicode_FromString(note); + if (str == NULL) { + goto exit; + } + + res = BaseException_add_note(exc_value, str); + if (res == NULL) { + goto exit; + } + err = 0; + +exit: + Py_DECREF(str); + Py_XDECREF(res); + + _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + return err; +} diff --git a/Objects/longobject.c b/Objects/longobject.c index c0bade18221843..fe3feb029fd610 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -7,6 +7,7 @@ #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_long.h" // _Py_SmallInts #include "pycore_object.h" // _PyObject_InitVar() +#include "pycore_pyerrors.h" // _PyErr_AddNote() #include "pycore_pystate.h" // _Py_IsMainInterpreter() #include "pycore_runtime.h" // _PY_NSMALLPOSINTS #include "pycore_structseq.h" // _PyStructSequence_FiniType() @@ -36,8 +37,9 @@ medium_value(PyLongObject *x) #define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) #define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS) -#define _MAX_STR_DIGITS_ERROR_FMT_TO_INT "Exceeds the limit (%d) for integer string conversion: value has %zd digits; use sys.set_int_max_str_digits() to increase the limit" -#define _MAX_STR_DIGITS_ERROR_FMT_TO_STR "Exceeds the limit (%d) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit" +#define _MAX_STR_DIGITS_ERROR_FMT_TO_INT "Exceeds the limit (%d) for integer string conversion: value has %zd digits" +#define _MAX_STR_DIGITS_ERROR_FMT_TO_STR "Exceeds the limit (%d) for integer string conversion" +#define _MAX_STR_DIGITS_ERROR_NOTE "Use sys.set_int_max_str_digits() to increase the limit" static inline void _Py_DECREF_INT(PyLongObject *op) @@ -1732,6 +1734,14 @@ rem1(PyLongObject *a, digit n) ); } + +static void +long_error_add_max_digits_note(void) +{ + (void)_PyErr_AddNote(_MAX_STR_DIGITS_ERROR_NOTE); +} + + /* Convert an integer to a base 10 string. Returns a new non-shared string. (Return value is non-shared so that callers can modify the returned value if necessary.) */ @@ -1772,6 +1782,7 @@ long_to_decimal_string_internal(PyObject *aa, (max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10)) { PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, max_str_digits); + long_error_add_max_digits_note(); return -1; } } @@ -1843,6 +1854,7 @@ long_to_decimal_string_internal(PyObject *aa, Py_DECREF(scratch); PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, max_str_digits); + long_error_add_max_digits_note(); return -1; } } @@ -2518,6 +2530,7 @@ digit beyond the first. if ((max_str_digits > 0) && (digits > max_str_digits)) { PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_INT, max_str_digits, digits); + long_error_add_max_digits_note(); return NULL; } }