labthings-fastapi 0.0.14__tar.gz → 0.0.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/PKG-INFO +2 -1
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/pyproject.toml +5 -1
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/actions.py +90 -66
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/base_descriptor.py +468 -20
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/client/__init__.py +34 -18
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/exceptions.py +24 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/invocations.py +19 -14
- labthings_fastapi-0.0.16/src/labthings_fastapi/middleware/__init__.py +1 -0
- labthings_fastapi-0.0.16/src/labthings_fastapi/middleware/url_for.py +205 -0
- labthings_fastapi-0.0.16/src/labthings_fastapi/outputs/blob.py +955 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/properties.py +237 -66
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/server/__init__.py +33 -4
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/server/config_model.py +12 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/testing.py +18 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/thing.py +134 -60
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/thing_description/_model.py +5 -3
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/thing_server_interface.py +6 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/thing_slots.py +3 -11
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/utilities/__init__.py +17 -2
- labthings_fastapi-0.0.14/src/labthings_fastapi/client/outputs.py +0 -77
- labthings_fastapi-0.0.14/src/labthings_fastapi/outputs/blob.py +0 -787
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/.gitignore +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/LICENSE +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/README.md +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/__init__.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/client/in_server.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/dependencies/__init__.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/dependencies/blocking_portal.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/dependencies/invocation.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/dependencies/metadata.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/dependencies/raw_thing.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/dependencies/thing.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/dependencies/thing_server.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/deps.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/endpoints.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/example_things/__init__.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/invocation_contexts.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/logs.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/notifications.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/outputs/__init__.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/outputs/mjpeg_stream.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/py.typed +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/server/cli.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/server/fallback.html.jinja +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/server/fallback.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/thing_description/__init__.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/thing_description/td-json-schema-validation.json +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/thing_description/validation.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/types/__init__.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/types/numpy.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/utilities/introspection.py +0 -0
- {labthings_fastapi-0.0.14 → labthings_fastapi-0.0.16}/src/labthings_fastapi/websockets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labthings-fastapi
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.16
|
|
4
4
|
Summary: An implementation of LabThings using FastAPI
|
|
5
5
|
Project-URL: Homepage, https://github.com/labthings/labthings-fastapi
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/labthings/labthings-fastapi/issues
|
|
@@ -35,6 +35,7 @@ Requires-Dist: sphinx-autoapi; extra == 'dev'
|
|
|
35
35
|
Requires-Dist: sphinx-rtd-theme; extra == 'dev'
|
|
36
36
|
Requires-Dist: sphinx-toolbox; extra == 'dev'
|
|
37
37
|
Requires-Dist: sphinx>=7.2; extra == 'dev'
|
|
38
|
+
Requires-Dist: tomli; (python_version < '3.11') and extra == 'dev'
|
|
38
39
|
Requires-Dist: types-jsonschema; extra == 'dev'
|
|
39
40
|
Description-Content-Type: text/markdown
|
|
40
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "labthings-fastapi"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.16"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Richard Bowman", email="richard.bowman@cantab.net" },
|
|
6
6
|
]
|
|
@@ -41,6 +41,7 @@ dev = [
|
|
|
41
41
|
"sphinx>=7.2",
|
|
42
42
|
"sphinx-autoapi",
|
|
43
43
|
"sphinx-toolbox",
|
|
44
|
+
"tomli; python_version < '3.11'",
|
|
44
45
|
"codespell",
|
|
45
46
|
]
|
|
46
47
|
|
|
@@ -171,5 +172,8 @@ check-return-types = false
|
|
|
171
172
|
check-class-attributes = false # prefer docstrings on the attributes
|
|
172
173
|
check-yield-types = false # use type annotations instead
|
|
173
174
|
|
|
175
|
+
[tool.codespell]
|
|
176
|
+
ignore-words-list = ["ser"]
|
|
177
|
+
|
|
174
178
|
[project.scripts]
|
|
175
179
|
labthings-server = "labthings_fastapi.server.cli:serve_from_cli"
|
|
@@ -39,18 +39,21 @@ import weakref
|
|
|
39
39
|
from fastapi import FastAPI, HTTPException, Request, Body, BackgroundTasks
|
|
40
40
|
from pydantic import BaseModel, create_model
|
|
41
41
|
|
|
42
|
-
from .
|
|
42
|
+
from .middleware.url_for import URLFor
|
|
43
|
+
from .base_descriptor import (
|
|
44
|
+
BaseDescriptor,
|
|
45
|
+
BaseDescriptorInfo,
|
|
46
|
+
DescriptorInfoCollection,
|
|
47
|
+
)
|
|
43
48
|
from .logs import add_thing_log_destination
|
|
44
49
|
from .utilities import model_to_dict, wrap_plain_types_in_rootmodel
|
|
45
|
-
from .invocations import InvocationModel, InvocationStatus
|
|
50
|
+
from .invocations import InvocationModel, InvocationStatus
|
|
46
51
|
from .dependencies.invocation import NonWarningInvocationID
|
|
47
52
|
from .exceptions import (
|
|
48
53
|
InvocationCancelledError,
|
|
49
54
|
InvocationError,
|
|
50
|
-
NoBlobManagerError,
|
|
51
55
|
NotConnectedToServerError,
|
|
52
56
|
)
|
|
53
|
-
from .outputs.blob import BlobIOContextDep, blobdata_to_url_ctx
|
|
54
57
|
from . import invocation_contexts
|
|
55
58
|
from .utilities.introspection import (
|
|
56
59
|
EmptyInput,
|
|
@@ -149,28 +152,12 @@ class Invocation(Thread):
|
|
|
149
152
|
|
|
150
153
|
@property
|
|
151
154
|
def output(self) -> Any:
|
|
152
|
-
"""Return value of the Action. If the Action is still running, returns None.
|
|
153
|
-
|
|
154
|
-
:raise NoBlobManagerError: If this is called in a context where the blob
|
|
155
|
-
manager context variables are not available. This stops errors being raised
|
|
156
|
-
later once the blob is returned and tries to serialise. If the errors
|
|
157
|
-
happen during serialisation the stack-trace will not clearly identify
|
|
158
|
-
the route with the missing dependency.
|
|
159
|
-
"""
|
|
160
|
-
try:
|
|
161
|
-
blobdata_to_url_ctx.get()
|
|
162
|
-
except LookupError as e:
|
|
163
|
-
raise NoBlobManagerError(
|
|
164
|
-
"An invocation output has been requested from a api route that "
|
|
165
|
-
"doesn't have a BlobIOContextDep dependency. This dependency is needed "
|
|
166
|
-
" for blobs to identify their url."
|
|
167
|
-
) from e
|
|
168
|
-
|
|
155
|
+
"""Return value of the Action. If the Action is still running, returns None."""
|
|
169
156
|
with self._status_lock:
|
|
170
157
|
return self._return_value
|
|
171
158
|
|
|
172
159
|
@property
|
|
173
|
-
def log(self) -> list[
|
|
160
|
+
def log(self) -> list[logging.LogRecord]:
|
|
174
161
|
"""A list of log items generated by the Action."""
|
|
175
162
|
with self._status_lock:
|
|
176
163
|
return list(self._log)
|
|
@@ -225,33 +212,28 @@ class Invocation(Thread):
|
|
|
225
212
|
"""
|
|
226
213
|
self.cancel_hook.set()
|
|
227
214
|
|
|
228
|
-
def response(self
|
|
215
|
+
def response(self) -> InvocationModel:
|
|
229
216
|
"""Generate a representation of the invocation suitable for HTTP.
|
|
230
217
|
|
|
231
218
|
When an invocation is polled, we return a JSON object that includes
|
|
232
219
|
its status, any log entries, a return value (if completed), and a link
|
|
233
220
|
to poll for updates.
|
|
234
221
|
|
|
235
|
-
:param request: is used to generate the ``href`` in the response, which
|
|
236
|
-
should retrieve an updated version of this response.
|
|
237
|
-
|
|
238
222
|
:return: an `.InvocationModel` representing this `.Invocation`.
|
|
239
223
|
"""
|
|
240
|
-
if request:
|
|
241
|
-
href = str(request.url_for("action_invocation", id=self.id))
|
|
242
|
-
else:
|
|
243
|
-
href = f"{ACTION_INVOCATIONS_PATH}/{self.id}"
|
|
244
224
|
links = [
|
|
245
|
-
LinkElement(rel="self", href=
|
|
246
|
-
LinkElement(
|
|
225
|
+
LinkElement(rel="self", href=URLFor("action_invocation", id=self.id)),
|
|
226
|
+
LinkElement(
|
|
227
|
+
rel="output", href=URLFor("action_invocation_output", id=self.id)
|
|
228
|
+
),
|
|
247
229
|
]
|
|
248
230
|
# The line below confuses MyPy because self.action **evaluates to** a Descriptor
|
|
249
231
|
# object (i.e. we don't call __get__ on the descriptor).
|
|
250
|
-
return self.action.invocation_model( # type: ignore[
|
|
232
|
+
return self.action.invocation_model( # type: ignore[attr-defined]
|
|
251
233
|
status=self.status,
|
|
252
234
|
id=self.id,
|
|
253
|
-
action=self.thing.path + self.action.name, # type: ignore[
|
|
254
|
-
href=
|
|
235
|
+
action=self.thing.path + self.action.name, # type: ignore[attr-defined]
|
|
236
|
+
href=URLFor("action_invocation", id=self.id),
|
|
255
237
|
timeStarted=self._start_time,
|
|
256
238
|
timeCompleted=self._end_time,
|
|
257
239
|
timeRequested=self._request_time,
|
|
@@ -290,7 +272,7 @@ class Invocation(Thread):
|
|
|
290
272
|
"""
|
|
291
273
|
# self.action evaluates to an ActionDescriptor. This confuses mypy,
|
|
292
274
|
# which thinks we are calling ActionDescriptor.__get__.
|
|
293
|
-
action: ActionDescriptor = self.action # type: ignore[
|
|
275
|
+
action: ActionDescriptor = self.action # type: ignore[assignment]
|
|
294
276
|
logger = self.thing.logger
|
|
295
277
|
# The line below saves records matching our ID to ``self._log``
|
|
296
278
|
add_thing_log_destination(self.id, self._log)
|
|
@@ -442,13 +424,10 @@ class ActionManager:
|
|
|
442
424
|
:return: A list of invocations, optionally filtered by Thing and/or Action.
|
|
443
425
|
"""
|
|
444
426
|
return [
|
|
445
|
-
i.response(
|
|
427
|
+
i.response()
|
|
446
428
|
for i in self.invocations
|
|
447
429
|
if thing is None or i.thing == thing
|
|
448
|
-
if action is None or i.action == action
|
|
449
|
-
# i.action evaluates to an ActionDescriptor, which confuses mypy - it
|
|
450
|
-
# thinks we are calling ActionDescriptor.__get__ but this isn't ever
|
|
451
|
-
# called.
|
|
430
|
+
if action is None or i.action == action
|
|
452
431
|
]
|
|
453
432
|
|
|
454
433
|
def expire_invocations(self) -> None:
|
|
@@ -470,25 +449,19 @@ class ActionManager:
|
|
|
470
449
|
"""
|
|
471
450
|
|
|
472
451
|
@app.get(ACTION_INVOCATIONS_PATH, response_model=list[InvocationModel])
|
|
473
|
-
def list_all_invocations(
|
|
474
|
-
request: Request, _blob_manager: BlobIOContextDep
|
|
475
|
-
) -> list[InvocationModel]:
|
|
452
|
+
def list_all_invocations(request: Request) -> list[InvocationModel]:
|
|
476
453
|
return self.list_invocations(request=request)
|
|
477
454
|
|
|
478
455
|
@app.get(
|
|
479
456
|
ACTION_INVOCATIONS_PATH + "/{id}",
|
|
480
457
|
responses={404: {"description": "Invocation ID not found"}},
|
|
481
458
|
)
|
|
482
|
-
def action_invocation(
|
|
483
|
-
id: uuid.UUID, request: Request, _blob_manager: BlobIOContextDep
|
|
484
|
-
) -> InvocationModel:
|
|
459
|
+
def action_invocation(id: uuid.UUID, request: Request) -> InvocationModel:
|
|
485
460
|
"""Return a description of a specific action.
|
|
486
461
|
|
|
487
462
|
:param id: The action's ID (from the path).
|
|
488
463
|
:param request: FastAPI dependency for the request object, used to
|
|
489
464
|
find URLs via ``url_for``.
|
|
490
|
-
:param _blob_manager: FastAPI dependency that enables `.Blob` objects
|
|
491
|
-
to be serialised.
|
|
492
465
|
|
|
493
466
|
:return: Details of the invocation.
|
|
494
467
|
|
|
@@ -497,7 +470,7 @@ class ActionManager:
|
|
|
497
470
|
"""
|
|
498
471
|
try:
|
|
499
472
|
with self._invocations_lock:
|
|
500
|
-
return self._invocations[id].response(
|
|
473
|
+
return self._invocations[id].response()
|
|
501
474
|
except KeyError as e:
|
|
502
475
|
raise HTTPException(
|
|
503
476
|
status_code=404,
|
|
@@ -518,17 +491,13 @@ class ActionManager:
|
|
|
518
491
|
503: {"description": "No result is available for this invocation"},
|
|
519
492
|
},
|
|
520
493
|
)
|
|
521
|
-
def action_invocation_output(
|
|
522
|
-
id: uuid.UUID, _blob_manager: BlobIOContextDep
|
|
523
|
-
) -> Any:
|
|
494
|
+
def action_invocation_output(id: uuid.UUID) -> Any:
|
|
524
495
|
"""Get the output of an action invocation.
|
|
525
496
|
|
|
526
497
|
This returns just the "output" component of the action invocation. If the
|
|
527
498
|
output is a file, it will return the file.
|
|
528
499
|
|
|
529
500
|
:param id: The action's ID (from the path).
|
|
530
|
-
:param _blob_manager: FastAPI dependency that enables `.Blob` objects
|
|
531
|
-
to be serialised.
|
|
532
501
|
|
|
533
502
|
:return: The output of the invocation, as a `pydantic.BaseModel`
|
|
534
503
|
instance. If this is a `.Blob`, it may be returned directly.
|
|
@@ -625,8 +594,56 @@ ActionReturn = TypeVar("ActionReturn")
|
|
|
625
594
|
OwnerT = TypeVar("OwnerT", bound="Thing")
|
|
626
595
|
|
|
627
596
|
|
|
597
|
+
class ActionInfo(
|
|
598
|
+
BaseDescriptorInfo[
|
|
599
|
+
"ActionDescriptor", OwnerT, Callable[ActionParams, ActionReturn]
|
|
600
|
+
],
|
|
601
|
+
Generic[OwnerT, ActionParams, ActionReturn],
|
|
602
|
+
):
|
|
603
|
+
"""Convenient access to the metadata of an action."""
|
|
604
|
+
|
|
605
|
+
@property
|
|
606
|
+
def response_timeout(self) -> float:
|
|
607
|
+
"""The time to wait before replying to the HTTP request initiating an action."""
|
|
608
|
+
return self.get_descriptor().response_timeout
|
|
609
|
+
|
|
610
|
+
@property
|
|
611
|
+
def retention_time(self) -> float:
|
|
612
|
+
"""How long to retain the action's output for, in seconds."""
|
|
613
|
+
return self.get_descriptor().retention_time
|
|
614
|
+
|
|
615
|
+
@property
|
|
616
|
+
def input_model(self) -> type[BaseModel]:
|
|
617
|
+
"""A Pydantic model for the input parameters of an Action."""
|
|
618
|
+
return self.get_descriptor().input_model
|
|
619
|
+
|
|
620
|
+
@property
|
|
621
|
+
def output_model(self) -> type[BaseModel]:
|
|
622
|
+
"""A Pydantic model for the output parameters of an Action."""
|
|
623
|
+
return self.get_descriptor().output_model
|
|
624
|
+
|
|
625
|
+
@property
|
|
626
|
+
def invocation_model(self) -> type[BaseModel]:
|
|
627
|
+
"""A Pydantic model for an invocation of this action."""
|
|
628
|
+
return self.get_descriptor().invocation_model
|
|
629
|
+
|
|
630
|
+
@property
|
|
631
|
+
def func(self) -> Callable[Concatenate[OwnerT, ActionParams], ActionReturn]:
|
|
632
|
+
"""The function that runs the action."""
|
|
633
|
+
return self.get_descriptor().func
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
class ActionCollection(
|
|
637
|
+
DescriptorInfoCollection[OwnerT, ActionInfo],
|
|
638
|
+
Generic[OwnerT],
|
|
639
|
+
):
|
|
640
|
+
"""Access to the metadata of each Action."""
|
|
641
|
+
|
|
642
|
+
_descriptorinfo_class = ActionInfo
|
|
643
|
+
|
|
644
|
+
|
|
628
645
|
class ActionDescriptor(
|
|
629
|
-
BaseDescriptor[Callable[ActionParams, ActionReturn]],
|
|
646
|
+
BaseDescriptor[OwnerT, Callable[ActionParams, ActionReturn]],
|
|
630
647
|
Generic[ActionParams, ActionReturn, OwnerT],
|
|
631
648
|
):
|
|
632
649
|
"""Wrap actions to enable them to be run over HTTP.
|
|
@@ -691,7 +708,7 @@ class ActionDescriptor(
|
|
|
691
708
|
)
|
|
692
709
|
self.invocation_model.__name__ = f"{name}_invocation"
|
|
693
710
|
|
|
694
|
-
def __set_name__(self, owner: type[
|
|
711
|
+
def __set_name__(self, owner: type[OwnerT], name: str) -> None:
|
|
695
712
|
"""Ensure the action name matches the function name.
|
|
696
713
|
|
|
697
714
|
It's assumed in a few places that the function name and the
|
|
@@ -709,7 +726,7 @@ class ActionDescriptor(
|
|
|
709
726
|
f"'{self.func.__name__}'",
|
|
710
727
|
)
|
|
711
728
|
|
|
712
|
-
def instance_get(self, obj:
|
|
729
|
+
def instance_get(self, obj: OwnerT) -> Callable[ActionParams, ActionReturn]:
|
|
713
730
|
"""Return the function, bound to an object as for a normal method.
|
|
714
731
|
|
|
715
732
|
This currently doesn't validate the arguments, though it may do so
|
|
@@ -721,10 +738,7 @@ class ActionDescriptor(
|
|
|
721
738
|
descriptor.
|
|
722
739
|
:return: the action function, bound to ``obj``.
|
|
723
740
|
"""
|
|
724
|
-
|
|
725
|
-
# isn't generic in the type of the owning Thing, so we can't express
|
|
726
|
-
# that here.
|
|
727
|
-
return partial(self.func, obj) # type: ignore[arg-type]
|
|
741
|
+
return partial(self.func, obj)
|
|
728
742
|
|
|
729
743
|
def _observers_set(self, obj: Thing) -> WeakSet:
|
|
730
744
|
"""Return a set used to notify changes.
|
|
@@ -806,8 +820,6 @@ class ActionDescriptor(
|
|
|
806
820
|
# The solution below is to manually add the annotation, before passing
|
|
807
821
|
# the function to the decorator.
|
|
808
822
|
def start_action(
|
|
809
|
-
_blob_manager: BlobIOContextDep,
|
|
810
|
-
request: Request,
|
|
811
823
|
body: Any, # This annotation will be overwritten below.
|
|
812
824
|
id: NonWarningInvocationID,
|
|
813
825
|
background_tasks: BackgroundTasks,
|
|
@@ -822,7 +834,7 @@ class ActionDescriptor(
|
|
|
822
834
|
id=id,
|
|
823
835
|
)
|
|
824
836
|
background_tasks.add_task(action_manager.expire_invocations)
|
|
825
|
-
return action.response(
|
|
837
|
+
return action.response()
|
|
826
838
|
|
|
827
839
|
if issubclass(self.input_model, EmptyInput):
|
|
828
840
|
annotation = Body(default_factory=StrictEmptyInput)
|
|
@@ -884,7 +896,7 @@ class ActionDescriptor(
|
|
|
884
896
|
),
|
|
885
897
|
summary=f"All invocations of {self.name}.",
|
|
886
898
|
)
|
|
887
|
-
def list_invocations(
|
|
899
|
+
def list_invocations() -> list[InvocationModel]:
|
|
888
900
|
action_manager = thing._thing_server_interface._action_manager
|
|
889
901
|
return action_manager.list_invocations(self, thing)
|
|
890
902
|
|
|
@@ -920,6 +932,18 @@ class ActionDescriptor(
|
|
|
920
932
|
output=type_to_dataschema(self.output_model, title=f"{self.name}_output"),
|
|
921
933
|
)
|
|
922
934
|
|
|
935
|
+
def descriptor_info(self, owner: OwnerT | None = None) -> ActionInfo:
|
|
936
|
+
r"""Return an `.ActionInfo` object describing this action.
|
|
937
|
+
|
|
938
|
+
The returned object will either refer to the class, or be bound to a particular
|
|
939
|
+
instance. If it is bound, more properties will be available - e.g. we will be
|
|
940
|
+
able to get the bound function.
|
|
941
|
+
|
|
942
|
+
:param owner: The owning object, or `None` to return an unbound `.ActionInfo`\ .
|
|
943
|
+
:return: An `.ActionInfo` object describing this Action.
|
|
944
|
+
"""
|
|
945
|
+
return self._descriptor_info(ActionInfo, owner)
|
|
946
|
+
|
|
923
947
|
|
|
924
948
|
@overload
|
|
925
949
|
def action(
|