diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5841d82e6..0efefaae3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,7 @@ set(PYBIND11_TEST_FILES test_buffers.cpp test_callbacks.cpp test_constants_and_functions.cpp + test_eigen.cpp test_enum.cpp test_eval.cpp test_exceptions.cpp @@ -28,14 +29,21 @@ set(PYBIND11_TEST_FILES test_virtual_functions.cpp ) -# Check if Eigen is available -find_package(Eigen3 QUIET) +string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") -if(EIGEN3_FOUND) - list(APPEND PYBIND11_TEST_FILES test_eigen.cpp) - message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}") -else() - message(STATUS "Building tests WITHOUT Eigen") +# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but +# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" +# skip message). +list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I) +if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) + find_package(Eigen3 QUIET) + + if(EIGEN3_FOUND) + message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}") + else() + list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) + message(STATUS "Building tests WITHOUT Eigen") + endif() endif() # Create the binding library @@ -74,5 +82,5 @@ if(NOT PYBIND11_PYTEST_FOUND) endif() # A single command to compile and run the tests -add_custom_target(pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest -rws +add_custom_target(pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest -rws ${PYBIND11_PYTEST_FILES} DEPENDS pybind11_tests WORKING_DIRECTORY ${testdir}) diff --git a/tests/pybind11_tests.cpp b/tests/pybind11_tests.cpp index 525f379eb..f3f557a23 100644 --- a/tests/pybind11_tests.cpp +++ b/tests/pybind11_tests.cpp @@ -10,32 +10,14 @@ #include "pybind11_tests.h" #include "constructor_stats.h" -void init_ex_methods_and_attributes(py::module &); -void init_ex_python_types(py::module &); -void init_ex_operator_overloading(py::module &); -void init_ex_constants_and_functions(py::module &); -void init_ex_callbacks(py::module &); -void init_ex_sequences_and_iterators(py::module &); -void init_ex_buffers(py::module &); -void init_ex_smart_ptr(py::module &); -void init_ex_modules(py::module &); -void init_ex_numpy_vectorize(py::module &); -void init_ex_arg_keywords_and_defaults(py::module &); -void init_ex_virtual_functions(py::module &); -void init_ex_keep_alive(py::module &); -void init_ex_opaque_types(py::module &); -void init_ex_pickling(py::module &); -void init_ex_inheritance(py::module &); -void init_ex_stl_binder_vector(py::module &); -void init_ex_eval(py::module &); -void init_ex_custom_exceptions(py::module &); -void init_ex_numpy_dtypes(py::module &); -void init_ex_enum(py::module &); -void init_issues(py::module &); +std::list> &initializers() { + static std::list> inits; + return inits; +} -#if defined(PYBIND11_TEST_EIGEN) - void init_eigen(py::module &); -#endif +test_initializer::test_initializer(std::function initializer) { + initializers().push_back(std::move(initializer)); +} void bind_ConstructorStats(py::module &m) { py::class_(m, "ConstructorStats") @@ -54,35 +36,10 @@ PYBIND11_PLUGIN(pybind11_tests) { bind_ConstructorStats(m); - init_ex_methods_and_attributes(m); - init_ex_python_types(m); - init_ex_operator_overloading(m); - init_ex_constants_and_functions(m); - init_ex_callbacks(m); - init_ex_sequences_and_iterators(m); - init_ex_buffers(m); - init_ex_smart_ptr(m); - init_ex_modules(m); - init_ex_numpy_vectorize(m); - init_ex_arg_keywords_and_defaults(m); - init_ex_virtual_functions(m); - init_ex_keep_alive(m); - init_ex_opaque_types(m); - init_ex_pickling(m); - init_ex_inheritance(m); - init_ex_stl_binder_vector(m); - init_ex_eval(m); - init_ex_custom_exceptions(m); - init_ex_numpy_dtypes(m); - init_ex_enum(m); - init_issues(m); + for (const auto &initializer : initializers()) + initializer(m); -#if defined(PYBIND11_TEST_EIGEN) - init_eigen(m); - m.attr("have_eigen") = py::cast(true); -#else - m.attr("have_eigen") = py::cast(false); -#endif + if (!m.attr("have_eigen")) m.attr("have_eigen") = py::cast(false); return m.ptr(); } diff --git a/tests/pybind11_tests.h b/tests/pybind11_tests.h index ab8fff796..8af3154ef 100644 --- a/tests/pybind11_tests.h +++ b/tests/pybind11_tests.h @@ -1,7 +1,15 @@ +#pragma once #include #include +#include +#include using std::cout; using std::endl; namespace py = pybind11; + +class test_initializer { +public: + test_initializer(std::function initializer); +}; diff --git a/tests/test_buffers.cpp b/tests/test_buffers.cpp index 5a4dc67d6..c3a7a9e02 100644 --- a/tests/test_buffers.cpp +++ b/tests/test_buffers.cpp @@ -74,7 +74,7 @@ private: float *m_data; }; -void init_ex_buffers(py::module &m) { +test_initializer buffers([](py::module &m) { py::class_ mtx(m, "Matrix"); mtx.def(py::init()) @@ -114,4 +114,4 @@ void init_ex_buffers(py::module &m) { ); }) ; -} +}); diff --git a/tests/test_callbacks.cpp b/tests/test_callbacks.cpp index 270ff5ccd..31d0740c2 100644 --- a/tests/test_callbacks.cpp +++ b/tests/test_callbacks.cpp @@ -71,7 +71,7 @@ struct Payload { } }; -void init_ex_callbacks(py::module &m) { +test_initializer callbacks([](py::module &m) { m.def("test_callback1", &test_callback1); m.def("test_callback2", &test_callback2); m.def("test_callback3", &test_callback3); @@ -95,4 +95,4 @@ void init_ex_callbacks(py::module &m) { m.def("test_dummy_function", &test_dummy_function); // Export the payload constructor statistics for testing purposes: m.def("payload_cstats", &ConstructorStats::get); -} +}); diff --git a/tests/test_constants_and_functions.cpp b/tests/test_constants_and_functions.cpp index 03f417790..29a27965a 100644 --- a/tests/test_constants_and_functions.cpp +++ b/tests/test_constants_and_functions.cpp @@ -38,7 +38,7 @@ std::string print_bytes(py::bytes bytes) { return ret; } -void init_ex_constants_and_functions(py::module &m) { +test_initializer constants_and_functions([](py::module &m) { m.attr("some_constant") = py::int_(14); m.def("test_function", &test_function1); @@ -52,4 +52,4 @@ void init_ex_constants_and_functions(py::module &m) { m.def("return_bytes", &return_bytes); m.def("print_bytes", &print_bytes); -} +}); diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp index 42bb969fb..518cfeccf 100644 --- a/tests/test_eigen.cpp +++ b/tests/test_eigen.cpp @@ -32,7 +32,7 @@ typedef Eigen::Matrix Ma MatrixXfRowMajor double_mat_rm(const MatrixXfRowMajor& x) { return 2.0f * x; } -void init_eigen(py::module &m) { +test_initializer eigen([](py::module &m) { typedef Eigen::Matrix FixedMatrixR; typedef Eigen::Matrix FixedMatrixC; typedef Eigen::Matrix DenseMatrixR; @@ -40,6 +40,8 @@ void init_eigen(py::module &m) { typedef Eigen::SparseMatrix SparseMatrixR; typedef Eigen::SparseMatrix SparseMatrixC; + m.attr("have_eigen") = py::cast(true); + // Non-symmetric matrix with zero elements Eigen::MatrixXf mat(5, 6); mat << 0, 3, 0, 0, 0, 11, 22, 0, 0, 0, 17, 11, 7, 5, 0, 1, 0, 11, 0, @@ -129,4 +131,4 @@ void init_eigen(py::module &m) { m.def("sparse_passthrough_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; }); -} +}); diff --git a/tests/test_enum.cpp b/tests/test_enum.cpp index e4b2594c5..6ed0a6aa9 100644 --- a/tests/test_enum.cpp +++ b/tests/test_enum.cpp @@ -35,7 +35,7 @@ std::string test_scoped_enum(ScopedEnum z) { return "ScopedEnum::" + std::string(z == ScopedEnum::Two ? "Two" : "Three"); } -void init_ex_enum(py::module &m) { +test_initializer enums([](py::module &m) { m.def("test_scoped_enum", &test_scoped_enum); py::enum_(m, "UnscopedEnum") @@ -54,4 +54,4 @@ void init_ex_enum(py::module &m) { .value("EFirstMode", ClassWithUnscopedEnum::EFirstMode) .value("ESecondMode", ClassWithUnscopedEnum::ESecondMode) .export_values(); -} +}); diff --git a/tests/test_eval.cpp b/tests/test_eval.cpp index 6b16e7c7b..45d811d74 100644 --- a/tests/test_eval.cpp +++ b/tests/test_eval.cpp @@ -11,7 +11,7 @@ #include #include "pybind11_tests.h" -void init_ex_eval(py::module & m) { +test_initializer eval([](py::module &m) { auto global = py::dict(py::module::import("__main__").attr("__dict__")); m.def("test_eval_statements", [global]() { @@ -77,4 +77,4 @@ void init_ex_eval(py::module & m) { } return false; }); -} +}); diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp index 492308faf..534f23bf8 100644 --- a/tests/test_exceptions.cpp +++ b/tests/test_exceptions.cpp @@ -66,7 +66,7 @@ void throws_logic_error() { throw std::logic_error("this error should fall through to the standard handler"); } -void init_ex_custom_exceptions(py::module &m) { +test_initializer custom_exceptions([](py::module &m) { // make a new custom exception and use it as a translation target static py::exception ex(m, "MyException"); py::register_exception_translator([](std::exception_ptr p) { @@ -104,5 +104,5 @@ void init_ex_custom_exceptions(py::module &m) { m.def("throws3", &throws3); m.def("throws4", &throws4); m.def("throws_logic_error", &throws_logic_error); -} +}); diff --git a/tests/test_inheritance.cpp b/tests/test_inheritance.cpp index 2997ccece..e1aad9920 100644 --- a/tests/test_inheritance.cpp +++ b/tests/test_inheritance.cpp @@ -44,7 +44,7 @@ struct BaseClass { virtual ~BaseClass() {} }; struct DerivedClass1 : BaseClass { }; struct DerivedClass2 : BaseClass { }; -void init_ex_inheritance(py::module &m) { +test_initializer inheritance([](py::module &m) { py::class_ pet_class(m, "Pet"); pet_class .def(py::init()) @@ -69,4 +69,4 @@ void init_ex_inheritance(py::module &m) { m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); }); m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); }); m.def("return_none", []() -> BaseClass* { return nullptr; }); -} +}); diff --git a/tests/test_issues.cpp b/tests/test_issues.cpp index 5f680ebdd..51013e559 100644 --- a/tests/test_issues.cpp +++ b/tests/test_issues.cpp @@ -173,3 +173,6 @@ void init_issues(py::module &m) { m2.def("get_NestB", [](const NestB &b) { return b.value; }); m2.def("get_NestC", [](const NestC &c) { return c.value; }); } + +// MSVC workaround: trying to use a lambda here crashes MSCV +test_initializer issues(&init_issues); diff --git a/tests/test_keep_alive.cpp b/tests/test_keep_alive.cpp index 25f852ea5..a07670b6a 100644 --- a/tests/test_keep_alive.cpp +++ b/tests/test_keep_alive.cpp @@ -25,7 +25,7 @@ public: Child *returnNullChild() { return nullptr; } }; -void init_ex_keep_alive(py::module &m) { +test_initializer keep_alive([](py::module &m) { py::class_(m, "Parent") .def(py::init<>()) .def("addChild", &Parent::addChild) @@ -37,4 +37,4 @@ void init_ex_keep_alive(py::module &m) { py::class_(m, "Child") .def(py::init<>()); -} +}); diff --git a/tests/test_kwargs_and_defaults.cpp b/tests/test_kwargs_and_defaults.cpp index 349b7ea9a..0656cc595 100644 --- a/tests/test_kwargs_and_defaults.cpp +++ b/tests/test_kwargs_and_defaults.cpp @@ -39,7 +39,7 @@ struct KWClass { void foo(int, float) {} }; -void init_ex_arg_keywords_and_defaults(py::module &m) { +test_initializer arg_keywords_and_defaults([](py::module &m) { m.def("kw_func0", &kw_func); m.def("kw_func1", &kw_func, py::arg("x"), py::arg("y")); m.def("kw_func2", &kw_func, py::arg("x") = 100, py::arg("y") = 200); @@ -63,4 +63,4 @@ void init_ex_arg_keywords_and_defaults(py::module &m) { py::class_(m, "KWClass") .def("foo0", &KWClass::foo) .def("foo1", &KWClass::foo, "x"_a, "y"_a); -} +}); diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index 9317d780d..8b0351e42 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -53,7 +53,7 @@ public: int value = 0; }; -void init_ex_methods_and_attributes(py::module &m) { +test_initializer methods_and_attributes([](py::module &m) { py::class_(m, "ExampleMandA") .def(py::init<>()) .def(py::init()) @@ -81,4 +81,4 @@ void init_ex_methods_and_attributes(py::module &m) { .def("__str__", &ExampleMandA::toString) .def_readwrite("value", &ExampleMandA::value) ; -} +}); diff --git a/tests/test_modules.cpp b/tests/test_modules.cpp index b77dacce0..50c7d8412 100644 --- a/tests/test_modules.cpp +++ b/tests/test_modules.cpp @@ -39,7 +39,7 @@ public: A a2{2}; }; -void init_ex_modules(py::module &m) { +test_initializer modules([](py::module &m) { py::module m_sub = m.def_submodule("submodule"); m_sub.def("submodule_func", &submodule_func); @@ -55,4 +55,4 @@ void init_ex_modules(py::module &m) { .def_readwrite("a2", &B::a2); m.attr("OD") = py::module::import("collections").attr("OrderedDict"); -} +}); diff --git a/tests/test_numpy_dtypes.cpp b/tests/test_numpy_dtypes.cpp index 7133a8047..3041e55d8 100644 --- a/tests/test_numpy_dtypes.cpp +++ b/tests/test_numpy_dtypes.cpp @@ -267,7 +267,7 @@ py::list test_dtype_methods() { return list; } -void init_ex_numpy_dtypes(py::module &m) { +test_initializer numpy_dtypes([](py::module &m) { try { py::module::import("numpy"); } catch (...) { @@ -297,6 +297,6 @@ void init_ex_numpy_dtypes(py::module &m) { m.def("test_array_ctors", &test_array_ctors); m.def("test_dtype_ctors", &test_dtype_ctors); m.def("test_dtype_methods", &test_dtype_methods); -} +}); #undef PYBIND11_PACKED diff --git a/tests/test_numpy_vectorize.cpp b/tests/test_numpy_vectorize.cpp index f05c655be..7b0c51eae 100644 --- a/tests/test_numpy_vectorize.cpp +++ b/tests/test_numpy_vectorize.cpp @@ -20,7 +20,7 @@ std::complex my_func3(std::complex c) { return c * std::complex(2.f); } -void init_ex_numpy_vectorize(py::module &m) { +test_initializer numpy_vectorize([](py::module &m) { // Vectorize all arguments of a function (though non-vector arguments are also allowed) m.def("vectorized_func", py::vectorize(my_func)); @@ -38,4 +38,4 @@ void init_ex_numpy_vectorize(py::module &m) { m.def("selective_func", [](py::array_t) { return "Int branch taken."; }); m.def("selective_func", [](py::array_t) { return "Float branch taken."; }); m.def("selective_func", [](py::array_t, py::array::c_style>) { return "Complex float branch taken."; }); -} +}); diff --git a/tests/test_opaque_types.cpp b/tests/test_opaque_types.cpp index 901f05e13..54f4dc7a5 100644 --- a/tests/test_opaque_types.cpp +++ b/tests/test_opaque_types.cpp @@ -21,7 +21,7 @@ public: /* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */ PYBIND11_MAKE_OPAQUE(StringList); -void init_ex_opaque_types(py::module &m) { +test_initializer opaque_types([](py::module &m) { py::class_(m, "StringList") .def(py::init<>()) .def("pop_back", &StringList::pop_back) @@ -59,4 +59,4 @@ void init_ex_opaque_types(py::module &m) { result->push_back("some value"); return std::unique_ptr(result); }); -} +}); diff --git a/tests/test_operator_overloading.cpp b/tests/test_operator_overloading.cpp index b84a5b845..93aea8010 100644 --- a/tests/test_operator_overloading.cpp +++ b/tests/test_operator_overloading.cpp @@ -52,7 +52,7 @@ private: float x, y; }; -void init_ex_operator_overloading(py::module &m) { +test_initializer operator_overloading([](py::module &m) { py::class_(m, "Vector2") .def(py::init()) .def(py::self + py::self) @@ -73,4 +73,4 @@ void init_ex_operator_overloading(py::module &m) { ; m.attr("Vector") = m.attr("Vector2"); -} +}); diff --git a/tests/test_pickling.cpp b/tests/test_pickling.cpp index 4a4845256..4494c24de 100644 --- a/tests/test_pickling.cpp +++ b/tests/test_pickling.cpp @@ -24,7 +24,7 @@ private: int m_extra2 = 0; }; -void init_ex_pickling(py::module &m) { +test_initializer pickling([](py::module &m) { py::class_(m, "Pickleable") .def(py::init()) .def("value", &Pickleable::value) @@ -48,4 +48,4 @@ void init_ex_pickling(py::module &m) { p.setExtra1(t[1].cast()); p.setExtra2(t[2].cast()); }); -} +}); diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp index 39e43eb63..8ec7e26a2 100644 --- a/tests/test_python_types.cpp +++ b/tests/test_python_types.cpp @@ -167,7 +167,7 @@ public: int ExamplePythonTypes::value = 0; const int ExamplePythonTypes::value2 = 5; -void init_ex_python_types(py::module &m) { +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 */ py::class_(m, "ExamplePythonTypes", "Example 2 documentation") @@ -197,4 +197,4 @@ void init_ex_python_types(py::module &m) { .def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member") .def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)") ; -} +}); diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index 39e342ba6..0a88cef6f 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -168,7 +168,7 @@ bool operator==(const NonZeroIterator>& it, const NonZeroSentine return !(*it).first || !(*it).second; } -void init_ex_sequences_and_iterators(py::module &m) { +test_initializer sequences_and_iterators([](py::module &m) { py::class_ seq(m, "Sequence"); @@ -271,4 +271,4 @@ void init_ex_sequences_and_iterators(py::module &m) { On the actual Sequence object, the iterator would be constructed as follows: .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast(), s); }) #endif -} +}); diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 46c83b374..d42596942 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -105,7 +105,7 @@ void print_myobject3_2(std::shared_ptr obj) { std::cout << obj->toStr void print_myobject3_3(const std::shared_ptr &obj) { std::cout << obj->toString() << std::endl; } void print_myobject3_4(const std::shared_ptr *obj) { std::cout << (*obj)->toString() << std::endl; } -void init_ex_smart_ptr(py::module &m) { +test_initializer smart_ptr([](py::module &m) { py::class_> obj(m, "Object"); obj.def("getRefCount", &Object::getRefCount); @@ -147,4 +147,4 @@ void init_ex_smart_ptr(py::module &m) { // Expose constructor stats for the ref type m.def("cstats_ref", &ConstructorStats::get); -} +}); diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp index da854fc46..a6ed6bf6a 100644 --- a/tests/test_stl_binders.cpp +++ b/tests/test_stl_binders.cpp @@ -24,7 +24,7 @@ std::ostream & operator<<(std::ostream &s, El const&v) { return s; } -void init_ex_stl_binder_vector(py::module &m) { +test_initializer stl_binder_vector([](py::module &m) { py::class_(m, "El") .def(py::init()); @@ -34,4 +34,4 @@ void init_ex_stl_binder_vector(py::module &m) { py::bind_vector(m, "VectorEl"); py::bind_vector>(m, "VectorVectorEl"); -} +}); diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index f0f4702a9..e591ba922 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -283,7 +283,7 @@ void initialize_inherited_virtuals(py::module &m) { }; -void init_ex_virtual_functions(py::module &m) { +test_initializer virtual_functions([](py::module &m) { /* Important: indicate the trampoline class PyExampleVirt using the third argument to py::class_. The second argument with the unique pointer is simply the default holder type used by pybind11. */ @@ -315,4 +315,4 @@ void init_ex_virtual_functions(py::module &m) { m.def("cstats_debug", &ConstructorStats::get); initialize_inherited_virtuals(m); -} +});