Skip to content

[BUG] py::type constructor from object does not throw when passed a non-type object #2700

Description

@phcerdan

Issue description

py::type(object) constructor is returning an instance, which is unexpected.

This report was suggested by @YannickJadoul in gitter: https://gitter.im/pybind/Lobby?at=5fc11de4b12c2622f909e92f

Reproducible example code

// py::type::of<int> fails at compilation time.
// But it works for any wrapped type.
template<typename T>
using IsCPPType = typename std::enable_if<std::is_base_of<pybind11::detail::type_caster_generic, pybind11::detail::make_caster<T>>::value>::type;

template<typename T>
using IsNotCPPType = typename std::enable_if<!std::is_base_of<pybind11::detail::type_caster_generic, pybind11::detail::make_caster<T>>::value>::type;

template<typename TT>
void def_TValue(
        pybind11::class_<TT, std::unique_ptr<TT>> & py_class,
        IsCPPType<typename TT::Value> * = nullptr) {
    py_class.def_property_readonly_static("TValue",
            [](pybind11::object /* self */ ) {
            return pybind11::type::of<typename TT::Value>();
            });
}
template<typename TT>
void def_TValue(pybind11::class_<TT, std::unique_ptr<TT>> & py_class,
        IsNotCPPType<typename TT::Value> * = nullptr) {
    py_class.def_property_readonly_static("TValue",
            [](pybind11::object /* self */ ) {
            return pybind11::type( // This is returning an instance,not a type, it should throw. <---
            // return pybind11::type::of( // This is the correct way of returning the type.
                        pybind11::cast(typename TT::Value())
                    );
            });
}
template<typename T>
struct foo {
  using Value = T;
}
using FooInt = foo<int>;
using FooWrapped = foo<other_cpp_class>;
py_class_with_int = py::class_<FooInt , std::unique_ptr<FooInt>>(m, "FooInt");
def_TValue(py_class_with_int);
py_class_with_wrapped_class = py::class_<FooWrapped , std::unique_ptr<FooWrapped>>(m, "FooWrapped");
def_TValue(py_class_with_wrapped_class);
print(FooInt().TValue)
0 # unexpected? returns an instance!!
print(FooFloat().TValue)
0.0 # same

print(FooWrapped().TValue)
`<class  'other_cpp_class'>` # correct

After changing pybind11::type( for pybind11::type::of( the result is the correct <class 'int'>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions