Commit 03764178 authored by Henry Schreiner's avatar Henry Schreiner
Browse files

Merge branch 'master' into v2.10

parents 80dc998e 0694ec6a
......@@ -39,3 +39,26 @@ def test_docstring_options():
# Suppression of user-defined docstrings for non-function objects
assert not m.DocstringTestFoo.__doc__
assert not m.DocstringTestFoo.value_prop.__doc__
# Check existig behaviour of enum docstings
assert (
m.DocstringTestEnum1.__doc__
== "Enum docstring\n\nMembers:\n\n Member1\n\n Member2"
)
# options.enable_enum_members_docstring()
assert (
m.DocstringTestEnum2.__doc__
== "Enum docstring\n\nMembers:\n\n Member1\n\n Member2"
)
# options.disable_enum_members_docstring()
assert m.DocstringTestEnum3.__doc__ == "Enum docstring"
# options.disable_user_defined_docstrings()
assert m.DocstringTestEnum4.__doc__ == "Members:\n\n Member1\n\n Member2"
# options.disable_user_defined_docstrings()
# options.disable_enum_members_docstring()
# When all options are disabled, no docstring (instead of an empty one) should be generated
assert m.DocstringTestEnum5.__doc__ is None
......@@ -7,15 +7,13 @@
BSD-style license that can be found in the LICENSE file.
*/
#include <pybind11/eigen.h>
#include <pybind11/eigen/matrix.h>
#include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#if defined(_MSC_VER)
# pragma warning(disable : 4996) // C4996: std::unary_negation is deprecated
#endif
PYBIND11_WARNING_DISABLE_MSVC(4996)
#include <Eigen/Cholesky>
......@@ -81,7 +79,7 @@ struct CustomOperatorNew {
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
};
TEST_SUBMODULE(eigen, m) {
TEST_SUBMODULE(eigen_matrix, m) {
using FixedMatrixR = Eigen::Matrix<float, 5, 6, Eigen::RowMajor>;
using FixedMatrixC = Eigen::Matrix<float, 5, 6>;
using DenseMatrixR = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
......
......@@ -3,7 +3,7 @@ import pytest
from pybind11_tests import ConstructorStats
np = pytest.importorskip("numpy")
m = pytest.importorskip("pybind11_tests.eigen")
m = pytest.importorskip("pybind11_tests.eigen_matrix")
ref = np.array(
......
/*
tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#define PYBIND11_TEST_EIGEN_TENSOR_NAMESPACE eigen_tensor
#ifdef EIGEN_AVOID_STL_ARRAY
# undef EIGEN_AVOID_STL_ARRAY
#endif
#include "test_eigen_tensor.inl"
#include "pybind11_tests.h"
test_initializer egien_tensor("eigen_tensor", eigen_tensor_test::test_module);
/*
tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor
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/eigen/tensor.h>
PYBIND11_NAMESPACE_BEGIN(eigen_tensor_test)
namespace py = pybind11;
PYBIND11_WARNING_DISABLE_MSVC(4127)
template <typename M>
void reset_tensor(M &x) {
for (int i = 0; i < x.dimension(0); i++) {
for (int j = 0; j < x.dimension(1); j++) {
for (int k = 0; k < x.dimension(2); k++) {
x(i, j, k) = i * (5 * 2) + j * 2 + k;
}
}
}
}
template <typename M>
bool check_tensor(M &x) {
for (int i = 0; i < x.dimension(0); i++) {
for (int j = 0; j < x.dimension(1); j++) {
for (int k = 0; k < x.dimension(2); k++) {
if (x(i, j, k) != (i * (5 * 2) + j * 2 + k)) {
return false;
}
}
}
}
return true;
}
template <int Options>
Eigen::Tensor<double, 3, Options> &get_tensor() {
static Eigen::Tensor<double, 3, Options> *x;
if (!x) {
x = new Eigen::Tensor<double, 3, Options>(3, 5, 2);
reset_tensor(*x);
}
return *x;
}
template <int Options>
Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> &get_tensor_map() {
static Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> *x;
if (!x) {
x = new Eigen::TensorMap<Eigen::Tensor<double, 3, Options>>(get_tensor<Options>());
}
return *x;
}
template <int Options>
Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options> &get_fixed_tensor() {
static Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options> *x;
if (!x) {
Eigen::aligned_allocator<Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>>
allocator;
x = new (allocator.allocate(1))
Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>();
reset_tensor(*x);
}
return *x;
}
template <int Options>
const Eigen::Tensor<double, 3, Options> &get_const_tensor() {
return get_tensor<Options>();
}
template <int Options>
struct CustomExample {
CustomExample() : member(get_tensor<Options>()), view_member(member) {}
Eigen::Tensor<double, 3, Options> member;
Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> view_member;
};
template <int Options>
void init_tensor_module(pybind11::module &m) {
const char *needed_options = "";
if (Options == Eigen::ColMajor) {
needed_options = "F";
} else {
needed_options = "C";
}
m.attr("needed_options") = needed_options;
m.def("setup", []() {
reset_tensor(get_tensor<Options>());
reset_tensor(get_fixed_tensor<Options>());
});
m.def("is_ok", []() {
return check_tensor(get_tensor<Options>()) && check_tensor(get_fixed_tensor<Options>());
});
py::class_<CustomExample<Options>>(m, "CustomExample", py::module_local())
.def(py::init<>())
.def_readonly(
"member", &CustomExample<Options>::member, py::return_value_policy::reference_internal)
.def_readonly("member_view",
&CustomExample<Options>::view_member,
py::return_value_policy::reference_internal);
m.def(
"copy_fixed_tensor",
[]() { return &get_fixed_tensor<Options>(); },
py::return_value_policy::copy);
m.def(
"copy_tensor", []() { return &get_tensor<Options>(); }, py::return_value_policy::copy);
m.def(
"copy_const_tensor",
[]() { return &get_const_tensor<Options>(); },
py::return_value_policy::copy);
m.def(
"move_fixed_tensor_copy",
[]() -> Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options> {
return get_fixed_tensor<Options>();
},
py::return_value_policy::move);
m.def(
"move_tensor_copy",
[]() -> Eigen::Tensor<double, 3, Options> { return get_tensor<Options>(); },
py::return_value_policy::move);
m.def(
"move_const_tensor",
[]() -> const Eigen::Tensor<double, 3, Options> & { return get_const_tensor<Options>(); },
py::return_value_policy::move);
m.def(
"take_fixed_tensor",
[]() {
Eigen::aligned_allocator<
Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>>
allocator;
return new (allocator.allocate(1))
Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>(
get_fixed_tensor<Options>());
},
py::return_value_policy::take_ownership);
m.def(
"take_tensor",
[]() { return new Eigen::Tensor<double, 3, Options>(get_tensor<Options>()); },
py::return_value_policy::take_ownership);
m.def(
"take_const_tensor",
[]() -> const Eigen::Tensor<double, 3, Options> * {
return new Eigen::Tensor<double, 3, Options>(get_tensor<Options>());
},
py::return_value_policy::take_ownership);
m.def(
"take_view_tensor",
[]() -> const Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> * {
return new Eigen::TensorMap<Eigen::Tensor<double, 3, Options>>(get_tensor<Options>());
},
py::return_value_policy::take_ownership);
m.def(
"reference_tensor",
[]() { return &get_tensor<Options>(); },
py::return_value_policy::reference);
m.def(
"reference_tensor_v2",
[]() -> Eigen::Tensor<double, 3, Options> & { return get_tensor<Options>(); },
py::return_value_policy::reference);
m.def(
"reference_tensor_internal",
[]() { return &get_tensor<Options>(); },
py::return_value_policy::reference_internal);
m.def(
"reference_fixed_tensor",
[]() { return &get_tensor<Options>(); },
py::return_value_policy::reference);
m.def(
"reference_const_tensor",
[]() { return &get_const_tensor<Options>(); },
py::return_value_policy::reference);
m.def(
"reference_const_tensor_v2",
[]() -> const Eigen::Tensor<double, 3, Options> & { return get_const_tensor<Options>(); },
py::return_value_policy::reference);
m.def(
"reference_view_of_tensor",
[]() -> Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> {
return get_tensor_map<Options>();
},
py::return_value_policy::reference);
m.def(
"reference_view_of_tensor_v2",
// NOLINTNEXTLINE(readability-const-return-type)
[]() -> const Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> {
return get_tensor_map<Options>(); // NOLINT(readability-const-return-type)
}, // NOLINT(readability-const-return-type)
py::return_value_policy::reference);
m.def(
"reference_view_of_tensor_v3",
[]() -> Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> * {
return &get_tensor_map<Options>();
},
py::return_value_policy::reference);
m.def(
"reference_view_of_tensor_v4",
[]() -> const Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> * {
return &get_tensor_map<Options>();
},
py::return_value_policy::reference);
m.def(
"reference_view_of_tensor_v5",
[]() -> Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> & {
return get_tensor_map<Options>();
},
py::return_value_policy::reference);
m.def(
"reference_view_of_tensor_v6",
[]() -> const Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> & {
return get_tensor_map<Options>();
},
py::return_value_policy::reference);
m.def(
"reference_view_of_fixed_tensor",
[]() {
return Eigen::TensorMap<
Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>>(
get_fixed_tensor<Options>());
},
py::return_value_policy::reference);
m.def("round_trip_tensor",
[](const Eigen::Tensor<double, 3, Options> &tensor) { return tensor; });
m.def(
"round_trip_tensor_noconvert",
[](const Eigen::Tensor<double, 3, Options> &tensor) { return tensor; },
py::arg("tensor").noconvert());
m.def("round_trip_tensor2",
[](const Eigen::Tensor<int32_t, 3, Options> &tensor) { return tensor; });
m.def("round_trip_fixed_tensor",
[](const Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options> &tensor) {
return tensor;
});
m.def(
"round_trip_view_tensor",
[](Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> view) { return view; },
py::return_value_policy::reference);
m.def(
"round_trip_view_tensor_ref",
[](Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> &view) { return view; },
py::return_value_policy::reference);
m.def(
"round_trip_view_tensor_ptr",
[](Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> *view) { return view; },
py::return_value_policy::reference);
m.def(
"round_trip_aligned_view_tensor",
[](Eigen::TensorMap<Eigen::Tensor<double, 3, Options>, Eigen::Aligned> view) {
return view;
},
py::return_value_policy::reference);
m.def(
"round_trip_const_view_tensor",
[](Eigen::TensorMap<const Eigen::Tensor<double, 3, Options>> view) {
return Eigen::Tensor<double, 3, Options>(view);
},
py::return_value_policy::move);
m.def(
"round_trip_rank_0",
[](const Eigen::Tensor<double, 0, Options> &tensor) { return tensor; },
py::return_value_policy::move);
m.def(
"round_trip_rank_0_noconvert",
[](const Eigen::Tensor<double, 0, Options> &tensor) { return tensor; },
py::arg("tensor").noconvert(),
py::return_value_policy::move);
m.def(
"round_trip_rank_0_view",
[](Eigen::TensorMap<Eigen::Tensor<double, 0, Options>> &tensor) { return tensor; },
py::return_value_policy::reference);
}
void test_module(py::module_ &m) {
auto f_style = m.def_submodule("f_style");
auto c_style = m.def_submodule("c_style");
init_tensor_module<Eigen::ColMajor>(f_style);
init_tensor_module<Eigen::RowMajor>(c_style);
}
PYBIND11_NAMESPACE_END(eigen_tensor_test)
import sys
import pytest
np = pytest.importorskip("numpy")
eigen_tensor = pytest.importorskip("pybind11_tests.eigen_tensor")
submodules = [eigen_tensor.c_style, eigen_tensor.f_style]
try:
import eigen_tensor_avoid_stl_array as avoid
submodules += [avoid.c_style, avoid.f_style]
except ImportError as e:
# Ensure config, build, toolchain, etc. issues are not masked here:
raise RuntimeError(
"import eigen_tensor_avoid_stl_array FAILED, while "
"import pybind11_tests.eigen_tensor succeeded. "
"Please ensure that "
"test_eigen_tensor.cpp & "
"eigen_tensor_avoid_stl_array.cpp "
"are built together (or both are not built if Eigen is not available)."
) from e
tensor_ref = np.empty((3, 5, 2), dtype=np.int64)
for i in range(tensor_ref.shape[0]):
for j in range(tensor_ref.shape[1]):
for k in range(tensor_ref.shape[2]):
tensor_ref[i, j, k] = i * (5 * 2) + j * 2 + k
indices = (2, 3, 1)
@pytest.fixture(autouse=True)
def cleanup():
for module in submodules:
module.setup()
yield
for module in submodules:
assert module.is_ok()
def test_import_avoid_stl_array():
pytest.importorskip("eigen_tensor_avoid_stl_array")
assert len(submodules) == 4
def assert_equal_tensor_ref(mat, writeable=True, modified=None):
assert mat.flags.writeable == writeable
copy = np.array(tensor_ref)
if modified is not None:
copy[indices] = modified
np.testing.assert_array_equal(mat, copy)
@pytest.mark.parametrize("m", submodules)
@pytest.mark.parametrize("member_name", ["member", "member_view"])
def test_reference_internal(m, member_name):
if not hasattr(sys, "getrefcount"):
pytest.skip("No reference counting")
foo = m.CustomExample()
counts = sys.getrefcount(foo)
mem = getattr(foo, member_name)
assert_equal_tensor_ref(mem, writeable=False)
new_counts = sys.getrefcount(foo)
assert new_counts == counts + 1
assert_equal_tensor_ref(mem, writeable=False)
del mem
assert sys.getrefcount(foo) == counts
assert_equal_funcs = [
"copy_tensor",
"copy_fixed_tensor",
"copy_const_tensor",
"move_tensor_copy",
"move_fixed_tensor_copy",
"take_tensor",
"take_fixed_tensor",
"reference_tensor",
"reference_tensor_v2",
"reference_fixed_tensor",
"reference_view_of_tensor",
"reference_view_of_tensor_v3",
"reference_view_of_tensor_v5",
"reference_view_of_fixed_tensor",
]
assert_equal_const_funcs = [
"reference_view_of_tensor_v2",
"reference_view_of_tensor_v4",
"reference_view_of_tensor_v6",
"reference_const_tensor",
"reference_const_tensor_v2",
]
@pytest.mark.parametrize("m", submodules)
@pytest.mark.parametrize("func_name", assert_equal_funcs + assert_equal_const_funcs)
def test_convert_tensor_to_py(m, func_name):
writeable = func_name in assert_equal_funcs
assert_equal_tensor_ref(getattr(m, func_name)(), writeable=writeable)
@pytest.mark.parametrize("m", submodules)
def test_bad_cpp_to_python_casts(m):
with pytest.raises(
RuntimeError, match="Cannot use reference internal when there is no parent"
):
m.reference_tensor_internal()
with pytest.raises(RuntimeError, match="Cannot move from a constant reference"):
m.move_const_tensor()
with pytest.raises(
RuntimeError, match="Cannot take ownership of a const reference"
):
m.take_const_tensor()
with pytest.raises(
RuntimeError,
match="Invalid return_value_policy for Eigen Map type, must be either reference or reference_internal",
):
m.take_view_tensor()
@pytest.mark.parametrize("m", submodules)
def test_bad_python_to_cpp_casts(m):
with pytest.raises(
TypeError, match=r"^round_trip_tensor\(\): incompatible function arguments"
):
m.round_trip_tensor(np.zeros((2, 3)))
with pytest.raises(TypeError, match=r"^Cannot cast array data from dtype"):
m.round_trip_tensor(np.zeros(dtype=np.str_, shape=(2, 3, 1)))
with pytest.raises(
TypeError,
match=r"^round_trip_tensor_noconvert\(\): incompatible function arguments",
):
m.round_trip_tensor_noconvert(tensor_ref)
assert_equal_tensor_ref(
m.round_trip_tensor_noconvert(tensor_ref.astype(np.float64))
)
if m.needed_options == "F":
bad_options = "C"
else:
bad_options = "F"
# Shape, dtype and the order need to be correct for a TensorMap cast
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
m.round_trip_view_tensor(
np.zeros((3, 5, 2), dtype=np.float64, order=bad_options)
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
m.round_trip_view_tensor(
np.zeros((3, 5, 2), dtype=np.float32, order=m.needed_options)
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
m.round_trip_view_tensor(
np.zeros((3, 5), dtype=np.float64, order=m.needed_options)
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
m.round_trip_view_tensor(
temp[:, ::-1, :],
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
temp.setflags(write=False)
m.round_trip_view_tensor(temp)
@pytest.mark.parametrize("m", submodules)
def test_references_actually_refer(m):
a = m.reference_tensor()
temp = a[indices]
a[indices] = 100
assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
a[indices] = temp
assert_equal_tensor_ref(m.copy_const_tensor())
a = m.reference_view_of_tensor()
a[indices] = 100
assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
a[indices] = temp
assert_equal_tensor_ref(m.copy_const_tensor())
@pytest.mark.parametrize("m", submodules)
def test_round_trip(m):
assert_equal_tensor_ref(m.round_trip_tensor(tensor_ref))
with pytest.raises(TypeError, match="^Cannot cast array data from"):
assert_equal_tensor_ref(m.round_trip_tensor2(tensor_ref))
assert_equal_tensor_ref(m.round_trip_tensor2(np.array(tensor_ref, dtype=np.int32)))
assert_equal_tensor_ref(m.round_trip_fixed_tensor(tensor_ref))
assert_equal_tensor_ref(m.round_trip_aligned_view_tensor(m.reference_tensor()))
copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
assert_equal_tensor_ref(m.round_trip_view_tensor(copy))
assert_equal_tensor_ref(m.round_trip_view_tensor_ref(copy))
assert_equal_tensor_ref(m.round_trip_view_tensor_ptr(copy))
copy.setflags(write=False)
assert_equal_tensor_ref(m.round_trip_const_view_tensor(copy))
np.testing.assert_array_equal(
tensor_ref[:, ::-1, :], m.round_trip_tensor(tensor_ref[:, ::-1, :])
)
assert m.round_trip_rank_0(np.float64(3.5)) == 3.5
assert m.round_trip_rank_0(3.5) == 3.5
with pytest.raises(
TypeError,
match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
):
m.round_trip_rank_0_noconvert(np.float64(3.5))
with pytest.raises(
TypeError,
match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
):
m.round_trip_rank_0_noconvert(3.5)
with pytest.raises(
TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
):
m.round_trip_rank_0_view(np.float64(3.5))
with pytest.raises(
TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
):
m.round_trip_rank_0_view(3.5)
@pytest.mark.parametrize("m", submodules)
def test_round_trip_references_actually_refer(m):
# Need to create a copy that matches the type on the C side
copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
a = m.round_trip_view_tensor(copy)
temp = a[indices]
a[indices] = 100
assert_equal_tensor_ref(copy, modified=100)
a[indices] = temp
assert_equal_tensor_ref(copy)
@pytest.mark.parametrize("m", submodules)
def test_doc_string(m, doc):
assert (
doc(m.copy_tensor) == "copy_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
)
assert (
doc(m.copy_fixed_tensor)
== "copy_fixed_tensor() -> numpy.ndarray[numpy.float64[3, 5, 2]]"
)
assert (
doc(m.reference_const_tensor)
== "reference_const_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
)
order_flag = f"flags.{m.needed_options.lower()}_contiguous"
assert doc(m.round_trip_view_tensor) == (
f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])"
+ f" -> numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]"
)
assert doc(m.round_trip_const_view_tensor) == (
f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])"
+ " -> numpy.ndarray[numpy.float64[?, ?, ?]]"
)
......@@ -3,11 +3,9 @@
#include <pybind11/embed.h>
#ifdef _MSC_VER
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
# pragma warning(disable : 4996)
#endif
PYBIND11_WARNING_DISABLE_MSVC(4996)
// Catch uses _ internally, which breaks gettext style defines
#ifdef _
......
#include <pybind11/embed.h>
#ifdef _MSC_VER
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
# pragma warning(disable : 4996)
#endif
PYBIND11_WARNING_DISABLE_MSVC(4996)
#include <catch.hpp>
#include <cstdlib>
......@@ -16,6 +14,11 @@
namespace py = pybind11;
using namespace py::literals;
size_t get_sys_path_size() {
auto sys_path = py::module::import("sys").attr("path");
return py::len(sys_path);
}
class Widget {
public:
explicit Widget(std::string message) : message(std::move(message)) {}
......@@ -168,6 +171,70 @@ TEST_CASE("There can be only one interpreter") {
py::initialize_interpreter();
}
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
TEST_CASE("Custom PyConfig") {
py::finalize_interpreter();
PyConfig config;
PyConfig_InitPythonConfig(&config);
REQUIRE_NOTHROW(py::scoped_interpreter{&config});
{
py::scoped_interpreter p{&config};
REQUIRE(py::module_::import("widget_module").attr("add")(1, 41).cast<int>() == 42);
}
py::initialize_interpreter();
}
TEST_CASE("Custom PyConfig with argv") {
py::finalize_interpreter();
{
PyConfig config;
PyConfig_InitIsolatedConfig(&config);
char *argv[] = {strdup("a.out")};
py::scoped_interpreter argv_scope{&config, 1, argv};
std::free(argv[0]);
auto module = py::module::import("test_interpreter");
auto py_widget = module.attr("DerivedWidget")("The question");
const auto &cpp_widget = py_widget.cast<const Widget &>();
REQUIRE(cpp_widget.argv0() == "a.out");
}
py::initialize_interpreter();
}
#endif
TEST_CASE("Add program dir to path pre-PyConfig") {
py::finalize_interpreter();
size_t path_size_add_program_dir_to_path_false = 0;
{
py::scoped_interpreter scoped_interp{true, 0, nullptr, false};
path_size_add_program_dir_to_path_false = get_sys_path_size();
}
{
py::scoped_interpreter scoped_interp{};
REQUIRE(get_sys_path_size() == path_size_add_program_dir_to_path_false + 1);
}
py::initialize_interpreter();
}
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
TEST_CASE("Add program dir to path using PyConfig") {
py::finalize_interpreter();
size_t path_size_add_program_dir_to_path_false = 0;
{
PyConfig config;
PyConfig_InitPythonConfig(&config);
py::scoped_interpreter scoped_interp{&config, 0, nullptr, false};
path_size_add_program_dir_to_path_false = get_sys_path_size();
}
{
PyConfig config;
PyConfig_InitPythonConfig(&config);
py::scoped_interpreter scoped_interp{&config};
REQUIRE(get_sys_path_size() == path_size_add_program_dir_to_path_false + 1);
}
py::initialize_interpreter();
}
#endif
bool has_pybind11_internals_builtin() {
auto builtins = py::handle(PyEval_GetBuiltins());
return builtins.contains(PYBIND11_INTERNALS_ID);
......
......@@ -4,6 +4,7 @@ import pytest
import env
import pybind11_cross_module_tests as cm
import pybind11_tests # noqa: F401
from pybind11_tests import exceptions as m
......@@ -72,9 +73,9 @@ def test_cross_module_exceptions(msg):
# TODO: FIXME
@pytest.mark.xfail(
"env.PYPY and env.MACOS",
"env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang'))",
raises=RuntimeError,
reason="Expected failure with PyPy and libc++ (Issue #2847 & PR #2999)",
reason="See Issue #2847, PR #2999, PR #4324",
)
def test_cross_module_exception_translator():
with pytest.raises(KeyError):
......
......@@ -5,6 +5,7 @@ import time
import pytest
import env
from pybind11_tests import gil_scoped as m
......@@ -144,7 +145,6 @@ def _intentional_deadlock():
ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK = ALL_BASIC_TESTS + (_intentional_deadlock,)
SKIP_IF_DEADLOCK = True # See PR #4216
def _run_in_process(target, *args, **kwargs):
......@@ -181,7 +181,7 @@ def _run_in_process(target, *args, **kwargs):
elif process.exitcode is None:
assert t_delta > 0.9 * timeout
msg = "DEADLOCK, most likely, exactly what this test is meant to detect."
if SKIP_IF_DEADLOCK:
if env.PYPY and env.WIN:
pytest.skip(msg)
raise RuntimeError(msg)
return process.exitcode
......
......@@ -44,14 +44,13 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
// test_args_and_kwargs
m.def("args_function", [](py::args args) -> py::tuple {
PYBIND11_WARNING_PUSH
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wreturn-std-move"
PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
#endif
return args;
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
# pragma clang diagnostic pop
#endif
PYBIND11_WARNING_POP
});
m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) {
return py::make_tuple(args, kwargs);
......
......@@ -521,4 +521,6 @@ TEST_SUBMODULE(numpy_array, sm) {
sm.def("test_fmt_desc_double", [](const py::array_t<double> &) {});
sm.def("test_fmt_desc_const_float", [](const py::array_t<const float> &) {});
sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {});
sm.def("round_trip_float", [](double d) { return d; });
}
......@@ -585,3 +585,9 @@ def test_dtype_refcount_leak():
m.ndim(a)
after = getrefcount(dtype)
assert after == before
def test_round_trip_float():
arr = np.zeros((), np.float64)
arr[()] = 37.2
assert m.round_trip_float(arr) == 37.2
......@@ -132,22 +132,18 @@ struct hash<HashMe> {
// Not a good abs function, but easy to test.
std::string abs(const Vector2 &) { return "abs(Vector2)"; }
// MSVC & Intel warns about unknown pragmas, and warnings are errors.
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to
// `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
// Here, we suppress the warning using `#pragma diagnostic`.
// Here, we suppress the warning
// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
# if defined(__APPLE__) && defined(__clang__)
# if (__clang_major__ >= 10)
# pragma GCC diagnostic ignored "-Wself-assign-overloaded"
# endif
# elif defined(__clang__)
# if (__clang_major__ >= 7)
# pragma GCC diagnostic ignored "-Wself-assign-overloaded"
# endif
#if defined(__APPLE__) && defined(__clang__)
# if (__clang_major__ >= 10)
PYBIND11_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
# endif
#elif defined(__clang__)
# if (__clang_major__ >= 7)
PYBIND11_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
# endif
#endif
......@@ -283,6 +279,3 @@ TEST_SUBMODULE(operators, m) {
m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; });
}
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
......@@ -309,3 +309,29 @@ def test_map_delitem():
del um["ua"]
assert sorted(list(um)) == ["ub"]
assert sorted(list(um.items())) == [("ub", 2.6)]
def test_map_view_types():
map_string_double = m.MapStringDouble()
unordered_map_string_double = m.UnorderedMapStringDouble()
map_string_double_const = m.MapStringDoubleConst()
unordered_map_string_double_const = m.UnorderedMapStringDoubleConst()
assert map_string_double.keys().__class__.__name__ == "KeysView[str]"
assert map_string_double.values().__class__.__name__ == "ValuesView[float]"
assert map_string_double.items().__class__.__name__ == "ItemsView[str, float]"
keys_type = type(map_string_double.keys())
assert type(unordered_map_string_double.keys()) is keys_type
assert type(map_string_double_const.keys()) is keys_type
assert type(unordered_map_string_double_const.keys()) is keys_type
values_type = type(map_string_double.values())
assert type(unordered_map_string_double.values()) is values_type
assert type(map_string_double_const.values()) is values_type
assert type(unordered_map_string_double_const.values()) is values_type
items_type = type(map_string_double.items())
assert type(unordered_map_string_double.items()) is items_type
assert type(map_string_double_const.items()) is items_type
assert type(unordered_map_string_double_const.items()) is items_type
......@@ -173,7 +173,8 @@ struct AdderBase {
using DataVisitor = std::function<void(const Data &)>;
virtual void
operator()(const Data &first, const Data &second, const DataVisitor &visitor) const = 0;
operator()(const Data &first, const Data &second, const DataVisitor &visitor) const
= 0;
virtual ~AdderBase() = default;
AdderBase() = default;
AdderBase(const AdderBase &) = delete;
......
......@@ -208,7 +208,9 @@ string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}")
string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}")
if(CMAKE_HOST_WIN32)
if(DEFINED PYTHON_LIBRARY)
# Don't write to PYTHON_LIBRARY if it's already set
elseif(CMAKE_HOST_WIN32)
set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib")
# when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the
......@@ -274,7 +276,7 @@ if(NOT PYTHON_DEBUG_LIBRARY)
endif()
set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}")
find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARY}"
find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARIES}"
"${PYTHON_EXECUTABLE}${PYTHON_VERSION_STRING}")
set(PYTHONLIBS_FOUND TRUE)
......
......@@ -311,6 +311,16 @@ function(_pybind11_generate_lto target prefer_thin_lto)
HAS_FLTO "-flto${cxx_append}" "-flto${linker_append}" PYBIND11_LTO_CXX_FLAGS
PYBIND11_LTO_LINKER_FLAGS)
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM")
# IntelLLVM equivalent to LTO is called IPO; also IntelLLVM is WIN32/UNIX
# WARNING/HELP WANTED: This block of code is currently not covered by pybind11 GitHub Actions!
if(WIN32)
_pybind11_return_if_cxx_and_linker_flags_work(
HAS_INTEL_IPO "-Qipo" "-Qipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
else()
_pybind11_return_if_cxx_and_linker_flags_work(
HAS_INTEL_IPO "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
# Intel equivalent to LTO is called IPO
_pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO "-ipo" "-ipo"
......
......@@ -9,7 +9,7 @@ if(CMAKE_VERSION VERSION_LESS 3.12)
message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12")
endif()
include_guard(GLOBAL)
include_guard(DIRECTORY)
get_property(
is_config
......@@ -235,7 +235,7 @@ function(pybind11_add_module target_name)
# Use case-insensitive comparison to match the result of $<CONFIG:cfgs>
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
if(NOT MSVC AND NOT ${uppercase_CMAKE_BUILD_TYPE} MATCHES DEBUG|RELWITHDEBINFO)
if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO)
# Strip unnecessary sections of the binary on Linux/macOS
pybind11_strip(${target_name})
endif()
......
......@@ -214,7 +214,7 @@ function(pybind11_add_module target_name)
# Use case-insensitive comparison to match the result of $<CONFIG:cfgs>
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
if(NOT MSVC AND NOT ${uppercase_CMAKE_BUILD_TYPE} MATCHES DEBUG|RELWITHDEBINFO)
if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO)
pybind11_strip(${target_name})
endif()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment