Skip to content
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

gh-90716: bugfixes and more tests for _pylong. #99073

Merged
merged 3 commits into from
Nov 3, 2022
Merged
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
39 changes: 39 additions & 0 deletions Lib/test/test_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
import time

import unittest
from unittest import mock
from test import support
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
INVALID_UNDERSCORE_LITERALS)

try:
import _pylong
except ImportError:
_pylong = None

L = [
('0', 0),
('1', 1),
Expand Down Expand Up @@ -841,6 +847,39 @@ def test_pylong_str_to_int(self):
with self.assertRaises(ValueError) as err:
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__":
unittest.main()
15 changes: 11 additions & 4 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1753,7 +1753,11 @@ pylong_int_to_decimal_string(PyObject *aa,
if (s == NULL) {
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) {
Py_ssize_t size = PyUnicode_GET_LENGTH(s);
if (_PyUnicodeWriter_Prepare(writer, size, '9') == -1) {
Expand Down Expand Up @@ -2362,6 +2366,7 @@ pylong_int_from_string(const char *start, const char *end, PyLongObject **res)
}
PyObject *s = PyUnicode_FromStringAndSize(start, end-start);
if (s == NULL) {
Py_DECREF(mod);
goto error;
}
PyObject *result = PyObject_CallMethod(mod, "int_from_string", "O", s);
Expand All @@ -2371,14 +2376,15 @@ pylong_int_from_string(const char *start, const char *end, PyLongObject **res)
goto error;
}
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;
}
*res = (PyLongObject *)result;
return 0;
error:
*res = NULL;
return 0;
return 0; // See the long_from_string_base() API comment.
}
#endif /* WITH_PYLONG_MODULE */

Expand Down Expand Up @@ -2617,7 +2623,8 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits,
* Return values:
*
* - 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!).
*
* Afterwards *str is set to point to the first non-digit (which may be *str!).
Expand Down