Skip to content

Commit 8793a31

Browse files
committed
unique_ptr_field_proxy_poc (proof of concept)
1 parent ee61917 commit 8793a31

File tree

2 files changed

+62
-32
lines changed

2 files changed

+62
-32
lines changed

tests/test_class_sh_property.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,6 @@ TEST_SUBMODULE(class_sh_property, m) {
7575
return std::shared_ptr<const Field>(self, self->m_cptr);
7676
},
7777
[](Outer &self, const Field *cptr) { self.m_cptr = cptr; })
78-
.def_property_readonly( //
79-
"m_uqmp",
80-
[](const std::shared_ptr<Outer> &self) {
81-
return std::shared_ptr<Field>(self, self->m_uqmp.get());
82-
})
8378
.def_property( //
8479
"m_uqmp_disown",
8580
[](const std::shared_ptr<Outer> &self) {
@@ -88,11 +83,6 @@ TEST_SUBMODULE(class_sh_property, m) {
8883
[](Outer &self, std::unique_ptr<Field> uqmp) {
8984
self.m_uqmp = std::move(uqmp); //
9085
})
91-
.def_property_readonly( //
92-
"m_uqcp",
93-
[](const std::shared_ptr<Outer> &self) {
94-
return std::shared_ptr<const Field>(self, self->m_uqcp.get());
95-
})
9686
.def_property( //
9787
"m_uqcp_disown",
9888
[](const std::shared_ptr<Outer> &self) {

tests/test_class_sh_property.py

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,35 +60,75 @@ def test_ptr(field_type, num_default, outer_type, m_attr):
6060
@pytest.mark.xfail(
6161
"env.PYPY", reason="gc after `del field_co_own` is apparently deferred"
6262
)
63-
@pytest.mark.parametrize("m_attr", ("m_uqmp", "m_uqcp"))
64-
def test_uqp(m_attr, msg):
65-
m_attr_disown = m_attr + "_disown"
63+
@pytest.mark.parametrize("m_attr_disown", ("m_uqmp_disown", "m_uqcp_disown"))
64+
def test_uqp(m_attr_disown, msg):
6665
outer = m.Outer()
67-
assert getattr(outer, m_attr) is None
6866
assert getattr(outer, m_attr_disown) is None
69-
field = m.Field()
70-
field.num = 39
71-
setattr(outer, m_attr_disown, field)
67+
field_orig = m.Field()
68+
field_orig.num = 39
69+
setattr(outer, m_attr_disown, field_orig)
7270
with pytest.raises(ValueError) as excinfo:
73-
field.num
71+
field_orig.num
7472
assert (
7573
msg(excinfo.value)
7674
== "Missing value for wrapped C++ type: Python instance was disowned."
7775
)
78-
field_co_own = getattr(outer, m_attr)
79-
assert getattr(outer, m_attr).num == 39
80-
assert field_co_own.num == 39
81-
# TODO: needs work.
82-
# with pytest.raises(RuntimeError) as excinfo:
83-
# getattr(outer, m_attr_disown)
84-
# assert (
85-
# msg(excinfo.value)
86-
# == "Invalid unique_ptr: another instance owns this pointer already."
87-
# )
88-
del field_co_own
89-
field_excl_own = getattr(outer, m_attr_disown)
90-
assert getattr(outer, m_attr) is None
91-
assert field_excl_own.num == 39
76+
field_retr1 = getattr(outer, m_attr_disown)
77+
assert getattr(outer, m_attr_disown) is None
78+
assert field_retr1.num == 39
79+
field_retr1.num = 93
80+
setattr(outer, m_attr_disown, field_retr1)
81+
with pytest.raises(ValueError):
82+
field_retr1.num
83+
field_retr2 = getattr(outer, m_attr_disown)
84+
assert field_retr2.num == 93
85+
86+
87+
def _dereference(proxy, xxxattr, *args, **kwargs):
88+
obj = object.__getattribute__(proxy, "__obj")
89+
field_name = object.__getattribute__(proxy, "__field_name")
90+
field = getattr(obj, field_name)
91+
assert field is not None
92+
try:
93+
return xxxattr(field, *args, **kwargs)
94+
finally:
95+
setattr(obj, field_name, field)
96+
97+
98+
class unique_ptr_field_proxy_poc(object): # noqa: N801
99+
def __init__(self, obj, field_name):
100+
object.__setattr__(self, "__obj", obj)
101+
object.__setattr__(self, "__field_name", field_name)
102+
103+
def __getattr__(self, *args, **kwargs):
104+
return _dereference(self, getattr, *args, **kwargs)
105+
106+
def __setattr__(self, *args, **kwargs):
107+
return _dereference(self, setattr, *args, **kwargs)
108+
109+
def __delattr__(self, *args, **kwargs):
110+
return _dereference(self, delattr, *args, **kwargs)
111+
112+
113+
@pytest.mark.parametrize("m_attr_disown", ("m_uqmp_disown", "m_uqcp_disown"))
114+
def test_unique_ptr_field_proxy_poc(m_attr_disown, msg):
115+
outer = m.Outer()
116+
field_orig = m.Field()
117+
field_orig.num = 45
118+
setattr(outer, m_attr_disown, field_orig)
119+
field_proxy = unique_ptr_field_proxy_poc(outer, m_attr_disown)
120+
assert field_proxy.num == 45
121+
assert field_proxy.num == 45
122+
with pytest.raises(AttributeError):
123+
field_proxy.xyz
124+
assert field_proxy.num == 45
125+
field_proxy.num = 82
126+
assert field_proxy.num == 82
127+
field_proxy = unique_ptr_field_proxy_poc(outer, m_attr_disown)
128+
assert field_proxy.num == 82
129+
with pytest.raises(AttributeError):
130+
del field_proxy.num
131+
assert field_proxy.num == 82
92132

93133

94134
@pytest.mark.parametrize("m_attr", ("m_shmp", "m_shcp"))

0 commit comments

Comments
 (0)