Merge pull request #381 from jagerman/tests-self-registering

Make test initialization self-registering
This commit is contained in:
Wenzel Jakob 2016-09-04 22:49:53 +09:00 committed by GitHub
commit 2ea4b8e8bb
26 changed files with 83 additions and 105 deletions

View File

@ -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})

View File

@ -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<std::function<void(py::module &)>> &initializers() {
static std::list<std::function<void(py::module &)>> inits;
return inits;
}
#if defined(PYBIND11_TEST_EIGEN)
void init_eigen(py::module &);
#endif
test_initializer::test_initializer(std::function<void(py::module &)> initializer) {
initializers().push_back(std::move(initializer));
}
void bind_ConstructorStats(py::module &m) {
py::class_<ConstructorStats>(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();
}

View File

@ -1,7 +1,15 @@
#pragma once
#include <pybind11/pybind11.h>
#include <iostream>
#include <functional>
#include <list>
using std::cout;
using std::endl;
namespace py = pybind11;
class test_initializer {
public:
test_initializer(std::function<void(py::module &)> initializer);
};

View File

@ -74,7 +74,7 @@ private:
float *m_data;
};
void init_ex_buffers(py::module &m) {
test_initializer buffers([](py::module &m) {
py::class_<Matrix> mtx(m, "Matrix");
mtx.def(py::init<size_t, size_t>())
@ -114,4 +114,4 @@ void init_ex_buffers(py::module &m) {
);
})
;
}
});

View File

@ -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<Payload>);
}
});

View File

@ -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);
}
});

View File

@ -32,7 +32,7 @@ typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> 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<float, 5, 6, Eigen::RowMajor> FixedMatrixR;
typedef Eigen::Matrix<float, 5, 6> FixedMatrixC;
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> DenseMatrixR;
@ -40,6 +40,8 @@ void init_eigen(py::module &m) {
typedef Eigen::SparseMatrix<float, Eigen::RowMajor> SparseMatrixR;
typedef Eigen::SparseMatrix<float> 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;
});
}
});

View File

@ -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_<UnscopedEnum>(m, "UnscopedEnum")
@ -54,4 +54,4 @@ void init_ex_enum(py::module &m) {
.value("EFirstMode", ClassWithUnscopedEnum::EFirstMode)
.value("ESecondMode", ClassWithUnscopedEnum::ESecondMode)
.export_values();
}
});

View File

@ -11,7 +11,7 @@
#include <pybind11/eval.h>
#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;
});
}
});

View File

@ -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<MyException> 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);
}
});

View File

@ -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> pet_class(m, "Pet");
pet_class
.def(py::init<std::string, std::string>())
@ -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; });
}
});

View File

@ -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);

View File

@ -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_<Parent>(m, "Parent")
.def(py::init<>())
.def("addChild", &Parent::addChild)
@ -37,4 +37,4 @@ void init_ex_keep_alive(py::module &m) {
py::class_<Child>(m, "Child")
.def(py::init<>());
}
});

View File

@ -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_<KWClass>(m, "KWClass")
.def("foo0", &KWClass::foo)
.def("foo1", &KWClass::foo, "x"_a, "y"_a);
}
});

View File

@ -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_<ExampleMandA>(m, "ExampleMandA")
.def(py::init<>())
.def(py::init<int>())
@ -81,4 +81,4 @@ void init_ex_methods_and_attributes(py::module &m) {
.def("__str__", &ExampleMandA::toString)
.def_readwrite("value", &ExampleMandA::value)
;
}
});

View File

@ -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");
}
});

View File

@ -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

View File

@ -20,7 +20,7 @@ std::complex<double> my_func3(std::complex<double> c) {
return c * std::complex<double>(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<int, py::array::c_style>) { return "Int branch taken."; });
m.def("selective_func", [](py::array_t<float, py::array::c_style>) { return "Float branch taken."; });
m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { return "Complex float branch taken."; });
}
});

View File

@ -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_<StringList>(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<StringList>(result);
});
}
});

View File

@ -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_<Vector2>(m, "Vector2")
.def(py::init<float, float>())
.def(py::self + py::self)
@ -73,4 +73,4 @@ void init_ex_operator_overloading(py::module &m) {
;
m.attr("Vector") = m.attr("Vector2");
}
});

View File

@ -24,7 +24,7 @@ private:
int m_extra2 = 0;
};
void init_ex_pickling(py::module &m) {
test_initializer pickling([](py::module &m) {
py::class_<Pickleable>(m, "Pickleable")
.def(py::init<std::string>())
.def("value", &Pickleable::value)
@ -48,4 +48,4 @@ void init_ex_pickling(py::module &m) {
p.setExtra1(t[1].cast<int>());
p.setExtra2(t[2].cast<int>());
});
}
});

View File

@ -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_<ExamplePythonTypes>(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)")
;
}
});

View File

@ -168,7 +168,7 @@ bool operator==(const NonZeroIterator<std::pair<A, B>>& 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_<Sequence> 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<const Sequence &>(), s); })
#endif
}
});

View File

@ -105,7 +105,7 @@ void print_myobject3_2(std::shared_ptr<MyObject3> obj) { std::cout << obj->toStr
void print_myobject3_3(const std::shared_ptr<MyObject3> &obj) { std::cout << obj->toString() << std::endl; }
void print_myobject3_4(const std::shared_ptr<MyObject3> *obj) { std::cout << (*obj)->toString() << std::endl; }
void init_ex_smart_ptr(py::module &m) {
test_initializer smart_ptr([](py::module &m) {
py::class_<Object, ref<Object>> 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<ref_tag>);
}
});

View File

@ -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_<El>(m, "El")
.def(py::init<int>());
@ -34,4 +34,4 @@ void init_ex_stl_binder_vector(py::module &m) {
py::bind_vector<El>(m, "VectorEl");
py::bind_vector<std::vector<El>>(m, "VectorVectorEl");
}
});

View File

@ -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<ExampleVirt>);
initialize_inherited_virtuals(m);
}
});