Skip to content

Commit 92dea65

Browse files
Fix property_descriptor __get__ and __set__ when not allowed
1 parent 4ee49c2 commit 92dea65

File tree

2 files changed

+61
-13
lines changed

2 files changed

+61
-13
lines changed

src/labthings_fastapi/client/__init__.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,18 +358,34 @@ def __get__(
358358
if obj is None:
359359
return self
360360
return obj.get_property(self.name)
361+
else:
361362

362-
__get__.__annotations__["return"] = model
363-
P.__get__ = __get__ # type: ignore[attr-defined]
363+
def __get__(
364+
self: PropertyClientDescriptor,
365+
obj: Optional[ThingClient] = None,
366+
_objtype: Optional[type[ThingClient]] = None,
367+
) -> Any:
368+
raise ClientPropertyError("Method Not Allowed")
369+
370+
__get__.__annotations__["return"] = model
371+
P.__get__ = __get__ # type: ignore[attr-defined]
372+
373+
# Set __set__ method based on whether writable
364374
if writeable:
365375

366376
def __set__(
367377
self: PropertyClientDescriptor, obj: ThingClient, value: Any
368378
) -> None:
369379
obj.set_property(self.name, value)
380+
else:
381+
382+
def __set__(
383+
self: PropertyClientDescriptor, obj: ThingClient, value: Any
384+
) -> None:
385+
raise ClientPropertyError("Method Not Allowed")
370386

371-
__set__.__annotations__["value"] = model
372-
P.__set__ = __set__ # type: ignore[attr-defined]
387+
__set__.__annotations__["value"] = model
388+
P.__set__ = __set__ # type: ignore[attr-defined]
373389
if description:
374390
P.__doc__ = description
375391
return P()

tests/test_thing_client.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,27 +59,50 @@ def throw_value_error(self) -> None:
5959

6060

6161
@pytest.fixture
62-
def thing_client():
63-
"""Yield a test client connected to a ThingServer."""
62+
def thing_client_and_thing():
63+
"""Yield a test client connected to a ThingServer and the Thing itself."""
6464
server = lt.ThingServer({"test_thing": ThingToTest})
6565
with TestClient(server.app) as client:
66-
yield lt.ThingClient.from_url("/test_thing/", client=client)
66+
thing_client = lt.ThingClient.from_url("/test_thing/", client=client)
67+
thing = server.things["test_thing"]
68+
yield thing_client, thing
69+
6770

71+
@pytest.fixture
72+
def thing_client(thing_client_and_thing):
73+
"""Yield a test client connected to a ThingServer."""
74+
return thing_client_and_thing[0]
6875

69-
def test_reading_and_setting_properties(thing_client):
76+
77+
def test_reading_and_setting_properties(thing_client_and_thing):
7078
"""Test reading and setting properties."""
79+
thing_client, thing = thing_client_and_thing
80+
81+
# Read the properties from the thing
7182
assert thing_client.int_prop == 1
7283
assert thing_client.float_prop == 0.1
7384
assert thing_client.str_prop == "foo"
7485

86+
# Update via thing client and check they change on the server and in the client
7587
thing_client.int_prop = 2
7688
thing_client.float_prop = 0.2
7789
thing_client.str_prop = "foo2"
90+
thing.int_prop = 2
91+
thing.float_prop = 0.2
92+
thing.str_prop = "foo2"
7893

7994
assert thing_client.int_prop == 2
8095
assert thing_client.float_prop == 0.2
8196
assert thing_client.str_prop == "foo2"
8297

98+
# Update them on the server side and read them again
99+
thing.int_prop = 3
100+
thing.float_prop = 0.3
101+
thing.str_prop = "foo3"
102+
assert thing_client.int_prop == 3
103+
assert thing_client.float_prop == 0.3
104+
assert thing_client.str_prop == "foo3"
105+
83106
# Set a property that doesn't exist.
84107
err = "Failed to get property foobar: Not Found"
85108
with pytest.raises(lt.exceptions.ClientPropertyError, match=err):
@@ -94,8 +117,9 @@ def test_reading_and_setting_properties(thing_client):
94117
thing_client.int_prop = "Bad value!"
95118

96119

97-
def test_reading_and_not_setting_read_only_properties(thing_client):
120+
def test_reading_and_not_setting_read_only_properties(thing_client_and_thing):
98121
"""Test reading read_only properties, but failing to set."""
122+
thing_client, thing = thing_client_and_thing
99123
assert thing_client.int_prop_read_only == 1
100124
assert thing_client.float_prop_read_only == 0.1
101125
assert thing_client.str_prop_read_only == "foo"
@@ -112,34 +136,42 @@ def test_reading_and_not_setting_read_only_properties(thing_client):
112136
assert thing_client.str_prop_read_only == "foo"
113137

114138

115-
def test_call_action(thing_client):
139+
def test_call_action(thing_client_and_thing):
116140
"""Test calling an action."""
141+
thing_client, thing = thing_client_and_thing
117142
assert thing_client.int_prop == 1
118143
thing_client.increment()
119144
assert thing_client.int_prop == 2
145+
assert thing.int_prop == 2
120146

121147

122-
def test_call_action_with_return(thing_client):
148+
def test_call_action_with_return(thing_client_and_thing):
123149
"""Test calling an action with a return."""
150+
thing_client, thing = thing_client_and_thing
124151
assert thing_client.int_prop == 1
125152
new_value = thing_client.increment_and_return()
126153
assert new_value == 2
127154
assert thing_client.int_prop == 2
155+
assert thing.int_prop == 2
128156

129157

130-
def test_call_action_with_args(thing_client):
158+
def test_call_action_with_args(thing_client_and_thing):
131159
"""Test calling an action."""
160+
thing_client, thing = thing_client_and_thing
132161
assert thing_client.int_prop == 1
133162
thing_client.increment_by_input(value=5)
134163
assert thing_client.int_prop == 6
164+
assert thing.int_prop == 6
135165

136166

137-
def test_call_action_with_args_and_return(thing_client):
167+
def test_call_action_with_args_and_return(thing_client_and_thing):
138168
"""Test calling an action with a return."""
169+
thing_client, thing = thing_client_and_thing
139170
assert thing_client.int_prop == 1
140171
new_value = thing_client.increment_by_input_and_return(value=5)
141172
assert new_value == 6
142173
assert thing_client.int_prop == 6
174+
assert thing.int_prop == 6
143175

144176

145177
def test_call_action_wrong_arg(thing_client):

0 commit comments

Comments
 (0)