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

Allow coords to be sequence of lists #8800

Merged
merged 2 commits into from
Mar 5, 2025
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
4 changes: 4 additions & 0 deletions Tests/test_imagedraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,17 @@
POINTS = (
((10, 10), (20, 40), (30, 30)),
[(10, 10), (20, 40), (30, 30)],
([10, 10], [20, 40], [30, 30]),
[[10, 10], [20, 40], [30, 30]],
(10, 10, 20, 40, 30, 30),
[10, 10, 20, 40, 30, 30],
)

KITE_POINTS = (
((10, 50), (70, 10), (90, 50), (70, 90), (10, 50)),
[(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)],
([10, 50], [70, 10], [90, 50], [70, 90], [10, 50]),
[[10, 50], [70, 10], [90, 50], [70, 90], [10, 50]],
)


Expand Down
17 changes: 3 additions & 14 deletions Tests/test_imagepath.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,10 @@ def test_path_constructors(
assert list(p) == [(0.0, 1.0)]


@pytest.mark.parametrize(
"coords",
(
("a", "b"),
([0, 1],),
[[0, 1]],
([0.0, 1.0],),
[[0.0, 1.0]],
),
)
def test_invalid_path_constructors(
coords: tuple[str, str] | Sequence[Sequence[int]],
) -> None:
def test_invalid_path_constructors() -> None:
# Arrange / Act
with pytest.raises(ValueError, match="incorrect coordinate type"):
ImagePath.Path(coords)
ImagePath.Path(("a", "b"))


@pytest.mark.parametrize(
Expand Down
78 changes: 50 additions & 28 deletions src/path.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,39 @@

#define PyPath_Check(op) (Py_TYPE(op) == &PyPathType)

static int
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_AS_LONG(op);
} else if (PyNumber_Check(op)) {
xy[j++] = PyFloat_AsDouble(op);

Check warning on line 119 in src/path.c

View check run for this annotation

Codecov / codecov/patch

src/path.c#L119

Added line #L119 was not covered by tests
} else if (PyList_Check(op)) {
for (int k = 0; k < 2; k++) {
PyObject *op1 = PyList_GetItemRef(op, k);
if (op1 == NULL) {
return -1;

Check warning on line 124 in src/path.c

View check run for this annotation

Codecov / codecov/patch

src/path.c#L124

Added line #L124 was not covered by tests
}
j = assign_item_to_array(xy, j, op1);
Py_DECREF(op1);
if (j == -1) {
return -1;

Check warning on line 129 in src/path.c

View check run for this annotation

Codecov / codecov/patch

src/path.c#L129

Added line #L129 was not covered by tests
}
}
} else {
double x, y;
if (PyArg_ParseTuple(op, "dd", &x, &y)) {
xy[j++] = x;
xy[j++] = y;
} else {
PyErr_SetString(PyExc_ValueError, "incorrect coordinate type");
return -1;
}
}
return j;
}

Py_ssize_t
PyPath_Flatten(PyObject *data, double **pxy) {
Py_ssize_t i, j, n;
Expand Down Expand Up @@ -164,48 +197,32 @@
return -1;
}

#define assign_item_to_array(op, decref) \
if (PyFloat_Check(op)) { \
xy[j++] = PyFloat_AS_DOUBLE(op); \
} else if (PyLong_Check(op)) { \
xy[j++] = (float)PyLong_AS_LONG(op); \
} else if (PyNumber_Check(op)) { \
xy[j++] = PyFloat_AsDouble(op); \
} else if (PyArg_ParseTuple(op, "dd", &x, &y)) { \
xy[j++] = x; \
xy[j++] = y; \
} else { \
PyErr_SetString(PyExc_ValueError, "incorrect coordinate type"); \
if (decref) { \
Py_DECREF(op); \
} \
free(xy); \
return -1; \
} \
if (decref) { \
Py_DECREF(op); \
}

/* Copy table to path array */
if (PyList_Check(data)) {
for (i = 0; i < n; i++) {
double x, y;
PyObject *op = PyList_GetItemRef(data, i);
if (op == NULL) {
free(xy);
return -1;
}
assign_item_to_array(op, 1);
j = assign_item_to_array(xy, j, op);
Py_DECREF(op);
if (j == -1) {
free(xy);
return -1;

Check warning on line 212 in src/path.c

View check run for this annotation

Codecov / codecov/patch

src/path.c#L211-L212

Added lines #L211 - L212 were not covered by tests
}
}
} else if (PyTuple_Check(data)) {
for (i = 0; i < n; i++) {
double x, y;
PyObject *op = PyTuple_GET_ITEM(data, i);
assign_item_to_array(op, 0);
j = assign_item_to_array(xy, j, op);
if (j == -1) {
free(xy);
return -1;
}
}
} else {
for (i = 0; i < n; i++) {
double x, y;
PyObject *op = PySequence_GetItem(data, i);
if (!op) {
/* treat IndexError as end of sequence */
Expand All @@ -217,7 +234,12 @@
return -1;
}
}
assign_item_to_array(op, 1);
j = assign_item_to_array(xy, j, op);
Py_DECREF(op);

Check warning on line 238 in src/path.c

View check run for this annotation

Codecov / codecov/patch

src/path.c#L237-L238

Added lines #L237 - L238 were not covered by tests
if (j == -1) {
free(xy);
return -1;

Check warning on line 241 in src/path.c

View check run for this annotation

Codecov / codecov/patch

src/path.c#L240-L241

Added lines #L240 - L241 were not covered by tests
}
}
}

Expand Down
Loading