Skip to content

Commit d2ac3f5

Browse files
authored
[smart_holder] Change throw_if_uninitialized_or_disowned_holder() to also show C++ type name. (#4977)
* Change `throw_if_uninitialized_or_disowned_holder()` to also show C++ type name. * Fix pre-commit errors.
1 parent feed8b1 commit d2ac3f5

File tree

6 files changed

+24
-36
lines changed

6 files changed

+24
-36
lines changed

include/pybind11/detail/smart_holder_type_casters.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ struct smart_holder_type_caster_load {
505505
}
506506
if (void_ptr == nullptr) {
507507
if (have_holder()) {
508-
throw_if_uninitialized_or_disowned_holder();
508+
throw_if_uninitialized_or_disowned_holder(typeid(T));
509509
void_ptr = holder().template as_raw_ptr_unowned<void>();
510510
} else if (load_impl.loaded_v_h.vh != nullptr) {
511511
void_ptr = load_impl.loaded_v_h.value_ptr();
@@ -548,7 +548,7 @@ struct smart_holder_type_caster_load {
548548
if (!have_holder()) {
549549
return nullptr;
550550
}
551-
throw_if_uninitialized_or_disowned_holder();
551+
throw_if_uninitialized_or_disowned_holder(typeid(T));
552552
holder_type &hld = holder();
553553
hld.ensure_is_not_disowned("loaded_as_shared_ptr");
554554
if (hld.vptr_is_using_noop_deleter) {
@@ -612,7 +612,7 @@ struct smart_holder_type_caster_load {
612612
if (!have_holder()) {
613613
return unique_with_deleter<T, D>(nullptr, std::unique_ptr<D>());
614614
}
615-
throw_if_uninitialized_or_disowned_holder();
615+
throw_if_uninitialized_or_disowned_holder(typeid(T));
616616
throw_if_instance_is_currently_owned_by_shared_ptr();
617617
holder().ensure_is_not_disowned(context);
618618
holder().template ensure_compatible_rtti_uqp_del<T, D>(context);
@@ -694,17 +694,22 @@ struct smart_holder_type_caster_load {
694694
holder_type &holder() const { return load_impl.loaded_v_h.holder<holder_type>(); }
695695

696696
// have_holder() must be true or this function will fail.
697-
void throw_if_uninitialized_or_disowned_holder() const {
697+
void throw_if_uninitialized_or_disowned_holder(const char *typeid_name) const {
698+
static const std::string missing_value_msg = "Missing value for wrapped C++ type `";
698699
if (!holder().is_populated) {
699-
pybind11_fail("Missing value for wrapped C++ type:"
700-
" Python instance is uninitialized.");
700+
throw value_error(missing_value_msg + clean_type_id(typeid_name)
701+
+ "`: Python instance is uninitialized.");
701702
}
702703
if (!holder().has_pointee()) {
703-
throw value_error("Missing value for wrapped C++ type:"
704-
" Python instance was disowned.");
704+
throw value_error(missing_value_msg + clean_type_id(typeid_name)
705+
+ "`: Python instance was disowned.");
705706
}
706707
}
707708

709+
void throw_if_uninitialized_or_disowned_holder(const std::type_info &type_info) const {
710+
throw_if_uninitialized_or_disowned_holder(type_info.name());
711+
}
712+
708713
// have_holder() must be true or this function will fail.
709714
void throw_if_instance_is_currently_owned_by_shared_ptr() const {
710715
auto vptr_gd_ptr = std::get_deleter<pybindit::memory::guarded_delete>(holder().vptr);

tests/test_class_sh_basic.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ def test_pass_unique_ptr_disowns(pass_f, rtrn_f, expected):
109109
with pytest.raises(ValueError) as exc_info:
110110
pass_f(obj)
111111
assert str(exc_info.value) == (
112-
"Missing value for wrapped C++ type: Python instance was disowned."
112+
"Missing value for wrapped C++ type"
113+
+ " `pybind11_tests::class_sh_basic::atyp`:"
114+
+ " Python instance was disowned."
113115
)
114116

115117

tests/test_class_sh_disowning_mi.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ def is_disowned(callable_method):
2020
try:
2121
callable_method()
2222
except ValueError as e:
23-
assert ( # noqa: PT017
24-
str(e)
25-
== "Missing value for wrapped C++ type: Python instance was disowned."
26-
)
23+
assert "Python instance was disowned" in str(e) # noqa: PT017
2724
return True
2825
return False
2926

tests/test_class_sh_mi_thunks.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,8 @@ def test_get_vec_size_raw_shared(get_fn, vec_size_fn):
3838
def test_get_vec_size_unique(get_fn):
3939
obj = get_fn()
4040
assert m.vec_size_base0_unique_ptr(obj) == 5
41-
with pytest.raises(ValueError) as exc_info:
41+
with pytest.raises(ValueError, match="Python instance was disowned"):
4242
m.vec_size_base0_unique_ptr(obj)
43-
assert (
44-
str(exc_info.value)
45-
== "Missing value for wrapped C++ type: Python instance was disowned."
46-
)
4743

4844

4945
def test_get_shared_vec_size_unique():

tests/test_class_sh_property.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,19 @@
99

1010
@pytest.mark.xfail("env.PYPY", reason="gc after `del field` is apparently deferred")
1111
@pytest.mark.parametrize("m_attr", ["m_valu_readonly", "m_valu_readwrite"])
12-
def test_valu_getter(msg, m_attr):
12+
def test_valu_getter(m_attr):
1313
# Reduced from PyCLIF test:
1414
# https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/testing/python/nested_fields_test.py#L56
1515
outer = m.Outer()
1616
field = getattr(outer, m_attr)
1717
assert field.num == -99
1818
with pytest.raises(ValueError) as excinfo:
1919
m.DisownOuter(outer)
20-
assert msg(excinfo.value) == "Cannot disown use_count != 1 (loaded_as_unique_ptr)."
20+
assert str(excinfo.value) == "Cannot disown use_count != 1 (loaded_as_unique_ptr)."
2121
del field
2222
m.DisownOuter(outer)
23-
with pytest.raises(ValueError) as excinfo:
23+
with pytest.raises(ValueError, match="Python instance was disowned") as excinfo:
2424
getattr(outer, m_attr)
25-
assert (
26-
msg(excinfo.value)
27-
== "Missing value for wrapped C++ type: Python instance was disowned."
28-
)
2925

3026

3127
def test_valu_setter():
@@ -85,18 +81,14 @@ def test_ptr(field_type, num_default, outer_type, m_attr, r_kind):
8581

8682

8783
@pytest.mark.parametrize("m_attr_readwrite", ["m_uqmp_readwrite", "m_uqcp_readwrite"])
88-
def test_uqp(m_attr_readwrite, msg):
84+
def test_uqp(m_attr_readwrite):
8985
outer = m.Outer()
9086
assert getattr(outer, m_attr_readwrite) is None
9187
field_orig = m.Field()
9288
field_orig.num = 39
9389
setattr(outer, m_attr_readwrite, field_orig)
94-
with pytest.raises(ValueError) as excinfo:
90+
with pytest.raises(ValueError, match="Python instance was disowned"):
9591
_ = field_orig.num
96-
assert (
97-
msg(excinfo.value)
98-
== "Missing value for wrapped C++ type: Python instance was disowned."
99-
)
10092
field_retr1 = getattr(outer, m_attr_readwrite)
10193
assert getattr(outer, m_attr_readwrite) is None
10294
assert field_retr1.num == 39

tests/test_class_sh_unique_ptr_member.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,8 @@ def test_pointee_and_ptr_owner(give_up_ownership_via):
1616
obj = m.pointee()
1717
assert obj.get_int() == 213
1818
owner = m.ptr_owner(obj)
19-
with pytest.raises(ValueError) as exc_info:
19+
with pytest.raises(ValueError, match="Python instance was disowned"):
2020
obj.get_int()
21-
assert (
22-
str(exc_info.value)
23-
== "Missing value for wrapped C++ type: Python instance was disowned."
24-
)
2521
assert owner.is_owner()
2622
reclaimed = getattr(owner, give_up_ownership_via)()
2723
assert not owner.is_owner()

0 commit comments

Comments
 (0)