Skip to content

Commit d0de40f

Browse files
committed
pythongh-90716: bugfixes and more tests for _pylong.
* Properly decref on _pylong import error. * Improve the error message on _pylong TypeError. * Tie the return value comments together. These are minor followups to issues not caught among the reviewers on python#96673.
1 parent 0faa0ba commit d0de40f

File tree

2 files changed

+36
-3
lines changed

2 files changed

+36
-3
lines changed

Lib/test/test_int.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
import time
33

44
import unittest
5+
from unittest import mock
56
from test import support
67
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
78
INVALID_UNDERSCORE_LITERALS)
89

10+
import _pylong
11+
912
L = [
1013
('0', 0),
1114
('1', 1),
@@ -841,6 +844,34 @@ def test_pylong_str_to_int(self):
841844
with self.assertRaises(ValueError) as err:
842845
int('_' + s)
843846

847+
@mock.patch.object(_pylong, "int_to_decimal_string")
848+
def test_pylong_misbehavior_error_path_to_str(
849+
self, mock_int_to_str):
850+
with support.adjust_int_max_str_digits(20_000):
851+
big_value = int('7'*19_999)
852+
mock_int_to_str.return_value = None # not a str
853+
with self.assertRaises(TypeError) as ctx:
854+
str(big_value)
855+
self.assertIn('non-string', str(ctx.exception))
856+
mock_int_to_str.side_effect = RuntimeError("testABC")
857+
with self.assertRaises(RuntimeError):
858+
str(big_value)
859+
860+
@mock.patch.object(_pylong, "int_from_string")
861+
def test_pylong_misbehavior_error_path_from_str(
862+
self, mock_int_from_str):
863+
big_value = '7'*19_999
864+
with support.adjust_int_max_str_digits(20_000):
865+
mock_int_from_str.return_value = b'not an int'
866+
with self.assertRaises(TypeError) as ctx:
867+
int(big_value)
868+
self.assertIn('_pylong.int_from_string', str(ctx.exception))
869+
870+
mock_int_from_str.side_effect = RuntimeError("test123")
871+
with self.assertRaises(RuntimeError):
872+
int(big_value)
873+
874+
844875

845876
if __name__ == "__main__":
846877
unittest.main()

Objects/longobject.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2362,6 +2362,7 @@ pylong_int_from_string(const char *start, const char *end, PyLongObject **res)
23622362
}
23632363
PyObject *s = PyUnicode_FromStringAndSize(start, end-start);
23642364
if (s == NULL) {
2365+
Py_DECREF(mod);
23652366
goto error;
23662367
}
23672368
PyObject *result = PyObject_CallMethod(mod, "int_from_string", "O", s);
@@ -2371,14 +2372,14 @@ pylong_int_from_string(const char *start, const char *end, PyLongObject **res)
23712372
goto error;
23722373
}
23732374
if (!PyLong_Check(result)) {
2374-
PyErr_SetString(PyExc_TypeError, "an integer is required");
2375+
PyErr_SetString(PyExc_TypeError, "_pylong.int_from_string did not return an int");
23752376
goto error;
23762377
}
23772378
*res = (PyLongObject *)result;
23782379
return 0;
23792380
error:
23802381
*res = NULL;
2381-
return 0;
2382+
return 0; // See the long_from_string_base() API comment.
23822383
}
23832384
#endif /* WITH_PYLONG_MODULE */
23842385

@@ -2617,7 +2618,8 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits,
26172618
* Return values:
26182619
*
26192620
* - Returns -1 on syntax error (exception needs to be set, *res is untouched)
2620-
* - Returns 0 and sets *res to NULL for MemoryError/OverflowError.
2621+
* - Returns 0 and sets *res to NULL for MemoryError, OverflowError, or
2622+
* _pylong.int_from_string() errors.
26212623
* - Returns 0 and sets *res to an unsigned, unnormalized PyLong (success!).
26222624
*
26232625
* Afterwards *str is set to point to the first non-digit (which may be *str!).

0 commit comments

Comments
 (0)