update test case

This commit is contained in:
Michael Carlstrom 2024-12-15 15:46:10 -05:00
parent 07f861382e
commit 14d2dda5eb
3 changed files with 33 additions and 14 deletions

View File

@ -88,6 +88,12 @@ class Final : public object {
using object::object; using object::object;
}; };
template <typename T>
class ClassVar : public object {
PYBIND11_OBJECT_DEFAULT(ClassVar, object, PyObject_Type)
using object::object;
};
template <typename T> template <typename T>
class TypeGuard : public bool_ { class TypeGuard : public bool_ {
using bool_::bool_; using bool_::bool_;
@ -257,14 +263,19 @@ struct handle_type_name<typing::Optional<T>> {
= const_name("Optional[") + as_return_type<make_caster<T>>::name + const_name("]"); = const_name("Optional[") + as_return_type<make_caster<T>>::name + const_name("]");
}; };
// TypeGuard and TypeIs use as_return_type to use the return type if available, which is usually
// the narrower type.
template <typename T> template <typename T>
struct handle_type_name<typing::Final<T>> { struct handle_type_name<typing::Final<T>> {
static constexpr auto name = const_name("Final[") + make_caster<T>::name + const_name("]"); static constexpr auto name = const_name("Final[") + make_caster<T>::name + const_name("]");
}; };
template <typename T>
struct handle_type_name<typing::ClassVar<T>> {
static constexpr auto name = const_name("ClassVar[") + make_caster<T>::name + const_name("]");
};
// TypeGuard and TypeIs use as_return_type to use the return type if available, which is usually
// the narrower type.
template <typename T> template <typename T>
struct handle_type_name<typing::TypeGuard<T>> { struct handle_type_name<typing::TypeGuard<T>> {
static constexpr auto name static constexpr auto name

View File

@ -1045,13 +1045,11 @@ TEST_SUBMODULE(pytypes, m) {
struct Empty {}; struct Empty {};
py::class_<Empty>(m, "EmptyAnnotationClass"); py::class_<Empty>(m, "EmptyAnnotationClass");
struct Point { struct Point {};
float x;
py::dict dict_str_int;
};
auto point = py::class_<Point>(m, "Point"); auto point = py::class_<Point>(m, "Point");
point.attr_with_type_hint<float>("x"); point.def(py::init());
point.attr_with_type_hint<py::typing::Dict<py::str, int>>("dict_str_int") = py::dict(); point.attr_with_type_hint<py::typing::ClassVar<float>>("x");
point.attr_with_type_hint<py::typing::ClassVar<py::typing::Dict<py::str, int>>>("dict_str_int") = py::dict();
m.attr_with_type_hint<py::typing::Final<int>>("CONST_INT") = 3; m.attr_with_type_hint<py::typing::Final<int>>("CONST_INT") = 3;
m.attr("defined_PYBIND11_CPP17") = true; m.attr("defined_PYBIND11_CPP17") = true;

View File

@ -1106,8 +1106,8 @@ def test_dict_ranges(tested_dict, expected):
# https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
def get_annotations_helper(o): def get_annotations_helper(o):
if isinstance(o, type): if isinstance(o, type):
return o.__dict__.get("__annotations__", {}) return o.__dict__.get("__annotations__", None)
return getattr(o, "__annotations__", {}) return getattr(o, "__annotations__", None)
@pytest.mark.skipif( @pytest.mark.skipif(
@ -1129,9 +1129,19 @@ def test_class_attribute_types() -> None:
empty_annotations = get_annotations_helper(m.EmptyAnnotationClass) empty_annotations = get_annotations_helper(m.EmptyAnnotationClass)
annotations = get_annotations_helper(m.Point) annotations = get_annotations_helper(m.Point)
assert empty_annotations == {} assert empty_annotations is None
assert annotations["x"] == "float" assert annotations["x"] == "ClassVar[float]"
assert annotations["dict_str_int"] == "dict[str, int]" assert annotations["dict_str_int"] == "ClassVar[dict[str, int]]"
m.Point.x = 1.0
assert m.Point.x == 1.0
point = m.Point()
assert point.x == 1.0
point.dict_str_int["hi"] = 3
assert m.Point().dict_str_int == {"hi": 3}
@pytest.mark.skipif( @pytest.mark.skipif(