Skip to content

Commit

Permalink
Fail on passing py::object with wrong Python type to py::object subcl…
Browse files Browse the repository at this point in the history
…ass using PYBIND11_OBJECT macro
  • Loading branch information
YannickJadoul committed Aug 16, 2020
1 parent 441bc04 commit f32aa52
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 2 deletions.
9 changes: 7 additions & 2 deletions include/pybind11/pytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -808,11 +808,16 @@ PYBIND11_NAMESPACE_END(detail)
: Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
{ if (!m_ptr) throw error_already_set(); }

#define PYBIND11_OBJECT_CHECK_FAILED(Name, o) \
type_error("Object of type '" + std::string(Py_TYPE(o.ptr())->tp_name) + "' is not an instance of '" #Name "'")

#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
Name(const object &o) : Parent(o) { } \
Name(object &&o) : Parent(std::move(o)) { }
Name(const object &o) : Parent(o) \
{ if (o && !check_(o)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, o); } \
Name(object &&o) : Parent(std::move(o)) \
{ if (o && !check_(o)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, o); }

#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
PYBIND11_OBJECT(Name, Parent, CheckFun) \
Expand Down
13 changes: 13 additions & 0 deletions tests/test_pytypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,19 @@ TEST_SUBMODULE(pytypes, m) {

m.def("convert_to_pybind11_str", [](py::object o) { return py::str(o); });

m.def("nonconverting_constructor", [](std::string type, py::object value) -> py::object {
if (type == "bytes") {
return py::bytes(value);
}
else if (type == "none") {
return py::none(value);
}
else if (type == "ellipsis") {
return py::ellipsis(value);
}
throw std::runtime_error("Invalid type");
});

m.def("get_implicit_casting", []() {
py::dict d;
d["char*_i1"] = "abc";
Expand Down
12 changes: 12 additions & 0 deletions tests/test_pytypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,18 @@ def test_constructors():
for k in noconv2:
assert noconv2[k] is expected[k]

type_error_tests = [
("bytes", range(10)),
("none", 42),
("ellipsis", 42),
]
for t, v in type_error_tests:
with pytest.raises(TypeError) as excinfo:
m.nonconverting_constructor(t, v)
expected_error = "Object of type '{}' is not an instance of '{}'".format(
type(v).__name__, t)
assert str(excinfo.value) == expected_error


def test_pybind11_str_raw_str():
# specifically to exercise pybind11::str::raw_str
Expand Down

0 comments on commit f32aa52

Please sign in to comment.