Skip to content
Open
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
7 changes: 7 additions & 0 deletions Tests/test_imagedraw.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import os.path
from decimal import Decimal

import pytest

Expand Down Expand Up @@ -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)
Expand Down
24 changes: 24 additions & 0 deletions Tests/test_imagepath.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import array
import math
import struct
from decimal import Decimal

import pytest

Expand Down Expand Up @@ -87,13 +88,36 @@ 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",
(
([[1, 2, 3]], "coordinate list must contain exactly 2 coordinates"),
([[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:
Expand Down
25 changes: 20 additions & 5 deletions src/path.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
Expand Down
Loading