From b1be7d92e6fb45d0945f9e4716c8c6ece2e1b6d1 Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Mon, 6 Jul 2026 12:56:01 +0300 Subject: [PATCH] ifcwrap: accept numpy scalars in aggregate type check #5873 check_aggregate_of_type used an exact type comparison (element->ob_type == type_obj), so a numpy array was rejected because its elements are numpy scalars (numpy.float64) rather than direct float instances. For the numeric types, accept subclasses: PyFloat_Check for double (numpy.float64 subclasses float) and PyLong_Check (excluding bool) for int. The SPF REAL vs INTEGER distinction is kept, so a float is not accepted where an int is expected and vice versa. This replaces the earlier Python-side walk() approach, which the maintainer preferred not to take since walk() is removed in v0.9. Verified with a runtime red-green (built as a shared lib, called via ctypes): the old check rejects np.array([3.0, 4.0]) and the new one accepts it, plain lists still work, an int list is still rejected where a REAL is expected, and bool is rejected for INTEGER. Co-Authored-By: Claude Fable 5 --- src/ifcwrap/utils/type_conversion.i | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/ifcwrap/utils/type_conversion.i b/src/ifcwrap/utils/type_conversion.i index 4b47cac55b..9c3cf82e7f 100644 --- a/src/ifcwrap/utils/type_conversion.i +++ b/src/ifcwrap/utils/type_conversion.i @@ -34,9 +34,19 @@ if (PySequence_Size(aggregate) == -1) return false; for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) { PyObject* element = PySequence_GetItem(aggregate, i); - // This is equivalent to the PyFloat_CheckExact macro. This means - // that direct instances of int, float, str, etc. need to be used. - bool b = element->ob_type == type_obj; + // Accept the exact type or, for the numeric types, a subclass such + // as a numpy scalar (numpy.float64 subclasses float), so that numpy + // arrays can be assigned. The REAL vs INTEGER distinction is kept: a + // float is not accepted where an int is expected and vice versa, and + // bool (a subclass of int) is still rejected for INTEGER. See #5873. + bool b; + if (type_obj == static_cast(&PyFloat_Type)) { + b = PyFloat_Check(element); + } else if (type_obj == static_cast(&PyLong_Type)) { + b = PyLong_Check(element) && !PyBool_Check(element); + } else { + b = element->ob_type == type_obj; + } Py_DECREF(element); if (!b) { return false;