Skip to content

gh-95778: Use a note for the max digits error message #96878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5493,15 +5493,17 @@ 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
>>> i_squared = i*i
>>> 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.
Expand Down
5 changes: 5 additions & 0 deletions Include/internal/pycore_pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to mention in the comment that there must be a current exception. I think otherwise it crashes or some assertion fails, not sure.

PyAPI_FUNC(int) _PyErr_AddNote(const char *note);

PyAPI_FUNC(void) _Py_DumpExtensionModules(int fd, PyInterpreterState *interp);

extern PyObject* _Py_Offer_Suggestions(PyObject* exception);
Expand Down
36 changes: 36 additions & 0 deletions Objects/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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;
}
17 changes: 15 additions & 2 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.) */
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down