Merge branch 'blender-v5.0-release'

This commit is contained in:
Campbell Barton 2025-11-11 14:56:56 +11:00
commit eac260efcb
6 changed files with 244 additions and 5 deletions

View file

@ -97,6 +97,20 @@ static PyObject *Color_vectorcall(PyObject *type,
return Color_CreatePyObject(col, (PyTypeObject *)type);
}
static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
/* Only called on sub-classes. */
if (UNLIKELY(kwds && PyDict_GET_SIZE(kwds))) {
PyErr_SetString(PyExc_TypeError,
"mathutils.Color(): "
"takes no keyword args");
return nullptr;
}
PyObject *const *args_array = &PyTuple_GET_ITEM(args, 0);
const size_t args_array_num = PyTuple_GET_SIZE(args);
return Color_vectorcall(reinterpret_cast<PyObject *>(type), args_array, args_array_num, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1391,7 +1405,7 @@ PyTypeObject color_Type = {
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_new*/ Color_new,
/*tp_free*/ nullptr,
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
/*tp_bases*/ nullptr,

View file

@ -147,6 +147,20 @@ static PyObject *Euler_vectorcall(PyObject *type,
return Euler_CreatePyObject(eul, order, (PyTypeObject *)type);
}
static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
/* Only called on sub-classes. */
if (UNLIKELY(kwds && PyDict_GET_SIZE(kwds))) {
PyErr_SetString(PyExc_TypeError,
"mathutils.Euler(): "
"takes no keyword args");
return nullptr;
}
PyObject *const *args_array = &PyTuple_GET_ITEM(args, 0);
const size_t args_array_num = PyTuple_GET_SIZE(args);
return Euler_vectorcall(reinterpret_cast<PyObject *>(type), args_array, args_array_num, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -946,7 +960,7 @@ PyTypeObject euler_Type = {
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_new*/ Euler_new,
/*tp_free*/ nullptr,
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
/*tp_bases*/ nullptr,

View file

@ -647,6 +647,21 @@ static PyObject *Matrix_vectorcall(PyObject *type,
return nullptr;
}
static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
/* Only called on sub-classes. */
if (UNLIKELY(kwds && PyDict_GET_SIZE(kwds))) {
PyErr_SetString(PyExc_TypeError,
"mathutils.Matrix(): "
"takes no keyword args");
return nullptr;
}
PyObject *const *args_array = &PyTuple_GET_ITEM(args, 0);
const size_t args_array_num = PyTuple_GET_SIZE(args);
return Matrix_vectorcall(
reinterpret_cast<PyObject *>(type), args_array, args_array_num, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -3576,7 +3591,7 @@ PyTypeObject matrix_Type = {
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_new*/ Matrix_new,
/*tp_free*/ nullptr,
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
/*tp_bases*/ nullptr,

View file

@ -172,6 +172,21 @@ static PyObject *Quaternion_vectorcall(PyObject *type,
return Quaternion_CreatePyObject(quat, (PyTypeObject *)type);
}
static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
/* Only called on sub-classes. */
if (UNLIKELY(kwds && PyDict_GET_SIZE(kwds))) {
PyErr_SetString(PyExc_TypeError,
"mathutils.Quaternion(): "
"takes no keyword args");
return nullptr;
}
PyObject *const *args_array = &PyTuple_GET_ITEM(args, 0);
const size_t args_array_num = PyTuple_GET_SIZE(args);
return Quaternion_vectorcall(
reinterpret_cast<PyObject *>(type), args_array, args_array_num, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1890,7 +1905,7 @@ PyTypeObject quaternion_Type = {
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_new*/ Quaternion_new,
/*tp_free*/ nullptr,
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
/*tp_bases*/ nullptr,

View file

@ -185,6 +185,20 @@ static PyObject *Vector_vectorcall(PyObject *type,
return Vector_CreatePyObject_alloc(vec, vec_num, (PyTypeObject *)type);
}
static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
if (UNLIKELY(kwds && PyDict_GET_SIZE(kwds))) {
PyErr_SetString(PyExc_TypeError,
"Vector(): "
"takes no keyword args");
return nullptr;
}
PyObject *const *args_array = &PyTuple_GET_ITEM(args, 0);
const size_t args_array_num = PyTuple_GET_SIZE(args);
return Vector_vectorcall(
reinterpret_cast<PyObject *>(type), args_array, args_array_num, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -3526,7 +3540,7 @@ PyTypeObject vector_Type = {
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_new*/ Vector_new,
/*tp_free*/ nullptr,
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
/*tp_bases*/ nullptr,

View file

@ -440,6 +440,173 @@ class TypeTesting(unittest.TestCase):
Euler(dummy=None)
self.assertEqual(str(context.exception), "mathutils.Euler(): takes no keyword args")
def test_subclass_matrix(self):
class MyMatrix(Matrix):
__slots__ = (
"offset",
)
def __new__(cls, *args, **kw):
del kw
return super().__new__(cls, *args)
def __init__(self, *args, offset=0.0):
self.offset = offset
def __str__(self):
return "{:s}({!r})".format(type(self).__name__, tuple(tuple(x) for x in self))
def my_sum_and_offset(self):
return sum(iter([i for v in self for i in v])) + self.offset
value = MyMatrix(offset=1.0)
self.assertEqual(
str(value),
"MyMatrix(((1.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, 0.0, 0.0, 1.0)))",
)
self.assertEqual(value.my_sum_and_offset(), 5.0)
value.offset += 1.0
self.assertEqual(value.my_sum_and_offset(), 6.0)
value = MyMatrix(((1.0, 2.0, 3.0), (4.0, 5.0, 6.0), (7.0, 8.0, 9.0)), offset=5.0)
self.assertEqual(str(value), "MyMatrix(((1.0, 2.0, 3.0), (4.0, 5.0, 6.0), (7.0, 8.0, 9.0)))")
self.assertEqual(value.my_sum_and_offset(), 50.0)
# Ensure `__slots__` is working.
with self.assertRaises(AttributeError):
value.offset_other = 5.0
def test_subclass_vector(self):
class MyVector(Vector):
__slots__ = (
"offset",
)
def __new__(cls, *args, **kw):
del kw
return super().__new__(cls, *args)
def __init__(self, *args, offset=0.0):
self.offset = offset
def __str__(self):
return "{:s}({!r})".format(type(self).__name__, tuple(self))
def my_sum_and_offset(self):
return sum(iter(self)) + self.offset
value = MyVector(offset=1.0)
self.assertEqual(str(value), "MyVector((0.0, 0.0, 0.0))")
self.assertEqual(value.my_sum_and_offset(), 1.0)
value.offset += 1.0
self.assertEqual(value.my_sum_and_offset(), 2.0)
value = MyVector((1.0, 2.0, 3.0), offset=4.0)
self.assertEqual(str(value), "MyVector((1.0, 2.0, 3.0))")
self.assertEqual(value.my_sum_and_offset(), 10.0)
# Ensure `__slots__` is working.
with self.assertRaises(AttributeError):
value.offset_other = 5.0
def test_subclass_color(self):
class MyColor(Color):
__slots__ = (
"offset",
)
def __new__(cls, *args, **kw):
del kw
return super().__new__(cls, *args)
def __init__(self, *args, offset=0.0):
self.offset = offset
def __str__(self):
return "{:s}({!r})".format(type(self).__name__, tuple(self))
def my_sum_and_offset(self):
return sum(iter(self)) + self.offset
value = MyColor(offset=1.0)
self.assertEqual(str(value), "MyColor((0.0, 0.0, 0.0))")
self.assertEqual(value.my_sum_and_offset(), 1.0)
value = MyColor((1.0, 2.0, 3.0), offset=4.0)
self.assertEqual(str(value), "MyColor((1.0, 2.0, 3.0))")
self.assertEqual(value.my_sum_and_offset(), 10.0)
# Ensure `__slots__` is working.
with self.assertRaises(AttributeError):
value.offset_other = 5.0
value.offset += 1.0
def test_subclass_quaternion(self):
class MyQuaternion(Quaternion):
__slots__ = (
"offset",
)
def __new__(cls, *args, **_kw):
return super().__new__(cls, *args)
def __init__(self, *args, offset=0.0):
self.offset = offset
def __str__(self):
return "{:s}({!r})".format(type(self).__name__, tuple(self))
def my_sum_and_offset(self):
return sum(iter(self)) + self.offset
value = MyQuaternion(offset=1.0)
self.assertEqual(str(value), "MyQuaternion((1.0, 0.0, 0.0, 0.0))")
self.assertEqual(value.my_sum_and_offset(), 2.0)
value.offset += 1.0
self.assertEqual(value.my_sum_and_offset(), 3.0)
value = MyQuaternion((1.0, 2.0, 3.0, 4.0), offset=4.0)
self.assertEqual(str(value), "MyQuaternion((1.0, 2.0, 3.0, 4.0))")
self.assertEqual(value.my_sum_and_offset(), 14.0)
# Ensure `__slots__` is working.
with self.assertRaises(AttributeError):
value.offset_other = 5.0
def test_subclass_euler(self):
class MyEuler(Euler):
__slots__ = (
"offset",
)
def __new__(cls, *args, **kw):
del kw
return super().__new__(cls, *args)
def __init__(self, *args, offset=0.0):
self.offset = offset
def __str__(self):
return "{:s}({!r})".format(type(self).__name__, tuple(self))
def my_sum_and_offset(self):
return sum(iter(self)) + self.offset
value = MyEuler(offset=1.0)
self.assertEqual(str(value), "MyEuler((0.0, 0.0, 0.0))")
self.assertEqual(value.my_sum_and_offset(), 1.0)
value.offset += 1.0
self.assertEqual(value.my_sum_and_offset(), 2.0)
value = MyEuler((1.0, 2.0, 3.0), offset=4.0)
self.assertEqual(str(value), "MyEuler((1.0, 2.0, 3.0))")
self.assertEqual(value.my_sum_and_offset(), 10.0)
# Ensure `__slots__` is working.
with self.assertRaises(AttributeError):
value.offset_other = 5.0
value.offset += 1.0
class KDTreeTesting(unittest.TestCase):
@staticmethod