From bdfb50f38491b18fed98cb94d5dd740c2f93972b Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Wed, 7 Jun 2017 16:52:50 +0200 Subject: [PATCH] Move tests from test_issues.cpp/py into appropriate files --- tests/CMakeLists.txt | 3 +- tests/test_copy_move.cpp | 19 ++ tests/test_copy_move.py | 10 + tests/test_inheritance.cpp | 20 ++ tests/test_inheritance.py | 13 + tests/test_issues.cpp | 377 ------------------------- tests/test_issues.py | 243 ---------------- tests/test_methods_and_attributes.cpp | 16 ++ tests/test_methods_and_attributes.py | 60 +++- tests/test_modules.cpp | 43 +++ tests/test_modules.py | 7 + tests/test_operator_overloading.cpp | 70 ++++- tests/test_operator_overloading.py | 56 +++- tests/test_python_types.cpp | 27 ++ tests/test_python_types.py | 14 + tests/test_sequences_and_iterators.cpp | 10 + tests/test_sequences_and_iterators.py | 17 ++ tests/test_smart_ptr.cpp | 29 ++ tests/test_smart_ptr.py | 12 + tests/test_virtual_functions.cpp | 57 ++++ tests/test_virtual_functions.py | 39 +++ 21 files changed, 510 insertions(+), 632 deletions(-) delete mode 100644 tests/test_issues.cpp delete mode 100644 tests/test_issues.py diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d40c25ac4..be223f577 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,7 +40,6 @@ set(PYBIND11_TEST_FILES test_eval.cpp test_exceptions.cpp test_inheritance.cpp - test_issues.cpp test_kwargs_and_defaults.cpp test_methods_and_attributes.cpp test_modules.cpp @@ -59,7 +58,7 @@ set(PYBIND11_TEST_FILES ) # Invoking cmake with something like: -# cmake -DPYBIND11_TEST_OVERRIDE="test_issues.cpp;test_picking.cpp" .. +# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_picking.cpp" .. # lets you override the tests that get compiled and run. You can restore to all tests with: # cmake -DPYBIND11_TEST_OVERRIDE= .. if (PYBIND11_TEST_OVERRIDE) diff --git a/tests/test_copy_move.cpp b/tests/test_copy_move.cpp index 90ca6da72..e80cdb8e2 100644 --- a/tests/test_copy_move.cpp +++ b/tests/test_copy_move.cpp @@ -187,4 +187,23 @@ test_initializer copy_move_policies([](py::module &m) { static PrivateOpNew x{}; return x; }, py::return_value_policy::reference); + + // #389: rvp::move should fall-through to copy on non-movable objects + struct MoveIssue1 { + int v; + MoveIssue1(int v) : v{v} {} + MoveIssue1(const MoveIssue1 &c) = default; + MoveIssue1(MoveIssue1 &&) = delete; + }; + + struct MoveIssue2 { + int v; + MoveIssue2(int v) : v{v} {} + MoveIssue2(MoveIssue2 &&) = default; + }; + + py::class_(m, "MoveIssue1").def(py::init()).def_readwrite("value", &MoveIssue1::v); + py::class_(m, "MoveIssue2").def(py::init()).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); }); diff --git a/tests/test_copy_move.py b/tests/test_copy_move.py index 386fce7b6..452f6fbba 100644 --- a/tests/test_copy_move.py +++ b/tests/test_copy_move.py @@ -109,3 +109,13 @@ def test_private_op_new(): assert "the object is neither movable nor copyable" in str(excinfo.value) assert m.private_op_new_reference().value == 1 + + +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) + assert m2.value == 2 + m1 = get_moveissue1(1) + assert m1.value == 1 diff --git a/tests/test_inheritance.cpp b/tests/test_inheritance.cpp index c19f58dc2..6129b3fcc 100644 --- a/tests/test_inheritance.cpp +++ b/tests/test_inheritance.cpp @@ -120,4 +120,24 @@ test_initializer inheritance([](py::module &m) { py::class_(m, "MismatchBase2"); py::class_, MismatchBase2>(m, "MismatchDerived2"); }); + + // #511: problem with inheritance + overwritten def_static + struct MyBase { + static std::unique_ptr make() { + return std::unique_ptr(new MyBase()); + } + }; + + struct MyDerived : MyBase { + static std::unique_ptr make() { + return std::unique_ptr(new MyDerived()); + } + }; + + py::class_(m, "MyBase") + .def_static("make", &MyBase::make); + + py::class_(m, "MyDerived") + .def_static("make", &MyDerived::make) + .def_static("make2", &MyDerived::make); }); diff --git a/tests/test_inheritance.py b/tests/test_inheritance.py index d1f537d1d..40630ae2e 100644 --- a/tests/test_inheritance.py +++ b/tests/test_inheritance.py @@ -76,3 +76,16 @@ def test_holder(): assert str(excinfo.value) == ("generic_type: type \"MismatchDerived2\" has a " "non-default holder type while its base " "\"MismatchBase2\" does not") + + +def test_inheritance_override_def_static(): + """#511: problem with inheritance + overwritten def_static""" + from pybind11_tests import MyBase, MyDerived + + b = MyBase.make() + d1 = MyDerived.make2() + d2 = MyDerived.make() + + assert isinstance(b, MyBase) + assert isinstance(d1, MyDerived) + assert isinstance(d2, MyDerived) diff --git a/tests/test_issues.cpp b/tests/test_issues.cpp deleted file mode 100644 index 6d88d9a9b..000000000 --- a/tests/test_issues.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* - tests/test_issues.cpp -- collection of testcases for miscellaneous issues - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#include "pybind11_tests.h" -#include "constructor_stats.h" -#include -#include -#include - -#define TRACKERS(CLASS) CLASS() { print_default_created(this); } ~CLASS() { print_destroyed(this); } -struct NestABase { int value = -2; TRACKERS(NestABase) }; -struct NestA : NestABase { int value = 3; NestA& operator+=(int i) { value += i; return *this; } TRACKERS(NestA) }; -struct NestB { NestA a; int value = 4; NestB& operator-=(int i) { value -= i; return *this; } TRACKERS(NestB) }; -struct NestC { NestB b; int value = 5; NestC& operator*=(int i) { value *= i; return *this; } TRACKERS(NestC) }; - -/// #393 -class OpTest1 {}; -class OpTest2 {}; - -OpTest1 operator+(const OpTest1 &, const OpTest1 &) { - py::print("Add OpTest1 with OpTest1"); - return OpTest1(); -} -OpTest2 operator+(const OpTest2 &, const OpTest2 &) { - py::print("Add OpTest2 with OpTest2"); - return OpTest2(); -} -OpTest2 operator+(const OpTest2 &, const OpTest1 &) { - py::print("Add OpTest2 with OpTest1"); - return OpTest2(); -} - -// #461 -class Dupe1 { -public: - Dupe1(int v) : v_{v} {} - int get_value() const { return v_; } -private: - int v_; -}; -class Dupe2 {}; -class Dupe3 {}; -class DupeException : public std::runtime_error {}; - -// #478 -template class custom_unique_ptr { -public: - custom_unique_ptr() { print_default_created(this); } - custom_unique_ptr(T *ptr) : _ptr{ptr} { print_created(this, ptr); } - custom_unique_ptr(custom_unique_ptr &&move) : _ptr{move._ptr} { move._ptr = nullptr; print_move_created(this); } - custom_unique_ptr &operator=(custom_unique_ptr &&move) { print_move_assigned(this); if (_ptr) destruct_ptr(); _ptr = move._ptr; move._ptr = nullptr; return *this; } - custom_unique_ptr(const custom_unique_ptr &) = delete; - void operator=(const custom_unique_ptr ©) = delete; - ~custom_unique_ptr() { print_destroyed(this); if (_ptr) destruct_ptr(); } -private: - T *_ptr = nullptr; - void destruct_ptr() { delete _ptr; } -}; -PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr); - -/// Issue #528: templated constructor -struct TplConstrClass { - template TplConstrClass(const T &arg) : str{arg} {} - std::string str; - bool operator==(const TplConstrClass &t) const { return t.str == str; } -}; -namespace std { -template <> struct hash { size_t operator()(const TplConstrClass &t) const { return std::hash()(t.str); } }; -} - -void init_issues(py::module &m) { - py::module m2 = m.def_submodule("issues"); - - // #137: const char* isn't handled properly - m2.def("print_cchar", [](const char *s) { return std::string(s); }); - - // #150: char bindings broken - m2.def("print_char", [](char c) { return std::string(1, c); }); - - // #159: virtual function dispatch has problems with similar-named functions - struct Base { virtual std::string dispatch() const { - /* for some reason MSVC2015 can't compile this if the function is pure virtual */ - return {}; - }; }; - - struct DispatchIssue : Base { - virtual std::string dispatch() const { - PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */); - } - }; - - py::class_(m2, "DispatchIssue") - .def(py::init<>()) - .def("dispatch", &Base::dispatch); - - m2.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); }); - - struct Placeholder { int i; Placeholder(int i) : i(i) { } }; - - py::class_(m2, "Placeholder") - .def(py::init()) - .def("__repr__", [](const Placeholder &p) { return "Placeholder[" + std::to_string(p.i) + "]"; }); - - // #181: iterator passthrough did not compile - m2.def("iterator_passthrough", [](py::iterator s) -> py::iterator { - return py::make_iterator(std::begin(s), std::end(s)); - }); - - // #187: issue involving std::shared_ptr<> return value policy & garbage collection - struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ }; - struct ElementA : ElementBase { - ElementA(int v) : v(v) { } - int value() { return v; } - int v; - }; - - struct ElementList { - void add(std::shared_ptr e) { l.push_back(e); } - std::vector> l; - }; - - py::class_> (m2, "ElementBase"); - - py::class_>(m2, "ElementA") - .def(py::init()) - .def("value", &ElementA::value); - - py::class_>(m2, "ElementList") - .def(py::init<>()) - .def("add", &ElementList::add) - .def("get", [](ElementList &el) { - py::list list; - for (auto &e : el.l) - list.append(py::cast(e)); - return list; - }); - - // (no id): should not be able to pass 'None' to a reference argument - m2.def("get_element", [](ElementA &el) { return el.value(); }); - - // (no id): don't cast doubles to ints - m2.def("expect_float", [](float f) { return f; }); - m2.def("expect_int", [](int i) { return i; }); - - try { - py::class_(m2, "Placeholder"); - throw std::logic_error("Expected an exception!"); - } catch (std::runtime_error &) { - /* All good */ - } - - // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid - class StrIssue { - public: - StrIssue(int i) : val{i} {} - StrIssue() : StrIssue(-1) {} - int value() const { return val; } - private: - int val; - }; - py::class_ si(m2, "StrIssue"); - si .def(py::init()) - .def(py::init<>()) - .def("__str__", [](const StrIssue &si) { return "StrIssue[" + std::to_string(si.value()) + "]"; }) - ; - - // Issue #328: first member in a class can't be used in operators - py::class_(m2, "NestABase").def(py::init<>()).def_readwrite("value", &NestABase::value); - py::class_(m2, "NestA").def(py::init<>()).def(py::self += int()) - .def("as_base", [](NestA &a) -> NestABase& { return (NestABase&) a; }, py::return_value_policy::reference_internal); - py::class_(m2, "NestB").def(py::init<>()).def(py::self -= int()).def_readwrite("a", &NestB::a); - py::class_(m2, "NestC").def(py::init<>()).def(py::self *= int()).def_readwrite("b", &NestC::b); - m2.def("get_NestA", [](const NestA &a) { return a.value; }); - m2.def("get_NestB", [](const NestB &b) { return b.value; }); - m2.def("get_NestC", [](const NestC &c) { return c.value; }); - - // Issue 389: r_v_p::move should fall-through to copy on non-movable objects - class MoveIssue1 { - public: - MoveIssue1(int v) : v{v} {} - MoveIssue1(const MoveIssue1 &c) { v = c.v; } - MoveIssue1(MoveIssue1 &&) = delete; - int v; - }; - class MoveIssue2 { - public: - MoveIssue2(int v) : v{v} {} - MoveIssue2(MoveIssue2 &&) = default; - int v; - }; - py::class_(m2, "MoveIssue1").def(py::init()).def_readwrite("value", &MoveIssue1::v); - py::class_(m2, "MoveIssue2").def(py::init()).def_readwrite("value", &MoveIssue2::v); - m2.def("get_moveissue1", [](int i) -> MoveIssue1 * { return new MoveIssue1(i); }, py::return_value_policy::move); - m2.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move); - - // Issues 392/397: overridding reference-returning functions - class OverrideTest { - public: - struct A { std::string value = "hi"; }; - std::string v; - A a; - explicit OverrideTest(const std::string &v) : v{v} {} - virtual std::string str_value() { return v; } - virtual std::string &str_ref() { return v; } - virtual A A_value() { return a; } - virtual A &A_ref() { return a; } - }; - class PyOverrideTest : public OverrideTest { - public: - using OverrideTest::OverrideTest; - std::string str_value() override { PYBIND11_OVERLOAD(std::string, OverrideTest, str_value); } - // Not allowed (uncommenting should hit a static_assert failure): we can't get a reference - // to a python numeric value, since we only copy values in the numeric type caster: -// std::string &str_ref() override { PYBIND11_OVERLOAD(std::string &, OverrideTest, str_ref); } - // But we can work around it like this: - private: - std::string _tmp; - std::string str_ref_helper() { PYBIND11_OVERLOAD(std::string, OverrideTest, str_ref); } - public: - std::string &str_ref() override { return _tmp = str_ref_helper(); } - - A A_value() override { PYBIND11_OVERLOAD(A, OverrideTest, A_value); } - A &A_ref() override { PYBIND11_OVERLOAD(A &, OverrideTest, A_ref); } - }; - py::class_(m2, "OverrideTest_A") - .def_readwrite("value", &OverrideTest::A::value); - py::class_(m2, "OverrideTest") - .def(py::init()) - .def("str_value", &OverrideTest::str_value) -// .def("str_ref", &OverrideTest::str_ref) - .def("A_value", &OverrideTest::A_value) - .def("A_ref", &OverrideTest::A_ref); - - /// Issue 393: need to return NotSupported to ensure correct arithmetic operator behavior - py::class_(m2, "OpTest1") - .def(py::init<>()) - .def(py::self + py::self); - - py::class_(m2, "OpTest2") - .def(py::init<>()) - .def(py::self + py::self) - .def("__add__", [](const OpTest2& c2, const OpTest1& c1) { return c2 + c1; }) - .def("__radd__", [](const OpTest2& c2, const OpTest1& c1) { return c2 + c1; }); - - // Issue 388: Can't make iterators via make_iterator() with different r/v policies - static std::vector list = { 1, 2, 3 }; - m2.def("make_iterator_1", []() { return py::make_iterator(list); }); - m2.def("make_iterator_2", []() { return py::make_iterator(list); }); - - static std::vector nothrows; - // Issue 461: registering two things with the same name: - py::class_(m2, "Dupe1") - .def("get_value", &Dupe1::get_value) - ; - m2.def("dupe1_factory", [](int v) { return new Dupe1(v); }); - - py::class_(m2, "Dupe2"); - py::exception(m2, "DupeException"); - - try { - m2.def("Dupe1", [](int v) { return new Dupe1(v); }); - nothrows.emplace_back("Dupe1"); - } - catch (std::runtime_error &) {} - try { - py::class_(m2, "dupe1_factory"); - nothrows.emplace_back("dupe1_factory"); - } - catch (std::runtime_error &) {} - try { - py::exception(m2, "Dupe2"); - nothrows.emplace_back("Dupe2"); - } - catch (std::runtime_error &) {} - try { - m2.def("DupeException", []() { return 30; }); - nothrows.emplace_back("DupeException1"); - } - catch (std::runtime_error &) {} - try { - py::class_(m2, "DupeException"); - nothrows.emplace_back("DupeException2"); - } - catch (std::runtime_error &) {} - m2.def("dupe_exception_failures", []() { - py::list l; - for (auto &e : nothrows) l.append(py::cast(e)); - return l; - }); - - /// Issue #471: shared pointer instance not dellocated - class SharedChild : public std::enable_shared_from_this { - public: - SharedChild() { print_created(this); } - ~SharedChild() { print_destroyed(this); } - }; - - class SharedParent { - public: - SharedParent() : child(std::make_shared()) { } - const SharedChild &get_child() const { return *child; } - - private: - std::shared_ptr child; - }; - - py::class_>(m, "SharedChild"); - py::class_>(m, "SharedParent") - .def(py::init<>()) - .def("get_child", &SharedParent::get_child, py::return_value_policy::reference); - - /// Issue/PR #478: unique ptrs constructed and freed without destruction - class SpecialHolderObj { - public: - int val = 0; - SpecialHolderObj *ch = nullptr; - SpecialHolderObj(int v, bool make_child = true) : val{v}, ch{make_child ? new SpecialHolderObj(val+1, false) : nullptr} - { print_created(this, val); } - ~SpecialHolderObj() { delete ch; print_destroyed(this); } - SpecialHolderObj *child() { return ch; } - }; - - py::class_>(m, "SpecialHolderObj") - .def(py::init()) - .def("child", &SpecialHolderObj::child, pybind11::return_value_policy::reference_internal) - .def_readwrite("val", &SpecialHolderObj::val) - .def_static("holder_cstats", &ConstructorStats::get>, - py::return_value_policy::reference); - - /// Issue #484: number conversion generates unhandled exceptions - m2.def("test_complex", [](float x) { py::print("{}"_s.format(x)); }); - m2.def("test_complex", [](std::complex x) { py::print("({}, {})"_s.format(x.real(), x.imag())); }); - - /// Issue #511: problem with inheritance + overwritten def_static - struct MyBase { - static std::unique_ptr make() { - return std::unique_ptr(new MyBase()); - } - }; - - struct MyDerived : MyBase { - static std::unique_ptr make() { - return std::unique_ptr(new MyDerived()); - } - }; - - py::class_(m2, "MyBase") - .def_static("make", &MyBase::make); - - py::class_(m2, "MyDerived") - .def_static("make", &MyDerived::make) - .def_static("make2", &MyDerived::make); - - py::dict d; - std::string bar = "bar"; - d["str"] = bar; - d["num"] = 3.7; - - /// Issue #528: templated constructor - m2.def("tpl_constr_vector", [](std::vector &) {}); - m2.def("tpl_constr_map", [](std::unordered_map &) {}); - m2.def("tpl_constr_set", [](std::unordered_set &) {}); -#if defined(PYBIND11_HAS_OPTIONAL) - m2.def("tpl_constr_optional", [](std::optional &) {}); -#elif defined(PYBIND11_HAS_EXP_OPTIONAL) - m2.def("tpl_constr_optional", [](std::experimental::optional &) {}); -#endif -} - -// MSVC workaround: trying to use a lambda here crashes MSVC -test_initializer issues(&init_issues); diff --git a/tests/test_issues.py b/tests/test_issues.py deleted file mode 100644 index 08678cfc5..000000000 --- a/tests/test_issues.py +++ /dev/null @@ -1,243 +0,0 @@ -import pytest -from pybind11_tests import ConstructorStats - - -def test_regressions(): - from pybind11_tests.issues import print_cchar, print_char - - # #137: const char* isn't handled properly - assert print_cchar("const char *") == "const char *" - # #150: char bindings broken - assert print_char("c") == "c" - - -def test_dispatch_issue(msg): - """#159: virtual function dispatch has problems with similar-named functions""" - from pybind11_tests.issues import DispatchIssue, dispatch_issue_go - - class PyClass1(DispatchIssue): - def dispatch(self): - return "Yay.." - - class PyClass2(DispatchIssue): - def dispatch(self): - with pytest.raises(RuntimeError) as excinfo: - super(PyClass2, self).dispatch() - assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"' - - p = PyClass1() - return dispatch_issue_go(p) - - b = PyClass2() - assert dispatch_issue_go(b) == "Yay.." - - -def test_iterator_passthrough(): - """#181: iterator passthrough did not compile""" - from pybind11_tests.issues import iterator_passthrough - - assert list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15]))) == [3, 5, 7, 9, 11, 13, 15] - - -def test_shared_ptr_gc(): - """// #187: issue involving std::shared_ptr<> return value policy & garbage collection""" - from pybind11_tests.issues import ElementList, ElementA - - el = ElementList() - for i in range(10): - el.add(ElementA(i)) - pytest.gc_collect() - for i, v in enumerate(el.get()): - assert i == v.value() - - -def test_no_id(msg): - from pybind11_tests.issues import get_element, expect_float, expect_int - - with pytest.raises(TypeError) as excinfo: - get_element(None) - assert msg(excinfo.value) == """ - get_element(): incompatible function arguments. The following argument types are supported: - 1. (arg0: m.issues.ElementA) -> int - - Invoked with: None - """ - - with pytest.raises(TypeError) as excinfo: - expect_int(5.2) - assert msg(excinfo.value) == """ - expect_int(): incompatible function arguments. The following argument types are supported: - 1. (arg0: int) -> int - - Invoked with: 5.2 - """ - assert expect_float(12) == 12 - - -def test_str_issue(msg): - """Issue #283: __str__ called on uninitialized instance when constructor arguments invalid""" - from pybind11_tests.issues import StrIssue - - assert str(StrIssue(3)) == "StrIssue[3]" - - with pytest.raises(TypeError) as excinfo: - str(StrIssue("no", "such", "constructor")) - assert msg(excinfo.value) == """ - __init__(): incompatible constructor arguments. The following argument types are supported: - 1. m.issues.StrIssue(arg0: int) - 2. m.issues.StrIssue() - - Invoked with: 'no', 'such', 'constructor' - """ - - -def test_nested(): - """ #328: first member in a class can't be used in operators""" - from pybind11_tests.issues import NestA, NestB, NestC, get_NestA, get_NestB, get_NestC - - a = NestA() - b = NestB() - c = NestC() - - a += 10 - assert get_NestA(a) == 13 - b.a += 100 - assert get_NestA(b.a) == 103 - c.b.a += 1000 - assert get_NestA(c.b.a) == 1003 - b -= 1 - assert get_NestB(b) == 3 - c.b -= 3 - assert get_NestB(c.b) == 1 - c *= 7 - assert get_NestC(c) == 35 - - abase = a.as_base() - assert abase.value == -2 - a.as_base().value += 44 - assert abase.value == 42 - assert c.b.a.as_base().value == -2 - c.b.a.as_base().value += 44 - assert c.b.a.as_base().value == 42 - - del c - pytest.gc_collect() - del a # Should't delete while abase is still alive - pytest.gc_collect() - - assert abase.value == 42 - del abase, b - pytest.gc_collect() - - -def test_move_fallback(): - from pybind11_tests.issues import get_moveissue1, get_moveissue2 - m2 = get_moveissue2(2) - assert m2.value == 2 - m1 = get_moveissue1(1) - assert m1.value == 1 - - -def test_override_ref(): - from pybind11_tests.issues import OverrideTest - o = OverrideTest("asdf") - - # Not allowed (see associated .cpp comment) - # i = o.str_ref() - # assert o.str_ref() == "asdf" - assert o.str_value() == "asdf" - - assert o.A_value().value == "hi" - a = o.A_ref() - assert a.value == "hi" - a.value = "bye" - assert a.value == "bye" - - -def test_operators_notimplemented(capture): - from pybind11_tests.issues import OpTest1, OpTest2 - with capture: - c1, c2 = OpTest1(), OpTest2() - c1 + c1 - c2 + c2 - c2 + c1 - c1 + c2 - assert capture == """ - Add OpTest1 with OpTest1 - Add OpTest2 with OpTest2 - Add OpTest2 with OpTest1 - Add OpTest2 with OpTest1 - """ - - -def test_iterator_rvpolicy(): - """ Issue 388: Can't make iterators via make_iterator() with different r/v policies """ - from pybind11_tests.issues import make_iterator_1 - from pybind11_tests.issues import make_iterator_2 - - assert list(make_iterator_1()) == [1, 2, 3] - assert list(make_iterator_2()) == [1, 2, 3] - assert not isinstance(make_iterator_1(), type(make_iterator_2())) - - -def test_dupe_assignment(): - """ Issue 461: overwriting a class with a function """ - from pybind11_tests.issues import dupe_exception_failures - assert dupe_exception_failures() == [] - - -def test_enable_shared_from_this_with_reference_rvp(): - """ Issue #471: shared pointer instance not dellocated """ - from pybind11_tests import SharedParent, SharedChild - - parent = SharedParent() - child = parent.get_child() - - cstats = ConstructorStats.get(SharedChild) - assert cstats.alive() == 1 - del child, parent - assert cstats.alive() == 0 - - -def test_non_destructed_holders(): - """ Issue #478: unique ptrs constructed and freed without destruction """ - from pybind11_tests import SpecialHolderObj - - a = SpecialHolderObj(123) - b = a.child() - - assert a.val == 123 - assert b.val == 124 - - cstats = SpecialHolderObj.holder_cstats() - assert cstats.alive() == 1 - del b - assert cstats.alive() == 1 - del a - assert cstats.alive() == 0 - - -def test_complex_cast(capture): - """ Issue #484: number conversion generates unhandled exceptions """ - from pybind11_tests.issues import test_complex - - with capture: - test_complex(1) - test_complex(2j) - - assert capture == """ - 1.0 - (0.0, 2.0) - """ - - -def test_inheritance_override_def_static(): - from pybind11_tests.issues import MyBase, MyDerived - - b = MyBase.make() - d1 = MyDerived.make2() - d2 = MyDerived.make() - - assert isinstance(b, MyBase) - assert isinstance(d1, MyDerived) - assert isinstance(d2, MyDerived) diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index 81b665b4d..670f6c3b7 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -170,6 +170,13 @@ int none3(std::shared_ptr &obj) { return obj ? obj->answer : -1; } int none4(std::shared_ptr *obj) { return obj && *obj ? (*obj)->answer : -1; } int none5(std::shared_ptr obj) { return obj ? obj->answer : -1; } +struct StrIssue { + int val = -1; + + StrIssue() = default; + StrIssue(int i) : val{i} {} +}; + test_initializer methods_and_attributes([](py::module &m) { py::class_ emna(m, "ExampleMandA"); emna.def(py::init<>()) @@ -315,6 +322,8 @@ test_initializer methods_and_attributes([](py::module &m) { m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); + m.def("ints_preferred", [](int i) { return i / 2; }, py::arg("i")); + m.def("ints_only", [](int i) { return i / 2; }, py::arg("i").noconvert()); /// Issue/PR #648: bad arg default debugging output #if !defined(NDEBUG) @@ -344,4 +353,11 @@ test_initializer methods_and_attributes([](py::module &m) { m.def("ok_none4", &none4, py::arg().none(true)); m.def("ok_none5", &none5); + // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid + py::class_(m, "StrIssue") + .def(py::init()) + .def(py::init<>()) + .def("__str__", [](const StrIssue &si) { + return "StrIssue[" + std::to_string(si.val) + "]"; } + ); }); diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 3ec3eb76b..95049cfd4 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -304,9 +304,9 @@ def test_cyclic_gc(): def test_noconvert_args(msg): - from pybind11_tests import ArgInspector, arg_inspect_func, floats_only, floats_preferred + import pybind11_tests as m - a = ArgInspector() + a = m.ArgInspector() assert msg(a.f("hi")) == """ loading ArgInspector1 argument WITH conversion allowed. Argument value = hi """ @@ -330,15 +330,15 @@ def test_noconvert_args(msg): """ assert (a.h("arg 1") == "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1") - assert msg(arg_inspect_func("A1", "A2")) == """ + assert msg(m.arg_inspect_func("A1", "A2")) == """ loading ArgInspector2 argument WITH conversion allowed. Argument value = A1 loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2 """ - assert floats_preferred(4) == 2.0 - assert floats_only(4.0) == 2.0 + assert m.floats_preferred(4) == 2.0 + assert m.floats_only(4.0) == 2.0 with pytest.raises(TypeError) as excinfo: - floats_only(4) + m.floats_only(4) assert msg(excinfo.value) == """ floats_only(): incompatible function arguments. The following argument types are supported: 1. (f: float) -> float @@ -346,6 +346,27 @@ def test_noconvert_args(msg): Invoked with: 4 """ + assert m.ints_preferred(4) == 2 + assert m.ints_preferred(True) == 0 + with pytest.raises(TypeError) as excinfo: + m.ints_preferred(4.0) + assert msg(excinfo.value) == """ + ints_preferred(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ # noqa: E501 line too long + + assert m.ints_only(4) == 2 + with pytest.raises(TypeError) as excinfo: + m.ints_only(4.0) + assert msg(excinfo.value) == """ + ints_only(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ + def test_bad_arg_default(msg): from pybind11_tests import debug_enabled, bad_arg_def_named, bad_arg_def_unnamed @@ -371,7 +392,7 @@ def test_bad_arg_default(msg): ) -def test_accepts_none(): +def test_accepts_none(msg): from pybind11_tests import (NoneTester, no_none1, no_none2, no_none3, no_none4, no_none5, ok_none1, ok_none2, ok_none3, ok_none4, ok_none5) @@ -407,9 +428,32 @@ def test_accepts_none(): # The first one still raises because you can't pass None as a lvalue reference arg: with pytest.raises(TypeError) as excinfo: assert ok_none1(None) == -1 - assert "incompatible function arguments" in str(excinfo.value) + assert msg(excinfo.value) == """ + ok_none1(): incompatible function arguments. The following argument types are supported: + 1. (arg0: m.NoneTester) -> int + + Invoked with: None + """ + # The rest take the argument as pointer or holder, and accept None: assert ok_none2(None) == -1 assert ok_none3(None) == -1 assert ok_none4(None) == -1 assert ok_none5(None) == -1 + + +def test_str_issue(msg): + """#283: __str__ called on uninitialized instance when constructor arguments invalid""" + from pybind11_tests import StrIssue + + assert str(StrIssue(3)) == "StrIssue[3]" + + with pytest.raises(TypeError) as excinfo: + str(StrIssue("no", "such", "constructor")) + assert msg(excinfo.value) == """ + __init__(): incompatible constructor arguments. The following argument types are supported: + 1. m.StrIssue(arg0: int) + 2. m.StrIssue() + + Invoked with: 'no', 'such', 'constructor' + """ diff --git a/tests/test_modules.cpp b/tests/test_modules.cpp index 50c7d8412..555ae079e 100644 --- a/tests/test_modules.cpp +++ b/tests/test_modules.cpp @@ -55,4 +55,47 @@ test_initializer modules([](py::module &m) { .def_readwrite("a2", &B::a2); m.attr("OD") = py::module::import("collections").attr("OrderedDict"); + + // Registering two things with the same name + m.def("duplicate_registration", []() { + class Dupe1 { }; + class Dupe2 { }; + class Dupe3 { }; + class DupeException { }; + + auto dm = py::module("dummy"); + auto failures = py::list(); + + py::class_(dm, "Dupe1"); + py::class_(dm, "Dupe2"); + dm.def("dupe1_factory", []() { return Dupe1(); }); + py::exception(dm, "DupeException"); + + try { + py::class_(dm, "Dupe1"); + failures.append("Dupe1 class"); + } catch (std::runtime_error &) {} + try { + dm.def("Dupe1", []() { return Dupe1(); }); + failures.append("Dupe1 function"); + } catch (std::runtime_error &) {} + try { + py::class_(dm, "dupe1_factory"); + failures.append("dupe1_factory"); + } catch (std::runtime_error &) {} + try { + py::exception(dm, "Dupe2"); + failures.append("Dupe2"); + } catch (std::runtime_error &) {} + try { + dm.def("DupeException", []() { return 30; }); + failures.append("DupeException1"); + } catch (std::runtime_error &) {} + try { + py::class_(dm, "DupeException"); + failures.append("DupeException2"); + } catch (std::runtime_error &) {} + + return failures; + }); }); diff --git a/tests/test_modules.py b/tests/test_modules.py index 2a2c7944b..1cb177f9a 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -61,3 +61,10 @@ def test_pydoc(): assert pybind11_tests.__doc__ == "pybind11 test module" assert pydoc.text.docmodule(pybind11_tests) + + +def test_duplicate_registration(): + """Registering two things with the same name""" + from pybind11_tests import duplicate_registration + + assert duplicate_registration() == [] diff --git a/tests/test_operator_overloading.cpp b/tests/test_operator_overloading.cpp index 4e868d939..cb22b12eb 100644 --- a/tests/test_operator_overloading.cpp +++ b/tests/test_operator_overloading.cpp @@ -56,7 +56,38 @@ private: float x, y; }; -test_initializer operator_overloading([](py::module &m) { +class C1 { }; +class C2 { }; + +int operator+(const C1 &, const C1 &) { return 11; } +int operator+(const C2 &, const C2 &) { return 22; } +int operator+(const C2 &, const C1 &) { return 21; } +int operator+(const C1 &, const C2 &) { return 12; } + +struct NestABase { + int value = -2; +}; + +struct NestA : NestABase { + int value = 3; + NestA& operator+=(int i) { value += i; return *this; } +}; + +struct NestB { + NestA a; + int value = 4; + NestB& operator-=(int i) { value -= i; return *this; } +}; + +struct NestC { + NestB b; + int value = 5; + NestC& operator*=(int i) { value *= i; return *this; } +}; + +test_initializer operator_overloading([](py::module &pm) { + auto m = pm.def_submodule("operators"); + py::class_(m, "Vector2") .def(py::init()) .def(py::self + py::self) @@ -81,4 +112,41 @@ test_initializer operator_overloading([](py::module &m) { ; m.attr("Vector") = m.attr("Vector2"); + + // #393: need to return NotSupported to ensure correct arithmetic operator behavior + py::class_(m, "C1") + .def(py::init<>()) + .def(py::self + py::self); + + py::class_(m, "C2") + .def(py::init<>()) + .def(py::self + py::self) + .def("__add__", [](const C2& c2, const C1& c1) { return c2 + c1; }) + .def("__radd__", [](const C2& c2, const C1& c1) { return c1 + c2; }); + + // #328: first member in a class can't be used in operators + py::class_(m, "NestABase") + .def(py::init<>()) + .def_readwrite("value", &NestABase::value); + + py::class_(m, "NestA") + .def(py::init<>()) + .def(py::self += int()) + .def("as_base", [](NestA &a) -> NestABase& { + return (NestABase&) a; + }, py::return_value_policy::reference_internal); + + py::class_(m, "NestB") + .def(py::init<>()) + .def(py::self -= int()) + .def_readwrite("a", &NestB::a); + + py::class_(m, "NestC") + .def(py::init<>()) + .def(py::self *= int()) + .def_readwrite("b", &NestC::b); + + m.def("get_NestA", [](const NestA &a) { return a.value; }); + m.def("get_NestB", [](const NestB &b) { return b.value; }); + m.def("get_NestC", [](const NestC &c) { return c.value; }); }); diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index dd37c3497..63dd54619 100644 --- a/tests/test_operator_overloading.py +++ b/tests/test_operator_overloading.py @@ -1,5 +1,9 @@ +import pytest +from pybind11_tests import ConstructorStats + + def test_operator_overloading(): - from pybind11_tests import Vector2, Vector, ConstructorStats + from pybind11_tests.operators import Vector2, Vector v1 = Vector2(1, 2) v2 = Vector(3, -1) @@ -51,3 +55,53 @@ def test_operator_overloading(): assert cstats.move_constructions >= 10 assert cstats.copy_assignments == 0 assert cstats.move_assignments == 0 + + +def test_operators_notimplemented(): + """#393: need to return NotSupported to ensure correct arithmetic operator behavior""" + from pybind11_tests.operators import C1, C2 + + c1, c2 = C1(), C2() + assert c1 + c1 == 11 + assert c2 + c2 == 22 + assert c2 + c1 == 21 + assert c1 + c2 == 12 + + +def test_nested(): + """#328: first member in a class can't be used in operators""" + from pybind11_tests.operators import NestA, NestB, NestC, get_NestA, get_NestB, get_NestC + + a = NestA() + b = NestB() + c = NestC() + + a += 10 + assert get_NestA(a) == 13 + b.a += 100 + assert get_NestA(b.a) == 103 + c.b.a += 1000 + assert get_NestA(c.b.a) == 1003 + b -= 1 + assert get_NestB(b) == 3 + c.b -= 3 + assert get_NestB(c.b) == 1 + c *= 7 + assert get_NestC(c) == 35 + + abase = a.as_base() + assert abase.value == -2 + a.as_base().value += 44 + assert abase.value == 42 + assert c.b.a.as_base().value == -2 + c.b.a.as_base().value += 44 + assert c.b.a.as_base().value == 42 + + del c + pytest.gc_collect() + del a # Should't delete while abase is still alive + pytest.gc_collect() + + assert abase.value == 42 + del abase, b + pytest.gc_collect() diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp index d130af6f1..46a23ee61 100644 --- a/tests/test_python_types.cpp +++ b/tests/test_python_types.cpp @@ -11,6 +11,7 @@ #include "pybind11_tests.h" #include "constructor_stats.h" #include +#include #ifdef _WIN32 # include @@ -215,6 +216,17 @@ std::vector> incr_int_wrappers() { return r; }; +/// Issue #528: templated constructor +struct TplCtorClass { + template TplCtorClass(const T &) { } + bool operator==(const TplCtorClass &) const { return true; } +}; + +namespace std { + template <> + struct hash { size_t operator()(const TplCtorClass &) const { return 0; } }; +} + test_initializer python_types([](py::module &m) { /* No constructor is explicitly defined below. An exception is raised when trying to construct it directly from Python */ @@ -501,6 +513,8 @@ test_initializer python_types([](py::module &m) { ); }); + m.def("string_roundtrip", [](const char *s) { return s; }); + // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/; char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; @@ -661,6 +675,19 @@ test_initializer python_types([](py::module &m) { return l; }); + /// Issue #484: number conversion generates unhandled exceptions + m.def("test_complex", [](float x) { return "{}"_s.format(x); }); + m.def("test_complex", [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); + + /// Issue #528: templated constructor + m.def("tpl_ctor_vector", [](std::vector &) {}); + m.def("tpl_ctor_map", [](std::unordered_map &) {}); + m.def("tpl_ctor_set", [](std::unordered_set &) {}); +#if defined(PYBIND11_HAS_OPTIONAL) + m.def("tpl_constr_optional", [](std::optional &) {}); +#elif defined(PYBIND11_HAS_EXP_OPTIONAL) + m.def("tpl_constr_optional", [](std::experimental::optional &) {}); +#endif }); #if defined(_MSC_VER) diff --git a/tests/test_python_types.py b/tests/test_python_types.py index 2af9432e4..79326028e 100644 --- a/tests/test_python_types.py +++ b/tests/test_python_types.py @@ -452,6 +452,12 @@ def test_implicit_casting(): assert z['l'] == [3, 6, 9, 12, 15] +def test_simple_string(): + from pybind11_tests import string_roundtrip + + assert string_roundtrip("const char *") == "const char *" + + def test_unicode_conversion(): """Tests unicode conversion and error reporting.""" import pybind11_tests @@ -699,3 +705,11 @@ def test_reference_wrapper(): assert refwrap_iiw(IncrIntWrapper(5)) == 5 assert refwrap_call_iiw(IncrIntWrapper(10), refwrap_iiw) == [10, 10, 10, 10] + + +def test_complex_cast(): + """#484: number conversion generates unhandled exceptions""" + from pybind11_tests import test_complex + + assert test_complex(1) == "1.0" + assert test_complex(2j) == "(0.0, 2.0)" diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index c2051fadb..89fde8f59 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -351,4 +351,14 @@ test_initializer sequences_and_iterators([](py::module &pm) { m.def("tuple_iterator", [](py::tuple x) { return test_random_access_iterator(x); }); m.def("list_iterator", [](py::list x) { return test_random_access_iterator(x); }); m.def("sequence_iterator", [](py::sequence x) { return test_random_access_iterator(x); }); + + // #181: iterator passthrough did not compile + m.def("iterator_passthrough", [](py::iterator s) -> py::iterator { + return py::make_iterator(std::begin(s), std::end(s)); + }); + + // #388: Can't make iterators via make_iterator() with different r/v policies + static std::vector list = { 1, 2, 3 }; + m.def("make_iterator_1", []() { return py::make_iterator(list); }); + m.def("make_iterator_2", []() { return py::make_iterator(list); }); }); diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index e04c579dd..012e97d97 100644 --- a/tests/test_sequences_and_iterators.py +++ b/tests/test_sequences_and_iterators.py @@ -147,3 +147,20 @@ def test_python_iterator_in_cpp(): assert all(m.tuple_iterator(tuple(r))) assert all(m.list_iterator(list(r))) assert all(m.sequence_iterator(r)) + + +def test_iterator_passthrough(): + """#181: iterator passthrough did not compile""" + from pybind11_tests.sequences_and_iterators import iterator_passthrough + + assert list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15]))) == [3, 5, 7, 9, 11, 13, 15] + + +def test_iterator_rvp(): + """#388: Can't make iterators via make_iterator() with different r/v policies """ + import pybind11_tests.sequences_and_iterators as m + + assert list(m.make_iterator_1()) == [1, 2, 3] + assert list(m.make_iterator_2()) == [1, 2, 3] + assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2())) + diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 1ece3d19d..91239e871 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -259,6 +259,18 @@ public: PYBIND11_DECLARE_HOLDER_TYPE(T, CustomUniquePtr); +struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ }; +struct ElementA : ElementBase { + ElementA(int v) : v(v) { } + int value() { return v; } + int v; +}; + +struct ElementList { + void add(std::shared_ptr e) { l.push_back(e); } + std::vector> l; +}; + test_initializer smart_ptr_and_references([](py::module &pm) { auto m = pm.def_submodule("smart_ptr"); @@ -309,4 +321,21 @@ test_initializer smart_ptr_and_references([](py::module &pm) { py::class_(m, "HeldByDefaultHolder") .def(py::init<>()) .def_static("load_shared_ptr", [](std::shared_ptr) {}); + + // #187: issue involving std::shared_ptr<> return value policy & garbage collection + py::class_>(m, "ElementBase"); + + py::class_>(m, "ElementA") + .def(py::init()) + .def("value", &ElementA::value); + + py::class_>(m, "ElementList") + .def(py::init<>()) + .def("add", &ElementList::add) + .def("get", [](ElementList &el) { + py::list list; + for (auto &e : el.l) + list.append(py::cast(e)); + return list; + }); }); diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 7396c9f77..144180d8c 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -234,3 +234,15 @@ def test_smart_ptr_from_default(): with pytest.raises(RuntimeError) as excinfo: HeldByDefaultHolder.load_shared_ptr(instance) assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo) + + +def test_shared_ptr_gc(): + """#187: issue involving std::shared_ptr<> return value policy & garbage collection""" + from pybind11_tests.smart_ptr import ElementList, ElementA + + el = ElementList() + for i in range(10): + el.add(ElementA(i)) + pytest.gc_collect() + for i, v in enumerate(el.get()): + assert i == v.value() diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index 5080b6db8..b42f650b5 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -311,6 +311,16 @@ void initialize_inherited_virtuals(py::module &m) { }; +struct Base { + /* for some reason MSVC2015 can't compile this if the function is pure virtual */ + virtual std::string dispatch() const { return {}; }; +}; + +struct DispatchIssue : Base { + virtual std::string dispatch() const { + PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */); + } +}; test_initializer virtual_functions([](py::module &m) { py::class_(m, "ExampleVirt") @@ -341,4 +351,51 @@ test_initializer virtual_functions([](py::module &m) { m.def("cstats_debug", &ConstructorStats::get); initialize_inherited_virtuals(m); + + // #159: virtual function dispatch has problems with similar-named functions + py::class_(m, "DispatchIssue") + .def(py::init<>()) + .def("dispatch", &Base::dispatch); + + m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); }); + + // #392/397: overridding reference-returning functions + class OverrideTest { + public: + struct A { std::string value = "hi"; }; + std::string v; + A a; + explicit OverrideTest(const std::string &v) : v{v} {} + virtual std::string str_value() { return v; } + virtual std::string &str_ref() { return v; } + virtual A A_value() { return a; } + virtual A &A_ref() { return a; } + }; + + class PyOverrideTest : public OverrideTest { + public: + using OverrideTest::OverrideTest; + std::string str_value() override { PYBIND11_OVERLOAD(std::string, OverrideTest, str_value); } + // Not allowed (uncommenting should hit a static_assert failure): we can't get a reference + // to a python numeric value, since we only copy values in the numeric type caster: +// std::string &str_ref() override { PYBIND11_OVERLOAD(std::string &, OverrideTest, str_ref); } + // But we can work around it like this: + private: + std::string _tmp; + std::string str_ref_helper() { PYBIND11_OVERLOAD(std::string, OverrideTest, str_ref); } + public: + std::string &str_ref() override { return _tmp = str_ref_helper(); } + + A A_value() override { PYBIND11_OVERLOAD(A, OverrideTest, A_value); } + A &A_ref() override { PYBIND11_OVERLOAD(A &, OverrideTest, A_ref); } + }; + + py::class_(m, "OverrideTest_A") + .def_readwrite("value", &OverrideTest::A::value); + py::class_(m, "OverrideTest") + .def(py::init()) + .def("str_value", &OverrideTest::str_value) +// .def("str_ref", &OverrideTest::str_ref) + .def("A_value", &OverrideTest::A_value) + .def("A_ref", &OverrideTest::A_ref); }); diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index b11c699df..3ec6fc230 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -257,3 +257,42 @@ def test_move_support(): assert mv_stats.copy_constructions == 1 assert nc_stats.move_constructions >= 0 assert mv_stats.move_constructions >= 0 + + +def test_dispatch_issue(msg): + """#159: virtual function dispatch has problems with similar-named functions""" + from pybind11_tests import DispatchIssue, dispatch_issue_go + + class PyClass1(DispatchIssue): + def dispatch(self): + return "Yay.." + + class PyClass2(DispatchIssue): + def dispatch(self): + with pytest.raises(RuntimeError) as excinfo: + super(PyClass2, self).dispatch() + assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"' + + p = PyClass1() + return dispatch_issue_go(p) + + b = PyClass2() + assert dispatch_issue_go(b) == "Yay.." + + +def test_override_ref(): + """#392/397: overridding reference-returning functions""" + from pybind11_tests import OverrideTest + + o = OverrideTest("asdf") + + # Not allowed (see associated .cpp comment) + # i = o.str_ref() + # assert o.str_ref() == "asdf" + assert o.str_value() == "asdf" + + assert o.A_value().value == "hi" + a = o.A_ref() + assert a.value == "hi" + a.value = "bye" + assert a.value == "bye"