Skip to content

Commit cb4d1a3

Browse files
author
Joel Collins
committed
Switched action builder to the same API as property builder
1 parent dedebb9 commit cb4d1a3

File tree

5 files changed

+50
-22
lines changed

5 files changed

+50
-22
lines changed

examples/builder.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
)
4949

5050
labthing.build_action(
51-
my_component.average_data, # Python function
51+
my_component, # Python object
52+
"average_data", # Objects method name
5253
description="Take an averaged measurement",
5354
safe=True, # Is the state of the Thing unchanged by calling the action?
5455
idempotent=True, # Can the action be called repeatedly with the same result?,

src/labthings/labthing.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,14 +547,17 @@ def build_property(
547547
urls = [url_for_property(property_object, property_name)]
548548
self.add_view(property_of(property_object, property_name, **kwargs), *urls)
549549

550-
def build_action(self, function: Callable, urls: list = None, **kwargs):
550+
def build_action(
551+
self, action_object: object, action_name: str, urls: list = None, **kwargs
552+
):
551553
"""
552554
553-
:param function: Callable:
555+
:param action_object: object:
556+
:param action_name: str:
554557
:param urls: list: (Default value = None)
555558
:param **kwargs:
556559
557560
"""
558561
if urls is None:
559-
urls = [url_for_action(function)]
562+
urls = [url_for_action(action_object, action_name)]
560563
self.add_view(action_from(function, **kwargs), *urls)

src/labthings/utilities.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
class TimeoutTracker:
3131
""" """
32+
3233
def __init__(self, timeout: int):
3334
self.timeout_time = time.time() + timeout
3435

@@ -358,12 +359,11 @@ def url_for_property(property_object: object, property_name: str):
358359
return f"/properties/{property_object.__class__.__name__}/{property_name}"
359360

360361

361-
def url_for_action(function: Callable):
362+
def url_for_action(action_object: object, action_name: str):
362363
"""
363364
364-
:param function: Callable:
365+
:param action_object: object:
366+
:param action_name: str:
365367
366368
"""
367-
full_name = getattr(function, "__qualname__", None) or function.__name__
368-
full_name_safe = full_name.replace(".", "/")
369-
return f"/actions/{full_name_safe}"
369+
return f"/actions/{action_object.__class__.__name__}/{action_name}"

src/labthings/views/builder.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ def _update(self, args):
100100

101101

102102
def action_from(
103-
function,
103+
action_object: object,
104+
action_name: str,
104105
name: str = None,
105106
description=None,
106107
safe=False,
@@ -111,8 +112,8 @@ def action_from(
111112
):
112113
"""
113114
114-
:param function:
115-
:param name: str: (Default value = None)
115+
:param action_object: object:
116+
:param action_name: str:
116117
:param description: (Default value = None)
117118
:param safe: (Default value = False)
118119
:param idempotent: (Default value = False)
@@ -124,20 +125,28 @@ def action_from(
124125

125126
# Create a class name
126127
if not name:
127-
name = f"{function.__name__}_action"
128+
name = type(action_object).__name__ + f"_{action_name}"
129+
130+
# Get pointer to action function
131+
action_f = getattr(action_object, action_name)
132+
# Ensure action function is actually a function
133+
if not callable(action_f):
134+
raise TypeError(
135+
f"Attribute {action_name} of {action_object} must be a callable"
136+
)
128137

129138
# Create inner functions
130139
def _post(self):
131140
""" """
132-
return function()
141+
return action_f()
133142

134143
def _post_with_args(self, args):
135144
"""
136145
137146
:param args:
138147
139148
"""
140-
return function(**args)
149+
return action_f(**args)
141150

142151
# Add decorators for arguments etc
143152
if args is not None:

tests/test_views_builder.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ def test_action_from(debug_app, debug_client):
117117
def f():
118118
return "response"
119119

120-
GeneratedClass = builder.action_from(f)
120+
obj = type("obj", (object,), {"f": f})
121+
122+
GeneratedClass = builder.action_from(obj, "f")
121123
debug_app.add_url_rule("/", view_func=GeneratedClass.as_view("index"))
122124

123125
with debug_client as c:
@@ -129,8 +131,10 @@ def test_action_from_with_args(app, client):
129131
def f(arg1, arg2=0):
130132
return {"arg1": arg1, "arg2": arg2}
131133

134+
obj = type("obj", (object,), {"f": f})
135+
132136
GeneratedClass = builder.action_from(
133-
f, args={"arg1": fields.Int(), "arg2": fields.Int(required=False)}
137+
obj, "f", args={"arg1": fields.Int(), "arg2": fields.Int(required=False)}
134138
)
135139
app.add_url_rule("/", view_func=GeneratedClass.as_view("index"))
136140

@@ -145,7 +149,9 @@ def test_action_from_with_schema(app, client):
145149
def f():
146150
return "response"
147151

148-
GeneratedClass = builder.action_from(f, schema=fields.String())
152+
obj = type("obj", (object,), {"f": f})
153+
154+
GeneratedClass = builder.action_from(obj, "f", schema=fields.String())
149155
app.add_url_rule("/", view_func=GeneratedClass.as_view("index"))
150156

151157
with client as c:
@@ -158,8 +164,11 @@ def test_action_from_with_options(app):
158164
def f():
159165
return "response"
160166

167+
obj = type("obj", (object,), {"f": f})
168+
161169
assert builder.action_from(
162-
f,
170+
obj,
171+
"f",
163172
name="action_name",
164173
description="action_description",
165174
safe=True,
@@ -171,7 +180,9 @@ def test_action_from_semtype_string():
171180
def f():
172181
return "response"
173182

174-
GeneratedClass = builder.action_from(f, semtype="SemanticType")
183+
obj = type("obj", (object,), {"f": f})
184+
185+
GeneratedClass = builder.action_from(obj, "f", semtype="SemanticType")
175186

176187
assert GeneratedClass.semtype == "SemanticType"
177188

@@ -180,9 +191,11 @@ def test_action_from_semtype_semantic():
180191
def f():
181192
return "response"
182193

194+
obj = type("obj", (object,), {"f": f})
195+
183196
semantic_annotation = Semantic()
184197

185-
GeneratedClass = builder.action_from(f, semtype=semantic_annotation)
198+
GeneratedClass = builder.action_from(obj, "f", semtype=semantic_annotation)
186199

187200
assert GeneratedClass.semtype == "Semantic"
188201

@@ -191,10 +204,12 @@ def test_action_from_semtype_invalid():
191204
def f():
192205
return "response"
193206

207+
obj = type("obj", (object,), {"f": f})
208+
194209
semantic_annotation = object
195210

196211
with pytest.raises(TypeError):
197-
GeneratedClass = builder.action_from(f, semtype=semantic_annotation)
212+
GeneratedClass = builder.action_from(obj, "f", semtype=semantic_annotation)
198213

199214

200215
def test_static_from(app, client, app_ctx, static_path):

0 commit comments

Comments
 (0)