mirror of https://github.com/python/cpython
gh-90716: bugfixes and more tests for _pylong. (#99073)
* Properly decref on _pylong import error. * Improve the error message on _pylong TypeError. * Fix the assertion error in pydebug builds to be a TypeError. * Tie the return value comments together. These are minor followups to issues not caught among the reviewers on https://github.com/python/cpython/pull/96673.
This commit is contained in:
parent
bee1070289
commit
4c4b5ce2e5
|
@ -2,10 +2,16 @@ import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
from test import support
|
from test import support
|
||||||
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
|
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
|
||||||
INVALID_UNDERSCORE_LITERALS)
|
INVALID_UNDERSCORE_LITERALS)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import _pylong
|
||||||
|
except ImportError:
|
||||||
|
_pylong = None
|
||||||
|
|
||||||
L = [
|
L = [
|
||||||
('0', 0),
|
('0', 0),
|
||||||
('1', 1),
|
('1', 1),
|
||||||
|
@ -841,6 +847,39 @@ class PyLongModuleTests(unittest.TestCase):
|
||||||
with self.assertRaises(ValueError) as err:
|
with self.assertRaises(ValueError) as err:
|
||||||
int('_' + s)
|
int('_' + s)
|
||||||
|
|
||||||
|
@support.cpython_only # tests implementation details of CPython.
|
||||||
|
@unittest.skipUnless(_pylong, "_pylong module required")
|
||||||
|
@mock.patch.object(_pylong, "int_to_decimal_string")
|
||||||
|
def test_pylong_misbehavior_error_path_to_str(
|
||||||
|
self, mock_int_to_str):
|
||||||
|
with support.adjust_int_max_str_digits(20_000):
|
||||||
|
big_value = int('7'*19_999)
|
||||||
|
mock_int_to_str.return_value = None # not a str
|
||||||
|
with self.assertRaises(TypeError) as ctx:
|
||||||
|
str(big_value)
|
||||||
|
self.assertIn('_pylong.int_to_decimal_string did not',
|
||||||
|
str(ctx.exception))
|
||||||
|
mock_int_to_str.side_effect = RuntimeError("testABC")
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
str(big_value)
|
||||||
|
|
||||||
|
@support.cpython_only # tests implementation details of CPython.
|
||||||
|
@unittest.skipUnless(_pylong, "_pylong module required")
|
||||||
|
@mock.patch.object(_pylong, "int_from_string")
|
||||||
|
def test_pylong_misbehavior_error_path_from_str(
|
||||||
|
self, mock_int_from_str):
|
||||||
|
big_value = '7'*19_999
|
||||||
|
with support.adjust_int_max_str_digits(20_000):
|
||||||
|
mock_int_from_str.return_value = b'not an int'
|
||||||
|
with self.assertRaises(TypeError) as ctx:
|
||||||
|
int(big_value)
|
||||||
|
self.assertIn('_pylong.int_from_string did not',
|
||||||
|
str(ctx.exception))
|
||||||
|
|
||||||
|
mock_int_from_str.side_effect = RuntimeError("test123")
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
int(big_value)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1753,7 +1753,11 @@ pylong_int_to_decimal_string(PyObject *aa,
|
||||||
if (s == NULL) {
|
if (s == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
assert(PyUnicode_Check(s));
|
if (!PyUnicode_Check(s)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"_pylong.int_to_decimal_string did not return a str");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
if (writer) {
|
if (writer) {
|
||||||
Py_ssize_t size = PyUnicode_GET_LENGTH(s);
|
Py_ssize_t size = PyUnicode_GET_LENGTH(s);
|
||||||
if (_PyUnicodeWriter_Prepare(writer, size, '9') == -1) {
|
if (_PyUnicodeWriter_Prepare(writer, size, '9') == -1) {
|
||||||
|
@ -2362,6 +2366,7 @@ pylong_int_from_string(const char *start, const char *end, PyLongObject **res)
|
||||||
}
|
}
|
||||||
PyObject *s = PyUnicode_FromStringAndSize(start, end-start);
|
PyObject *s = PyUnicode_FromStringAndSize(start, end-start);
|
||||||
if (s == NULL) {
|
if (s == NULL) {
|
||||||
|
Py_DECREF(mod);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
PyObject *result = PyObject_CallMethod(mod, "int_from_string", "O", s);
|
PyObject *result = PyObject_CallMethod(mod, "int_from_string", "O", s);
|
||||||
|
@ -2371,14 +2376,15 @@ pylong_int_from_string(const char *start, const char *end, PyLongObject **res)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (!PyLong_Check(result)) {
|
if (!PyLong_Check(result)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "an integer is required");
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"_pylong.int_from_string did not return an int");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
*res = (PyLongObject *)result;
|
*res = (PyLongObject *)result;
|
||||||
return 0;
|
return 0;
|
||||||
error:
|
error:
|
||||||
*res = NULL;
|
*res = NULL;
|
||||||
return 0;
|
return 0; // See the long_from_string_base() API comment.
|
||||||
}
|
}
|
||||||
#endif /* WITH_PYLONG_MODULE */
|
#endif /* WITH_PYLONG_MODULE */
|
||||||
|
|
||||||
|
@ -2617,7 +2623,8 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits,
|
||||||
* Return values:
|
* Return values:
|
||||||
*
|
*
|
||||||
* - Returns -1 on syntax error (exception needs to be set, *res is untouched)
|
* - Returns -1 on syntax error (exception needs to be set, *res is untouched)
|
||||||
* - Returns 0 and sets *res to NULL for MemoryError/OverflowError.
|
* - Returns 0 and sets *res to NULL for MemoryError, OverflowError, or
|
||||||
|
* _pylong.int_from_string() errors.
|
||||||
* - Returns 0 and sets *res to an unsigned, unnormalized PyLong (success!).
|
* - Returns 0 and sets *res to an unsigned, unnormalized PyLong (success!).
|
||||||
*
|
*
|
||||||
* Afterwards *str is set to point to the first non-digit (which may be *str!).
|
* Afterwards *str is set to point to the first non-digit (which may be *str!).
|
||||||
|
|
Loading…
Reference in New Issue