Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions docs/source/blobs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,13 @@ A camera might want to return an image as a :class:`.Blob` object. The code for

.. code-block:: python

from labthings_fastapi.blob import Blob
from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action
import labthings_fastapi as lt

class JPEGBlob(Blob):
class JPEGBlob(lt.blob.Blob):
content_type = "image/jpeg"

class Camera(Thing):
@thing_action
class Camera(lt.Thing):
@lt.thing_action
def capture_image(self) -> JPEGBlob:
# Capture an image and return it as a Blob
image_data = self._capture_image() # This returns a bytes object holding the JPEG data
Expand All @@ -48,7 +46,7 @@ The corresponding client code might look like this:
.. code-block:: python

from PIL import Image
from labthings_fastapi.client import ThingClient
from labthings_fastapi import ThingClient

camera = ThingClient.from_url("http://localhost:5000/camera/")
image_blob = camera.capture_image()
Expand All @@ -63,30 +61,28 @@ We could define a more sophisticated camera that can capture raw images and conv

.. code-block:: python

from labthings_fastapi.blob import Blob
from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action
import labthings_fastapi as lt

class JPEGBlob(Blob):
class JPEGBlob(lt.Blob):
content_type = "image/jpeg"

class RAWBlob(Blob):
class RAWBlob(lt.Blob):
content_type = "image/x-raw"

class Camera(Thing):
@thing_action
class Camera(lt.Thing):
@lt.thing_action
def capture_raw_image(self) -> RAWBlob:
# Capture a raw image and return it as a Blob
raw_data = self._capture_raw_image() # This returns a bytes object holding the raw data
return RAWBlob.from_bytes(raw_data)

@thing_action
@lt.thing_action
def convert_raw_to_jpeg(self, raw_blob: RAWBlob) -> JPEGBlob:
# Convert a raw image Blob to a JPEG Blob
jpeg_data = self._convert_raw_to_jpeg(raw_blob.data) # This returns a bytes object holding the JPEG data
return JPEGBlob.from_bytes(jpeg_data)

@thing_action
@lt.thing_action
def capture_image(self) -> JPEGBlob:
# Capture an image and return it as a Blob
raw_blob = self.capture_raw_image() # Capture the raw image
Expand All @@ -99,7 +95,7 @@ On the client, we can use the `capture_image` action directly (as before), or we
.. code-block:: python

from PIL import Image
from labthings_fastapi.client import ThingClient
from labthings_fastapi import ThingClient

camera = ThingClient.from_url("http://localhost:5000/camera/")

Expand Down
13 changes: 5 additions & 8 deletions docs/source/dependencies/example.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action
from labthings_fastapi.dependencies.thing import direct_thing_client_dependency
import labthings_fastapi as lt
from labthings_fastapi.example_things import MyThing
from labthings_fastapi.server import ThingServer

MyThingDep = direct_thing_client_dependency(MyThing, "/mything/")
MyThingDep = lt.deps.direct_thing_client_dependency(MyThing, "/mything/")


class TestThing(Thing):
class TestThing(lt.Thing):
"""A test thing with a counter property and a couple of actions"""

@thing_action
@lt.thing_action
def increment_counter(self, my_thing: MyThingDep) -> None:
"""Increment the counter on another thing"""
my_thing.increment_counter()


server = ThingServer()
server = lt.ThingServer()
server.add_thing(MyThing(), "/mything/")
server.add_thing(TestThing(), "/testthing/")

Expand Down
15 changes: 6 additions & 9 deletions docs/source/quickstart/counter.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import time
from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action
from labthings_fastapi.descriptors import ThingProperty
import labthings_fastapi as lt


class TestThing(Thing):
class TestThing(lt.Thing):
"""A test thing with a counter property and a couple of actions"""

@thing_action
@lt.thing_action
def increment_counter(self) -> None:
"""Increment the counter property

Expand All @@ -17,23 +15,22 @@ def increment_counter(self) -> None:
"""
self.counter += 1

@thing_action
@lt.thing_action
def slowly_increase_counter(self) -> None:
"""Increment the counter slowly over a minute"""
for i in range(60):
time.sleep(1)
self.increment_counter()

counter = ThingProperty(
counter = lt.ThingProperty(
model=int, initial_value=0, readonly=True, description="A pointless counter"
)


if __name__ == "__main__":
from labthings_fastapi.server import ThingServer
import uvicorn

server = ThingServer()
server = lt.ThingServer()

# The line below creates a TestThing instance and adds it to the server
server.add_thing(TestThing(), "/counter/")
Expand Down
2 changes: 1 addition & 1 deletion docs/source/quickstart/counter_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from labthings_fastapi.client import ThingClient
from labthings_fastapi import ThingClient

counter = ThingClient.from_url("http://localhost:5000/counter/")

Expand Down
11 changes: 5 additions & 6 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

The files in this folder are example code that was used in development and may be helpful to users. It's not currently tested, so there are no guarantees as to how current each example is. Some of them have been moved into `/tests/` and those ones do get checked: at some point in the future a combined documentation/testing system might usefully deduplicate this.

To run the `demo_thing_server` example, you need to have `labthings_fastapi` installed (we recommend in a virtual environment, see the top-level README), and then do
Two camera-related examples have been removed, there are better `Thing`s already written for handling cameras as part of the [OpenFlexure Microscope] and you can find the relevant [camera Thing code] there.

```shell
cd examples/
uvicorn demo_thing_server:thing_server.app --reload --reload-dir=..
```
To run these examples, it's best to look at the tutorial or quickstart guides on our [readthedocs] site.

The two arguments starting `--reload` will reload the demo if anything changes in the repository, which is useful for development (if you've previously installed the repository as editable) but not necessary if you just want to play with the demo.
[readthedocs]: https://labthings-fastapi.readthedocs.io/
[OpenFlexure Microscope]: https://openflexure.org/
[camera Thing code]: https://gitlab.com/openflexure/openflexure-microscope-server/-/tree/v3/src/openflexure_microscope_server/things/camera?ref_type=heads
16 changes: 7 additions & 9 deletions examples/counter.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import time
from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action
from labthings_fastapi.descriptors import ThingProperty
from labthings_fastapi.server import ThingServer

import labthings_fastapi as lt

class TestThing(Thing):

class TestThing(lt.Thing):
"""A test thing with a counter property and a couple of actions"""

@thing_action
@lt.thing_action
def increment_counter(self) -> None:
"""Increment the counter property

Expand All @@ -18,17 +16,17 @@ def increment_counter(self) -> None:
"""
self.counter += 1

@thing_action
@lt.thing_action
def slowly_increase_counter(self) -> None:
"""Increment the counter slowly over a minute"""
for i in range(60):
time.sleep(1)
self.increment_counter()

counter = ThingProperty(
counter = lt.ThingProperty(
model=int, initial_value=0, readonly=True, description="A pointless counter"
)


server = ThingServer()
server = lt.ThingServer()
server.add_thing(TestThing(), "/test")
20 changes: 9 additions & 11 deletions examples/demo_thing_server.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import logging
import time
from typing import Optional, Annotated
from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action
from labthings_fastapi.server import ThingServer
from labthings_fastapi.descriptors import ThingProperty
from pydantic import Field
from fastapi.responses import HTMLResponse

import labthings_fastapi as lt

logging.basicConfig(level=logging.INFO)


class MyThing(Thing):
@thing_action
class MyThing(lt.Thing):
@lt.thing_action
def anaction(
self,
repeats: Annotated[
Expand Down Expand Up @@ -43,7 +41,7 @@ def anaction(
self.increment_counter()
return "finished!!"

@thing_action
@lt.thing_action
def increment_counter(self):
"""Increment the counter property

Expand All @@ -53,25 +51,25 @@ def increment_counter(self):
"""
self.counter += 1

@thing_action
@lt.thing_action
def slowly_increase_counter(self):
"""Increment the counter slowly over a minute"""
for i in range(60):
time.sleep(1)
self.increment_counter()

counter = ThingProperty(
counter = lt.ThingProperty(
model=int, initial_value=0, readonly=True, description="A pointless counter"
)

foo = ThingProperty(
foo = lt.ThingProperty(
model=str,
initial_value="Example",
description="A pointless string for demo purposes.",
)


thing_server = ThingServer()
thing_server = lt.ThingServer()
my_thing = MyThing()
td = my_thing.thing_description()
my_thing.validate_thing_description()
Expand Down
Loading