Browse Source

python: add actual exception messages to all Trade and *Tools APIs.

The time saved on not writing those messages back then was really not
worth the time wasted on figuring out what the hell every time one of
these fires.

And yet, somehow Python itself and a ton of libraries raises just an
IndexError alone, with nothing saying *how* wrong it was. Those details,
present in Magnum's own C++ asserts, proved to be extremely valuable for
being able to quickly figure out what's wrong, often even without even
having to look at the code or step through in the debugger.

Also fixes an issue where SceneData.field_object_offset() was rejecting
offsets right at the end of the field. Wasn't caught by tests because
apparently LookupError is a base of IndexError and as there was no
message it just failed elsewhere without being noticed.
next
Vladimír Vondruš 3 years ago
parent
commit
1303032829
  1. 52
      src/python/magnum/meshtools.cpp
  2. 2
      src/python/magnum/scenetools.cpp
  3. 52
      src/python/magnum/test/test_meshtools.py
  4. 2
      src/python/magnum/test/test_scenetools.py
  5. 255
      src/python/magnum/test/test_trade.py
  6. 509
      src/python/magnum/trade.cpp

52
src/python/magnum/meshtools.cpp

@ -105,11 +105,11 @@ void meshtools(py::module_& m) {
mesh.primitive() == MeshPrimitive::TriangleStrip ||
mesh.primitive() == MeshPrimitive::TriangleFan)
{
PyErr_SetString(PyExc_AssertionError, "invalid mesh primitive");
PyErr_Format(PyExc_AssertionError, "%S is not supported, turn it into a plain indexed mesh first", py::cast(mesh.primitive()).ptr());
throw py::error_already_set{};
}
if(mesh.primitive() != primitive) {
PyErr_SetString(PyExc_AssertionError, "inconsistent mesh primitive");
PyErr_Format(PyExc_AssertionError, "expected %S but got %S in mesh %zu", py::cast(primitive).ptr(), py::cast(mesh.primitive()).ptr(), i);
throw py::error_already_set{};
}
}
@ -161,7 +161,7 @@ void meshtools(py::module_& m) {
mesh.primitive() != MeshPrimitive::TriangleStrip &&
mesh.primitive() != MeshPrimitive::TriangleFan)
{
PyErr_SetString(PyExc_AssertionError, "invalid mesh primitive");
PyErr_Format(PyExc_AssertionError, "invalid primitive %S", py::cast(mesh.primitive()).ptr());
throw py::error_already_set{};
}
/** @todo check that the indices aren't impl-specific once it's
@ -190,11 +190,14 @@ void meshtools(py::module_& m) {
.def("transform2d", [](const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, Int morphTargetId, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u", id);
else
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u in morph target %i", id, morphTargetId);
throw py::error_already_set{};
}
if(vertexFormatComponentCount(mesh.attributeFormat(*positionAttributeId)) != 2) {
PyErr_SetString(PyExc_AssertionError, "positions are not 2D");
PyErr_Format(PyExc_AssertionError, "expected 2D positions but got %S", py::cast(mesh.attributeFormat(*positionAttributeId)).ptr());
throw py::error_already_set{};
}
/** @todo check that the positions aren't impl-specific once
@ -214,11 +217,14 @@ void meshtools(py::module_& m) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u", id);
else
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u in morph target %i", id, morphTargetId);
throw py::error_already_set{};
}
if(mesh.attributeFormat(*positionAttributeId) != VertexFormat::Vector2) {
PyErr_SetString(PyExc_AssertionError, "positions are not VECTOR2");
PyErr_Format(PyExc_AssertionError, "expected %S positions but got %S", py::cast(VertexFormat::Vector2).ptr(), py::cast(mesh.attributeFormat(*positionAttributeId)).ptr());
throw py::error_already_set{};
}
@ -231,11 +237,14 @@ void meshtools(py::module_& m) {
.def("transform3d", [](const Trade::MeshData& mesh, const Matrix4& transformation, UnsignedInt id, Int morphTargetId, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u", id);
else
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u in morph target %i", id, morphTargetId);
throw py::error_already_set{};
}
if(vertexFormatComponentCount(mesh.attributeFormat(*positionAttributeId)) != 3) {
PyErr_SetString(PyExc_AssertionError, "mesh positions are not 3D");
PyErr_Format(PyExc_AssertionError, "expected 3D positions but got %S", py::cast(mesh.attributeFormat(*positionAttributeId)).ptr());
throw py::error_already_set{};
}
/** @todo check that the positions, normals, ... aren't
@ -255,11 +264,14 @@ void meshtools(py::module_& m) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u", id);
else
PyErr_Format(PyExc_KeyError, "the mesh has no positions with index %u in morph target %i", id, morphTargetId);
throw py::error_already_set{};
}
if(mesh.attributeFormat(*positionAttributeId) != VertexFormat::Vector3) {
PyErr_SetString(PyExc_AssertionError, "positions are not VECTOR3");
PyErr_Format(PyExc_AssertionError, "expected %S positions but got %S", py::cast(VertexFormat::Vector3).ptr(), py::cast(mesh.attributeFormat(*positionAttributeId)).ptr());
throw py::error_already_set{};
}
@ -270,15 +282,15 @@ void meshtools(py::module_& m) {
(mesh.attributeFormat(*tangentAttributeId) != VertexFormat::Vector3 &&
mesh.attributeFormat(*tangentAttributeId) != VertexFormat::Vector4))
{
PyErr_SetString(PyExc_AssertionError, "tangents are not VECTOR3 or VECTOR4");
PyErr_Format(PyExc_AssertionError, "expected %S or %S tangents but got %S", py::cast(VertexFormat::Vector3).ptr(), py::cast(VertexFormat::Vector4).ptr(), py::cast(mesh.attributeFormat(*tangentAttributeId)).ptr());
throw py::error_already_set{};
}
if(bitangentAttributeId && mesh.attributeFormat(*bitangentAttributeId) != VertexFormat::Vector3) {
PyErr_SetString(PyExc_AssertionError, "bitangents are not VECTOR3");
PyErr_Format(PyExc_AssertionError, "expected %S bitangents but got %S", py::cast(VertexFormat::Vector3).ptr(), py::cast(mesh.attributeFormat(*bitangentAttributeId)).ptr());
throw py::error_already_set{};
}
if(normalAttributeId && mesh.attributeFormat(*normalAttributeId) != VertexFormat::Vector3) {
PyErr_SetString(PyExc_AssertionError, "normals are not VECTOR3");
PyErr_Format(PyExc_AssertionError, "expected %S normals but got %S", py::cast(VertexFormat::Vector3).ptr(), py::cast(mesh.attributeFormat(*normalAttributeId)).ptr());
throw py::error_already_set{};
}
@ -291,7 +303,10 @@ void meshtools(py::module_& m) {
.def("transform_texture_coordinates2d", [](const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, Int morphTargetId, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id, morphTargetId);
if(!textureCoordinateAttributeId) {
PyErr_SetString(PyExc_KeyError, "texture coordinates attribute not found");
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "the mesh has no texture coordinates with index %u", id);
else
PyErr_Format(PyExc_KeyError, "the mesh has no texture coordinates with index %u in morph target %i", id, morphTargetId);
throw py::error_already_set{};
}
/** @todo check that the texture coordinates aren't impl-specific
@ -311,11 +326,14 @@ void meshtools(py::module_& m) {
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id, morphTargetId);
if(!textureCoordinateAttributeId) {
PyErr_SetString(PyExc_KeyError, "texture coordinates attribute not found");
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "the mesh has no texture coordinates with index %u", id);
else
PyErr_Format(PyExc_KeyError, "the mesh has no texture coordinates with index %u in morph target %i", id, morphTargetId);
throw py::error_already_set{};
}
if(mesh.attributeFormat(*textureCoordinateAttributeId) != VertexFormat::Vector2) {
PyErr_SetString(PyExc_AssertionError, "texture coordinates are not VECTOR2");
PyErr_Format(PyExc_AssertionError, "expected %S texture coordinates but got %S", py::cast(VertexFormat::Vector2).ptr(), py::cast(mesh.attributeFormat(*textureCoordinateAttributeId)).ptr());
throw py::error_already_set{};
}

2
src/python/magnum/scenetools.cpp

@ -86,7 +86,7 @@ void scenetools(py::module_& m) {
for(std::size_t i = 0; i != entriesToKeep.size(); ++i) {
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(entriesToKeep[i].first());
if(!fieldId) {
PyErr_Format(PyExc_AssertionError, "field at index %zu not found", i, scene.fieldCount());
PyErr_Format(PyExc_AssertionError, "%S not found among %u fields", py::cast(entriesToKeep[i].first()).ptr(), scene.fieldCount());
throw py::error_already_set{};
}
if(usedFields[*fieldId]) {

52
src/python/magnum/test/test_meshtools.py

@ -61,16 +61,16 @@ class Concatenate(unittest.TestCase):
meshtools.concatenate([])
def test_invalid_primitive(self):
with self.assertRaisesRegex(AssertionError, "invalid mesh primitive"):
with self.assertRaisesRegex(AssertionError, "MeshPrimitive.TRIANGLE_STRIP is not supported, turn it into a plain indexed mesh first"):
meshtools.concatenate([primitives.cube_solid(), primitives.plane_solid()])
# Should check that also for the first argument
with self.assertRaisesRegex(AssertionError, "invalid mesh primitive"):
with self.assertRaisesRegex(AssertionError, "MeshPrimitive.TRIANGLE_STRIP is not supported, turn it into a plain indexed mesh first"):
meshtools.concatenate([primitives.plane_solid()])
def test_inconsistent_primitive(self):
with self.assertRaisesRegex(AssertionError, "inconsistent mesh primitive"):
with self.assertRaisesRegex(AssertionError, "expected MeshPrimitive.TRIANGLES but got MeshPrimitive.LINES in mesh 1"):
meshtools.concatenate([primitives.cube_solid(), primitives.line3d()])
with self.assertRaisesRegex(AssertionError, "inconsistent mesh primitive"):
with self.assertRaisesRegex(AssertionError, "expected MeshPrimitive.LINES but got MeshPrimitive.TRIANGLES in mesh 1"):
meshtools.concatenate([primitives.line3d(), primitives.cube_solid()])
class Duplicate(unittest.TestCase):
@ -102,7 +102,7 @@ class GenerateIndices(unittest.TestCase):
mesh = primitives.cube_solid()
self.assertEqual(mesh.primitive, MeshPrimitive.TRIANGLES)
with self.assertRaisesRegex(AssertionError, "invalid mesh primitive"):
with self.assertRaisesRegex(AssertionError, "invalid primitive MeshPrimitive.TRIANGLES"):
meshtools.generate_indices(mesh)
class Filter(unittest.TestCase):
@ -297,44 +297,44 @@ class Transform(unittest.TestCase):
mesh = meshtools.copy(primitives.square_solid(primitives.SquareFlags.TEXTURE_COORDINATES))
# ID not found
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 1"):
meshtools.transform2d(mesh, Matrix3(), id=1)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 1"):
meshtools.transform2d_in_place(mesh, Matrix3(), id=1)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 1"):
meshtools.transform3d(mesh, Matrix4(), id=1)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 1"):
meshtools.transform3d_in_place(mesh, Matrix4(), id=1)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no texture coordinates with index 1"):
meshtools.transform_texture_coordinates2d(mesh, Matrix3(), id=1)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no texture coordinates with index 1"):
meshtools.transform_texture_coordinates2d_in_place(mesh, Matrix3(), id=1)
# Morph target not found
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 0 in morph target 37"):
meshtools.transform2d(mesh, Matrix3(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 0 in morph target 37"):
meshtools.transform2d_in_place(mesh, Matrix3(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 0 in morph target 37"):
meshtools.transform3d(mesh, Matrix4(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no positions with index 0 in morph target 37"):
meshtools.transform3d_in_place(mesh, Matrix4(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no texture coordinates with index 0 in morph target 37"):
meshtools.transform_texture_coordinates2d(mesh, Matrix3(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
with self.assertRaisesRegex(KeyError, "the mesh has no texture coordinates with index 0 in morph target 37"):
meshtools.transform_texture_coordinates2d_in_place(mesh, Matrix3(), morph_target_id=37)
def test_not_2d_not_3d(self):
mesh2d = primitives.line2d()
mesh3d = primitives.line3d()
with self.assertRaisesRegex(AssertionError, "positions are not 2D"):
with self.assertRaisesRegex(AssertionError, "expected 2D positions but got VertexFormat.VECTOR3"):
meshtools.transform2d(mesh3d, Matrix3())
with self.assertRaisesRegex(AssertionError, "positions are not VECTOR2"):
with self.assertRaisesRegex(AssertionError, "expected VertexFormat.VECTOR2 positions but got VertexFormat.VECTOR3"):
meshtools.transform2d_in_place(mesh3d, Matrix3())
with self.assertRaisesRegex(AssertionError, "positions are not 3D"):
with self.assertRaisesRegex(AssertionError, "expected 3D positions but got VertexFormat.VECTOR2"):
meshtools.transform3d(mesh2d, Matrix4())
with self.assertRaisesRegex(AssertionError, "positions are not VECTOR3"):
with self.assertRaisesRegex(AssertionError, "expected VertexFormat.VECTOR3 positions but got VertexFormat.VECTOR2"):
meshtools.transform3d_in_place(mesh2d, Matrix4())
def test_not_float(self):
@ -355,17 +355,17 @@ class Transform(unittest.TestCase):
self.assertEqual(packed_texcoords.attribute(trade.MeshAttribute.TEXTURE_COORDINATES)[1], (-0.5, 0.0))
# TODO test 2D position with something that's actually 2D
with self.assertRaisesRegex(AssertionError, "positions are not VECTOR2"):
with self.assertRaisesRegex(AssertionError, "expected VertexFormat.VECTOR2 positions but got VertexFormat.VECTOR3US"):
meshtools.transform2d_in_place(importer.mesh('packed positions'), Matrix3())
with self.assertRaisesRegex(AssertionError, "positions are not VECTOR3"):
with self.assertRaisesRegex(AssertionError, "expected VertexFormat.VECTOR3 positions but got VertexFormat.VECTOR3US"):
meshtools.transform3d_in_place(importer.mesh('packed positions'), Matrix4())
# TODO test also with an explicit ID and morph target ID to verify it's
# correctly propagated
with self.assertRaisesRegex(AssertionError, "normals are not VECTOR3"):
with self.assertRaisesRegex(AssertionError, "expected VertexFormat.VECTOR3 normals but got VertexFormat.VECTOR3S_NORMALIZED"):
meshtools.transform3d_in_place(importer.mesh('packed normals'), Matrix4())
with self.assertRaisesRegex(AssertionError, "tangents are not VECTOR3 or VECTOR4"):
with self.assertRaisesRegex(AssertionError, "expected VertexFormat.VECTOR3 or VertexFormat.VECTOR4 tangents but got VertexFormat.VECTOR4S_NORMALIZED"):
meshtools.transform3d_in_place(importer.mesh('packed tangents'), Matrix4())
with self.assertRaisesRegex(AssertionError, "texture coordinates are not VECTOR2"):
with self.assertRaisesRegex(AssertionError, "expected VertexFormat.VECTOR2 texture coordinates but got VertexFormat.VECTOR2US_NORMALIZED"):
meshtools.transform_texture_coordinates2d_in_place(importer.mesh('packed texcoords'), Matrix3())
def test_in_place_not_mutable(self):

2
src/python/magnum/test/test_scenetools.py

@ -228,7 +228,7 @@ class Filter(unittest.TestCase):
scenetools.filter_field_entries(scene, [
(8, containers.BitArrayView())
])
with self.assertRaisesRegex(AssertionError, "field at index 1 not found"):
with self.assertRaisesRegex(AssertionError, "SceneField.LIGHT not found among 8 fields"):
scenetools.filter_field_entries(scene, [
(trade.SceneField.CAMERA, containers.BitArray.value_init(2)),
(trade.SceneField.LIGHT, containers.BitArrayView())

255
src/python/magnum/test/test_trade.py

@ -198,7 +198,7 @@ class ImageData(unittest.TestCase):
image = importer.image2d(0)
self.assertEqual(image.format, PixelFormat.DEPTH32F_STENCIL8UI)
with self.assertRaisesRegex(NotImplementedError, "access to this pixel format is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to PixelFormat.DEPTH32F_STENCIL8UI is not implemented yet, sorry"):
image.pixels
class MeshData(unittest.TestCase):
@ -597,70 +597,75 @@ class MeshData(unittest.TestCase):
mesh = importer.mesh(0)
# Access by OOB ID
with self.assertRaises(IndexError):
# Access by OOB ID. Deprecated build contains additional 2 backwards
# compatibility skinning attributes.
if magnum.BUILD_DEPRECATED:
indexOutOfRangeMessage = "index 11 out of range for 11 attributes"
else:
indexOutOfRangeMessage = "index 9 out of range for 9 attributes"
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.attribute_name(mesh.attribute_count())
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.attribute_id(mesh.attribute_count())
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.attribute_format(mesh.attribute_count())
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.attribute_offset(mesh.attribute_count())
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.attribute_stride(mesh.attribute_count())
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.attribute_array_size(mesh.attribute_count())
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.attribute(mesh.attribute_count())
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, indexOutOfRangeMessage):
mesh.mutable_attribute(mesh.attribute_count())
# Access by nonexistent name
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TANGENT attributes"):
mesh.attribute_id(trade.MeshAttribute.TANGENT)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TANGENT attributes"):
mesh.attribute_format(trade.MeshAttribute.TANGENT)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TANGENT attributes"):
mesh.attribute_offset(trade.MeshAttribute.TANGENT)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TANGENT attributes"):
mesh.attribute_stride(trade.MeshAttribute.TANGENT)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TANGENT attributes"):
mesh.attribute_array_size(trade.MeshAttribute.TANGENT)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TANGENT attributes"):
mesh.attribute(trade.MeshAttribute.TANGENT)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TANGENT attributes"):
mesh.mutable_attribute(trade.MeshAttribute.TANGENT)
# Access by existing name + OOB ID
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 2 out of range for 2 MeshAttribute.TEXTURE_COORDINATES attributes"):
mesh.attribute_id(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 2 out of range for 2 MeshAttribute.TEXTURE_COORDINATES attributes"):
mesh.attribute_format(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 2 out of range for 2 MeshAttribute.TEXTURE_COORDINATES attributes"):
mesh.attribute_offset(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 2 out of range for 2 MeshAttribute.TEXTURE_COORDINATES attributes"):
mesh.attribute_stride(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 2 out of range for 2 MeshAttribute.TEXTURE_COORDINATES attributes"):
mesh.attribute_array_size(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 2 out of range for 2 MeshAttribute.TEXTURE_COORDINATES attributes"):
mesh.attribute(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 2 out of range for 2 MeshAttribute.TEXTURE_COORDINATES attributes"):
mesh.mutable_attribute(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
# Access by existing name + OOB morph target ID
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TEXTURE_COORDINATES attributes in morph target 37"):
mesh.attribute_id(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TEXTURE_COORDINATES attributes in morph target 37"):
mesh.attribute_format(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TEXTURE_COORDINATES attributes in morph target 37"):
mesh.attribute_offset(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TEXTURE_COORDINATES attributes in morph target 37"):
mesh.attribute_stride(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TEXTURE_COORDINATES attributes in morph target 37"):
mesh.attribute_array_size(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TEXTURE_COORDINATES attributes in morph target 37"):
mesh.attribute(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "index 0 out of range for 0 MeshAttribute.TEXTURE_COORDINATES attributes in morph target 37"):
mesh.mutable_attribute(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
def test_attribute_access_array(self):
@ -688,13 +693,13 @@ class MeshData(unittest.TestCase):
mesh = importer.mesh(0)
custom_attribute_id = mesh.attribute_id(custom_attribute)
with self.assertRaisesRegex(NotImplementedError, "access to this vertex format is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to VertexFormat.MATRIX2X2 is not implemented yet, sorry"):
mesh.attribute(custom_attribute_id)
with self.assertRaisesRegex(NotImplementedError, "access to this vertex format is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to VertexFormat.MATRIX2X2 is not implemented yet, sorry"):
mesh.mutable_attribute(custom_attribute_id)
with self.assertRaisesRegex(NotImplementedError, "access to this vertex format is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to VertexFormat.MATRIX2X2 is not implemented yet, sorry"):
mesh.attribute(custom_attribute)
with self.assertRaisesRegex(NotImplementedError, "access to this vertex format is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to VertexFormat.MATRIX2X2 is not implemented yet, sorry"):
mesh.mutable_attribute(custom_attribute)
class SceneData(unittest.TestCase):
@ -1049,79 +1054,79 @@ class SceneData(unittest.TestCase):
scene = importer.scene(0)
# Access by OOB field ID
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.field_name(scene.field_count)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.field_flags(scene.field_count)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.field_type(scene.field_count)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.field_size(scene.field_count)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.field_array_size(scene.field_count)
with self.assertRaisesRegex(IndexError, "field out of range"):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.has_field_object(scene.field_count, 0)
with self.assertRaisesRegex(IndexError, "field out of range"):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.field_object_offset(scene.field_count, 0)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.mapping(scene.field_count)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.mutable_mapping(scene.field_count)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.field(scene.field_count)
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "index 8 out of range for 8 fields"):
scene.mutable_field(scene.field_count)
# Access by nonexistent field name
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.field_id(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.field_flags(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.field_type(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.field_size(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.field_array_size(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.has_field_object(trade.SceneField.SCALING, 0)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.field_object_offset(trade.SceneField.SCALING, 0)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.mapping(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.mutable_mapping(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.field(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "SceneField.SCALING not found among 8 fields"):
scene.mutable_field(trade.SceneField.SCALING)
# OOB object ID
with self.assertRaisesRegex(IndexError, "object out of range"):
with self.assertRaisesRegex(IndexError, "index 4 out of range for 4 objects"):
scene.has_field_object(0, 4) # PARENT
with self.assertRaisesRegex(IndexError, "object out of range"):
with self.assertRaisesRegex(IndexError, "index 4 out of range for 4 objects"):
scene.has_field_object(trade.SceneField.PARENT, 4)
with self.assertRaisesRegex(IndexError, "object out of range"):
with self.assertRaisesRegex(IndexError, "index 4 out of range for 4 objects"):
scene.field_object_offset(0, 4) # PARENT
with self.assertRaisesRegex(IndexError, "object out of range"):
with self.assertRaisesRegex(IndexError, "index 4 out of range for 4 objects"):
scene.field_object_offset(trade.SceneField.PARENT, 4)
# Lookup error
with self.assertRaises(LookupError):
with self.assertRaisesRegex(LookupError, "object 1 not found in field SceneField.CAMERA starting at offset 0"):
scene.field_object_offset(4, 1) # CAMERA
with self.assertRaises(LookupError):
with self.assertRaisesRegex(LookupError, "object 1 not found in field SceneField.CAMERA starting at offset 0"):
scene.field_object_offset(trade.SceneField.CAMERA, 1)
# Lookup error due to field offset being at the end
with self.assertRaises(LookupError):
with self.assertRaisesRegex(LookupError, "object 1 not found in field SceneField.PARENT starting at offset 4"):
scene.field_object_offset(0, 1, scene.field_size(0)) # PARENT
with self.assertRaises(LookupError):
with self.assertRaisesRegex(LookupError, "object 1 not found in field SceneField.PARENT starting at offset 4"):
scene.field_object_offset(trade.SceneField.PARENT, 1, scene.field_size(trade.SceneField.PARENT))
# OOB field offset (offset == size is allowed, tested above)
with self.assertRaisesRegex(IndexError, "offset out of range"):
with self.assertRaisesRegex(IndexError, "offset 5 out of range for a field of size 4"):
scene.field_object_offset(0, 1, scene.field_size(0) + 1) # PARENT
with self.assertRaisesRegex(IndexError, "offset out of range"):
with self.assertRaisesRegex(IndexError, "offset 5 out of range for a field of size 4"):
scene.field_object_offset(trade.SceneField.PARENT, 1, scene.field_size(trade.SceneField.PARENT) + 1)
def test_field_access_array(self):
@ -1138,13 +1143,13 @@ class SceneData(unittest.TestCase):
scene = importer.scene(0)
string_field_id = scene.field_id(string_field)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to SceneFieldType.STRING_OFFSET32 is not implemented yet, sorry"):
scene.field(string_field_id)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to SceneFieldType.STRING_OFFSET32 is not implemented yet, sorry"):
scene.mutable_field(string_field_id)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to SceneFieldType.STRING_OFFSET32 is not implemented yet, sorry"):
scene.field(string_field)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
with self.assertRaisesRegex(NotImplementedError, "access to SceneFieldType.STRING_OFFSET32 is not implemented yet, sorry"):
scene.mutable_field(string_field)
class TextureData(unittest.TestCase):
@ -1397,50 +1402,54 @@ class Importer(unittest.TestCase):
importer.image3d('')
def test_index_oob(self):
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgb.png'))
with self.assertRaises(IndexError):
importer.scene_name(0)
with self.assertRaises(IndexError):
importer.object_name(0)
with self.assertRaises(IndexError):
importer.scene(0)
with self.assertRaises(IndexError):
importer.mesh_level_count(0)
with self.assertRaises(IndexError):
importer.mesh_name(0)
with self.assertRaisesRegex(IndexError, "ID out of range"):
importer.mesh(0)
with self.assertRaises(IndexError):
importer.texture_name(0)
with self.assertRaises(IndexError):
importer.texture(0)
with self.assertRaises(IndexError):
importer.image1d_level_count(0)
with self.assertRaises(IndexError):
importer.image2d_level_count(1)
with self.assertRaises(IndexError):
importer.image3d_level_count(0)
with self.assertRaises(IndexError):
importer.image1d_name(0)
with self.assertRaises(IndexError):
importer.image2d_name(1)
with self.assertRaises(IndexError):
importer.image3d_name(0)
with self.assertRaisesRegex(IndexError, "ID out of range"):
importer.image1d(0)
with self.assertRaisesRegex(IndexError, "level out of range"):
importer.image2d(0, 1)
with self.assertRaisesRegex(IndexError, "ID out of range"):
importer.image2d(1)
with self.assertRaisesRegex(IndexError, "ID out of range"):
importer.image3d(0)
texture_importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
texture_importer.open_file(os.path.join(os.path.dirname(__file__), 'texture.gltf'))
mesh_importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
mesh_importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
scene_importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
scene_importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
with self.assertRaisesRegex(IndexError, "index 3 out of range for 3 entries"):
scene_importer.scene_name(3)
with self.assertRaisesRegex(IndexError, "index 5 out of range for 5 entries"):
scene_importer.object_name(5)
with self.assertRaisesRegex(IndexError, "index 3 out of range for 3 entries"):
scene_importer.scene(3)
with self.assertRaisesRegex(IndexError, "index 5 out of range for 5 entries"):
mesh_importer.mesh_level_count(5)
with self.assertRaisesRegex(IndexError, "index 5 out of range for 5 entries"):
mesh_importer.mesh_name(5)
with self.assertRaisesRegex(IndexError, "index 5 out of range for 5 entries"):
mesh_importer.mesh(5)
with self.assertRaisesRegex(IndexError, "index 3 out of range for 3 entries"):
texture_importer.texture_name(3)
with self.assertRaisesRegex(IndexError, "index 3 out of range for 3 entries"):
texture_importer.texture(3)
with self.assertRaisesRegex(IndexError, "index 0 out of range for 0 entries"):
texture_importer.image1d_level_count(0)
with self.assertRaisesRegex(IndexError, "index 2 out of range for 2 entries"):
texture_importer.image2d_level_count(2)
with self.assertRaisesRegex(IndexError, "index 0 out of range for 0 entries"):
texture_importer.image3d_level_count(0)
with self.assertRaisesRegex(IndexError, "index 0 out of range for 0 entries"):
texture_importer.image1d_name(0)
with self.assertRaisesRegex(IndexError, "index 2 out of range for 2 entries"):
texture_importer.image2d_name(2)
with self.assertRaisesRegex(IndexError, "index 0 out of range for 0 entries"):
texture_importer.image3d_name(0)
with self.assertRaisesRegex(IndexError, "index 0 out of range for 0 entries"):
texture_importer.image1d(0)
with self.assertRaisesRegex(IndexError, "index 2 out of range for 2 entries"):
texture_importer.image2d(2)
with self.assertRaisesRegex(IndexError, "index 0 out of range for 0 entries"):
texture_importer.image3d(0)
def test_open_failed(self):
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
@ -1493,7 +1502,7 @@ class Importer(unittest.TestCase):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "name Nonexistent not found among 3 entries"):
importer.scene('Nonexistent')
def test_scene_failed(self):
@ -1545,7 +1554,7 @@ class Importer(unittest.TestCase):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
with self.assertRaises(IndexError):
with self.assertRaisesRegex(IndexError, "level 1 out of range for 1 entries"):
importer.mesh(0, 1)
def test_mesh_by_name(self):
@ -1559,14 +1568,14 @@ class Importer(unittest.TestCase):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "name Nonexistent not found among 5 entries"):
importer.mesh('Nonexistent')
def test_mesh_by_name_level_oob(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
with self.assertRaisesRegex(IndexError, "level out of range"):
with self.assertRaisesRegex(IndexError, "level 1 out of range for 1 entries"):
importer.mesh('Non-indexed mesh', 1)
def test_mesh_failed(self):
@ -1589,7 +1598,7 @@ class Importer(unittest.TestCase):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'texture.gltf'))
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "name Nonexistent not found among 3 entries"):
importer.texture('Nonexistent')
def test_texture_failed(self):
@ -1624,12 +1633,12 @@ class Importer(unittest.TestCase):
del importer
self.assertEqual(sys.getrefcount(manager), manager_refcount)
def test_image_level_oob(self):
def test_image2d_level_oob(self):
# importer refcounting tested in image2d
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgb.png'))
with self.assertRaisesRegex(IndexError, "level out of range"):
with self.assertRaisesRegex(IndexError, "level 1 out of range for 1 entries"):
importer.image2d(0, 1)
def test_image2d_by_name(self):
@ -1643,7 +1652,7 @@ class Importer(unittest.TestCase):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'texture.gltf'))
with self.assertRaises(KeyError):
with self.assertRaisesRegex(KeyError, "name Nonexistent not found among 2 entries"):
importer.image2d('Nonexistent')
def test_image2d_data(self):

509
src/python/magnum/trade.cpp

@ -198,7 +198,7 @@ template<UnsignedInt dimensions, class T> Containers::PyArrayViewHolder<Containe
const std::size_t itemsize = pixelFormatSize(format);
const Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> formatStringGetitemSetitem = accessorsForPixelFormat(format);
if(!formatStringGetitemSetitem.first()) {
PyErr_SetString(PyExc_NotImplementedError, "access to this pixel format is not implemented yet, sorry");
PyErr_Format(PyExc_NotImplementedError, "access to %S is not implemented yet, sorry", py::cast(format).ptr());
throw py::error_already_set{};
}
return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<dimensions, T>{flattenPixelView(data, pixels), formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, py::cast(image));
@ -356,7 +356,7 @@ template<class R, R(Trade::AbstractImporter::*f)(UnsignedInt), UnsignedInt(Trade
}
if(id >= (self.*bounds)()) {
PyErr_SetNone(PyExc_IndexError);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u entries", id, (self.*bounds)());
throw py::error_already_set{};
}
@ -370,7 +370,7 @@ template<class R, Containers::String(Trade::AbstractImporter::*f)(R), R(Trade::A
}
if(id >= (self.*bounds)()) {
PyErr_SetNone(PyExc_IndexError);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u entries", id, (self.*bounds)());
throw py::error_already_set{};
}
@ -384,7 +384,7 @@ template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedI
}
if(id >= (self.*bounds)()) {
PyErr_SetNone(PyExc_IndexError);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u entries", id, (self.*bounds)());
throw py::error_already_set{};
}
@ -399,7 +399,7 @@ template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedI
return *std::move(out);
}
/** @todo drop std::string in favor of our own string caster */
template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedInt), Int(Trade::AbstractImporter::*indexForName)(Containers::StringView)> R checkOpenedBoundsResultString(Trade::AbstractImporter& self, const std::string& name) {
template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedInt), Int(Trade::AbstractImporter::*indexForName)(Containers::StringView), UnsignedInt(Trade::AbstractImporter::*bounds)() const> R checkOpenedBoundsResultString(Trade::AbstractImporter& self, const std::string& name) {
if(!self.isOpened()) {
PyErr_SetString(PyExc_AssertionError, "no file opened");
throw py::error_already_set{};
@ -407,7 +407,9 @@ template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedI
const Int id = (self.*indexForName)(name);
if(id == -1) {
PyErr_SetNone(PyExc_KeyError);
/** @todo may need extra attention when it's no longer a
null-terminated std::string */
PyErr_Format(PyExc_KeyError, "name %s not found among %u entries", name.data(), (self.*bounds)());
throw py::error_already_set{};
}
@ -429,12 +431,13 @@ template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedI
}
if(id >= (self.*bounds)()) {
PyErr_SetString(PyExc_IndexError, "ID out of range");
PyErr_Format(PyExc_IndexError, "index %u out of range for %u entries", id, (self.*bounds)());
throw py::error_already_set{};
}
if(level >= (self.*levelBounds)(id)) {
PyErr_SetString(PyExc_IndexError, "level out of range");
const UnsignedInt levelCount = (self.*levelBounds)(id);
if(level >= levelCount) {
PyErr_Format(PyExc_IndexError, "level %u out of range for %u entries", level, levelCount);
throw py::error_already_set{};
}
@ -449,7 +452,7 @@ template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedI
return *std::move(out);
}
/** @todo drop std::string in favor of our own string caster */
template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedInt, UnsignedInt), Int(Trade::AbstractImporter::*indexForName)(Containers::StringView), UnsignedInt(Trade::AbstractImporter::*levelBounds)(UnsignedInt)> R checkOpenedBoundsResultString(Trade::AbstractImporter& self, const std::string& name, UnsignedInt level) {
template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedInt, UnsignedInt), Int(Trade::AbstractImporter::*indexForName)(Containers::StringView), UnsignedInt(Trade::AbstractImporter::*bounds)() const, UnsignedInt(Trade::AbstractImporter::*levelBounds)(UnsignedInt)> R checkOpenedBoundsResultString(Trade::AbstractImporter& self, const std::string& name, UnsignedInt level) {
if(!self.isOpened()) {
PyErr_SetString(PyExc_AssertionError, "no file opened");
throw py::error_already_set{};
@ -457,12 +460,15 @@ template<class R, Containers::Optional<R>(Trade::AbstractImporter::*f)(UnsignedI
const Int id = (self.*indexForName)(name);
if(id == -1) {
PyErr_SetNone(PyExc_KeyError);
/** @todo may need extra attention when it's no longer a
null-terminated std::string */
PyErr_Format(PyExc_KeyError, "name %s not found among %u entries", name.data(), (self.*bounds)());
throw py::error_already_set{};
}
if(level >= (self.*levelBounds)(id)) {
PyErr_SetString(PyExc_IndexError, "level out of range");
const UnsignedInt levelCount = (self.*levelBounds)(id);
if(level >= levelCount) {
PyErr_Format(PyExc_IndexError, "level %u out of range for %u entries", level, levelCount);
throw py::error_already_set{};
}
@ -643,7 +649,7 @@ template<class T> Containers::PyArrayViewHolder<Containers::PyStridedArrayView<1
const std::size_t itemsize = vertexFormatSize(format);
const Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> formatStringGetitemSetitem = accessorsForVertexFormat(format);
if(!formatStringGetitemSetitem.first()) {
PyErr_SetString(PyExc_NotImplementedError, "access to this vertex format is not implemented yet, sorry");
PyErr_Format(PyExc_NotImplementedError, "access to %S is not implemented yet, sorry", py::cast(format).ptr());
throw py::error_already_set{};
}
return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<1, T>{data.template transposed<0, 1>()[0], formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, py::cast(mesh));
@ -840,7 +846,7 @@ template<class T> Containers::PyArrayViewHolder<Containers::PyStridedArrayView<1
const std::size_t itemsize = Trade::sceneFieldTypeSize(type);
const Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> formatStringGetitemSetitem = accessorsForSceneFieldType(type);
if(!formatStringGetitemSetitem.first()) {
PyErr_SetString(PyExc_NotImplementedError, "access to this scene field type is not implemented yet, sorry");
PyErr_Format(PyExc_NotImplementedError, "access to %S is not implemented yet, sorry", py::cast(type).ptr());
throw py::error_already_set{};
}
return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<1, T>{data.template transposed<0, 1>()[0], formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, py::cast(scene));
@ -981,16 +987,21 @@ void trade(py::module_& m) {
#endif
py::arg("morph_target_id"))
.def("attribute_name", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.attributeName(id);
if(id < self.attributeCount())
return self.attributeName(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Attribute name", py::arg("id"))
.def("attribute_id", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return *found;
PyErr_SetNone(PyExc_KeyError);
const UnsignedInt attributeCount = self.attributeCount(name, morphTargetId);
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes", id, attributeCount, py::cast(name).ptr());
else
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes in morph target %i", id, attributeCount, py::cast(name).ptr(), morphTargetId);
throw py::error_already_set{};
}, "Absolute ID of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
@ -998,16 +1009,21 @@ void trade(py::module_& m) {
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_id", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.attributeId(id);
if(id < self.attributeCount())
return self.attributeId(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Attribute ID in a set of attributes of the same name", py::arg("id"))
.def("attribute_format", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeFormat(*found);
PyErr_SetNone(PyExc_KeyError);
const UnsignedInt attributeCount = self.attributeCount(name, morphTargetId);
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes", id, attributeCount, py::cast(name).ptr());
else
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes in morph target %i", id, attributeCount, py::cast(name).ptr(), morphTargetId);
throw py::error_already_set{};
}, "Format of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
@ -1015,16 +1031,21 @@ void trade(py::module_& m) {
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_format", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.attributeFormat(id);
if(id < self.attributeCount())
return self.attributeFormat(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Attribute format", py::arg("id"))
.def("attribute_offset", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeOffset(*found);
PyErr_SetNone(PyExc_KeyError);
const UnsignedInt attributeCount = self.attributeCount(name, morphTargetId);
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes", id, attributeCount, py::cast(name).ptr());
else
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes in morph target %i", id, attributeCount, py::cast(name).ptr(), morphTargetId);
throw py::error_already_set{};
}, "Offset of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
@ -1032,16 +1053,21 @@ void trade(py::module_& m) {
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_offset", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.attributeOffset(id);
if(id < self.attributeCount())
return self.attributeOffset(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Attribute offset", py::arg("id"))
.def("attribute_stride", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeStride(*found);
PyErr_SetNone(PyExc_KeyError);
const UnsignedInt attributeCount = self.attributeCount(name, morphTargetId);
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes", id, attributeCount, py::cast(name).ptr());
else
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes in morph target %i", id, attributeCount, py::cast(name).ptr(), morphTargetId);
throw py::error_already_set{};
}, "Stride of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
@ -1049,16 +1075,21 @@ void trade(py::module_& m) {
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_stride", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.attributeStride(id);
if(id < self.attributeCount())
return self.attributeStride(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Attribute stride", py::arg("id"))
.def("attribute_array_size", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeArraySize(*found);
PyErr_SetNone(PyExc_KeyError);
const UnsignedInt attributeCount = self.attributeCount(name, morphTargetId);
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes", id, attributeCount, py::cast(name).ptr());
else
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes in morph target %i", id, attributeCount, py::cast(name).ptr(), morphTargetId);
throw py::error_already_set{};
}, "Array size of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
@ -1066,81 +1097,93 @@ void trade(py::module_& m) {
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_array_size", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.attributeArraySize(id);
if(id < self.attributeCount())
return self.attributeArraySize(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Attribute array size", py::arg("id"))
.def("attribute", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
/** @todo handle arrays (return a 2D view, and especially annotate
the return type properly in the docs) */
if(self.attributeArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
}
return meshAttributeView(self, *found, self.attribute(*found));
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId)) {
/** @todo handle arrays (return a 2D view, and especially
annotate the return type properly in the docs) */
if(self.attributeArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
}
return meshAttributeView(self, *found, self.attribute(*found));
}
const UnsignedInt attributeCount = self.attributeCount(name, morphTargetId);
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes", id, attributeCount, py::cast(name).ptr());
else
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes in morph target %i", id, attributeCount, py::cast(name).ptr(), morphTargetId);
throw py::error_already_set{};
}, "Data for given named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
/** @todo handle arrays (return a 2D view, and especially annotate
the return type properly in the docs) */
if(self.attributeArraySize(id) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
if(id < self.attributeCount()) {
/** @todo handle arrays (return a 2D view, and especially
annotate the return type properly in the docs) */
if(self.attributeArraySize(id) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
}
return meshAttributeView(self, id, self.attribute(id));
}
return meshAttributeView(self, id, self.attribute(id));
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Data for given attribute", py::arg("id"))
.def("mutable_attribute", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(!(self.vertexDataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AttributeError, "mesh vertex data is not mutable");
throw py::error_already_set{};
}
/** @todo handle arrays (return a 2D view, and especially annotate
the return type properly in the docs) */
if(self.attributeArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId)) {
/** @todo handle arrays (return a 2D view, and especially
annotate the return type properly in the docs) */
if(self.attributeArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
}
return meshAttributeView(self, *found, self.mutableAttribute(*found));
}
return meshAttributeView(self, *found, self.mutableAttribute(*found));
const UnsignedInt attributeCount = self.attributeCount(name, morphTargetId);
if(morphTargetId == -1)
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes", id, attributeCount, py::cast(name).ptr());
else
PyErr_Format(PyExc_KeyError, "index %u out of range for %u %S attributes in morph target %i", id, attributeCount, py::cast(name).ptr(), morphTargetId);
throw py::error_already_set{};
}, "Mutable data for given named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("mutable_attribute", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
if(!(self.vertexDataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AttributeError, "mesh vertex data is not mutable");
throw py::error_already_set{};
}
/** @todo handle arrays (return a 2D view, and especially annotate
the return type properly in the docs) */
if(self.attributeArraySize(id) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
if(id < self.attributeCount()) {
/** @todo handle arrays (return a 2D view, and especially
annotate the return type properly in the docs) */
if(self.attributeArraySize(id) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array attributes not implemented yet, sorry");
throw py::error_already_set{};
}
return meshAttributeView(self, id, self.mutableAttribute(id));
}
return meshAttributeView(self, id, self.mutableAttribute(id));
PyErr_Format(PyExc_IndexError, "index %u out of range for %u attributes", id, self.attributeCount());
throw py::error_already_set{};
}, "Mutable data for given attribute", py::arg("id"))
.def_property_readonly("owner", [](Trade::MeshData& self) {
@ -1311,200 +1354,194 @@ void trade(py::module_& m) {
overload gets picked even if an enum is passed from Python, causing
massive suffering */
.def("field_name", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.fieldName(id);
if(id < self.fieldCount())
return self.fieldName(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}, "Field name", py::arg("id"))
.def("field_flags", [](Trade::SceneData& self, Trade::SceneField fieldName) {
const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName);
if(!foundField) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
return Trade::SceneFieldFlag(Containers::enumCastUnderlyingType(self.fieldFlags(*foundField)));
if(const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName))
return Trade::SceneFieldFlag(Containers::enumCastUnderlyingType(self.fieldFlags(*foundField)));
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(fieldName).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Flags of a named field", py::arg("name"))
.def("field_flags", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return Trade::SceneFieldFlag(Containers::enumCastUnderlyingType(self.fieldFlags(id)));
if(id < self.fieldCount())
return Trade::SceneFieldFlag(Containers::enumCastUnderlyingType(self.fieldFlags(id)));
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}, "Field flags", py::arg("id"))
.def("field_type", [](Trade::SceneData& self, Trade::SceneField fieldName) {
const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName);
if(!foundField) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
return self.fieldType(*foundField);
if(const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName))
return self.fieldType(*foundField);
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(fieldName).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Type of a named field", py::arg("name"))
.def("field_type", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.fieldType(id);
if(id < self.fieldCount())
return self.fieldType(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}, "Field type", py::arg("id"))
.def("field_size", [](Trade::SceneData& self, Trade::SceneField fieldName) {
const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName);
if(!foundField) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
return self.fieldSize(*foundField);
if(const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName))
return self.fieldSize(*foundField);
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(fieldName).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Number of entries in a named field", py::arg("name"))
.def("field_size", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.fieldSize(id);
if(id < self.fieldCount())
return self.fieldSize(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}, "Number of entries in a field", py::arg("id"))
.def("field_array_size", [](Trade::SceneData& self, Trade::SceneField fieldName) {
const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName);
if(!foundField) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName))
return self.fieldArraySize(*foundField);
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(fieldName).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Array size of a named field", py::arg("name"))
.def("field_array_size", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self.fieldArraySize(id);
if(id < self.fieldCount())
return self.fieldArraySize(id);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}, "Field array size", py::arg("id"))
.def("field_id", [](Trade::SceneData& self, Trade::SceneField name) {
if(const Containers::Optional<UnsignedInt> found = self.findFieldId(name))
return *found;
PyErr_SetNone(PyExc_KeyError);
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(name).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Absolute ID of a named field", py::arg("name"))
.def("has_field", &Trade::SceneData::hasField, "Whether the scene has given field")
.def("field_object_offset", [](Trade::SceneData& self, Trade::SceneField fieldName, UnsignedLong object, std::size_t offset) {
const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName);
if(!foundField) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(object >= self.mappingBound()) {
PyErr_SetString(PyExc_IndexError, "object out of range");
throw py::error_already_set{};
}
if(offset >= self.fieldSize(*foundField)) {
PyErr_SetString(PyExc_IndexError, "offset out of range");
throw py::error_already_set{};
}
const Containers::Optional<std::size_t> found = self.findFieldObjectOffset(*foundField, object, offset);
if(!found) {
PyErr_SetNone(PyExc_LookupError);
if(const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName)) {
if(object >= self.mappingBound()) {
PyErr_Format(PyExc_IndexError, "index %llu out of range for %llu objects", object, self.mappingBound());
throw py::error_already_set{};
}
if(offset > self.fieldSize(*foundField)) {
PyErr_Format(PyExc_IndexError, "offset %zu out of range for a field of size %zu", offset, self.fieldSize(*foundField));
throw py::error_already_set{};
}
if(const Containers::Optional<std::size_t> found = self.findFieldObjectOffset(*foundField, object, offset))
return *found;
PyErr_Format(PyExc_LookupError, "object %llu not found in field %S starting at offset %zu", object, py::cast(fieldName).ptr(), offset);
throw py::error_already_set{};
}
return *found;
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(fieldName).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Offset of an object in given name field", py::arg("field_name"), py::arg("object"), py::arg("offset") = 0)
.def("field_object_offset", [](Trade::SceneData& self, UnsignedInt fieldId, UnsignedLong object, std::size_t offset) {
if(fieldId >= self.fieldCount()) {
PyErr_SetString(PyExc_IndexError, "field out of range");
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", fieldId, self.fieldCount());
throw py::error_already_set{};
}
if(object >= self.mappingBound()) {
PyErr_SetString(PyExc_IndexError, "object out of range");
throw py::error_already_set{};
}
if(offset >= self.fieldSize(fieldId)) {
PyErr_SetString(PyExc_IndexError, "offset out of range");
PyErr_Format(PyExc_IndexError, "index %llu out of range for %llu objects", object, self.mappingBound());
throw py::error_already_set{};
}
const Containers::Optional<std::size_t> found = self.findFieldObjectOffset(fieldId, object, offset);
if(!found) {
PyErr_SetNone(PyExc_LookupError);
if(offset > self.fieldSize(fieldId)) {
PyErr_Format(PyExc_IndexError, "offset %zu out of range for a field of size %zu", offset, self.fieldSize(fieldId));
throw py::error_already_set{};
}
return *found;
if(const Containers::Optional<std::size_t> found = self.findFieldObjectOffset(fieldId, object, offset))
return *found;
PyErr_Format(PyExc_LookupError, "object %llu not found in field %S starting at offset %zu", object, py::cast(self.fieldName(fieldId)).ptr(), offset);
throw py::error_already_set{};
}, "Offset of an object in given field", py::arg("field_id"), py::arg("object"), py::arg("offset") = 0)
.def("has_field_object", [](Trade::SceneData& self, Trade::SceneField fieldName, UnsignedLong object) {
const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName);
if(!foundField) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(object >= self.mappingBound()) {
PyErr_SetString(PyExc_IndexError, "object out of range");
throw py::error_already_set{};
if(const Containers::Optional<UnsignedInt> foundField = self.findFieldId(fieldName)) {
if(object >= self.mappingBound()) {
PyErr_Format(PyExc_IndexError, "index %llu out of range for %llu objects", object, self.mappingBound());
throw py::error_already_set{};
}
return self.hasFieldObject(*foundField, object);
}
return self.hasFieldObject(*foundField, object);
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(fieldName).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Whether a scene field has given object", py::arg("field_name"), py::arg("object"))
.def("has_field_object", [](Trade::SceneData& self, UnsignedInt fieldId, UnsignedLong object) {
if(fieldId >= self.fieldCount()) {
PyErr_SetString(PyExc_IndexError, "field out of range");
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", fieldId, self.fieldCount());
throw py::error_already_set{};
}
if(object >= self.mappingBound()) {
PyErr_SetString(PyExc_IndexError, "object out of range");
PyErr_Format(PyExc_IndexError, "index %llu out of range for %llu objects", object, self.mappingBound());
throw py::error_already_set{};
}
return self.hasFieldObject(fieldId, object);
}, "Whether a scene field has given object", py::arg("field_id"), py::arg("object"))
.def("mapping", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
if(const Containers::Optional<UnsignedInt> found = self.findFieldId(name)) {
return sceneMappingView(self, self.mapping(*found));
}
return sceneMappingView(self, self.mapping(*found));
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(name).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Object mapping data for given named field", py::arg("name"))
.def("mapping", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return sceneMappingView(self, self.mapping(id));
if(id < self.fieldCount())
return sceneMappingView(self, self.mapping(id));
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}, "Object mapping data for given field", py::arg("name"))
.def("mutable_mapping", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AttributeError, "scene data is not mutable");
throw py::error_already_set{};
}
return sceneMappingView(self, self.mutableMapping(*found));
if(const Containers::Optional<UnsignedInt> found = self.findFieldId(name))
return sceneMappingView(self, self.mutableMapping(*found));
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(name).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Mutable object mapping data for given named field", py::arg("name"))
.def("mutable_mapping", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AttributeError, "scene data is not mutable");
throw py::error_already_set{};
}
return sceneMappingView(self, self.mutableMapping(id));
if(id < self.fieldCount())
return sceneMappingView(self, self.mutableMapping(id));
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}, "Mutable object mapping data for given field", py::arg("name"))
.def("field", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
/** @todo handle arrays (return a 2D (bit) view) */
if(self.fieldArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry");
throw py::error_already_set{};
}
/** @todo annotate the return type properly in the docs */
if(self.fieldType(*found) == Trade::SceneFieldType::Bit)
return pyCastButNotShitty(Containers::pyArrayViewHolder(self.fieldBits(*found), py::cast(self)));
return pyCastButNotShitty(sceneFieldView(self, *found, self.field(*found)));
if(const Containers::Optional<UnsignedInt> found = self.findFieldId(name)) {
/** @todo handle arrays (return a 2D (bit) view) */
if(self.fieldArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry");
throw py::error_already_set{};
}
/** @todo annotate the return type properly in the docs */
if(self.fieldType(*found) == Trade::SceneFieldType::Bit)
return pyCastButNotShitty(Containers::pyArrayViewHolder(self.fieldBits(*found), py::cast(self)));
return pyCastButNotShitty(sceneFieldView(self, *found, self.field(*found)));
}
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(name).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Data for given named field", py::arg("name"))
.def("field", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}
/** @todo handle arrays (return a 2D (bit) view) */
@ -1518,28 +1555,28 @@ void trade(py::module_& m) {
return pyCastButNotShitty(sceneFieldView(self, id, self.field(id)));
}, "Data for given field", py::arg("name"))
.def("mutable_field", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AttributeError, "scene data is not mutable");
throw py::error_already_set{};
}
/** @todo handle arrays (return a 2D (bit) view) */
if(self.fieldArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry");
throw py::error_already_set{};
if(const Containers::Optional<UnsignedInt> found = self.findFieldId(name)) {
/** @todo handle arrays (return a 2D (bit) view) */
if(self.fieldArraySize(*found) != 0) {
PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry");
throw py::error_already_set{};
}
/** @todo annotate the return type properly in the docs */
if(self.fieldType(*found) == Trade::SceneFieldType::Bit)
return pyCastButNotShitty(Containers::pyArrayViewHolder(self.mutableFieldBits(*found), py::cast(self)));
return pyCastButNotShitty(sceneFieldView(self, *found, self.mutableField(*found)));
}
/** @todo annotate the return type properly in the docs */
if(self.fieldType(*found) == Trade::SceneFieldType::Bit)
return pyCastButNotShitty(Containers::pyArrayViewHolder(self.mutableFieldBits(*found), py::cast(self)));
return pyCastButNotShitty(sceneFieldView(self, *found, self.mutableField(*found)));
PyErr_Format(PyExc_KeyError, "%S not found among %u fields", py::cast(name).ptr(), self.fieldCount());
throw py::error_already_set{};
}, "Mutable data for given named field", py::arg("name"))
.def("mutable_field", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
PyErr_Format(PyExc_IndexError, "index %u out of range for %u fields", id, self.fieldCount());
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
@ -1656,7 +1693,7 @@ void trade(py::module_& m) {
.def("scene_name", checkOpenedBoundsReturnsString<UnsignedInt, &Trade::AbstractImporter::sceneName, &Trade::AbstractImporter::sceneCount>, "Scene name", py::arg("id"))
.def("object_name", checkOpenedBoundsReturnsString<UnsignedLong, &Trade::AbstractImporter::objectName, &Trade::AbstractImporter::objectCount>, "Scene name", py::arg("id"))
.def("scene", checkOpenedBoundsResult<Trade::SceneData, &Trade::AbstractImporter::scene, &Trade::AbstractImporter::sceneCount>, "Scene", py::arg("id"))
.def("scene", checkOpenedBoundsResultString<Trade::SceneData, &Trade::AbstractImporter::scene, &Trade::AbstractImporter::sceneForName>, "Scene for given name", py::arg("name"))
.def("scene", checkOpenedBoundsResultString<Trade::SceneData, &Trade::AbstractImporter::scene, &Trade::AbstractImporter::sceneForName, &Trade::AbstractImporter::sceneCount>, "Scene for given name", py::arg("name"))
/** @todo drop std::string in favor of our own string caster */
.def("scene_field_for_name", [](Trade::AbstractImporter& self, const std::string& name) -> Containers::Optional<Trade::SceneField> {
const Trade::SceneField field = self.sceneFieldForName(name);
@ -1677,7 +1714,7 @@ void trade(py::module_& m) {
.def("mesh_for_name", checkOpenedString<Int, &Trade::AbstractImporter::meshForName>, "Mesh ID for given name", py::arg("name"))
.def("mesh_name", checkOpenedBoundsReturnsString<UnsignedInt, &Trade::AbstractImporter::meshName, &Trade::AbstractImporter::meshCount>, "Mesh name", py::arg("id"))
.def("mesh", checkOpenedBoundsResult<Trade::MeshData, &Trade::AbstractImporter::mesh, &Trade::AbstractImporter::meshCount, &Trade::AbstractImporter::meshLevelCount>, "Mesh", py::arg("id"), py::arg("level") = 0)
.def("mesh", checkOpenedBoundsResultString<Trade::MeshData, &Trade::AbstractImporter::mesh, &Trade::AbstractImporter::meshForName, &Trade::AbstractImporter::meshLevelCount>, "Mesh for given name", py::arg("name"), py::arg("level") = 0)
.def("mesh", checkOpenedBoundsResultString<Trade::MeshData, &Trade::AbstractImporter::mesh, &Trade::AbstractImporter::meshForName, &Trade::AbstractImporter::meshCount, &Trade::AbstractImporter::meshLevelCount>, "Mesh for given name", py::arg("name"), py::arg("level") = 0)
/** @todo drop std::string in favor of our own string caster */
.def("mesh_attribute_for_name", [](Trade::AbstractImporter& self, const std::string& name) -> Containers::Optional<Trade::MeshAttribute> {
const Trade::MeshAttribute attribute = self.meshAttributeForName(name);
@ -1696,7 +1733,7 @@ void trade(py::module_& m) {
.def("texture_for_name", checkOpenedString<Int, &Trade::AbstractImporter::textureForName>, "Texture ID for given name", py::arg("name"))
.def("texture_name", checkOpenedBoundsReturnsString<UnsignedInt, &Trade::AbstractImporter::textureName, &Trade::AbstractImporter::textureCount>, "Texture name", py::arg("id"))
.def("texture", checkOpenedBoundsResult<Trade::TextureData, &Trade::AbstractImporter::texture, &Trade::AbstractImporter::textureCount>, "Texture", py::arg("id"))
.def("texture", checkOpenedBoundsResultString<Trade::TextureData, &Trade::AbstractImporter::texture, &Trade::AbstractImporter::textureForName>, "Texture for given name", py::arg("name"))
.def("texture", checkOpenedBoundsResultString<Trade::TextureData, &Trade::AbstractImporter::texture, &Trade::AbstractImporter::textureForName, &Trade::AbstractImporter::textureCount>, "Texture for given name", py::arg("name"))
.def_property_readonly("image1d_count", checkOpened<UnsignedInt, &Trade::AbstractImporter::image1DCount>, "One-dimensional image count")
.def_property_readonly("image2d_count", checkOpened<UnsignedInt, &Trade::AbstractImporter::image2DCount>, "Two-dimensional image count")
@ -1711,11 +1748,11 @@ void trade(py::module_& m) {
.def("image2d_name", checkOpenedBoundsReturnsString<UnsignedInt, &Trade::AbstractImporter::image2DName, &Trade::AbstractImporter::image2DCount>, "Two-dimensional image name", py::arg("id"))
.def("image3d_name", checkOpenedBoundsReturnsString<UnsignedInt, &Trade::AbstractImporter::image3DName, &Trade::AbstractImporter::image3DCount>, "Three-dimensional image name", py::arg("id"))
.def("image1d", checkOpenedBoundsResult<Trade::ImageData1D, &Trade::AbstractImporter::image1D, &Trade::AbstractImporter::image1DCount, &Trade::AbstractImporter::image1DLevelCount>, "One-dimensional image", py::arg("id"), py::arg("level") = 0)
.def("image1d", checkOpenedBoundsResultString<Trade::ImageData1D, &Trade::AbstractImporter::image1D, &Trade::AbstractImporter::image1DForName, &Trade::AbstractImporter::image1DLevelCount>, "One-dimensional image for given name", py::arg("name"), py::arg("level") = 0)
.def("image1d", checkOpenedBoundsResultString<Trade::ImageData1D, &Trade::AbstractImporter::image1D, &Trade::AbstractImporter::image1DForName, &Trade::AbstractImporter::image1DCount, &Trade::AbstractImporter::image1DLevelCount>, "One-dimensional image for given name", py::arg("name"), py::arg("level") = 0)
.def("image2d", checkOpenedBoundsResult<Trade::ImageData2D, &Trade::AbstractImporter::image2D, &Trade::AbstractImporter::image2DCount, &Trade::AbstractImporter::image2DLevelCount>, "Two-dimensional image", py::arg("id"), py::arg("level") = 0)
.def("image2d", checkOpenedBoundsResultString<Trade::ImageData2D, &Trade::AbstractImporter::image2D, &Trade::AbstractImporter::image2DForName, &Trade::AbstractImporter::image2DLevelCount>, "Two-dimensional image for given name", py::arg("name"), py::arg("level") = 0)
.def("image2d", checkOpenedBoundsResultString<Trade::ImageData2D, &Trade::AbstractImporter::image2D, &Trade::AbstractImporter::image2DForName, &Trade::AbstractImporter::image2DCount, &Trade::AbstractImporter::image2DLevelCount>, "Two-dimensional image for given name", py::arg("name"), py::arg("level") = 0)
.def("image3d", checkOpenedBoundsResult<Trade::ImageData3D, &Trade::AbstractImporter::image3D, &Trade::AbstractImporter::image3DCount, &Trade::AbstractImporter::image3DLevelCount>, "Three-dimensional image", py::arg("id"), py::arg("level") = 0)
.def("image3d", checkOpenedBoundsResultString<Trade::ImageData3D, &Trade::AbstractImporter::image3D, &Trade::AbstractImporter::image3DForName, &Trade::AbstractImporter::image3DLevelCount>, "Threee-dimensional image for given name", py::arg("name"), py::arg("level") = 0);
.def("image3d", checkOpenedBoundsResultString<Trade::ImageData3D, &Trade::AbstractImporter::image3D, &Trade::AbstractImporter::image3DForName, &Trade::AbstractImporter::image3DCount, &Trade::AbstractImporter::image3DLevelCount>, "Threee-dimensional image for given name", py::arg("name"), py::arg("level") = 0);
py::class_<PluginManager::Manager<Trade::AbstractImporter>, PluginManager::AbstractManager> importerManager{m, "ImporterManager", "Manager for importer plugins"};
corrade::manager(importerManager);

Loading…
Cancel
Save