Move tests from short translation units into their logical parents

This commit is contained in:
Dean Moldovan 2017-06-22 23:42:11 +02:00
parent 83e328f58c
commit 0bc272b2e9
10 changed files with 365 additions and 426 deletions

View File

@ -325,7 +325,7 @@ ensuring member initialization and (eventual) destruction.
.. seealso:: .. seealso::
See the file :file:`tests/test_alias_initialization.cpp` for complete examples See the file :file:`tests/test_virtual_functions.cpp` for complete examples
showing both normal and forced trampoline instantiation. showing both normal and forced trampoline instantiation.
.. _custom_constructors: .. _custom_constructors:

View File

@ -26,7 +26,6 @@ endif()
# Full set of test files (you can override these; see below) # Full set of test files (you can override these; see below)
set(PYBIND11_TEST_FILES set(PYBIND11_TEST_FILES
test_alias_initialization.cpp
test_buffers.cpp test_buffers.cpp
test_builtin_casters.cpp test_builtin_casters.cpp
test_call_policies.cpp test_call_policies.cpp
@ -40,7 +39,6 @@ set(PYBIND11_TEST_FILES
test_enum.cpp test_enum.cpp
test_eval.cpp test_eval.cpp
test_exceptions.cpp test_exceptions.cpp
test_inheritance.cpp
test_kwargs_and_defaults.cpp test_kwargs_and_defaults.cpp
test_methods_and_attributes.cpp test_methods_and_attributes.cpp
test_modules.cpp test_modules.cpp

View File

@ -1,62 +0,0 @@
/*
tests/test_alias_initialization.cpp -- test cases and example of different trampoline
initialization modes
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, Jason Rhinelander <jason@imaginary.ca>
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"
test_initializer alias_initialization([](py::module &m) {
// don't invoke Python dispatch classes by default when instantiating C++ classes that were not
// extended on the Python side
struct A {
virtual ~A() {}
virtual void f() { py::print("A.f()"); }
};
struct PyA : A {
PyA() { py::print("PyA.PyA()"); }
~PyA() { py::print("PyA.~PyA()"); }
void f() override {
py::print("PyA.f()");
PYBIND11_OVERLOAD(void, A, f);
}
};
auto call_f = [](A *a) { a->f(); };
py::class_<A, PyA>(m, "A")
.def(py::init<>())
.def("f", &A::f);
m.def("call_f", call_f);
// ... unless we explicitly request it, as in this example:
struct A2 {
virtual ~A2() {}
virtual void f() { py::print("A2.f()"); }
};
struct PyA2 : A2 {
PyA2() { py::print("PyA2.PyA2()"); }
~PyA2() { py::print("PyA2.~PyA2()"); }
void f() override {
py::print("PyA2.f()");
PYBIND11_OVERLOAD(void, A2, f);
}
};
py::class_<A2, PyA2>(m, "A2")
.def(py::init_alias<>())
.def("f", &A2::f);
m.def("call_f", [](A2 *a2) { a2->f(); });
});

View File

@ -1,80 +0,0 @@
import pytest
def test_alias_delay_initialization1(capture):
"""
A only initializes its trampoline class when we inherit from it; if we just
create and use an A instance directly, the trampoline initialization is
bypassed and we only initialize an A() instead (for performance reasons).
"""
from pybind11_tests import A, call_f
class B(A):
def __init__(self):
super(B, self).__init__()
def f(self):
print("In python f()")
# C++ version
with capture:
a = A()
call_f(a)
del a
pytest.gc_collect()
assert capture == "A.f()"
# Python version
with capture:
b = B()
call_f(b)
del b
pytest.gc_collect()
assert capture == """
PyA.PyA()
PyA.f()
In python f()
PyA.~PyA()
"""
def test_alias_delay_initialization2(capture):
"""A2, unlike the above, is configured to always initialize the alias; while
the extra initialization and extra class layer has small virtual dispatch
performance penalty, it also allows us to do more things with the trampoline
class such as defining local variables and performing construction/destruction.
"""
from pybind11_tests import A2, call_f
class B2(A2):
def __init__(self):
super(B2, self).__init__()
def f(self):
print("In python B2.f()")
# No python subclass version
with capture:
a2 = A2()
call_f(a2)
del a2
pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
A2.f()
PyA2.~PyA2()
"""
# Python subclass version
with capture:
b2 = B2()
call_f(b2)
del b2
pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
In python B2.f()
PyA2.~PyA2()
"""

View File

@ -23,6 +23,133 @@ TEST_SUBMODULE(class_, m) {
py::class_<NoConstructor>(m, "NoConstructor") py::class_<NoConstructor>(m, "NoConstructor")
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); .def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
// test_inheritance
class Pet {
public:
Pet(const std::string &name, const std::string &species)
: m_name(name), m_species(species) {}
std::string name() const { return m_name; }
std::string species() const { return m_species; }
private:
std::string m_name;
std::string m_species;
};
class Dog : public Pet {
public:
Dog(const std::string &name) : Pet(name, "dog") {}
std::string bark() const { return "Woof!"; }
};
class Rabbit : public Pet {
public:
Rabbit(const std::string &name) : Pet(name, "parrot") {}
};
class Hamster : public Pet {
public:
Hamster(const std::string &name) : Pet(name, "rodent") {}
};
class Chimera : public Pet {
Chimera() : Pet("Kimmy", "chimera") {}
};
py::class_<Pet> pet_class(m, "Pet");
pet_class
.def(py::init<std::string, std::string>())
.def("name", &Pet::name)
.def("species", &Pet::species);
/* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class)
.def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit, Pet>(m, "Rabbit")
.def(py::init<std::string>());
/* And another: list parent in class template arguments */
py::class_<Hamster, Pet>(m, "Hamster")
.def(py::init<std::string>());
/* Constructors are not inherited by default */
py::class_<Chimera, Pet>(m, "Chimera");
m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
// test_automatic_upcasting
struct BaseClass { virtual ~BaseClass() {} };
struct DerivedClass1 : BaseClass { };
struct DerivedClass2 : BaseClass { };
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
m.def("return_class_n", [](int n) -> BaseClass* {
if (n == 1) return new DerivedClass1();
if (n == 2) return new DerivedClass2();
return new BaseClass();
});
m.def("return_none", []() -> BaseClass* { return nullptr; });
// test_isinstance
m.def("check_instances", [](py::list l) {
return py::make_tuple(
py::isinstance<py::tuple>(l[0]),
py::isinstance<py::dict>(l[1]),
py::isinstance<Pet>(l[2]),
py::isinstance<Pet>(l[3]),
py::isinstance<Dog>(l[4]),
py::isinstance<Rabbit>(l[5]),
py::isinstance<UnregisteredType>(l[6])
);
});
// test_mismatched_holder
struct MismatchBase1 { };
struct MismatchDerived1 : MismatchBase1 { };
struct MismatchBase2 { };
struct MismatchDerived2 : MismatchBase2 { };
m.def("mismatched_holder_1", []() {
auto mod = py::module::import("__main__");
py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1");
py::class_<MismatchDerived1, MismatchBase1>(mod, "MismatchDerived1");
});
m.def("mismatched_holder_2", []() {
auto mod = py::module::import("__main__");
py::class_<MismatchBase2>(mod, "MismatchBase2");
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>,
MismatchBase2>(mod, "MismatchDerived2");
});
// test_override_static
// #511: problem with inheritance + overwritten def_static
struct MyBase {
static std::unique_ptr<MyBase> make() {
return std::unique_ptr<MyBase>(new MyBase());
}
};
struct MyDerived : MyBase {
static std::unique_ptr<MyDerived> make() {
return std::unique_ptr<MyDerived>(new MyDerived());
}
};
py::class_<MyBase>(m, "MyBase")
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m, "MyDerived")
.def_static("make", &MyDerived::make)
.def_static("make2", &MyDerived::make);
} }
template <int N> class BreaksBase {}; template <int N> class BreaksBase {};

View File

@ -42,3 +42,80 @@ def test_docstrings(doc):
Return an instance Return an instance
""" """
def test_inheritance(msg):
roger = m.Rabbit('Rabbit')
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
assert m.pet_name_species(roger) == "Rabbit is a parrot"
polly = m.Pet('Polly', 'parrot')
assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
assert m.pet_name_species(polly) == "Polly is a parrot"
molly = m.Dog('Molly')
assert molly.name() + " is a " + molly.species() == "Molly is a dog"
assert m.pet_name_species(molly) == "Molly is a dog"
fred = m.Hamster('Fred')
assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
assert m.dog_bark(molly) == "Woof!"
with pytest.raises(TypeError) as excinfo:
m.dog_bark(polly)
assert msg(excinfo.value) == """
dog_bark(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.class_.Dog) -> str
Invoked with: <m.class_.Pet object at 0>
"""
with pytest.raises(TypeError) as excinfo:
m.Chimera("lion", "goat")
assert "No constructor defined!" in str(excinfo.value)
def test_automatic_upcasting():
assert type(m.return_class_1()).__name__ == "DerivedClass1"
assert type(m.return_class_2()).__name__ == "DerivedClass2"
assert type(m.return_none()).__name__ == "NoneType"
# Repeat these a few times in a random order to ensure no invalid caching is applied
assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
assert type(m.return_class_n(0)).__name__ == "BaseClass"
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
assert type(m.return_class_n(0)).__name__ == "BaseClass"
assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
def test_isinstance():
objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4
expected = (True, True, True, True, True, False, False)
assert m.check_instances(objects) == expected
def test_mismatched_holder():
import re
with pytest.raises(RuntimeError) as excinfo:
m.mismatched_holder_1()
assert re.match('generic_type: type ".*MismatchDerived1" does not have a non-default '
'holder type while its base ".*MismatchBase1" does', str(excinfo.value))
with pytest.raises(RuntimeError) as excinfo:
m.mismatched_holder_2()
assert re.match('generic_type: type ".*MismatchDerived2" has a non-default holder type '
'while its base ".*MismatchBase2" does not', str(excinfo.value))
def test_override_static():
"""#511: problem with inheritance + overwritten def_static"""
b = m.MyBase.make()
d1 = m.MyDerived.make2()
d2 = m.MyDerived.make()
assert isinstance(b, m.MyBase)
assert isinstance(d1, m.MyDerived)
assert isinstance(d2, m.MyDerived)

View File

@ -1,141 +0,0 @@
/*
tests/test_inheritance.cpp -- inheritance, automatic upcasting for polymorphic types
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
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"
class Pet {
public:
Pet(const std::string &name, const std::string &species)
: m_name(name), m_species(species) {}
std::string name() const { return m_name; }
std::string species() const { return m_species; }
private:
std::string m_name;
std::string m_species;
};
class Dog : public Pet {
public:
Dog(const std::string &name) : Pet(name, "dog") {}
std::string bark() const { return "Woof!"; }
};
class Rabbit : public Pet {
public:
Rabbit(const std::string &name) : Pet(name, "parrot") {}
};
class Hamster : public Pet {
public:
Hamster(const std::string &name) : Pet(name, "rodent") {}
};
class Chimera : public Pet {
Chimera() : Pet("Kimmy", "chimera") {}
};
std::string pet_name_species(const Pet &pet) {
return pet.name() + " is a " + pet.species();
}
std::string dog_bark(const Dog &dog) {
return dog.bark();
}
struct BaseClass { virtual ~BaseClass() {} };
struct DerivedClass1 : BaseClass { };
struct DerivedClass2 : BaseClass { };
struct MismatchBase1 { };
struct MismatchDerived1 : MismatchBase1 { };
struct MismatchBase2 { };
struct MismatchDerived2 : MismatchBase2 { };
test_initializer inheritance([](py::module &m) {
py::class_<Pet> pet_class(m, "Pet");
pet_class
.def(py::init<std::string, std::string>())
.def("name", &Pet::name)
.def("species", &Pet::species);
/* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class)
.def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit, Pet>(m, "Rabbit")
.def(py::init<std::string>());
/* And another: list parent in class template arguments */
py::class_<Hamster, Pet>(m, "Hamster")
.def(py::init<std::string>());
py::class_<Chimera, Pet>(m, "Chimera");
m.def("pet_name_species", pet_name_species);
m.def("dog_bark", dog_bark);
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
m.def("return_class_n", [](int n) -> BaseClass* {
if (n == 1) return new DerivedClass1();
if (n == 2) return new DerivedClass2();
return new BaseClass();
});
m.def("return_none", []() -> BaseClass* { return nullptr; });
m.def("test_isinstance", [](py::list l) {
return py::make_tuple(
py::isinstance<py::tuple>(l[0]),
py::isinstance<py::dict>(l[1]),
py::isinstance<Pet>(l[2]),
py::isinstance<Pet>(l[3]),
py::isinstance<Dog>(l[4]),
py::isinstance<Rabbit>(l[5]),
py::isinstance<UnregisteredType>(l[6])
);
});
m.def("test_mismatched_holder_type_1", []() {
auto m = py::module::import("__main__");
py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(m, "MismatchBase1");
py::class_<MismatchDerived1, MismatchBase1>(m, "MismatchDerived1");
});
m.def("test_mismatched_holder_type_2", []() {
auto m = py::module::import("__main__");
py::class_<MismatchBase2>(m, "MismatchBase2");
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(m, "MismatchDerived2");
});
// #511: problem with inheritance + overwritten def_static
struct MyBase {
static std::unique_ptr<MyBase> make() {
return std::unique_ptr<MyBase>(new MyBase());
}
};
struct MyDerived : MyBase {
static std::unique_ptr<MyDerived> make() {
return std::unique_ptr<MyDerived>(new MyDerived());
}
};
py::class_<MyBase>(m, "MyBase")
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m, "MyDerived")
.def_static("make", &MyDerived::make)
.def_static("make2", &MyDerived::make);
});

View File

@ -1,91 +0,0 @@
import pytest
def test_inheritance(msg):
from pybind11_tests import Pet, Dog, Rabbit, Hamster, Chimera, dog_bark, pet_name_species
roger = Rabbit('Rabbit')
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
assert pet_name_species(roger) == "Rabbit is a parrot"
polly = Pet('Polly', 'parrot')
assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
assert pet_name_species(polly) == "Polly is a parrot"
molly = Dog('Molly')
assert molly.name() + " is a " + molly.species() == "Molly is a dog"
assert pet_name_species(molly) == "Molly is a dog"
fred = Hamster('Fred')
assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
assert dog_bark(molly) == "Woof!"
with pytest.raises(TypeError) as excinfo:
dog_bark(polly)
assert msg(excinfo.value) == """
dog_bark(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.Dog) -> str
Invoked with: <m.Pet object at 0>
"""
with pytest.raises(TypeError) as excinfo:
Chimera("lion", "goat")
assert "No constructor defined!" in str(excinfo.value)
def test_automatic_upcasting():
from pybind11_tests import return_class_1, return_class_2, return_class_n, return_none
assert type(return_class_1()).__name__ == "DerivedClass1"
assert type(return_class_2()).__name__ == "DerivedClass2"
assert type(return_none()).__name__ == "NoneType"
# Repeat these a few times in a random order to ensure no invalid caching
# is applied
assert type(return_class_n(1)).__name__ == "DerivedClass1"
assert type(return_class_n(2)).__name__ == "DerivedClass2"
assert type(return_class_n(0)).__name__ == "BaseClass"
assert type(return_class_n(2)).__name__ == "DerivedClass2"
assert type(return_class_n(2)).__name__ == "DerivedClass2"
assert type(return_class_n(0)).__name__ == "BaseClass"
assert type(return_class_n(1)).__name__ == "DerivedClass1"
def test_isinstance():
from pybind11_tests import test_isinstance, Pet, Dog
objects = [tuple(), dict(), Pet("Polly", "parrot")] + [Dog("Molly")] * 4
expected = (True, True, True, True, True, False, False)
assert test_isinstance(objects) == expected
def test_holder():
from pybind11_tests import test_mismatched_holder_type_1, test_mismatched_holder_type_2
with pytest.raises(RuntimeError) as excinfo:
test_mismatched_holder_type_1()
assert str(excinfo.value) == ("generic_type: type \"MismatchDerived1\" does not have "
"a non-default holder type while its base "
"\"MismatchBase1\" does")
with pytest.raises(RuntimeError) as excinfo:
test_mismatched_holder_type_2()
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)

View File

@ -322,7 +322,7 @@ struct DispatchIssue : Base {
} }
}; };
test_initializer virtual_functions([](py::module &m) { TEST_SUBMODULE(virtual_functions, m) {
py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt") py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt")
.def(py::init<int>()) .def(py::init<int>())
/* Reference original class in function definitions */ /* Reference original class in function definitions */
@ -352,6 +352,52 @@ test_initializer virtual_functions([](py::module &m) {
m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>); m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>);
initialize_inherited_virtuals(m); initialize_inherited_virtuals(m);
// test_alias_delay_initialization1
// don't invoke Python dispatch classes by default when instantiating C++ classes
// that were not extended on the Python side
struct A {
virtual ~A() {}
virtual void f() { py::print("A.f()"); }
};
struct PyA : A {
PyA() { py::print("PyA.PyA()"); }
~PyA() { py::print("PyA.~PyA()"); }
void f() override {
py::print("PyA.f()");
PYBIND11_OVERLOAD(void, A, f);
}
};
py::class_<A, PyA>(m, "A")
.def(py::init<>())
.def("f", &A::f);
m.def("call_f", [](A *a) { a->f(); });
// test_alias_delay_initialization2
// ... unless we explicitly request it, as in this example:
struct A2 {
virtual ~A2() {}
virtual void f() { py::print("A2.f()"); }
};
struct PyA2 : A2 {
PyA2() { py::print("PyA2.PyA2()"); }
~PyA2() { py::print("PyA2.~PyA2()"); }
void f() override {
py::print("PyA2.f()");
PYBIND11_OVERLOAD(void, A2, f);
}
};
py::class_<A2, PyA2>(m, "A2")
.def(py::init_alias<>())
.def("f", &A2::f);
m.def("call_f", [](A2 *a2) { a2->f(); });
// #159: virtual function dispatch has problems with similar-named functions // #159: virtual function dispatch has problems with similar-named functions
py::class_<Base, DispatchIssue>(m, "DispatchIssue") py::class_<Base, DispatchIssue>(m, "DispatchIssue")
.def(py::init<>()) .def(py::init<>())
@ -398,4 +444,4 @@ test_initializer virtual_functions([](py::module &m) {
// .def("str_ref", &OverrideTest::str_ref) // .def("str_ref", &OverrideTest::str_ref)
.def("A_value", &OverrideTest::A_value) .def("A_value", &OverrideTest::A_value)
.def("A_ref", &OverrideTest::A_ref); .def("A_ref", &OverrideTest::A_ref);
}); }

View File

@ -1,13 +1,11 @@
import pytest import pytest
import pybind11_tests
from pybind11_tests import virtual_functions as m
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
def test_override(capture, msg): def test_override(capture, msg):
from pybind11_tests import (ExampleVirt, runExampleVirt, runExampleVirtVirtual, class ExtendedExampleVirt(m.ExampleVirt):
runExampleVirtBool)
class ExtendedExampleVirt(ExampleVirt):
def __init__(self, state): def __init__(self, state):
super(ExtendedExampleVirt, self).__init__(state + 1) super(ExtendedExampleVirt, self).__init__(state + 1)
self.data = "Hello world" self.data = "Hello world"
@ -33,40 +31,40 @@ def test_override(capture, msg):
def get_string2(self): def get_string2(self):
return "override2" return "override2"
ex12 = ExampleVirt(10) ex12 = m.ExampleVirt(10)
with capture: with capture:
assert runExampleVirt(ex12, 20) == 30 assert m.runExampleVirt(ex12, 20) == 30
assert capture == """ assert capture == """
Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2) Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
""" # noqa: E501 line too long """ # noqa: E501 line too long
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
runExampleVirtVirtual(ex12) m.runExampleVirtVirtual(ex12)
assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"' assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'
ex12p = ExtendedExampleVirt(10) ex12p = ExtendedExampleVirt(10)
with capture: with capture:
assert runExampleVirt(ex12p, 20) == 32 assert m.runExampleVirt(ex12p, 20) == 32
assert capture == """ assert capture == """
ExtendedExampleVirt::run(20), calling parent.. ExtendedExampleVirt::run(20), calling parent..
Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2) Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
""" # noqa: E501 line too long """ # noqa: E501 line too long
with capture: with capture:
assert runExampleVirtBool(ex12p) is False assert m.runExampleVirtBool(ex12p) is False
assert capture == "ExtendedExampleVirt::run_bool()" assert capture == "ExtendedExampleVirt::run_bool()"
with capture: with capture:
runExampleVirtVirtual(ex12p) m.runExampleVirtVirtual(ex12p)
assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world" assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"
ex12p2 = ExtendedExampleVirt2(15) ex12p2 = ExtendedExampleVirt2(15)
with capture: with capture:
assert runExampleVirt(ex12p2, 50) == 68 assert m.runExampleVirt(ex12p2, 50) == 68
assert capture == """ assert capture == """
ExtendedExampleVirt::run(50), calling parent.. ExtendedExampleVirt::run(50), calling parent..
Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2) Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
""" # noqa: E501 line too long """ # noqa: E501 line too long
cstats = ConstructorStats.get(ExampleVirt) cstats = ConstructorStats.get(m.ExampleVirt)
assert cstats.alive() == 3 assert cstats.alive() == 3
del ex12, ex12p, ex12p2 del ex12, ex12p, ex12p2
assert cstats.alive() == 0 assert cstats.alive() == 0
@ -75,14 +73,88 @@ def test_override(capture, msg):
assert cstats.move_constructions >= 0 assert cstats.move_constructions >= 0
def test_inheriting_repeat(): def test_alias_delay_initialization1(capture):
from pybind11_tests import A_Repeat, B_Repeat, C_Repeat, D_Repeat, A_Tpl, B_Tpl, C_Tpl, D_Tpl """`A` only initializes its trampoline class when we inherit from it
class AR(A_Repeat): If we just create and use an A instance directly, the trampoline initialization is
bypassed and we only initialize an A() instead (for performance reasons).
"""
class B(m.A):
def __init__(self):
super(B, self).__init__()
def f(self):
print("In python f()")
# C++ version
with capture:
a = m.A()
m.call_f(a)
del a
pytest.gc_collect()
assert capture == "A.f()"
# Python version
with capture:
b = B()
m.call_f(b)
del b
pytest.gc_collect()
assert capture == """
PyA.PyA()
PyA.f()
In python f()
PyA.~PyA()
"""
def test_alias_delay_initialization2(capture):
"""`A2`, unlike the above, is configured to always initialize the alias
While the extra initialization and extra class layer has small virtual dispatch
performance penalty, it also allows us to do more things with the trampoline
class such as defining local variables and performing construction/destruction.
"""
class B2(m.A2):
def __init__(self):
super(B2, self).__init__()
def f(self):
print("In python B2.f()")
# No python subclass version
with capture:
a2 = m.A2()
m.call_f(a2)
del a2
pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
A2.f()
PyA2.~PyA2()
"""
# Python subclass version
with capture:
b2 = B2()
m.call_f(b2)
del b2
pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
In python B2.f()
PyA2.~PyA2()
"""
def test_inheriting_repeat():
class AR(m.A_Repeat):
def unlucky_number(self): def unlucky_number(self):
return 99 return 99
class AT(A_Tpl): class AT(m.A_Tpl):
def unlucky_number(self): def unlucky_number(self):
return 999 return 999
@ -96,21 +168,21 @@ def test_inheriting_repeat():
assert obj.unlucky_number() == 999 assert obj.unlucky_number() == 999
assert obj.say_everything() == "hi 999" assert obj.say_everything() == "hi 999"
for obj in [B_Repeat(), B_Tpl()]: for obj in [m.B_Repeat(), m.B_Tpl()]:
assert obj.say_something(3) == "B says hi 3 times" assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 13 assert obj.unlucky_number() == 13
assert obj.lucky_number() == 7.0 assert obj.lucky_number() == 7.0
assert obj.say_everything() == "B says hi 1 times 13" assert obj.say_everything() == "B says hi 1 times 13"
for obj in [C_Repeat(), C_Tpl()]: for obj in [m.C_Repeat(), m.C_Tpl()]:
assert obj.say_something(3) == "B says hi 3 times" assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444 assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 888.0 assert obj.lucky_number() == 888.0
assert obj.say_everything() == "B says hi 1 times 4444" assert obj.say_everything() == "B says hi 1 times 4444"
class CR(C_Repeat): class CR(m.C_Repeat):
def lucky_number(self): def lucky_number(self):
return C_Repeat.lucky_number(self) + 1.25 return m.C_Repeat.lucky_number(self) + 1.25
obj = CR() obj = CR()
assert obj.say_something(3) == "B says hi 3 times" assert obj.say_something(3) == "B says hi 3 times"
@ -118,7 +190,7 @@ def test_inheriting_repeat():
assert obj.lucky_number() == 889.25 assert obj.lucky_number() == 889.25
assert obj.say_everything() == "B says hi 1 times 4444" assert obj.say_everything() == "B says hi 1 times 4444"
class CT(C_Tpl): class CT(m.C_Tpl):
pass pass
obj = CT() obj = CT()
@ -147,14 +219,14 @@ def test_inheriting_repeat():
assert obj.lucky_number() == 888000.0 assert obj.lucky_number() == 888000.0
assert obj.say_everything() == "B says hi 1 times 4444" assert obj.say_everything() == "B says hi 1 times 4444"
class DR(D_Repeat): class DR(m.D_Repeat):
def unlucky_number(self): def unlucky_number(self):
return 123 return 123
def lucky_number(self): def lucky_number(self):
return 42.0 return 42.0
for obj in [D_Repeat(), D_Tpl()]: for obj in [m.D_Repeat(), m.D_Tpl()]:
assert obj.say_something(3) == "B says hi 3 times" assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444 assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 888.0 assert obj.lucky_number() == 888.0
@ -166,7 +238,7 @@ def test_inheriting_repeat():
assert obj.lucky_number() == 42.0 assert obj.lucky_number() == 42.0
assert obj.say_everything() == "B says hi 1 times 123" assert obj.say_everything() == "B says hi 1 times 123"
class DT(D_Tpl): class DT(m.D_Tpl):
def say_something(self, times): def say_something(self, times):
return "DT says:" + (' quack' * times) return "DT says:" + (' quack' * times)
@ -189,7 +261,7 @@ def test_inheriting_repeat():
def unlucky_number(self): def unlucky_number(self):
return -3 return -3
class BT(B_Tpl): class BT(m.B_Tpl):
def say_something(self, times): def say_something(self, times):
return "BT" * times return "BT" * times
@ -209,31 +281,28 @@ def test_inheriting_repeat():
# PyPy: Reference count > 1 causes call with noncopyable instance # PyPy: Reference count > 1 causes call with noncopyable instance
# to fail in ncv1.print_nc() # to fail in ncv1.print_nc()
@pytest.unsupported_on_pypy @pytest.unsupported_on_pypy
@pytest.mark.skipif(not hasattr(pybind11_tests, 'NCVirt'), @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC")
reason="NCVirt test broken on ICPC")
def test_move_support(): def test_move_support():
from pybind11_tests import NCVirt, NonCopyable, Movable class NCVirtExt(m.NCVirt):
class NCVirtExt(NCVirt):
def get_noncopyable(self, a, b): def get_noncopyable(self, a, b):
# Constructs and returns a new instance: # Constructs and returns a new instance:
nc = NonCopyable(a * a, b * b) nc = m.NonCopyable(a * a, b * b)
return nc return nc
def get_movable(self, a, b): def get_movable(self, a, b):
# Return a referenced copy # Return a referenced copy
self.movable = Movable(a, b) self.movable = m.Movable(a, b)
return self.movable return self.movable
class NCVirtExt2(NCVirt): class NCVirtExt2(m.NCVirt):
def get_noncopyable(self, a, b): def get_noncopyable(self, a, b):
# Keep a reference: this is going to throw an exception # Keep a reference: this is going to throw an exception
self.nc = NonCopyable(a, b) self.nc = m.NonCopyable(a, b)
return self.nc return self.nc
def get_movable(self, a, b): def get_movable(self, a, b):
# Return a new instance without storing it # Return a new instance without storing it
return Movable(a, b) return m.Movable(a, b)
ncv1 = NCVirtExt() ncv1 = NCVirtExt()
assert ncv1.print_nc(2, 3) == "36" assert ncv1.print_nc(2, 3) == "36"
@ -244,8 +313,8 @@ def test_move_support():
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
ncv2.print_nc(9, 9) ncv2.print_nc(9, 9)
nc_stats = ConstructorStats.get(NonCopyable) nc_stats = ConstructorStats.get(m.NonCopyable)
mv_stats = ConstructorStats.get(Movable) mv_stats = ConstructorStats.get(m.Movable)
assert nc_stats.alive() == 1 assert nc_stats.alive() == 1
assert mv_stats.alive() == 1 assert mv_stats.alive() == 1
del ncv1, ncv2 del ncv1, ncv2
@ -261,30 +330,26 @@ def test_move_support():
def test_dispatch_issue(msg): def test_dispatch_issue(msg):
"""#159: virtual function dispatch has problems with similar-named functions""" """#159: virtual function dispatch has problems with similar-named functions"""
from pybind11_tests import DispatchIssue, dispatch_issue_go class PyClass1(m.DispatchIssue):
class PyClass1(DispatchIssue):
def dispatch(self): def dispatch(self):
return "Yay.." return "Yay.."
class PyClass2(DispatchIssue): class PyClass2(m.DispatchIssue):
def dispatch(self): def dispatch(self):
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
super(PyClass2, self).dispatch() super(PyClass2, self).dispatch()
assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"' assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
p = PyClass1() p = PyClass1()
return dispatch_issue_go(p) return m.dispatch_issue_go(p)
b = PyClass2() b = PyClass2()
assert dispatch_issue_go(b) == "Yay.." assert m.dispatch_issue_go(b) == "Yay.."
def test_override_ref(): def test_override_ref():
"""#392/397: overridding reference-returning functions""" """#392/397: overridding reference-returning functions"""
from pybind11_tests import OverrideTest o = m.OverrideTest("asdf")
o = OverrideTest("asdf")
# Not allowed (see associated .cpp comment) # Not allowed (see associated .cpp comment)
# i = o.str_ref() # i = o.str_ref()