diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 86df0fc4078..fb27befa287 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,6 +1,7 @@ from __future__ import annotations import os.path +from decimal import Decimal import pytest @@ -68,6 +69,12 @@ def test_sanity() -> None: draw.rectangle(list(range(4))) +def test_line_invalid_coordinate_conversion() -> None: + draw = ImageDraw.Draw(Image.new("RGB", (4, 4))) + with pytest.raises(ValueError, match="[Cc]annot convert signaling NaN to float"): + draw.line([Decimal("sNaN"), 0, 1, 1]) # type: ignore[arg-type] + + def test_new_color() -> None: with Image.open("Tests/images/chi.gif") as im: draw = ImageDraw.Draw(im) diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py index 1e291ba7c46..1ad1475b8ba 100644 --- a/Tests/test_imagepath.py +++ b/Tests/test_imagepath.py @@ -3,6 +3,7 @@ import array import math import struct +from decimal import Decimal import pytest @@ -87,6 +88,28 @@ def test_path_list_of_lists( assert list(p) == expected +def test_path_item_assignment_requires_tuple() -> None: + p = ImagePath.Path([(0, 1)]) + + with pytest.raises(TypeError): + p[0] = [4, 5] + assert p[0] == (0.0, 1.0) + + p[0] = (4, 5) + assert p[0] == (4.0, 5.0) + + +def test_path_coordinate_conversion_errors() -> None: + with pytest.raises(ValueError, match="[Cc]annot convert signaling NaN to float"): + ImagePath.Path([Decimal("sNaN"), 0]) + + large_int = (1 << 30) + 1 + assert ImagePath.Path([large_int, 0])[0] == (float(large_int), 0.0) + + with pytest.raises(OverflowError): + ImagePath.Path([1 << 1024, 0]) + + @pytest.mark.parametrize( "coords, message", ( @@ -94,6 +117,7 @@ def test_path_list_of_lists( ([[1]], "coordinate list must contain exactly 2 coordinates"), ([[[1, 2], [3, 4]]], "coordinate list must contain numbers"), ([["a", "b"]], "coordinate list must contain numbers"), + ([[Decimal("sNaN"), 0]], "[Cc]annot convert signaling NaN to float"), ), ) def test_invalid_list_coords(coords: list[list[object]], message: str) -> None: diff --git a/src/path.c b/src/path.c index 16f58cf70e4..26500a550fd 100644 --- a/src/path.c +++ b/src/path.c @@ -117,9 +117,17 @@ assign_item_to_array(double *xy, Py_ssize_t j, PyObject *op) { if (PyFloat_Check(op)) { xy[j++] = PyFloat_AS_DOUBLE(op); } else if (PyLong_Check(op)) { - xy[j++] = (float)PyLong_AsLong(op); + double value = PyLong_AsDouble(op); + if (value == -1.0 && PyErr_Occurred()) { + return -1; + } + xy[j++] = value; } else if (PyNumber_Check(op)) { - xy[j++] = PyFloat_AsDouble(op); + double value = PyFloat_AsDouble(op); + if (value == -1.0 && PyErr_Occurred()) { + return -1; + } + xy[j++] = value; } else if (PyList_Check(op)) { if (PyList_GET_SIZE(op) != 2) { PyErr_SetString( @@ -139,9 +147,11 @@ assign_item_to_array(double *xy, Py_ssize_t j, PyObject *op) { } Py_DECREF(op1); if (j == -1) { - PyErr_SetString( - PyExc_ValueError, "coordinate list must contain numbers" - ); + if (!PyErr_Occurred()) { + PyErr_SetString( + PyExc_ValueError, "coordinate list must contain numbers" + ); + } return -1; } } @@ -488,6 +498,11 @@ path_setitem(PyPathObject *self, Py_ssize_t i, PyObject *op) { xy = &self->xy[i + i]; + if (!PyTuple_Check(op)) { + PyErr_SetString(PyExc_TypeError, "argument must be tuple of length 2"); + return -1; + } + if (!PyArg_ParseTuple(op, "dd", &xy[0], &xy[1])) { return -1; }