Skip to content

Commit 85d7f25

Browse files
author
Joel Collins
committed
Added action builder and function signature to schema builder
1 parent 640e446 commit 85d7f25

File tree

12 files changed

+346
-177
lines changed

12 files changed

+346
-177
lines changed

examples/builder.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66

77
from labthings.server.quick import create_app
8-
from labthings.server.view.builder import property_of
8+
from labthings.server.view.builder import property_of, action_from
99

1010
from components.pdf_component import PdfComponent
1111

@@ -42,6 +42,10 @@ def cleanup():
4242
),
4343
"/dictionary",
4444
)
45+
labthing.add_view(
46+
action_from(my_component.average_data, description="Take an averaged measurement"),
47+
"/average",
48+
)
4549

4650
atexit.register(cleanup)
4751

examples/components/pdf_component.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import math
33
import time
44

5+
from typing import List
6+
57
"""
68
Class for our lab component functionality. This could include serial communication,
79
equipment API calls, network requests, or a "virtual" device as seen here.
@@ -38,7 +40,7 @@ def data(self):
3840
"""Return a 1D data trace."""
3941
return [self.noisy_pdf(x) for x in self.x_range]
4042

41-
def average_data(self, n: int):
43+
def average_data(self, n: int = 10, optlist: List[int] = [1, 2, 3]):
4244
"""Average n-sets of data. Emulates a measurement that may take a while."""
4345
summed_data = self.data
4446

examples/simple_extensions.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ def ext_on_my_component(component):
4040

4141

4242
static_folder = path_relative_to(__file__, "static")
43-
print(static_folder)
4443

4544
example_extension = BaseExtension(
4645
"org.labthings.examples.extension", static_folder=static_folder

labthings/core/tasks/thread.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,10 @@ def check_thread(self, record):
158158

159159
def emit(self, record):
160160
"""Do something with a logged message"""
161-
print("emitting a log")
162161
record_dict = {"message": record.getMessage()}
163162
for k in ["created", "levelname", "levelno", "lineno", "filename"]:
164163
record_dict[k] = getattr(record, k)
165164
self.dest.append(record_dict)
166-
print(self.thread.log)
167165
# FIXME: make sure this doesn't become a memory disaster!
168166
# We probably need to check the size of the list...
169167
# TODO: think about whether any of the keys are security flaws

labthings/server/logging.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ def __init__(self, labthing):
99

1010
def emit(self, record):
1111
log_event = self.rest_format_record(record)
12-
print(log_event)
1312

1413
# Broadcast to subscribers
1514
subscribers = getattr(self.labthing, "subscribers", [])

labthings/server/types.py

Lines changed: 0 additions & 168 deletions
This file was deleted.

labthings/server/types/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .properties import value_to_field, data_dict_to_schema
2+
from .annotations import function_signature_to_schema

labthings/server/types/annotations.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from .registry import TypeRegistry
2+
from labthings.server import fields
3+
4+
from typing import Dict, List, Tuple, Union
5+
from inspect import Parameter
6+
from marshmallow.base import FieldABC
7+
8+
import inspect
9+
10+
NoneType = type(None)
11+
12+
13+
class AnnotationConverter:
14+
def __init__(self):
15+
self._registry = TypeRegistry()
16+
self._registry.register(List, self._list_converter)
17+
self._registry.register(list, self._list_converter)
18+
19+
def _list_converter(self, subtypes: Tuple[type], **opts) -> FieldABC:
20+
sub_opts = opts.pop("_interior", {})
21+
return fields.List(self.convert(subtypes[0], **sub_opts), **opts)
22+
23+
def convert(self, parameter, **kwargs) -> FieldABC:
24+
# sane defaults
25+
allow_none = False
26+
required = True
27+
optional = False
28+
subtypes = ()
29+
30+
if isinstance(parameter, Parameter):
31+
typehint = parameter.annotation
32+
optional = not (parameter.default is parameter.empty)
33+
elif isinstance(parameter, type):
34+
typehint = parameter
35+
else:
36+
typehint = type(parameter)
37+
38+
if optional:
39+
allow_none = True
40+
required = False
41+
42+
if isinstance(parameter, Parameter):
43+
# Get subtypes
44+
subtypes = getattr(typehint, "__args__", ())
45+
if subtypes != ():
46+
typehint = typehint.__origin__
47+
48+
# Get default
49+
if not (parameter.default is parameter.empty):
50+
kwargs.setdefault("default", parameter.default)
51+
kwargs.setdefault("example", parameter.default)
52+
53+
kwargs.setdefault("allow_none", allow_none)
54+
kwargs.setdefault("required", required)
55+
56+
field_constructor = self._registry.get(typehint)
57+
return field_constructor(subtypes, **kwargs)
58+
59+
60+
def function_signature_to_schema(function: callable):
61+
"""
62+
"""
63+
converter = AnnotationConverter()
64+
65+
schema_dict = {}
66+
params = inspect.signature(function).parameters
67+
68+
for k, p in params.items():
69+
schema_dict[k] = converter.convert(p)
70+
71+
return schema_dict
72+
73+
74+
def param_to_field(value):
75+
converter = AnnotationConverter()
76+
77+
return converter.convert(value)

0 commit comments

Comments
 (0)