Skip to content

Commit d7eb18c

Browse files
committed
Improve module level docstring
This adds a sequence of events for blob serialisation,
1 parent 503a124 commit d7eb18c

File tree

1 file changed

+44
-1
lines changed
  • src/labthings_fastapi/outputs

1 file changed

+44
-1
lines changed

src/labthings_fastapi/outputs/blob.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""BLOB Output Module.
1+
r"""BLOB Output Module.
22
33
The ``.Blob`` class is used when you need to return something file-like that can't
44
easily (or efficiently) be converted to JSON. This is useful for returning large objects
@@ -36,6 +36,49 @@ def get_image(self) -> MyImageBlob:
3636
action outputs may be retrieved multiple times after the action has
3737
completed, possibly concurrently. Creating a temp folder and making a file inside it
3838
with `.Blob.from_temporary_directory` is the safest way to deal with this.
39+
40+
**Serialisation**
41+
42+
`.Blob` objects are serialised to a JSON representation that includes a download
43+
``href``\ . This is generated using `.middleware.url_for` which uses a context
44+
variable to pass the function that generates URLs to the serialiser code. That
45+
context variable is available in every response handler function in the FastAPI
46+
app - but it is not, in general, available in action or property code (because
47+
actions and properties run their code in separate threads). The sequence of events
48+
that leads to a `Blob` being downloaded as a result of an action is roughly:
49+
50+
* A `POST` request invokes the action.
51+
* `.middleware.url_for.url_for_middleware` makes `url_for` accessible via
52+
a context variable
53+
* A `201` response is returned that includes an ``href`` to poll the action.
54+
* Action code is run in a separate thread (without `url_for` in the context):
55+
* The action creates a `.Blob` object.
56+
* The function that creates the `.Blob` object also creates a `.BlobData`
57+
object as a property of the `.Blob`
58+
* The `.BlobData` object's constructor adds it to the ``blob_manager`` and
59+
sets its ``id`` property accordingly.
60+
* The `.Blob` is returned by the action.
61+
* The output value of the action is stored in the `.Invocation` thread.
62+
* A `GET` request polls the action. Once it has completed:
63+
* `.middleware.url_for.url_for_middleware` makes `url_for` accessible via
64+
a context variable
65+
* The `.Invocation` model is returned, which includes the `.Blob` in the
66+
``output`` field.
67+
* FastAPI serialises the invocation model, which in turn serialises the `.Blob`
68+
and uses ``url_for`` to generate a valid download ``href`` including the ``id``
69+
of the `.BlobData` object.
70+
* A further `GET` request actually downloads the `.Blob`\ .
71+
72+
This slightly complicated sequence ensures that we only ever send URLs back to the
73+
client using `url_for` from the current `.fastapi.Request` object. That means the
74+
URL used should be consistent with the URL of the request - so if an action is
75+
started by a client using one IP address or DNS name, and polled by a different
76+
client, each client will get a download ``href`` that matches the address they are
77+
already using.
78+
79+
In the future, it may be possible to respond directly with the `.Blob` data to
80+
the original `POST` request, however this only works for quick actions so for now
81+
we use the sequence above, which will work for both quick and slow actions.
3982
"""
4083

4184
from __future__ import annotations

0 commit comments

Comments
 (0)