Commit 391c7544 authored by Jason Rhinelander's avatar Jason Rhinelander
Browse files

Update all remaining tests to new test styles

This udpates all the remaining tests to the new test suite code and
comment styles started in #898.  For the most part, the test coverage
here is unchanged, with a few minor exceptions as noted below.

- test_constants_and_functions: this adds more overload tests with
  overloads with different number of arguments for more comprehensive
  overload_cast testing.  The test style conversion broke the overload
  tests under MSVC 2015, prompting the additional tests while looking
  for a workaround.

- test_eigen: this dropped the unused functions `get_cm_corners` and
  `get_cm_corners_const`--these same tests were duplicates of the same
  things provided (and used) via ReturnTester methods.

- test_opaque_types: this test had a hidden dependence on ExampleMandA
  which is now fixed by using the global UserType which suffices for the
  relevant test.

- test_methods_and_attributes: this required some additions to UserType
  to make it usable as a replacement for the test's previous SimpleType:
  UserType gained a value mutator, and the `value` property is not
  mutable (it was previously readonly).  Some overload tests were also
  added to better test overload_cast (as described above).

- test_numpy_array: removed the untemplated mutate_data/mutate_data_t:
  the templated versions with an empty parameter pack expand to the same
  thing.

- test_stl: this was already mostly in the new style; this just tweaks
  things a bit, localizing a class, and adding some missing
  `// test_whatever` comments.

- test_virtual_functions: like `test_stl`, this was mostly in the new
  test style already, but needed some `// test_whatever` comments.
  This commit also moves the inherited virtual example code to the end
  of the file, after the main set of tests (since it is less important
  than the other tests, and rather length); it also got renamed to
  `test_inherited_virtuals` (from `test_inheriting_repeat`) because it
  tests both inherited virtual approaches, not just the repeat approach.
parent 9866a0f9
......@@ -196,7 +196,7 @@ def pytest_namespace():
except ImportError:
scipy = None
try:
from pybind11_tests import have_eigen
from pybind11_tests.eigen import have_eigen
except ImportError:
have_eigen = False
pypy = platform.python_implementation() == "PyPy"
......
......@@ -77,7 +77,8 @@ PYBIND11_MODULE(pybind11_tests, m) {
.def(py::init<>())
.def(py::init<int>())
.def("get_value", &UserType::value, "Get value using a method")
.def_property_readonly("value", &UserType::value, "Get value using a property")
.def("set_value", &UserType::set, "Set value using a method")
.def_property("value", &UserType::value, &UserType::set, "Get/set value using a property")
.def("__repr__", [](const UserType& u) { return "UserType({})"_s.format(u.value()); });
py::class_<IncType, UserType>(m, "IncType")
......
......@@ -33,6 +33,7 @@ public:
UserType(int i) : i(i) { }
int value() const { return i; }
void set(int set) { i = set; }
private:
int i = -1;
......
......@@ -10,105 +10,73 @@
#include "pybind11_tests.h"
#include "constructor_stats.h"
class Matrix {
public:
Matrix(ssize_t rows, ssize_t cols) : m_rows(rows), m_cols(cols) {
print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[(size_t) (rows*cols)];
memset(m_data, 0, sizeof(float) * (size_t) (rows * cols));
}
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
}
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
print_move_created(this);
s.m_rows = 0;
s.m_cols = 0;
s.m_data = nullptr;
}
~Matrix() {
print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data;
}
Matrix &operator=(const Matrix &s) {
print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data;
m_rows = s.m_rows;
m_cols = s.m_cols;
m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
return *this;
}
Matrix &operator=(Matrix &&s) {
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
if (&s != this) {
delete[] m_data;
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data;
s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr;
TEST_SUBMODULE(buffers, m) {
// test_from_python / test_to_python:
class Matrix {
public:
Matrix(ssize_t rows, ssize_t cols) : m_rows(rows), m_cols(cols) {
print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[(size_t) (rows*cols)];
memset(m_data, 0, sizeof(float) * (size_t) (rows * cols));
}
return *this;
}
float operator()(ssize_t i, ssize_t j) const {
return m_data[(size_t) (i*m_cols + j)];
}
float &operator()(ssize_t i, ssize_t j) {
return m_data[(size_t) (i*m_cols + j)];
}
float *data() { return m_data; }
ssize_t rows() const { return m_rows; }
ssize_t cols() const { return m_cols; }
private:
ssize_t m_rows;
ssize_t m_cols;
float *m_data;
};
class SquareMatrix : public Matrix {
public:
SquareMatrix(ssize_t n) : Matrix(n, n) { }
};
struct PTMFBuffer {
int32_t value = 0;
py::buffer_info get_buffer_info() {
return py::buffer_info(&value, sizeof(value),
py::format_descriptor<int32_t>::format(), 1);
}
};
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
}
class ConstPTMFBuffer {
std::unique_ptr<int32_t> value;
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
print_move_created(this);
s.m_rows = 0;
s.m_cols = 0;
s.m_data = nullptr;
}
public:
int32_t get_value() const { return *value; }
void set_value(int32_t v) { *value = v; }
~Matrix() {
print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data;
}
py::buffer_info get_buffer_info() const {
return py::buffer_info(value.get(), sizeof(*value),
py::format_descriptor<int32_t>::format(), 1);
}
Matrix &operator=(const Matrix &s) {
print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data;
m_rows = s.m_rows;
m_cols = s.m_cols;
m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
return *this;
}
ConstPTMFBuffer() : value(new int32_t{0}) { };
};
Matrix &operator=(Matrix &&s) {
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
if (&s != this) {
delete[] m_data;
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data;
s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr;
}
return *this;
}
struct DerivedPTMFBuffer : public PTMFBuffer { };
float operator()(ssize_t i, ssize_t j) const {
return m_data[(size_t) (i*m_cols + j)];
}
test_initializer buffers([](py::module &m) {
py::class_<Matrix> mtx(m, "Matrix", py::buffer_protocol());
float &operator()(ssize_t i, ssize_t j) {
return m_data[(size_t) (i*m_cols + j)];
}
mtx.def(py::init<ssize_t, ssize_t>())
float *data() { return m_data; }
ssize_t rows() const { return m_rows; }
ssize_t cols() const { return m_cols; }
private:
ssize_t m_rows;
ssize_t m_cols;
float *m_data;
};
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
.def(py::init<ssize_t, ssize_t>())
/// Construct from a buffer
.def("__init__", [](Matrix &v, py::buffer b) {
py::buffer_info info = b.request();
......@@ -143,24 +111,57 @@ test_initializer buffers([](py::module &m) {
})
;
// test_inherited_protocol
class SquareMatrix : public Matrix {
public:
SquareMatrix(ssize_t n) : Matrix(n, n) { }
};
// Derived classes inherit the buffer protocol and the buffer access function
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix")
.def(py::init<ssize_t>());
py::class_<PTMFBuffer>(m, "PTMFBuffer", py::buffer_protocol())
// test_pointer_to_member_fn
// Tests that passing a pointer to member to the base class works in
// the derived class.
struct Buffer {
int32_t value = 0;
py::buffer_info get_buffer_info() {
return py::buffer_info(&value, sizeof(value),
py::format_descriptor<int32_t>::format(), 1);
}
};
py::class_<Buffer>(m, "Buffer", py::buffer_protocol())
.def(py::init<>())
.def_readwrite("value", &PTMFBuffer::value)
.def_buffer(&PTMFBuffer::get_buffer_info);
.def_readwrite("value", &Buffer::value)
.def_buffer(&Buffer::get_buffer_info);
py::class_<ConstPTMFBuffer>(m, "ConstPTMFBuffer", py::buffer_protocol())
class ConstBuffer {
std::unique_ptr<int32_t> value;
public:
int32_t get_value() const { return *value; }
void set_value(int32_t v) { *value = v; }
py::buffer_info get_buffer_info() const {
return py::buffer_info(value.get(), sizeof(*value),
py::format_descriptor<int32_t>::format(), 1);
}
ConstBuffer() : value(new int32_t{0}) { };
};
py::class_<ConstBuffer>(m, "ConstBuffer", py::buffer_protocol())
.def(py::init<>())
.def_property("value", &ConstPTMFBuffer::get_value, &ConstPTMFBuffer::set_value)
.def_buffer(&ConstPTMFBuffer::get_buffer_info);
.def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value)
.def_buffer(&ConstBuffer::get_buffer_info);
// Tests that passing a pointer to member to the base class works in
// the derived class.
py::class_<DerivedPTMFBuffer>(m, "DerivedPTMFBuffer", py::buffer_protocol())
struct DerivedBuffer : public Buffer { };
py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol())
.def(py::init<>())
.def_readwrite("value", (int32_t DerivedPTMFBuffer::*) &DerivedPTMFBuffer::value)
.def_buffer(&DerivedPTMFBuffer::get_buffer_info);
});
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
.def_buffer(&DerivedBuffer::get_buffer_info);
}
import struct
import pytest
from pybind11_tests import Matrix, ConstructorStats, PTMFBuffer, ConstPTMFBuffer, DerivedPTMFBuffer
from pybind11_tests import buffers as m
from pybind11_tests import ConstructorStats
pytestmark = pytest.requires_numpy
......@@ -10,17 +11,17 @@ with pytest.suppress(ImportError):
def test_from_python():
with pytest.raises(RuntimeError) as excinfo:
Matrix(np.array([1, 2, 3])) # trying to assign a 1D array
m.Matrix(np.array([1, 2, 3])) # trying to assign a 1D array
assert str(excinfo.value) == "Incompatible buffer format!"
m3 = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
m4 = Matrix(m3)
m4 = m.Matrix(m3)
for i in range(m4.rows()):
for j in range(m4.cols()):
assert m3[i, j] == m4[i, j]
cstats = ConstructorStats.get(Matrix)
cstats = ConstructorStats.get(m.Matrix)
assert cstats.alive() == 1
del m3, m4
assert cstats.alive() == 0
......@@ -35,26 +36,26 @@ def test_from_python():
# https://bitbucket.org/pypy/pypy/issues/2444
@pytest.unsupported_on_pypy
def test_to_python():
m = Matrix(5, 5)
assert memoryview(m).shape == (5, 5)
mat = m.Matrix(5, 5)
assert memoryview(mat).shape == (5, 5)
assert m[2, 3] == 0
m[2, 3] = 4
assert m[2, 3] == 4
assert mat[2, 3] == 0
mat[2, 3] = 4
assert mat[2, 3] == 4
m2 = np.array(m, copy=False)
assert m2.shape == (5, 5)
assert abs(m2).sum() == 4
assert m2[2, 3] == 4
m2[2, 3] = 5
assert m2[2, 3] == 5
mat2 = np.array(mat, copy=False)
assert mat2.shape == (5, 5)
assert abs(mat2).sum() == 4
assert mat2[2, 3] == 4
mat2[2, 3] = 5
assert mat2[2, 3] == 5
cstats = ConstructorStats.get(Matrix)
cstats = ConstructorStats.get(m.Matrix)
assert cstats.alive() == 1
del m
del mat
pytest.gc_collect()
assert cstats.alive() == 1
del m2 # holds an m reference
del mat2 # holds a mat reference
pytest.gc_collect()
assert cstats.alive() == 0
assert cstats.values() == ["5x5 matrix"]
......@@ -67,16 +68,15 @@ def test_to_python():
@pytest.unsupported_on_pypy
def test_inherited_protocol():
"""SquareMatrix is derived from Matrix and inherits the buffer protocol"""
from pybind11_tests import SquareMatrix
matrix = SquareMatrix(5)
matrix = m.SquareMatrix(5)
assert memoryview(matrix).shape == (5, 5)
assert np.asarray(matrix).shape == (5, 5)
@pytest.unsupported_on_pypy
def test_ptmf():
for cls in [PTMFBuffer, ConstPTMFBuffer, DerivedPTMFBuffer]:
def test_pointer_to_member_fn():
for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]:
buf = cls()
buf.value = 0x12345678
value = struct.unpack('i', bytearray(buf))[0]
......
......@@ -9,47 +9,6 @@
#include "pybind11_tests.h"
class Child {
public:
Child() { py::print("Allocating child."); }
~Child() { py::print("Releasing child."); }
};
class Parent {
public:
Parent() { py::print("Allocating parent."); }
~Parent() { py::print("Releasing parent."); }
void addChild(Child *) { }
Child *returnChild() { return new Child(); }
Child *returnNullChild() { return nullptr; }
};
#if !defined(PYPY_VERSION)
class ParentGC : public Parent {
public:
using Parent::Parent;
};
#endif
test_initializer keep_alive([](py::module &m) {
py::class_<Parent>(m, "Parent")
.def(py::init<>())
.def("addChild", &Parent::addChild)
.def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
.def("returnChild", &Parent::returnChild)
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>());
#if !defined(PYPY_VERSION)
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr())
.def(py::init<>());
#endif
py::class_<Child>(m, "Child")
.def(py::init<>());
});
struct CustomGuard {
static bool enabled;
......@@ -58,7 +17,6 @@ struct CustomGuard {
static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
};
bool CustomGuard::enabled = false;
struct DependentGuard {
......@@ -69,12 +27,48 @@ struct DependentGuard {
static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
};
bool DependentGuard::enabled = false;
test_initializer call_guard([](py::module &pm) {
auto m = pm.def_submodule("call_policies");
TEST_SUBMODULE(call_policies, m) {
// Parent/Child are used in:
// test_keep_alive_argument, test_keep_alive_return_value, test_alive_gc_derived,
// test_alive_gc_multi_derived, test_return_none
class Child {
public:
Child() { py::print("Allocating child."); }
~Child() { py::print("Releasing child."); }
};
py::class_<Child>(m, "Child")
.def(py::init<>());
class Parent {
public:
Parent() { py::print("Allocating parent."); }
~Parent() { py::print("Releasing parent."); }
void addChild(Child *) { }
Child *returnChild() { return new Child(); }
Child *returnNullChild() { return nullptr; }
};
py::class_<Parent>(m, "Parent")
.def(py::init<>())
.def("addChild", &Parent::addChild)
.def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
.def("returnChild", &Parent::returnChild)
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>());
#if !defined(PYPY_VERSION)
// test_alive_gc
class ParentGC : public Parent {
public:
using Parent::Parent;
};
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr())
.def(py::init<>());
#endif
// test_call_guard
m.def("unguarded_call", &CustomGuard::report_status);
m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
......@@ -100,4 +94,4 @@ test_initializer call_guard([](py::module &pm) {
m.def("with_gil", report_gil_status);
m.def("without_gil", report_gil_status, py::call_guard<py::gil_scoped_release>());
#endif
});
}
import pytest
from pybind11_tests import call_policies as m
from pybind11_tests import ConstructorStats
def test_keep_alive_argument(capture):
from pybind11_tests import Parent, Child, ConstructorStats
n_inst = ConstructorStats.detail_reg_inst()
with capture:
p = Parent()
p = m.Parent()
assert capture == "Allocating parent."
with capture:
p.addChild(Child())
p.addChild(m.Child())
assert ConstructorStats.detail_reg_inst() == n_inst + 1
assert capture == """
Allocating child.
......@@ -21,10 +21,10 @@ def test_keep_alive_argument(capture):
assert capture == "Releasing parent."
with capture:
p = Parent()
p = m.Parent()
assert capture == "Allocating parent."
with capture:
p.addChildKeepAlive(Child())
p.addChildKeepAlive(m.Child())
assert ConstructorStats.detail_reg_inst() == n_inst + 2
assert capture == "Allocating child."
with capture:
......@@ -37,11 +37,9 @@ def test_keep_alive_argument(capture):
def test_keep_alive_return_value(capture):
from pybind11_tests import Parent, ConstructorStats
n_inst = ConstructorStats.detail_reg_inst()
with capture:
p = Parent()
p = m.Parent()
assert capture == "Allocating parent."
with capture:
p.returnChild()
......@@ -56,7 +54,7 @@ def test_keep_alive_return_value(capture):
assert capture == "Releasing parent."
with capture:
p = Parent()
p = m.Parent()
assert capture == "Allocating parent."
with capture:
p.returnChildKeepAlive()
......@@ -74,11 +72,9 @@ def test_keep_alive_return_value(capture):
# https://bitbucket.org/pypy/pypy/issues/2447
@pytest.unsupported_on_pypy
def test_alive_gc(capture):
from pybind11_tests import ParentGC, Child, ConstructorStats
n_inst = ConstructorStats.detail_reg_inst()
p = ParentGC()
p.addChildKeepAlive(Child())
p = m.ParentGC()
p.addChildKeepAlive(m.Child())
assert ConstructorStats.detail_reg_inst() == n_inst + 2
lst = [p]
lst.append(lst) # creates a circular reference
......@@ -92,14 +88,12 @@ def test_alive_gc(capture):
def test_alive_gc_derived(capture):
from pybind11_tests import Parent, Child, ConstructorStats
class Derived(Parent):
class Derived(m.Parent):
pass
n_inst = ConstructorStats.detail_reg_inst()
p = Derived()
p.addChildKeepAlive(Child())
p.addChildKeepAlive(m.Child())
assert ConstructorStats.detail_reg_inst() == n_inst + 2
lst = [p]
lst.append(lst) # creates a circular reference
......@@ -113,16 +107,14 @@ def test_alive_gc_derived(capture):
def test_alive_gc_multi_derived(capture):
from pybind11_tests import Parent, Child, ConstructorStats
class Derived(Parent, Child):
class Derived(m.Parent, m.Child):
def __init__(self):
Parent.__init__(self)
Child.__init__(self)
m.Parent.__init__(self)
m.Child.__init__(self)
n_inst = ConstructorStats.detail_reg_inst()
p = Derived()
p.addChildKeepAlive(Child())
p.addChildKeepAlive(m.Child())
# +3 rather than +2 because Derived corresponds to two registered instances
assert ConstructorStats.detail_reg_inst() == n_inst + 3
lst = [p]
......@@ -138,11 +130,9 @@ def test_alive_gc_multi_derived(capture):
def test_return_none(capture):
from pybind11_tests import Parent, ConstructorStats
n_inst = ConstructorStats.detail_reg_inst()
with capture:
p = Parent()
p = m.Parent()
assert capture == "Allocating parent."
with capture:
p.returnNullChildKeepAliveChild()
......@@ -154,7 +144,7 @@ def test_return_none(capture):
assert capture == "Releasing parent."
with capture:
p = Parent()
p = m.Parent()
assert capture == "Allocating parent."
with capture:
p.returnNullChildKeepAliveParent()
......@@ -167,14 +157,12 @@ def test_return_none(capture):
def test_call_guard():
from pybind11_tests import call_policies
assert call_policies.unguarded_call() == "unguarded"
assert call_policies.guarded_call() == "guarded"
assert m.unguarded_call() == "unguarded"
assert m.guarded_call() == "guarded"
assert call_policies.multiple_guards_correct_order() == "guarded & guarded"
assert call_policies.multiple_guards_wrong_order() == "unguarded & guarded"
assert m.multiple_guards_correct_order() == "guarded & guarded"
assert m.multiple_guards_wrong_order() == "unguarded & guarded"
if hasattr(call_policies, "with_gil"):
assert call_policies.with_gil() == "GIL held"
assert call_policies.without_gil() == "GIL released"
if hasattr(m, "with_gil"):
assert m.with_gil() == "GIL held"
assert m.without_gil() == "GIL released"
......@@ -12,94 +12,20 @@
#include <pybind11/functional.h>
py::object test_callback1(py::object func) {
return func();
}
py::tuple test_callback2(py::object func) {
return func("Hello", 'x', true, 5);
}
std::string test_callback3(const std::function<int(int)> &func) {
return "func(43) = " + std::to_string(func(43));
}
std::function<int(int)> test_callback4() {
return [](int i) { return i+1; };
}
py::cpp_function test_callback5() {
return py::cpp_function([](int i) { return i+1; },
py::arg("number"));
}
int dummy_function(int i) { return i + 1; }
int dummy_function2(int i, int j) { return i + j; }
std::function<int(int)> roundtrip(std::function<int(int)> f, bool expect_none = false) {
if (expect_none && f) {
throw std::runtime_error("Expected None to be converted to empty std::function");
}
return f;
}
std::string test_dummy_function(const std::function<int(int)> &f) {
using fn_type = int (*)(int);
auto result = f.target<fn_type>();
if (!result) {
auto r = f(1);
return "can't convert to function pointer: eval(1) = " + std::to_string(r);
} else if (*result == dummy_function) {
auto r = (*result)(1);
return "matches dummy_function: eval(1) = " + std::to_string(r);
} else {
return "argument does NOT match dummy_function. This should never happen!";
}
}
TEST_SUBMODULE(callbacks, m) {
// test_callbacks, test_function_signatures
m.def("test_callback1", [](py::object func) { return func(); });
m.def("test_callback2", [](py::object func) { return func("Hello", 'x', true, 5); });
m.def("test_callback3", [](const std::function<int(int)> &func) {
return "func(43) = " + std::to_string(func(43)); });
m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; });
m.def("test_callback5", []() {
return py::cpp_function([](int i) { return i+1; }, py::arg("number"));
});
struct Payload {
Payload() {
print_default_created(this);
}
~Payload() {
print_destroyed(this);
}
Payload(const Payload &) {
print_copy_created(this);
}
Payload(Payload &&) {
print_move_created(this);
}
};
class AbstractBase {
public:
virtual unsigned int func() = 0;
};
void func_accepting_func_accepting_base(std::function<double(AbstractBase&)>) { }
struct MovableObject {
bool valid = true;
MovableObject() = default;
MovableObject(const MovableObject &) = default;
MovableObject &operator=(const MovableObject &) = default;
MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; }
MovableObject &operator=(MovableObject &&o) {
valid = o.valid;
o.valid = false;
return *this;
}
};
test_initializer callbacks([](py::module &m) {
m.def("test_callback1", &test_callback1);
m.def("test_callback2", &test_callback2);
m.def("test_callback3", &test_callback3);
m.def("test_callback4", &test_callback4);
m.def("test_callback5", &test_callback5);
// Test keyword args and generalized unpacking
// test_keyword_args_and_generalized_unpacking
m.def("test_tuple_unpacking", [](py::function f) {
auto t1 = py::make_tuple(2, 3);
auto t2 = py::make_tuple(5, 6);
......@@ -148,6 +74,15 @@ test_initializer callbacks([](py::module &m) {
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567);
});
// test_lambda_closure_cleanup
struct Payload {
Payload() { print_default_created(this); }
~Payload() { print_destroyed(this); }
Payload(const Payload &) { print_copy_created(this); }
Payload(Payload &&) { print_move_created(this); }
};
// Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>);
/* Test cleanup of lambda closure */
m.def("test_cleanup", []() -> std::function<void(void)> {
Payload p;
......@@ -158,27 +93,57 @@ test_initializer callbacks([](py::module &m) {
};
});
// test_cpp_function_roundtrip
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
m.def("dummy_function", &dummy_function);
m.def("dummy_function2", &dummy_function2);
m.def("roundtrip", &roundtrip, py::arg("f"), py::arg("expect_none")=false);
m.def("test_dummy_function", &test_dummy_function);
// Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>);
m.def("func_accepting_func_accepting_base",
func_accepting_func_accepting_base);
m.def("dummy_function2", [](int i, int j) { return i + j; });
m.def("roundtrip", [](std::function<int(int)> f, bool expect_none = false) {
if (expect_none && f)
throw std::runtime_error("Expected None to be converted to empty std::function");
return f;
}, py::arg("f"), py::arg("expect_none")=false);
m.def("test_dummy_function", [](const std::function<int(int)> &f) -> std::string {
using fn_type = int (*)(int);
auto result = f.target<fn_type>();
if (!result) {
auto r = f(1);
return "can't convert to function pointer: eval(1) = " + std::to_string(r);
} else if (*result == dummy_function) {
auto r = (*result)(1);
return "matches dummy_function: eval(1) = " + std::to_string(r);
} else {
return "argument does NOT match dummy_function. This should never happen!";
}
});
class AbstractBase { public: virtual unsigned int func() = 0; };
m.def("func_accepting_func_accepting_base", [](std::function<double(AbstractBase&)>) { });
struct MovableObject {
bool valid = true;
MovableObject() = default;
MovableObject(const MovableObject &) = default;
MovableObject &operator=(const MovableObject &) = default;
MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; }
MovableObject &operator=(MovableObject &&o) {
valid = o.valid;
o.valid = false;
return *this;
}
};
py::class_<MovableObject>(m, "MovableObject");
// test_movable_object
m.def("callback_with_movable", [](std::function<void(MovableObject &)> f) {
auto x = MovableObject();
f(x); // lvalue reference shouldn't move out object
return x.valid; // must still return `true`
});
});
// test_bound_method_callback
struct CppBoundMethodTest {};
py::class_<CppBoundMethodTest>(m, "CppBoundMethodTest")
.def(py::init<>())
.def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; });
});
}
import pytest
from pybind11_tests import callbacks as m
def test_callbacks():
from functools import partial
from pybind11_tests import (test_callback1, test_callback2, test_callback3,
test_callback4, test_callback5)
def func1():
return "func1"
......@@ -15,73 +14,65 @@ def test_callbacks():
def func3(a):
return "func3({})".format(a)
assert test_callback1(func1) == "func1"
assert test_callback2(func2) == ("func2", "Hello", "x", True, 5)
assert test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4)
assert test_callback1(partial(func3, "partial")) == "func3(partial)"
assert test_callback3(lambda i: i + 1) == "func(43) = 44"
assert m.test_callback1(func1) == "func1"
assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5)
assert m.test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4)
assert m.test_callback1(partial(func3, "partial")) == "func3(partial)"
assert m.test_callback3(lambda i: i + 1) == "func(43) = 44"
f = test_callback4()
f = m.test_callback4()
assert f(43) == 44
f = test_callback5()
f = m.test_callback5()
assert f(number=43) == 44
def test_bound_method_callback():
from pybind11_tests import test_callback3, CppBoundMethodTest
# Bound Python method:
class MyClass:
def double(self, val):
return 2 * val
z = MyClass()
assert test_callback3(z.double) == "func(43) = 86"
assert m.test_callback3(z.double) == "func(43) = 86"
z = CppBoundMethodTest()
assert test_callback3(z.triple) == "func(43) = 129"
z = m.CppBoundMethodTest()
assert m.test_callback3(z.triple) == "func(43) = 129"
def test_keyword_args_and_generalized_unpacking():
from pybind11_tests import (test_tuple_unpacking, test_dict_unpacking, test_keyword_args,
test_unpacking_and_keywords1, test_unpacking_and_keywords2,
test_unpacking_error1, test_unpacking_error2,
test_arg_conversion_error1, test_arg_conversion_error2)
def f(*args, **kwargs):
return args, kwargs
assert test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {})
assert test_dict_unpacking(f) == (("positional", 1), {"key": "value", "a": 1, "b": 2})
assert test_keyword_args(f) == ((), {"x": 10, "y": 20})
assert test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4})
assert test_unpacking_and_keywords2(f) == (
assert m.test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {})
assert m.test_dict_unpacking(f) == (("positional", 1), {"key": "value", "a": 1, "b": 2})
assert m.test_keyword_args(f) == ((), {"x": 10, "y": 20})
assert m.test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4})
assert m.test_unpacking_and_keywords2(f) == (
("positional", 1, 2, 3, 4, 5),
{"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
)
with pytest.raises(TypeError) as excinfo:
test_unpacking_error1(f)
m.test_unpacking_error1(f)
assert "Got multiple values for keyword argument" in str(excinfo.value)
with pytest.raises(TypeError) as excinfo:
test_unpacking_error2(f)
m.test_unpacking_error2(f)
assert "Got multiple values for keyword argument" in str(excinfo.value)
with pytest.raises(RuntimeError) as excinfo:
test_arg_conversion_error1(f)
m.test_arg_conversion_error1(f)
assert "Unable to convert call argument" in str(excinfo.value)
with pytest.raises(RuntimeError) as excinfo:
test_arg_conversion_error2(f)
m.test_arg_conversion_error2(f)
assert "Unable to convert call argument" in str(excinfo.value)
def test_lambda_closure_cleanup():
from pybind11_tests import test_cleanup, payload_cstats
test_cleanup()
cstats = payload_cstats()
m.test_cleanup()
cstats = m.payload_cstats()
assert cstats.alive() == 0
assert cstats.copy_constructions == 1
assert cstats.move_constructions >= 1
......@@ -89,31 +80,28 @@ def test_lambda_closure_cleanup():
def test_cpp_function_roundtrip():
"""Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
from pybind11_tests import dummy_function, dummy_function2, test_dummy_function, roundtrip
assert test_dummy_function(dummy_function) == "matches dummy_function: eval(1) = 2"
assert test_dummy_function(roundtrip(dummy_function)) == "matches dummy_function: eval(1) = 2"
assert roundtrip(None, expect_none=True) is None
assert test_dummy_function(lambda x: x + 2) == "can't convert to function pointer: eval(1) = 3"
assert m.test_dummy_function(m.dummy_function) == "matches dummy_function: eval(1) = 2"
assert (m.test_dummy_function(m.roundtrip(m.dummy_function)) ==
"matches dummy_function: eval(1) = 2")
assert m.roundtrip(None, expect_none=True) is None
assert (m.test_dummy_function(lambda x: x + 2) ==
"can't convert to function pointer: eval(1) = 3")
with pytest.raises(TypeError) as excinfo:
test_dummy_function(dummy_function2)
m.test_dummy_function(m.dummy_function2)
assert "incompatible function arguments" in str(excinfo.value)
with pytest.raises(TypeError) as excinfo:
test_dummy_function(lambda x, y: x + y)
m.test_dummy_function(lambda x, y: x + y)
assert any(s in str(excinfo.value) for s in ("missing 1 required positional argument",
"takes exactly 2 arguments"))
def test_function_signatures(doc):
from pybind11_tests import test_callback3, test_callback4
assert doc(test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str"
assert doc(test_callback4) == "test_callback4() -> Callable[[int], int]"
assert doc(m.test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str"
assert doc(m.test_callback4) == "test_callback4() -> Callable[[int], int]"
def test_movable_object():
from pybind11_tests import callback_with_movable
assert callback_with_movable(lambda _: None) is True
assert m.callback_with_movable(lambda _: None) is True
......@@ -8,58 +8,40 @@
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/chrono.h>
// Return the current time off the wall clock
std::chrono::system_clock::time_point test_chrono1() {
return std::chrono::system_clock::now();
}
TEST_SUBMODULE(chrono, m) {
using system_time = std::chrono::system_clock::time_point;
using steady_time = std::chrono::steady_clock::time_point;
// test_chrono_system_clock
// Return the current time off the wall clock
m.def("test_chrono1", []() { return std::chrono::system_clock::now(); });
// Round trip the passed in system clock time
std::chrono::system_clock::time_point test_chrono2(std::chrono::system_clock::time_point t) {
return t;
}
// Round trip the passed in duration
std::chrono::system_clock::duration test_chrono3(std::chrono::system_clock::duration d) {
return d;
}
// test_chrono_system_clock_roundtrip
// Round trip the passed in system clock time
m.def("test_chrono2", [](system_time t) { return t; });
// Difference between two passed in time_points
std::chrono::system_clock::duration test_chrono4(std::chrono::system_clock::time_point a, std::chrono::system_clock::time_point b) {
return a - b;
}
// test_chrono_duration_roundtrip
// Round trip the passed in duration
m.def("test_chrono3", [](std::chrono::system_clock::duration d) { return d; });
// Return the current time off the steady_clock
std::chrono::steady_clock::time_point test_chrono5() {
return std::chrono::steady_clock::now();
}
// test_chrono_duration_subtraction_equivalence
// Difference between two passed in time_points
m.def("test_chrono4", [](system_time a, system_time b) { return a - b; });
// Round trip a steady clock timepoint
std::chrono::steady_clock::time_point test_chrono6(std::chrono::steady_clock::time_point t) {
return t;
}
// test_chrono_steady_clock
// Return the current time off the steady_clock
m.def("test_chrono5", []() { return std::chrono::steady_clock::now(); });
// Roundtrip a duration in microseconds from a float argument
std::chrono::microseconds test_chrono7(std::chrono::microseconds t) {
return t;
}
// test_chrono_steady_clock_roundtrip
// Round trip a steady clock timepoint
m.def("test_chrono6", [](steady_time t) { return t; });
// Float durations (issue #719)
std::chrono::duration<double> test_chrono_float_diff(std::chrono::duration<float> a, std::chrono::duration<float> b) {
return a - b;
// test_floating_point_duration
// Roundtrip a duration in microseconds from a float argument
m.def("test_chrono7", [](std::chrono::microseconds t) { return t; });
// Float durations (issue #719)
m.def("test_chrono_float_diff", [](std::chrono::duration<float> a, std::chrono::duration<float> b) {
return a - b; });
}
test_initializer chrono([] (py::module &m) {
m.def("test_chrono1", &test_chrono1);
m.def("test_chrono2", &test_chrono2);
m.def("test_chrono3", &test_chrono3);
m.def("test_chrono4", &test_chrono4);
m.def("test_chrono5", &test_chrono5);
m.def("test_chrono6", &test_chrono6);
m.def("test_chrono7", &test_chrono7);
m.def("test_chrono_float_diff", &test_chrono_float_diff);
});
from pybind11_tests import chrono as m
import datetime
def test_chrono_system_clock():
from pybind11_tests import test_chrono1
import datetime
# Get the time from both c++ and datetime
date1 = test_chrono1()
date1 = m.test_chrono1()
date2 = datetime.datetime.today()
# The returned value should be a datetime
......@@ -25,13 +25,10 @@ def test_chrono_system_clock():
def test_chrono_system_clock_roundtrip():
from pybind11_tests import test_chrono2
import datetime
date1 = datetime.datetime.today()
# Roundtrip the time
date2 = test_chrono2(date1)
date2 = m.test_chrono2(date1)
# The returned value should be a datetime
assert isinstance(date2, datetime.datetime)
......@@ -44,8 +41,6 @@ def test_chrono_system_clock_roundtrip():
def test_chrono_duration_roundtrip():
from pybind11_tests import test_chrono3
import datetime
# Get the difference between two times (a timedelta)
date1 = datetime.datetime.today()
......@@ -55,7 +50,7 @@ def test_chrono_duration_roundtrip():
# Make sure this is a timedelta
assert isinstance(diff, datetime.timedelta)
cpp_diff = test_chrono3(diff)
cpp_diff = m.test_chrono3(diff)
assert cpp_diff.days == diff.days
assert cpp_diff.seconds == diff.seconds
......@@ -63,14 +58,12 @@ def test_chrono_duration_roundtrip():
def test_chrono_duration_subtraction_equivalence():
from pybind11_tests import test_chrono4
import datetime
date1 = datetime.datetime.today()
date2 = datetime.datetime.today()
diff = date2 - date1
cpp_diff = test_chrono4(date2, date1)
cpp_diff = m.test_chrono4(date2, date1)
assert cpp_diff.days == diff.days
assert cpp_diff.seconds == diff.seconds
......@@ -78,22 +71,13 @@ def test_chrono_duration_subtraction_equivalence():
def test_chrono_steady_clock():
from pybind11_tests import test_chrono5
import datetime
time1 = test_chrono5()
time2 = test_chrono5()
time1 = m.test_chrono5()
assert isinstance(time1, datetime.timedelta)
assert isinstance(time2, datetime.timedelta)
def test_chrono_steady_clock_roundtrip():
from pybind11_tests import test_chrono6
import datetime
time1 = datetime.timedelta(days=10, seconds=10, microseconds=100)
time2 = test_chrono6(time1)
time2 = m.test_chrono6(time1)
assert isinstance(time2, datetime.timedelta)
......@@ -104,17 +88,14 @@ def test_chrono_steady_clock_roundtrip():
def test_floating_point_duration():
from pybind11_tests import test_chrono7, test_chrono_float_diff
import datetime
# Test using 35.525123 seconds as an example floating point number in seconds
time = test_chrono7(35.525123)
# Test using a floating point number in seconds
time = m.test_chrono7(35.525123)
assert isinstance(time, datetime.timedelta)
assert time.seconds == 35
assert 525122 <= time.microseconds <= 525123
diff = test_chrono_float_diff(43.789012, 1.123456)
diff = m.test_chrono_float_diff(43.789012, 1.123456)
assert diff.seconds == 42
assert 665556 <= diff.microseconds <= 665557
......@@ -35,7 +35,7 @@ def test_docstrings(doc):
Get value using a method
"""
assert doc(UserType.value) == "Get value using a property"
assert doc(UserType.value) == "Get/set value using a property"
assert doc(m.NoConstructor.new_instance) == """
new_instance() -> m.class_.NoConstructor
......
......@@ -23,6 +23,8 @@ std::string test_function3(int i) {
return "test_function(" + std::to_string(i) + ")";
}
py::str test_function4() { return "test_function()"; }
py::str test_function4(char *) { return "test_function(char *)"; }
py::str test_function4(int, float) { return "test_function(int, float)"; }
py::str test_function4(float, int) { return "test_function(float, int)"; }
......@@ -61,17 +63,23 @@ struct C {
}
test_initializer constants_and_functions([](py::module &m) {
TEST_SUBMODULE(constants_and_functions, m) {
// test_constants
m.attr("some_constant") = py::int_(14);
// test_function_overloading
m.def("test_function", &test_function1);
m.def("test_function", &test_function2);
m.def("test_function", &test_function3);
#if defined(PYBIND11_OVERLOAD_CAST)
m.def("test_function", py::overload_cast<>(&test_function4));
m.def("test_function", py::overload_cast<char *>(&test_function4));
m.def("test_function", py::overload_cast<int, float>(&test_function4));
m.def("test_function", py::overload_cast<float, int>(&test_function4));
#else
m.def("test_function", static_cast<py::str (*)()>(&test_function4));
m.def("test_function", static_cast<py::str (*)(char *)>(&test_function4));
m.def("test_function", static_cast<py::str (*)(int, float)>(&test_function4));
m.def("test_function", static_cast<py::str (*)(float, int)>(&test_function4));
#endif
......@@ -81,12 +89,13 @@ test_initializer constants_and_functions([](py::module &m) {
.value("ESecondEntry", ESecondEntry)
.export_values();
// test_bytes
m.def("return_bytes", &return_bytes);
m.def("print_bytes", &print_bytes);
// test_exception_specifiers
using namespace test_exc_sp;
py::module m2 = m.def_submodule("exc_sp");
py::class_<C>(m2, "C")
py::class_<C>(m, "C")
.def(py::init<>())
.def("m1", &C::m1)
.def("m2", &C::m2)
......@@ -97,8 +106,8 @@ test_initializer constants_and_functions([](py::module &m) {
.def("m7", &C::m7)
.def("m8", &C::m8)
;
m2.def("f1", f1);
m2.def("f2", f2);
m2.def("f3", f3);
m2.def("f4", f4);
});
m.def("f1", f1);
m.def("f2", f2);
m.def("f3", f3);
m.def("f4", f4);
}
from pybind11_tests import constants_and_functions as m
def test_constants():
from pybind11_tests import some_constant
assert some_constant == 14
assert m.some_constant == 14
def test_function_overloading():
from pybind11_tests import MyEnum, test_function
assert test_function() == "test_function()"
assert test_function(7) == "test_function(7)"
assert test_function(MyEnum.EFirstEntry) == "test_function(enum=1)"
assert test_function(MyEnum.ESecondEntry) == "test_function(enum=2)"
assert m.test_function() == "test_function()"
assert m.test_function(7) == "test_function(7)"
assert m.test_function(m.MyEnum.EFirstEntry) == "test_function(enum=1)"
assert m.test_function(m.MyEnum.ESecondEntry) == "test_function(enum=2)"
assert test_function(1, 1.0) == "test_function(int, float)"
assert test_function(2.0, 2) == "test_function(float, int)"
assert m.test_function() == "test_function()"
assert m.test_function("abcd") == "test_function(char *)"
assert m.test_function(1, 1.0) == "test_function(int, float)"
assert m.test_function(1, 1.0) == "test_function(int, float)"
assert m.test_function(2.0, 2) == "test_function(float, int)"
def test_bytes():
from pybind11_tests import return_bytes, print_bytes
assert print_bytes(return_bytes()) == "bytes[1 0 2 0]"
assert m.print_bytes(m.return_bytes()) == "bytes[1 0 2 0]"
def test_exception_specifiers():
from pybind11_tests.exc_sp import C, f1, f2, f3, f4
c = C()
c = m.C()
assert c.m1(2) == 1
assert c.m2(3) == 1
assert c.m3(5) == 2
......@@ -37,7 +33,7 @@ def test_exception_specifiers():
assert c.m7(20) == 13
assert c.m8(29) == 21
assert f1(33) == 34
assert f2(53) == 55
assert f3(86) == 89
assert f4(140) == 144
assert m.f1(33) == 34
assert m.f2(53) == 55
assert m.f3(86) == 89
assert m.f4(140) == 144
......@@ -68,7 +68,8 @@ public:
int value;
};
namespace pybind11 { namespace detail {
NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)
template <> struct type_caster<MoveOnlyInt> {
PYBIND11_TYPE_CASTER(MoveOnlyInt, _("MoveOnlyInt"));
bool load(handle src, bool) { value = MoveOnlyInt(src.cast<int>()); return true; }
......@@ -96,32 +97,20 @@ public:
operator CopyOnlyInt&() { return value; }
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
};
}}
NAMESPACE_END(detail)
NAMESPACE_END(pybind11)
struct PrivateOpNew {
int value = 1;
private:
void *operator new(size_t bytes);
};
test_initializer copy_move_policies([](py::module &m) {
TEST_SUBMODULE(copy_move_policies, m) {
// test_lacking_copy_ctor
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
.def_static("get_one", &lacking_copy_ctor::get_one,
py::return_value_policy::copy);
// test_lacking_move_ctor
py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
.def_static("get_one", &lacking_move_ctor::get_one,
py::return_value_policy::move);
m.def("move_only", [](MoveOnlyInt m) {
return m.value;
});
m.def("move_or_copy", [](MoveOrCopyInt m) {
return m.value;
});
m.def("copy_only", [](CopyOnlyInt m) {
return m.value;
});
// test_move_and_copy_casts
m.def("move_and_copy_casts", [](py::object o) {
int r = 0;
r += py::cast<MoveOrCopyInt>(o).value; /* moves */
......@@ -134,6 +123,11 @@ test_initializer copy_move_policies([](py::module &m) {
return r;
});
// test_move_and_copy_loads
m.def("move_only", [](MoveOnlyInt m) { return m.value; });
m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; });
m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) {
return p.first.value + p.second.value;
});
......@@ -163,6 +157,7 @@ test_initializer copy_move_policies([](py::module &m) {
return d;
});
#ifdef PYBIND11_HAS_OPTIONAL
// test_move_and_copy_load_optional
m.attr("has_optional") = true;
m.def("move_optional", [](std::optional<MoveOnlyInt> o) {
return o->value;
......@@ -181,6 +176,14 @@ test_initializer copy_move_policies([](py::module &m) {
#endif
// #70 compilation issue if operator new is not public
struct PrivateOpNew {
int value = 1;
private:
#if defined(_MSC_VER)
# pragma warning(disable: 4822) // warning C4822: local class member function does not have a body
#endif
void *operator new(size_t bytes);
};
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
m.def("private_op_new_value", []() { return PrivateOpNew(); });
m.def("private_op_new_reference", []() -> const PrivateOpNew & {
......@@ -188,6 +191,7 @@ test_initializer copy_move_policies([](py::module &m) {
return x;
}, py::return_value_policy::reference);
// test_move_fallback
// #389: rvp::move should fall-through to copy on non-movable objects
struct MoveIssue1 {
int v;
......@@ -195,15 +199,15 @@ test_initializer copy_move_policies([](py::module &m) {
MoveIssue1(const MoveIssue1 &c) = default;
MoveIssue1(MoveIssue1 &&) = delete;
};
py::class_<MoveIssue1>(m, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v);
struct MoveIssue2 {
int v;
MoveIssue2(int v) : v{v} {}
MoveIssue2(MoveIssue2 &&) = default;
};
py::class_<MoveIssue1>(m, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v);
py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
m.def("get_moveissue1", [](int i) { return new MoveIssue1(i); }, py::return_value_policy::move);
m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
});
}
import pytest
from pybind11_tests import has_optional
from pybind11_tests import copy_move_policies as m
def test_lacking_copy_ctor():
from pybind11_tests import lacking_copy_ctor
with pytest.raises(RuntimeError) as excinfo:
lacking_copy_ctor.get_one()
m.lacking_copy_ctor.get_one()
assert "the object is non-copyable!" in str(excinfo.value)
def test_lacking_move_ctor():
from pybind11_tests import lacking_move_ctor
with pytest.raises(RuntimeError) as excinfo:
lacking_move_ctor.get_one()
m.lacking_move_ctor.get_one()
assert "the object is neither movable nor copyable!" in str(excinfo.value)
def test_move_and_copy_casts():
"""Cast some values in C++ via custom type casters and count the number of moves/copies."""
from pybind11_tests import move_and_copy_casts, move_and_copy_cstats
cstats = move_and_copy_cstats()
cstats = m.move_and_copy_cstats()
c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"]
# The type move constructions/assignments below each get incremented: the move assignment comes
# from the type_caster load; the move construction happens when extracting that via a cast or
# loading into an argument.
assert move_and_copy_casts(3) == 18
assert m.move_and_copy_casts(3) == 18
assert c_m.copy_assignments + c_m.copy_constructions == 0
assert c_m.move_assignments == 2
assert c_m.move_constructions >= 2
......@@ -43,21 +40,19 @@ def test_move_and_copy_casts():
def test_move_and_copy_loads():
"""Call some functions that load arguments via custom type casters and count the number of
moves/copies."""
from pybind11_tests import (move_and_copy_cstats, move_only, move_or_copy, copy_only,
move_pair, move_tuple, copy_tuple, move_copy_nested)
cstats = move_and_copy_cstats()
cstats = m.move_and_copy_cstats()
c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"]
assert move_only(10) == 10 # 1 move, c_m
assert move_or_copy(11) == 11 # 1 move, c_mc
assert copy_only(12) == 12 # 1 copy, c_c
assert move_pair((13, 14)) == 27 # 1 c_m move, 1 c_mc move
assert move_tuple((15, 16, 17)) == 48 # 2 c_m moves, 1 c_mc move
assert copy_tuple((18, 19)) == 37 # 2 c_c copies
assert m.move_only(10) == 10 # 1 move, c_m
assert m.move_or_copy(11) == 11 # 1 move, c_mc
assert m.copy_only(12) == 12 # 1 copy, c_c
assert m.move_pair((13, 14)) == 27 # 1 c_m move, 1 c_mc move
assert m.move_tuple((15, 16, 17)) == 48 # 2 c_m moves, 1 c_mc move
assert m.copy_tuple((18, 19)) == 37 # 2 c_c copies
# Direct constructions: 2 c_m moves, 2 c_mc moves, 1 c_c copy
# Extra moves/copies when moving pairs/tuples: 3 c_m, 3 c_mc, 2 c_c
assert move_copy_nested((1, ((2, 3, (4,)), 5))) == 15
assert m.move_copy_nested((1, ((2, 3, (4,)), 5))) == 15
assert c_m.copy_assignments + c_m.copy_constructions == 0
assert c_m.move_assignments == 6
......@@ -70,24 +65,22 @@ def test_move_and_copy_loads():
assert c_m.alive() + c_mc.alive() + c_c.alive() == 0
@pytest.mark.skipif(not has_optional, reason='no <optional>')
@pytest.mark.skipif(not m.has_optional, reason='no <optional>')
def test_move_and_copy_load_optional():
"""Tests move/copy loads of std::optional arguments"""
from pybind11_tests import (move_and_copy_cstats, move_optional, move_or_copy_optional,
copy_optional, move_optional_tuple)
cstats = move_and_copy_cstats()
cstats = m.move_and_copy_cstats()
c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"]
# The extra move/copy constructions below come from the std::optional move (which has to move
# its arguments):
assert move_optional(10) == 10 # c_m: 1 move assign, 2 move construct
assert move_or_copy_optional(11) == 11 # c_mc: 1 move assign, 2 move construct
assert copy_optional(12) == 12 # c_c: 1 copy assign, 2 copy construct
assert m.move_optional(10) == 10 # c_m: 1 move assign, 2 move construct
assert m.move_or_copy_optional(11) == 11 # c_mc: 1 move assign, 2 move construct
assert m.copy_optional(12) == 12 # c_c: 1 copy assign, 2 copy construct
# 1 move assign + move construct moves each of c_m, c_mc, 1 c_c copy
# +1 move/copy construct each from moving the tuple
# +1 move/copy construct each from moving the optional (which moves the tuple again)
assert move_optional_tuple((3, 4, 5)) == 12
assert m.move_optional_tuple((3, 4, 5)) == 12
assert c_m.copy_assignments + c_m.copy_constructions == 0
assert c_m.move_assignments == 2
......@@ -102,7 +95,6 @@ def test_move_and_copy_load_optional():
def test_private_op_new():
"""An object with a private `operator new` cannot be returned by value"""
import pybind11_tests as m
with pytest.raises(RuntimeError) as excinfo:
m.private_op_new_value()
......@@ -113,9 +105,8 @@ def test_private_op_new():
def test_move_fallback():
"""#389: rvp::move should fall-through to copy on non-movable objects"""
from pybind11_tests import get_moveissue1, get_moveissue2
m2 = get_moveissue2(2)
m2 = m.get_moveissue2(2)
assert m2.value == 2
m1 = get_moveissue1(1)
m1 = m.get_moveissue1(1)
assert m1.value == 1
......@@ -9,14 +9,8 @@
#include "pybind11_tests.h"
struct DocstringTestFoo {
int value;
void setValue(int v) { value = v; }
int getValue() const { return value; }
};
test_initializer docstring_generation([](py::module &m) {
TEST_SUBMODULE(docstring_options, m) {
// test_docstring_options
{
py::options options;
options.disable_function_signatures();
......@@ -55,8 +49,13 @@ test_initializer docstring_generation([](py::module &m) {
py::options options;
options.disable_user_defined_docstrings();
struct DocstringTestFoo {
int value;
void setValue(int v) { value = v; }
int getValue() const { return value; }
};
py::class_<DocstringTestFoo>(m, "DocstringTestFoo", "This is a class docstring")
.def_property("value_prop", &DocstringTestFoo::getValue, &DocstringTestFoo::setValue, "This is a property docstring")
;
}
});
}
from pybind11_tests import docstring_options as m
def test_docstring_options():
from pybind11_tests import (test_function1, test_function2, test_function3,
test_function4, test_function5, test_function6,
test_function7, DocstringTestFoo,
test_overloaded1, test_overloaded2, test_overloaded3)
# options.disable_function_signatures()
assert not test_function1.__doc__
assert not m.test_function1.__doc__
assert test_function2.__doc__ == "A custom docstring"
assert m.test_function2.__doc__ == "A custom docstring"
# docstring specified on just the first overload definition:
assert test_overloaded1.__doc__ == "Overload docstring"
assert m.test_overloaded1.__doc__ == "Overload docstring"
# docstring on both overloads:
assert test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2"
assert m.test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2"
# docstring on only second overload:
assert test_overloaded3.__doc__ == "Overload docstr"
assert m.test_overloaded3.__doc__ == "Overload docstr"
# options.enable_function_signatures()
assert test_function3.__doc__ .startswith("test_function3(a: int, b: int) -> None")
assert m.test_function3.__doc__ .startswith("test_function3(a: int, b: int) -> None")
assert test_function4.__doc__ .startswith("test_function4(a: int, b: int) -> None")
assert test_function4.__doc__ .endswith("A custom docstring\n")
assert m.test_function4.__doc__ .startswith("test_function4(a: int, b: int) -> None")
assert m.test_function4.__doc__ .endswith("A custom docstring\n")
# options.disable_function_signatures()
# options.disable_user_defined_docstrings()
assert not test_function5.__doc__
assert not m.test_function5.__doc__
# nested options.enable_user_defined_docstrings()
assert test_function6.__doc__ == "A custom docstring"
assert m.test_function6.__doc__ == "A custom docstring"
# RAII destructor
assert test_function7.__doc__ .startswith("test_function7(a: int, b: int) -> None")
assert test_function7.__doc__ .endswith("A custom docstring\n")
assert m.test_function7.__doc__ .startswith("test_function7(a: int, b: int) -> None")
assert m.test_function7.__doc__ .endswith("A custom docstring\n")
# Suppression of user-defined docstrings for non-function objects
assert not DocstringTestFoo.__doc__
assert not DocstringTestFoo.value_prop.__doc__
assert not m.DocstringTestFoo.__doc__
assert not m.DocstringTestFoo.value_prop.__doc__
......@@ -70,20 +70,21 @@ struct CustomOperatorNew {
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
};
test_initializer eigen([](py::module &m) {
typedef Eigen::Matrix<float, 5, 6, Eigen::RowMajor> FixedMatrixR;
typedef Eigen::Matrix<float, 5, 6> FixedMatrixC;
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> DenseMatrixR;
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> DenseMatrixC;
typedef Eigen::Matrix<float, 4, Eigen::Dynamic> FourRowMatrixC;
typedef Eigen::Matrix<float, Eigen::Dynamic, 4> FourColMatrixC;
typedef Eigen::Matrix<float, 4, Eigen::Dynamic> FourRowMatrixR;
typedef Eigen::Matrix<float, Eigen::Dynamic, 4> FourColMatrixR;
typedef Eigen::SparseMatrix<float, Eigen::RowMajor> SparseMatrixR;
typedef Eigen::SparseMatrix<float> SparseMatrixC;
TEST_SUBMODULE(eigen, m) {
using FixedMatrixR = Eigen::Matrix<float, 5, 6, Eigen::RowMajor>;
using FixedMatrixC = Eigen::Matrix<float, 5, 6>;
using DenseMatrixR = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
using DenseMatrixC = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>;
using FourRowMatrixC = Eigen::Matrix<float, 4, Eigen::Dynamic>;
using FourColMatrixC = Eigen::Matrix<float, Eigen::Dynamic, 4>;
using FourRowMatrixR = Eigen::Matrix<float, 4, Eigen::Dynamic>;
using FourColMatrixR = Eigen::Matrix<float, Eigen::Dynamic, 4>;
using SparseMatrixR = Eigen::SparseMatrix<float, Eigen::RowMajor>;
using SparseMatrixC = Eigen::SparseMatrix<float>;
m.attr("have_eigen") = true;
// various tests
m.def("double_col", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return 2.0f * x; });
m.def("double_row", [](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; });
m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; });
......@@ -92,12 +93,14 @@ test_initializer eigen([](py::module &m) {
m.def("double_mat_cm", [](Eigen::MatrixXf x) -> Eigen::MatrixXf { return 2.0f * x; });
m.def("double_mat_rm", [](DenseMatrixR x) -> DenseMatrixR { return 2.0f * x; });
// test_eigen_ref_to_python
// Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
m.def("cholesky1", [](Eigen::Ref<MatrixXdR> x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky3", [](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky4", [](Eigen::Ref<const MatrixXdR> x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
// test_eigen_ref_mutators
// Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into
// the numpy array data and so the result should show up there. There are three versions: one that
// works on a contiguous-row matrix (numpy's default), one for a contiguous-column matrix, and one
......@@ -122,19 +125,6 @@ test_initializer eigen([](py::module &m) {
// The same references, but non-mutable (numpy maps into eigen variables, but is !writeable)
m.def("get_cm_const_ref", []() { return Eigen::Ref<const Eigen::MatrixXd>(get_cm()); });
m.def("get_rm_const_ref", []() { return Eigen::Ref<const MatrixXdR>(get_rm()); });
// Just the corners (via a Map instead of a Ref):
m.def("get_cm_corners", []() {
auto &x = get_cm();
return py::EigenDMap<Eigen::Matrix2d>(
x.data(),
py::EigenDStride(x.outerStride() * (x.rows() - 1), x.innerStride() * (x.cols() - 1)));
});
m.def("get_cm_corners_const", []() {
const auto &x = get_cm();
return py::EigenDMap<const Eigen::Matrix2d>(
x.data(),
py::EigenDStride(x.outerStride() * (x.rows() - 1), x.innerStride() * (x.cols() - 1)));
});
m.def("reset_refs", reset_refs); // Restores get_{cm,rm}_ref to original values
......@@ -174,6 +164,7 @@ test_initializer eigen([](py::module &m) {
return x.block(start_row, start_col, block_rows, block_cols);
});
// test_eigen_return_references, test_eigen_keepalive
// return value referencing/copying tests:
class ReturnTester {
Eigen::MatrixXd mat = create();
......@@ -220,6 +211,7 @@ test_initializer eigen([](py::module &m) {
.def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal)
;
// test_special_matrix_objects
// Returns a DiagonalMatrix with diagonal (1,2,3,...)
m.def("incr_diag", [](int k) {
Eigen::DiagonalMatrix<int, Eigen::Dynamic> m(k);
......@@ -244,27 +236,33 @@ test_initializer eigen([](py::module &m) {
0, 0, 0, 0, 0, 11,
0, 0, 14, 0, 8, 11;
// test_fixed, and various other tests
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); });
m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); });
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });
m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; });
// test_mutator_descriptors
m.def("fixed_mutator_r", [](Eigen::Ref<FixedMatrixR>) {});
m.def("fixed_mutator_c", [](Eigen::Ref<FixedMatrixC>) {});
m.def("fixed_mutator_a", [](py::EigenDRef<FixedMatrixC>) {});
// test_dense
m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(mat); });
m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); });
m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; });
m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; });
// test_sparse, test_sparse_signature
m.def("sparse_r", [mat]() -> SparseMatrixR { return Eigen::SparseView<Eigen::MatrixXf>(mat); });
m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); });
m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; });
m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; });
// test_partially_fixed
m.def("partial_copy_four_rm_r", [](const FourRowMatrixR &m) -> FourRowMatrixR { return m; });
m.def("partial_copy_four_rm_c", [](const FourColMatrixR &m) -> FourColMatrixR { return m; });
m.def("partial_copy_four_cm_r", [](const FourRowMatrixC &m) -> FourRowMatrixC { return m; });
m.def("partial_copy_four_cm_c", [](const FourColMatrixC &m) -> FourColMatrixC { return m; });
// test_cpp_casting
// Test that we can cast a numpy object to a Eigen::MatrixXd explicitly
m.def("cpp_copy", [](py::handle m) { return m.cast<Eigen::MatrixXd>()(1, 0); });
m.def("cpp_ref_c", [](py::handle m) { return m.cast<Eigen::Ref<Eigen::MatrixXd>>()(1, 0); });
......@@ -272,6 +270,7 @@ test_initializer eigen([](py::module &m) {
m.def("cpp_ref_any", [](py::handle m) { return m.cast<py::EigenDRef<Eigen::MatrixXd>>()(1, 0); });
// test_nocopy_wrapper
// Test that we can prevent copying into an argument that would normally copy: First a version
// that would allow copying (if types or strides don't match) for comparison:
m.def("get_elem", &get_elem);
......@@ -282,12 +281,14 @@ test_initializer eigen([](py::module &m) {
m.def("get_elem_rm_nocopy", [](Eigen::Ref<const Eigen::Matrix<long, -1, -1, Eigen::RowMajor>> &m) -> long { return m(2, 1); },
py::arg().noconvert());
// test_issue738
// Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an
// incompatible stride value on the length-1 dimension--but that should be allowed (without
// requiring a copy!) because the stride value can be safely ignored on a size-1 dimension.
m.def("iss738_f1", &adjust_matrix<const Eigen::Ref<const Eigen::MatrixXd> &>, py::arg().noconvert());
m.def("iss738_f2", &adjust_matrix<const Eigen::Ref<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>> &>, py::arg().noconvert());
// test_named_arguments
// Make sure named arguments are working properly:
m.def("matrix_multiply", [](const py::EigenDRef<const Eigen::MatrixXd> A, const py::EigenDRef<const Eigen::MatrixXd> B)
-> Eigen::MatrixXd {
......@@ -295,6 +296,7 @@ test_initializer eigen([](py::module &m) {
return A * B;
}, py::arg("A"), py::arg("B"));
// test_custom_operator_new
py::class_<CustomOperatorNew>(m, "CustomOperatorNew")
.def(py::init<>())
.def_readonly("a", &CustomOperatorNew::a)
......@@ -312,4 +314,4 @@ test_initializer eigen([](py::module &m) {
py::module::import("numpy").attr("ones")(10);
return v[0](5);
});
});
}
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment