From d1a7a7c6c86246b8618e14ebcea091eb2c0c027e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 18 May 2023 21:31:52 +0200 Subject: [PATCH] python: convert more Trade assertions to Python exceptions. --- doc/python/magnum.trade.rst | 32 ++++++++ package/ci/appveyor-desktop-gles.bat | 1 + package/ci/appveyor-desktop.bat | 1 + package/ci/unix-desktop-gles.sh | 1 + package/ci/unix-desktop.sh | 1 + src/python/magnum/test/test_trade.py | 108 +++++++++++++++++++++++++++ src/python/magnum/trade.cpp | 79 ++++++++++++++++++-- 7 files changed, 217 insertions(+), 6 deletions(-) diff --git a/doc/python/magnum.trade.rst b/doc/python/magnum.trade.rst index b3904e8..be0e93a 100644 --- a/doc/python/magnum.trade.rst +++ b/doc/python/magnum.trade.rst @@ -374,6 +374,8 @@ behavior. .. py:function:: magnum.trade.AbstractImporter.open_data + :raise AssertionError: If :ref:`trade.ImporterFeatures.OPEN_DATA` is not + supported :raise RuntimeError: If file opening fails .. py:function:: magnum.trade.AbstractImporter.open_file @@ -584,12 +586,21 @@ behavior. .. py:function:: magnum.trade.AbstractSceneConverter.convert + :raise AssertionError: If :ref:`trade.SceneConverterFeatures.CONVERT_MESH` + is not supported :raise RuntimeError: If conversion fails .. py:function:: magnum.trade.AbstractSceneConverter.convert_in_place + :raise AssertionError: If :ref:`trade.SceneConverterFeatures.CONVERT_MESH_IN_PLACE` + is not supported :raise RuntimeError: If conversion fails .. py:function:: magnum.trade.AbstractSceneConverter.convert_to_file + :raise AssertionError: If neither + :ref:`SceneConverterFeatures.CONVERT_MESH_TO_FILE` nor the + combination of :ref:`SceneConverterFeatures.CONVERT_MULTIPLE_TO_FILE` + and :ref:`SceneConverterFeatures.ADD_MESHES` + is supported :raise RuntimeError: If conversion fails For compatibility with :ref:`os.path`, on Windows this function converts @@ -598,6 +609,9 @@ forward slashes as directory separators on all platforms. .. py:function:: magnum.trade.AbstractSceneConverter.begin_file + :raise AssertionError: If neither + :ref:`SceneConverterFeatures.CONVERT_MULTIPLE_TO_FILE` nor + :ref:`SceneConverterFeatures.CONVERT_MESH_TO_FILE` is supported :raise RuntimeError: If beginning the conversion fails For compatibility with :ref:`os.path`, on Windows this function converts @@ -616,19 +630,36 @@ :raise AssertionError: If no conversion is in progress .. py:function:: magnum.trade.AbstractSceneConverter.add + :raise AssertionError: If corresponding + :ref:`SceneConverterFeatures.ADD_* ` is not + supported, or alternatively at least one of + :ref:`SceneConverterFeatures.CONVERT_MESH`, + :ref:`SceneConverterFeatures.CONVERT_MESH_TO_DATA` or + :ref:`SceneConverterFeatures.CONVERT_MESH_TO_FILE` is not supported + for meshes :raise AssertionError: If no conversion is in progress :raise RuntimeError: If adding the data fails .. py:function:: magnum.trade.AbstractSceneConverter.set_mesh_attribute_name + :raise AssertionError: If none of + :ref:`SceneConverterFeatures.ADD_MESHES`, + :ref:`SceneConverterFeatures.CONVERT_MESH`, + :ref:`SceneConverterFeatures.CONVERT_MESH_IN_PLACE`, + :ref:`SceneConverterFeatures.CONVERT_MESH_TO_DATA` or + :ref:`SceneConverterFeatures.CONVERT_MESH_TO_FILE` is supported :raise AssertionError: If no conversion is in progress :raise AssertionError: If :p:`attribute` is not custom .. py:function:: magnum.trade.AbstractSceneConverter.set_default_scene + :raise AssertionError: If :ref:`trade.SceneConverterFeatures.ADD_SCENES` + is not supported :raise AssertionError: If no conversion is in progress :raise AssertionError: If :p:`id` is negative or not less than :ref:`scene_count` .. py:function:: magnum.trade.AbstractSceneConverter.set_scene_field_name + :raise AssertionError: If :ref:`trade.SceneConverterFeatures.ADD_SCENES` + is not supported :raise AssertionError: If no conversion is in progress :raise AssertionError: If :p:`field` is not custom @@ -637,5 +668,6 @@ :raise RuntimeError: If adding the importer contents fails .. py:function:: magnum.trade.AbstractSceneConverter.add_supported_importer_contents + :raise AssertionError: If :p:`importer` is not opened :raise AssertionError: If no conversion is in progress :raise RuntimeError: If adding the importer contents fails diff --git a/package/ci/appveyor-desktop-gles.bat b/package/ci/appveyor-desktop-gles.bat index 2cf3070..1dbe498 100644 --- a/package/ci/appveyor-desktop-gles.bat +++ b/package/ci/appveyor-desktop-gles.bat @@ -63,6 +63,7 @@ cmake .. ^ -DMAGNUM_WITH_WINDOWLESSWGLAPPLICATION=ON ^ -DMAGNUM_WITH_ANYIMAGEIMPORTER=ON ^ -DMAGNUM_WITH_ANYSCENECONVERTER=ON ^ + -DMAGNUM_WITH_ANYSCENEIMPORTER=ON ^ -DMAGNUM_WITH_TGAIMPORTER=ON ^ -G Ninja || exit /b cmake --build . || exit /b diff --git a/package/ci/appveyor-desktop.bat b/package/ci/appveyor-desktop.bat index 77c8560..116ee84 100644 --- a/package/ci/appveyor-desktop.bat +++ b/package/ci/appveyor-desktop.bat @@ -73,6 +73,7 @@ cmake .. ^ -DMAGNUM_WITH_WINDOWLESSWGLAPPLICATION=ON ^ -DMAGNUM_WITH_ANYIMAGEIMPORTER=ON ^ -DMAGNUM_WITH_ANYSCENECONVERTER=ON ^ + -DMAGNUM_WITH_ANYSCENEIMPORTER=ON ^ -DMAGNUM_WITH_TGAIMPORTER=ON ^ %COMPILER_EXTRA% -G Ninja || exit /b cmake --build . || exit /b diff --git a/package/ci/unix-desktop-gles.sh b/package/ci/unix-desktop-gles.sh index f2687b0..e3c5219 100755 --- a/package/ci/unix-desktop-gles.sh +++ b/package/ci/unix-desktop-gles.sh @@ -46,6 +46,7 @@ cmake .. \ -DMAGNUM_WITH_WINDOWLESSEGLAPPLICATION=ON \ -DMAGNUM_WITH_ANYIMAGEIMPORTER=ON \ -DMAGNUM_WITH_ANYSCENECONVERTER=ON \ + -DMAGNUM_WITH_ANYSCENEIMPORTER=ON \ -DMAGNUM_WITH_TGAIMPORTER=ON \ -G Ninja ninja install diff --git a/package/ci/unix-desktop.sh b/package/ci/unix-desktop.sh index 3998e9d..7d500b8 100755 --- a/package/ci/unix-desktop.sh +++ b/package/ci/unix-desktop.sh @@ -47,6 +47,7 @@ cmake .. \ -DMAGNUM_WITH_WINDOWLESS${PLATFORM_GL_API}APPLICATION=ON \ -DMAGNUM_WITH_ANYIMAGEIMPORTER=ON \ -DMAGNUM_WITH_ANYSCENECONVERTER=ON \ + -DMAGNUM_WITH_ANYSCENEIMPORTER=ON \ -DMAGNUM_WITH_TGAIMPORTER=ON \ -G Ninja diff --git a/src/python/magnum/test/test_trade.py b/src/python/magnum/test/test_trade.py index 34421b4..f472146 100644 --- a/src/python/magnum/test/test_trade.py +++ b/src/python/magnum/test/test_trade.py @@ -1424,6 +1424,12 @@ class Importer(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "opening data failed"): importer.open_data(b'') + def test_open_data_not_supported(self): + importer = trade.ImporterManager().load_and_instantiate('AnySceneImporter') + + with self.assertRaisesRegex(AssertionError, "feature not supported"): + importer.open_data(b'') + def test_scene(self): # importer refcounting tested in image2d importer = trade.ImporterManager().load_and_instantiate('GltfImporter') @@ -1719,6 +1725,12 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "conversion failed"): converted_mesh = converter.convert(mesh) + def test_mesh_not_supported(self): + converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter') + + with self.assertRaisesRegex(AssertionError, "mesh conversion not supported"): + converter.convert(primitives.cube_solid()) + def test_mesh_in_place(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) @@ -1747,6 +1759,12 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "conversion failed"): converter.convert_in_place(mesh) + def test_mesh_in_place_not_supported(self): + converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter') + + with self.assertRaisesRegex(AssertionError, "mesh conversion not supported"): + converter.convert_in_place(primitives.cube_solid()) + def test_mesh_to_file(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) @@ -1769,6 +1787,17 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "conversion failed"): converter.convert_to_file(mesh, os.path.join(tmp, "mesh.obj")) + def test_mesh_to_file_not_supported(self): + converter_manager = trade.SceneConverterManager() + if 'MeshOptimizerSceneConverter' not in converter_manager.plugin_list: + self.skipTest("MeshOptimizerSceneConverter plugin not available") + + converter = converter_manager.load_and_instantiate('MeshOptimizerSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + with self.assertRaisesRegex(AssertionError, "mesh conversion not supported"): + converter.convert_to_file(primitives.cube_solid(), os.path.join(tmp, "mesh.foo")) + def test_batch_file(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) @@ -1812,6 +1841,17 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "ending the conversion failed"): converter.end_file() + def test_batch_file_not_supported(self): + converter_manager = trade.SceneConverterManager() + if 'MeshOptimizerSceneConverter' not in converter_manager.plugin_list: + self.skipTest("MeshOptimizerSceneConverter plugin not available") + + converter = converter_manager.load_and_instantiate('MeshOptimizerSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + with self.assertRaisesRegex(AssertionError, "feature not supported"): + converter.begin_file(os.path.join(tmp, "mesh.foo")) + def test_batch_add_mesh_failed(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) @@ -1824,6 +1864,11 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "adding the mesh failed"): converter.add(mesh) + def test_batch_add_mesh_not_supported(self): + # TODO implement once there's a converter that doesn't support meshes + # or has only in-place conversion + pass + def test_batch_set_mesh_attribute_name(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) @@ -1851,6 +1896,10 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(AssertionError, "not a custom attribute"): converter.set_mesh_attribute_name(trade.MeshAttribute.POSITION, 'foo') + def test_batch_set_mesh_attribute_name_not_supported(self): + # TODO implement once there's a converter that doesn't support meshes + pass + def test_batch_add_scene(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) @@ -1892,6 +1941,25 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "adding the scene failed"): converter.add(scene) + def test_batch_add_scene_not_supported(self): + # Static builds with non-static plugins cause assertions with non-owned + # array deleters used by PrimitiveImporter, skip in that case + if magnum.BUILD_STATIC: + self.skipTest("dynamic PrimitiveImporter doesn't work with a static build") + + importer = trade.ImporterManager().load_and_instantiate('PrimitiveImporter') + importer.open_data(containers.ArrayView()) + + scene = importer.scene(0) + + converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + converter.begin_file(os.path.join(tmp, "scene.ply")) + + with self.assertRaisesRegex(AssertionError, "scene conversion not supported"): + converter.add(scene) + def test_batch_set_default_scene(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) @@ -1919,6 +1987,15 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(AssertionError, "index 1 out of range for 0 scenes"): converter.set_default_scene(1) + def test_batch_set_default_scene_not_supported(self): + converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + converter.begin_file(os.path.join(tmp, "scene.ply")) + + with self.assertRaisesRegex(AssertionError, "feature not supported"): + converter.set_default_scene(0) + def test_batch_set_scene_field_name(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) @@ -1954,6 +2031,15 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(AssertionError, "not a custom field"): converter.set_scene_field_name(trade.SceneField.SCALING, 'foo') + def test_batch_set_scene_field_name_not_supported(self): + converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + converter.begin_file(os.path.join(tmp, "scene.ply")) + + with self.assertRaisesRegex(AssertionError, "feature not supported"): + converter.set_scene_field_name(trade.SceneField.CUSTOM(1), 'foo') + def test_batch_add_importer_contents(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'two-meshes.gltf')) @@ -1983,6 +2069,17 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "adding importer contents failed"): converter.add_importer_contents(importer) + def test_batch_add_importer_contents_not_opened(self): + importer = trade.ImporterManager().load_and_instantiate('AnySceneImporter') + + converter = trade.SceneConverterManager().load_and_instantiate('GltfSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + converter.begin_file(os.path.join(tmp, "file.gltf")) + + with self.assertRaisesRegex(AssertionError, "the importer is not opened"): + converter.add_importer_contents(importer) + def test_batch_add_supported_importer_contents(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) @@ -2016,6 +2113,17 @@ class SceneConverter(unittest.TestCase): with self.assertRaisesRegex(RuntimeError, "adding importer contents failed"): converter.add_supported_importer_contents(importer) + def test_batch_add_supported_importer_contents_not_opened(self): + importer = trade.ImporterManager().load_and_instantiate('AnySceneImporter') + + converter = trade.SceneConverterManager().load_and_instantiate('GltfSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + converter.begin_file(os.path.join(tmp, "file.gltf")) + + with self.assertRaisesRegex(AssertionError, "the importer is not opened"): + converter.add_supported_importer_contents(importer) + def test_batch_no_conversion_in_progress(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) diff --git a/src/python/magnum/trade.cpp b/src/python/magnum/trade.cpp index 0238f8a..96794fc 100644 --- a/src/python/magnum/trade.cpp +++ b/src/python/magnum/trade.cpp @@ -1532,6 +1532,11 @@ void trade(py::module_& m) { }, "Importer flags") .def_property_readonly("is_opened", &Trade::AbstractImporter::isOpened, "Whether any file is opened") .def("open_data", [](Trade::AbstractImporter& self, Containers::ArrayView data) { + if(!(self.features() >= Trade::ImporterFeature::OpenData)) { + PyErr_SetString(PyExc_AssertionError, "feature not supported"); + throw py::error_already_set{}; + } + /** @todo log redirection -- but we'd need assertions to not be part of that so when it dies, the user can still see why */ if(self.openData(data)) return; @@ -1759,6 +1764,11 @@ void trade(py::module_& m) { self.setFlags(flags); }, "Converter flags") .def("convert", [](Trade::AbstractSceneConverter& self, const Trade::MeshData& mesh) { + if(!(self.features() >= Trade::SceneConverterFeature::ConvertMesh)) { + PyErr_SetString(PyExc_AssertionError, "mesh conversion not supported"); + throw py::error_already_set{}; + } + /** @todo log redirection -- but we'd need assertions to not be part of that so when it dies, the user can still see why */ Containers::Optional out = self.convert(mesh); @@ -1769,6 +1779,11 @@ void trade(py::module_& m) { return out; }, "Convert a mesh", py::arg("mesh")) .def("convert_in_place", [](Trade::AbstractSceneConverter& self, Trade::MeshData& mesh) { + if(!(self.features() >= Trade::SceneConverterFeature::ConvertMeshInPlace)) { + PyErr_SetString(PyExc_AssertionError, "mesh conversion not supported"); + throw py::error_already_set{}; + } + /** @todo log redirection -- but we'd need assertions to not be part of that so when it dies, the user can still see why */ if(!self.convertInPlace(mesh)) { @@ -1779,6 +1794,12 @@ void trade(py::module_& m) { /** @todo conversion to data */ /** @todo drop std::string in favor of our own string caster */ .def("convert_to_file", [](Trade::AbstractSceneConverter& self, const Trade::MeshData& mesh, const std::string& filename) { + if(!(self.features() >= (Trade::SceneConverterFeature::ConvertMeshToFile)) && + !(self.features() >= (Trade::SceneConverterFeature::ConvertMultipleToFile|Trade::SceneConverterFeature::AddMeshes))) { + PyErr_SetString(PyExc_AssertionError, "mesh conversion not supported"); + throw py::error_already_set{}; + } + /** @todo log redirection -- but we'd need assertions to not be part of that so when it dies, the user can still see why */ bool out = self.convertToFile(mesh, @@ -1801,6 +1822,12 @@ void trade(py::module_& m) { /** @todo begin/end (MeshOptimizer), begin/end data */ /** @todo drop std::string in favor of our own string caster */ .def("begin_file", [](Trade::AbstractSceneConverter& self, const std::string& filename) { + if(!(self.features() >= Trade::SceneConverterFeature::ConvertMultipleToFile) && + !(self.features() >= Trade::SceneConverterFeature::ConvertMeshToFile)) { + PyErr_SetString(PyExc_AssertionError, "feature not supported"); + throw py::error_already_set{}; + } + if(!self.beginFile( #ifdef CORRADE_TARGET_WINDOWS /* To allow people to conveniently use Python's os.path, we @@ -1828,6 +1855,10 @@ void trade(py::module_& m) { } }, "End converting a scene to a file") .def("set_default_scene", [](Trade::AbstractSceneConverter& self, const UnsignedInt id) { + if(!(self.features() >= Trade::SceneConverterFeature::AddScenes)) { + PyErr_SetString(PyExc_AssertionError, "feature not supported"); + throw py::error_already_set{}; + } if(!self.isConverting()) { PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); throw py::error_already_set{}; @@ -1848,6 +1879,10 @@ void trade(py::module_& m) { }, "Count of added scenes") /** @todo drop std::string in favor of our own string caster */ .def("add", [](Trade::AbstractSceneConverter& self, const Trade::SceneData& scene, const std::string& name) { + if(!(self.features() >= Trade::SceneConverterFeature::AddScenes)) { + PyErr_SetString(PyExc_AssertionError, "scene conversion not supported"); + throw py::error_already_set{}; + } if(!self.isConverting()) { PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); throw py::error_already_set{}; @@ -1859,14 +1894,18 @@ void trade(py::module_& m) { throw py::error_already_set{}; }, "Add a scene", py::arg("scene"), py::arg("name") = std::string{}) .def("set_scene_field_name", [](Trade::AbstractSceneConverter& self, const Trade::SceneField field, const std::string& name) { - if(!Trade::isSceneFieldCustom(field)) { - PyErr_SetString(PyExc_AssertionError, "not a custom field"); + if(!(self.features() >= Trade::SceneConverterFeature::AddScenes)) { + PyErr_SetString(PyExc_AssertionError, "feature not supported"); throw py::error_already_set{}; } if(!self.isConverting()) { PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); throw py::error_already_set{}; } + if(!Trade::isSceneFieldCustom(field)) { + PyErr_SetString(PyExc_AssertionError, "not a custom field"); + throw py::error_already_set{}; + } self.setSceneFieldName(field, name); }, "Set name of a custom scene field", py::arg("field"), py::arg("name")) .def_property_readonly("mesh_count", [](Trade::AbstractSceneConverter& self) { @@ -1878,6 +1917,13 @@ void trade(py::module_& m) { }, "Count of added meshes") /** @todo drop std::string in favor of our own string caster */ .def("add", [](Trade::AbstractSceneConverter& self, const Trade::MeshData& mesh, const std::string& name) { + if(!(self.features() >= Trade::SceneConverterFeature::AddMeshes) && + !(self.features() & (Trade::SceneConverterFeature::ConvertMesh| + Trade::SceneConverterFeature::ConvertMeshToData| + Trade::SceneConverterFeature::ConvertMeshToFile))) { + PyErr_SetString(PyExc_AssertionError, "mesh conversion not supported"); + throw py::error_already_set{}; + } if(!self.isConverting()) { PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); throw py::error_already_set{}; @@ -1890,12 +1936,25 @@ void trade(py::module_& m) { }, "Add a mesh", py::arg("mesh"), py::arg("name") = std::string{}) /** @todo mesh levels */ .def("set_mesh_attribute_name", [](Trade::AbstractSceneConverter& self, const Trade::MeshAttribute attribute, const std::string& name) { - if(!Trade::isMeshAttributeCustom(attribute)) { - PyErr_SetString(PyExc_AssertionError, "not a custom attribute"); + if(!(self.features() & (Trade::SceneConverterFeature::AddMeshes| + Trade::SceneConverterFeature::ConvertMesh| + Trade::SceneConverterFeature::ConvertMeshInPlace| + Trade::SceneConverterFeature::ConvertMeshToData| + Trade::SceneConverterFeature::ConvertMeshToFile))) { + PyErr_SetString(PyExc_AssertionError, "feature not supported"); + throw py::error_already_set{}; + } + /* Unless single mesh conversion is supported, allow this function + to be called only if begin*() was called before */ + if(!(self.features() & (Trade::SceneConverterFeature::ConvertMesh| + Trade::SceneConverterFeature::ConvertMeshInPlace| + Trade::SceneConverterFeature::ConvertMeshToData| + Trade::SceneConverterFeature::ConvertMeshToFile)) && !self.isConverting()) { + PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); throw py::error_already_set{}; } - if(!self.isConverting()) { - PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); + if(!Trade::isMeshAttributeCustom(attribute)) { + PyErr_SetString(PyExc_AssertionError, "not a custom attribute"); throw py::error_already_set{}; } self.setMeshAttributeName(attribute, name); @@ -1905,6 +1964,10 @@ void trade(py::module_& m) { PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); throw py::error_already_set{}; } + if(!importer.isOpened()) { + PyErr_SetString(PyExc_AssertionError, "the importer is not opened"); + throw py::error_already_set{}; + } /** @todo check if contents present in the file are supported? or make that a runtime failure in Magnum for easier use? */ if(!self.addImporterContents(importer, contents)) { @@ -1917,6 +1980,10 @@ void trade(py::module_& m) { PyErr_SetString(PyExc_AssertionError, "no conversion in progress"); throw py::error_already_set{}; } + if(!importer.isOpened()) { + PyErr_SetString(PyExc_AssertionError, "the importer is not opened"); + throw py::error_already_set{}; + } /** @todo check if contents present in the file are supported? or make that a runtime failure in Magnum for easier use? */ if(!self.addSupportedImporterContents(importer, contents)) {