Skip to content
Permalink
Browse files

python: move away from concrete pybind exceptions.

Use only py::error_already_set as that's the least heavy of them. Also
changed all occurences of "throw" to "raise" so next time I'm doing a
prune of all C++ exceptions for good, those are easy to find.
  • Loading branch information...
mosra committed Sep 16, 2019
1 parent ee54dc6 commit f20c5beb76f96bb3177290584681f038eeeb43e9

Large diffs are not rendered by default.

@@ -26,7 +26,6 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h> /* for Mesh.buffers */
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Utility/FormatStl.h>
#include <Magnum/Image.h>
#include <Magnum/ImageView.h>
#include <Magnum/GL/AbstractShaderProgram.h>
@@ -777,7 +776,10 @@ void gl(py::module& m) {
self.setPrimitive(py::cast<MeshPrimitive>(primitive));
else if(py::isinstance<GL::MeshPrimitive>(primitive))
self.setPrimitive(py::cast<GL::MeshPrimitive>(primitive));
else throw py::type_error{Utility::formatString("expected MeshPrimitive or gl.MeshPrimitive, got {}", std::string(py::str{primitive.get_type()}))};
else {
PyErr_Format(PyExc_TypeError, "expected MeshPrimitive or gl.MeshPrimitive, got %A", primitive.get_type().ptr());
throw py::error_already_set{};
}
}, "Primitive type")
/* Have to use a lambda because it returns GL::Mesh which is not
tracked (unlike PyMesh) */
@@ -177,14 +177,20 @@ template<class T> void boolVector(py::class_<T>& c) {
.def("none", &T::none, "Whether no bits are set")
.def("any", &T::any, "Whether any bit is set")

/* Set / get. Need to throw IndexError in order to allow iteration:
/* Set / get. Need to raise IndexError in order to allow iteration:
https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */
.def("__setitem__",[](T& self, std::size_t i, bool value) {
if(i >= T::Size) throw pybind11::index_error{};
if(i >= T::Size) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
self.set(i, value);
}, "Set a bit at given position")
.def("__getitem__", [](const T& self, std::size_t i) {
if(i >= T::Size) throw pybind11::index_error{};
if(i >= T::Size) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return self[i];
}, "Bit at given position")

@@ -28,7 +28,6 @@
#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Utility/FormatStl.h>
#include <Magnum/Math/Matrix3.h>
#include <Magnum/Math/Matrix4.h>

@@ -114,11 +113,15 @@ template<class T, class ...Args> void everyRectangularMatrixBuffer(py::class_<T,

Containers::ScopeGuard e{&buffer, PyBuffer_Release};

if(buffer.ndim != 2)
throw py::buffer_error{Utility::formatString("expected 2 dimensions but got {}", buffer.ndim)};
if(buffer.ndim != 2) {
PyErr_Format(PyExc_BufferError, "expected 2 dimensions but got %i", buffer.ndim);
throw py::error_already_set{};
}

if(buffer.shape[0] != T::Rows || buffer.shape[1] != T::Cols)
throw py::buffer_error{Utility::formatString("expected {}x{} elements but got {}x{}", T::Cols, T::Rows, buffer.shape[1], buffer.shape[0])};
if(buffer.shape[0] != T::Rows || buffer.shape[1] != T::Cols) {
PyErr_Format(PyExc_BufferError, "expected %zux%zu elements but got %zix%zi", T::Cols, T::Rows, buffer.shape[1], buffer.shape[0]);
throw py::error_already_set{};
}

T out{Math::NoInit};

@@ -127,7 +130,10 @@ template<class T, class ...Args> void everyRectangularMatrixBuffer(py::class_<T,
initFromBuffer<Float>(out, buffer);
else if(buffer.format[0] == 'd' && !buffer.format[1])
initFromBuffer<Double>(out, buffer);
else throw py::buffer_error{Utility::formatString("expected format f or d but got {}", buffer.format)};
else {
PyErr_Format(PyExc_BufferError, "expected format f or d but got %s", buffer.format);
throw py::error_already_set{};
}

return out;
}), "Construct from a buffer");
@@ -176,22 +182,22 @@ template<class T> void rectangularMatrix(py::class_<T>& c) {
.def(py::self == py::self, "Equality comparison")
.def(py::self != py::self, "Non-equality comparison")

/* Set / get. Need to throw IndexError in order to allow iteration:
/* Set / get. Need to raise IndexError in order to allow iteration:
https://docs.python.org/3/reference/datamodel.html#object.__getitem__
Using error_already_set is slightly faster than throwing index_error
directly, but still much slower than not throwing at all. Waiting
for https://github.com/pybind/pybind11/pull/1853 to get merged. */
.def("__setitem__", [](T& self, std::size_t i, const typename VectorTraits<T::Rows, typename T::Type>::Type& value) {
if(i >= T::Cols) {
PyErr_SetString(PyExc_IndexError, "");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
self[i] = value;
}, "Set a column at given position")
.def("__getitem__", [](const T& self, std::size_t i) -> typename VectorTraits<T::Rows, typename T::Type>::Type {
if(i >= T::Cols) {
PyErr_SetString(PyExc_IndexError, "");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
return self[i];
}, "Column at given position")
@@ -200,14 +206,14 @@ template<class T> void rectangularMatrix(py::class_<T>& c) {
.def("__setitem__", [](T& self, const std::pair<std::size_t, std::size_t>& i, typename T::Type value) {
if(i.first >= T::Cols || i.second >= T::Rows) {
PyErr_SetString(PyExc_IndexError, "");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
self[i.first][i.second] = value;
}, "Set a value at given col/row")
.def("__getitem__", [](const T& self, const std::pair<std::size_t, std::size_t>& i) {
if(i.first >= T::Cols || i.second >= T::Rows) {
PyErr_SetString(PyExc_IndexError, "");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
return self[i.first][i.second];
}, "Value at given col/row")
@@ -27,7 +27,6 @@

#include <pybind11/operators.h>
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Utility/FormatStl.h>
#include <Magnum/Math/Color.h>
#include <Magnum/Math/Vector4.h>

@@ -142,15 +141,21 @@ template<class T, class ...Args> void everyVectorBuffer(py::class_<T, Args...>&

Containers::ScopeGuard e{&buffer, PyBuffer_Release};

if(buffer.ndim != 1)
throw py::buffer_error{Utility::formatString("expected 1 dimension but got {}", buffer.ndim)};
if(buffer.ndim != 1) {
PyErr_Format(PyExc_BufferError, "expected 1 dimension but got %i", buffer.ndim);
throw py::error_already_set{};
}

if(buffer.shape[0] != T::Size)
throw py::buffer_error{Utility::formatString("expected {} elements but got {}", T::Size, buffer.shape[0])};
if(buffer.shape[0] != T::Size) {
PyErr_Format(PyExc_BufferError, "expected %zu elements but got %zi", T::Size, buffer.shape[0]);
throw py::error_already_set{};
}

/* Expecting just an one-letter format */
if(!buffer.format[0] || buffer.format[1] || !isTypeCompatible<typename T::Type>(buffer.format[0]))
throw py::buffer_error{Utility::formatString("unexpected format {} for a {} vector", buffer.format, FormatStrings[formatIndex<typename T::Type>()])};
if(!buffer.format[0] || buffer.format[1] || !isTypeCompatible<typename T::Type>(buffer.format[0])) {
PyErr_Format(PyExc_BufferError, "unexpected format %s for a %s vector", buffer.format, FormatStrings[formatIndex<typename T::Type>()]);
throw py::error_already_set{};
}

T out{Math::NoInit};
initFromBuffer<T>(out, buffer);
@@ -208,22 +213,22 @@ template<class T> void vector(py::module& m, py::class_<T>& c) {
.def(py::self <= py::self, "Component-wise less than or equal comparison")
.def(py::self >= py::self, "Component-wise greater than or equal comparison")

/* Set / get. Need to throw IndexError in order to allow iteration:
/* Set / get. Need to raise IndexError in order to allow iteration:
https://docs.python.org/3/reference/datamodel.html#object.__getitem__
Using error_already_set is slightly faster than throwing index_error
directly, but still much slower than not throwing at all. Waiting
for https://github.com/pybind/pybind11/pull/1853 to get merged. */
.def("__setitem__", [](T& self, std::size_t i, typename T::Type value) {
if(i >= T::Size) {
PyErr_SetString(PyExc_IndexError, "");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
self[i] = value;
}, "Set a value at given position")
.def("__getitem__", [](const T& self, std::size_t i) {
if(i >= T::Size) {
PyErr_SetString(PyExc_IndexError, "");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
return self[i];
}, "Value at given position")
@@ -235,7 +240,7 @@ template<class T> void vector(py::module& m, py::class_<T>& c) {
.def("__getattr__", [](T& self, const std::string& name) -> py::object {
if(name.size() > 4) {
PyErr_SetString(PyExc_AttributeError, "only four-component swizzles are supported at most");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}

Math::Vector4<typename T::Type> out;
@@ -246,7 +251,7 @@ template<class T> void vector(py::module& m, py::class_<T>& c) {
else if(T::Size > 3 && (name[i] == 'w' || name[i] == 'a')) out[i] = self[3];
else {
PyErr_SetString(PyExc_AttributeError, "invalid swizzle");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
}

@@ -268,11 +273,11 @@ template<class T> void vector(py::module& m, py::class_<T>& c) {
(name.compare("rgb") == 0 && T::Size > 3) ||
name.find_first_not_of("xyzwrgba") != std::string::npos) {
if(PySuper_Type.tp_setattro(py::cast(self).ptr(), nameO.ptr(), valueO.ptr()) != 0)
throw pybind11::error_already_set{};
throw py::error_already_set{};
return;
}

/* Here we can be certain it's a swizzle attempt, so throw clear
/* Here we can be certain it's a swizzle attempt, so raise clear
error messages */
const typename T::Type* data;
std::size_t size;
@@ -287,12 +292,12 @@ template<class T> void vector(py::module& m, py::class_<T>& c) {
size = 4;
} else {
PyErr_SetString(PyExc_TypeError, "unrecognized swizzle type");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}

if(name.size() != size) {
PyErr_SetString(PyExc_TypeError, "swizzle doesn't match passed vector component count");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
for(std::size_t i = 0; i != name.size(); ++i) {
if(name[i] == 'x' || name[i] == 'r') self[0] = data[i];
@@ -301,7 +306,7 @@ template<class T> void vector(py::module& m, py::class_<T>& c) {
else if(T::Size > 3 && (name[i] == 'w' || name[i] == 'a')) self[3] = data[i];
else {
PyErr_SetString(PyExc_AttributeError, "invalid swizzle");
throw pybind11::error_already_set{};
throw py::error_already_set{};
}
}
}, "Vector swizzle")
@@ -64,10 +64,13 @@ template<class PyFeature, UnsignedInt dimensions, class Feature, class T> void f
.def("__len__", &SceneGraph::FeatureGroup<dimensions, Feature, T>::size,
"Count of features in the group")
/* Get item. Fetching the already registered instance and returning
that instead of wrapping the pointer again. Need to throw IndexError
that instead of wrapping the pointer again. Need to raise IndexError
in order to allow iteration: https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */
.def("__getitem__", [](SceneGraph::FeatureGroup<dimensions, Feature, T>& self, std::size_t index) -> PyFeature& {
if(index >= self.size()) throw pybind11::index_error{};
if(index >= self.size()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return static_cast<PyFeature&>(self[index]);
}, "Feature at given index")
.def("add", [](SceneGraph::FeatureGroup<dimensions, Feature, T>& self, PyFeature& feature) {
@@ -26,7 +26,6 @@
*/

#include <pybind11/pybind11.h>
#include <Corrade/Utility/FormatStl.h>
#include <Magnum/SceneGraph/Object.h>
#include <Magnum/SceneGraph/Scene.h>

@@ -61,7 +60,10 @@ template<UnsignedInt dimensions, class T, class Transformation> void object(py::
parent = py::cast<SceneGraph::Scene<Transformation>*>(parentobj);
else if(py::isinstance<py::none>(parentobj))
parent = nullptr;
else throw py::type_error{Utility::formatString("expected Scene, Object or None, got {}", std::string(py::str{parentobj.get_type()}))};
else {
PyErr_Format(PyExc_TypeError, "expected Scene, Object or None, got %A", parentobj.get_type().ptr());
throw py::error_already_set{};
}

/* Decrease refcount if a parent is removed, increase it if a
parent gets added */

0 comments on commit f20c5be

Please sign in to comment.
You can’t perform that action at this time.