Browse Source

python: expose Corrade's utility.copy().

Because having to pull in the whole numpy just to efficiently copy some
data around is *silly*.
next
Vladimír Vondruš 3 years ago
parent
commit
8b30876304
  1. 3
      doc/python/corrade.utility.rst
  2. 2
      doc/python/pages/changelog.rst
  3. 3
      src/python/corrade/test/test_utility.py
  4. 29
      src/python/corrade/utility.cpp
  5. 64
      src/python/magnum/test/test.py

3
doc/python/corrade.utility.rst

@ -23,6 +23,9 @@
DEALINGS IN THE SOFTWARE.
..
.. py:function:: corrade.utility.copy
:raise AssertionError: If :p:`src` and :p:`dst` sizes, type sizes or types are different
.. py:function:: corrade.utility.ConfigurationGroup.group
:raise KeyError: If group :p:`name` doesn't exist

2
doc/python/pages/changelog.rst

@ -135,6 +135,8 @@ Changelog
- Exposed :ref:`Color3.red()` and other convenience constructors (see
:gh:`mosra/magnum-bindings#12`)
- Exposed the :ref:`scenetools` and :ref:`text` libraries
- Exposed :ref:`utility.copy()` for convenient, fast and safe copying of
multi-dimensional strided arrays
- Exposed the minimal interface of :ref:`utility.ConfigurationGroup` and
:ref:`utility.Configuration`
- Exposed :ref:`pluginmanager.AbstractManager.set_preferred_plugins()`,

3
src/python/corrade/test/test_utility.py

@ -30,6 +30,9 @@ import unittest
from corrade import utility
# The copy() algorithms can't be tested here as there's nothing that would
# return a StridedArrayView. A test is in magnum/test/test.py instead.
# Tests also the ConfigurationGroup bindings, as a ConfigurationGroup cannot be
# constructed as a standalone type
class Configuration(unittest.TestCase):

29
src/python/corrade/utility.cpp

@ -24,16 +24,45 @@
*/
#include <pybind11/pybind11.h>
#include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Configuration.h>
#include "corrade/bootstrap.h"
#include "Corrade/Containers/StridedArrayViewPythonBindings.h"
namespace corrade {
template<unsigned dimensions> void algorithmsCopy(py::module_& m) {
m.def("copy", [](const Containers::PyStridedArrayView<dimensions, const char>& src, const Containers::PyStridedArrayView<dimensions, char>& dst) {
if(src.size() != dst.size()) {
PyErr_SetString(PyExc_AssertionError, "sizes don't match");
throw py::error_already_set{};
}
if(src.itemsize != dst.itemsize) {
PyErr_SetString(PyExc_AssertionError, "type sizes don't match");
throw py::error_already_set{};
}
if(Containers::StringView{src.format} != Containers::StringView{dst.format}) {
PyErr_SetString(PyExc_AssertionError, "types don't match");
throw py::error_already_set{};
}
Utility::copy(
Containers::arrayCast<dimensions + 1, const char>(Containers::StridedArrayView<dimensions, const void>{src}, src.itemsize),
Containers::arrayCast<dimensions + 1, char>(Containers::StridedArrayView<dimensions, void>{dst}, dst.itemsize));
}, "Copy a strided array view to another", py::arg("src"), py::arg("dst"));
}
void utility(py::module_& m) {
m.doc() = "Utilities";
algorithmsCopy<1>(m);
algorithmsCopy<2>(m);
algorithmsCopy<3>(m);
algorithmsCopy<4>(m);
py::class_<Utility::ConfigurationGroup>{m, "ConfigurationGroup", "Group of values in a configuration file"}
.def_property_readonly("has_groups", &Utility::ConfigurationGroup::hasGroups, "Whether this group has any subgroups")
.def("group", [](Utility::ConfigurationGroup& self, const std::string& name) {

64
src/python/magnum/test/test.py

@ -27,8 +27,11 @@ import array
import sys
import unittest
from corrade import utility
from magnum import *
# tests also corrade.utility.copy() in UtilityCopy
class PixelStorage_(unittest.TestCase):
def test_init(self):
a = PixelStorage()
@ -447,3 +450,64 @@ class ImageView(unittest.TestCase):
with self.assertRaisesRegex(NotImplementedError, "access to this pixel format is not implemented yet, sorry"):
a.pixels
class UtilityCopy(unittest.TestCase):
def test_1d(self):
a_data = array.array('f', [1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
a = ImageView1D(PixelFormat.RGB32F, 2, a_data)
b_data = array.array('f', [0.0]*6)
b = MutableImageView1D(PixelFormat.RGB32F, 2, b_data)
utility.copy(a.pixels, b.pixels)
self.assertEqual(list(b_data), [1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
def test_2d(self):
a_data = array.array('f', [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
a = ImageView2D(PixelFormat.RG32F, (2, 2), a_data)
b_data = array.array('f', [0.0]*8)
b = MutableImageView2D(PixelFormat.RG32F, (2, 2), b_data)
utility.copy(a.pixels, b.pixels)
self.assertEqual(list(b_data), [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
def test_3d(self):
a_data = array.array('f', [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
a = ImageView3D(PixelFormat.R32F, (3, 1, 3), a_data)
b_data = array.array('f', [0.0]*9)
b = MutableImageView3D(PixelFormat.R32F, (3, 1, 3), b_data)
utility.copy(a.pixels, b.pixels)
self.assertEqual(list(b_data), [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
def test_size_mismatch(self):
a_data = array.array('f', [0.0]*12)
a = ImageView2D(PixelFormat.RG32F, (3, 2), a_data)
b_data = array.array('f', [0.0]*12)
b = MutableImageView2D(PixelFormat.RG32F, (2, 3), b_data)
with self.assertRaisesRegex(AssertionError, "sizes don't match"):
utility.copy(a.pixels, b.pixels)
def test_itemsize_mismatch(self):
a_data = array.array('f', [0.0]*12)
a = ImageView2D(PixelFormat.RG32F, (3, 2), a_data)
b_data = array.array('f', [0.0]*18)
b = MutableImageView2D(PixelFormat.RGB32F, (3, 2), b_data)
with self.assertRaisesRegex(AssertionError, "type sizes don't match"):
utility.copy(a.pixels, b.pixels)
def test_format_mismatch(self):
a_data = array.array('f', [0.0]*12)
a = ImageView2D(PixelFormat.RG32F, (3, 2), a_data)
b_data = array.array('I', [0]*18)
b = MutableImageView2D(PixelFormat.RG32UI, (3, 2), b_data)
with self.assertRaisesRegex(AssertionError, "types don't match"):
utility.copy(a.pixels, b.pixels)

Loading…
Cancel
Save