From 2f543e31813328413889dbc3afc4cd43d974edac Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Fri, 18 Jul 2025 17:22:20 +0100 Subject: [PATCH 1/2] Add CodeSpell to Actions, and fix some spellings --- .codespellrc | 24 +++++++++++++++++ .github/workflows/test.yml | 3 +++ dev-requirements.txt | 26 +++---------------- docs/source/quickstart/quickstart.rst | 2 +- docs/source/quickstart/quickstart_example.sh | 2 +- docs/source/wot_core_concepts.rst | 2 +- pyproject.toml | 1 + src/labthings_fastapi/client/in_server.py | 2 +- src/labthings_fastapi/decorators/__init__.py | 2 +- .../dependencies/__init__.py | 2 +- .../dependencies/blocking_portal.py | 2 +- src/labthings_fastapi/descriptors/action.py | 2 +- src/labthings_fastapi/descriptors/property.py | 2 +- .../example_things/__init__.py | 2 +- src/labthings_fastapi/outputs/blob.py | 6 ++--- src/labthings_fastapi/outputs/mjpeg_stream.py | 2 +- src/labthings_fastapi/server/fallback.py | 2 +- src/labthings_fastapi/thing.py | 2 +- .../thing_description/__init__.py | 4 +-- tests/test_example_thing.py | 4 +-- tests/test_fallback.py | 4 +-- tests/test_server_cli.py | 4 +-- 22 files changed, 56 insertions(+), 46 deletions(-) create mode 100644 .codespellrc diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 00000000..98be2bf7 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,24 @@ +[codespell] + +skip = *.git, + *.pyc, + __pycache__, + ./build, + ./dist, + ./docs/_build, + ./htmlcov, + ./src/openflexure_microscope_server/static, + .venv, + *.egg-info, + report.xml, + dev-requirements.txt, + coverage.yml, + coverage.lcov, + coverage.xml, + LICENSE, + +# The regex allows it to also check snake case strings. This stops codespell doing +# autocorrection. If a lot of changes are needed this can be commented out. +# Having this does mean that contractions in variable names will be marked as spelt +# incorrectly. +regex = [a-zA-Z0-9\-'’]+ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3b9d5e3..97dc8a5a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,6 +54,9 @@ jobs: - name: Format with Ruff run: ruff format --check . + - name: Check spelling + run: codespell . + - name: Lint with Flake8 (for docstrings) if: ${{ contains('3.12,3.13', matrix.python) }} # Flake8 crashes on Python < 3.12, so we exclude those versions. diff --git a/dev-requirements.txt b/dev-requirements.txt index 2982bb8b..ac4a5d0e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -32,12 +32,8 @@ click==8.2.1 # rich-toolkit # typer # uvicorn -colorama==0.4.6 - # via - # click - # pytest - # sphinx - # uvicorn +codespell==2.4.1 + # via labthings-fastapi (pyproject.toml) coverage==7.9.2 # via pytest-cov dnspython==2.7.0 @@ -53,10 +49,6 @@ email-validator==2.2.0 # via # fastapi # pydantic -exceptiongroup==1.3.0 - # via - # anyio - # pytest fastapi==0.116.1 # via labthings-fastapi (pyproject.toml) fastapi-cli==0.0.8 @@ -247,14 +239,6 @@ sphinxcontrib-serializinghtml==2.0.0 # via sphinx starlette==0.47.1 # via fastapi -tomli==2.2.1 - # via - # coverage - # flake8-pyproject - # mypy - # pydoclint - # pytest - # sphinx typer==0.16.0 # via # fastapi-cli @@ -265,20 +249,16 @@ typing-extensions==4.14.1 # via # labthings-fastapi (pyproject.toml) # anyio - # astroid - # exceptiongroup # fastapi # mypy # pydantic # pydantic-core # pydantic-extra-types # referencing - # rich # rich-toolkit # starlette # typer # typing-inspection - # uvicorn typing-inspection==0.4.1 # via pydantic-settings ujson==5.10.0 @@ -292,6 +272,8 @@ uvicorn==0.35.0 # fastapi # fastapi-cli # fastapi-cloud-cli +uvloop==0.21.0 + # via uvicorn watchfiles==1.1.0 # via uvicorn websockets==15.0.1 diff --git a/docs/source/quickstart/quickstart.rst b/docs/source/quickstart/quickstart.rst index 5c159af2..f43e0712 100644 --- a/docs/source/quickstart/quickstart.rst +++ b/docs/source/quickstart/quickstart.rst @@ -36,7 +36,7 @@ You can also interact with it from another Python instance, for example by runni .. literalinclude:: counter_client.py :language: python -It's best to write ``Thing`` subclasses in Python packages that can be imported. This makes them easier to re-use and distribute, and also allows us to run a LabThings server from the command line, configured by a configuration file. An example config file is below: +It's best to write ``Thing`` subclasses in Python packages that can be imported. This makes them easier to reuse and distribute, and also allows us to run a LabThings server from the command line, configured by a configuration file. An example config file is below: .. literalinclude:: example_config.json :language: JSON diff --git a/docs/source/quickstart/quickstart_example.sh b/docs/source/quickstart/quickstart_example.sh index 53b84cc9..e862152a 100644 --- a/docs/source/quickstart/quickstart_example.sh +++ b/docs/source/quickstart/quickstart_example.sh @@ -1,4 +1,4 @@ -echo "Setting up environemnt" +echo "Setting up environment" # BEGIN venv python -m venv .venv --prompt labthings source .venv/bin/activate # or .venv/Scripts/activate on Windows diff --git a/docs/source/wot_core_concepts.rst b/docs/source/wot_core_concepts.rst index 605832a1..ead89eaf 100644 --- a/docs/source/wot_core_concepts.rst +++ b/docs/source/wot_core_concepts.rst @@ -39,7 +39,7 @@ Events An event "describes an event source that pushes data asynchronously from the Thing to the Consumer. Here not state, but state transitions (i.e., events) are communicated. Events MAY be triggered through conditions that are not exposed as Properties." -Common examples are notifying clients when a Property is changed, or when an Action starts or finishes. However, Thing developers can introduce new Events such as warnings, status messages, and logs. For example, a device may emit an events when the internal temperature gets too high, or when an interlock is tripped. This Event can then be pushed to both users AND other Things, allowing automtic response to external conditions. +Common examples are notifying clients when a Property is changed, or when an Action starts or finishes. However, Thing developers can introduce new Events such as warnings, status messages, and logs. For example, a device may emit an events when the internal temperature gets too high, or when an interlock is tripped. This Event can then be pushed to both users AND other Things, allowing automatic response to external conditions. A good example of this might be having Things automatically pause data-acquisition Actions upon detection of an overheat or interlock Event from another Thing. Events are not currently implemented in `labthings-fastapi`, but are planned for future releases. diff --git a/pyproject.toml b/pyproject.toml index 58e01fea..5f780d40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ dev = [ "sphinx-rtd-theme", "sphinx>=7.2", "sphinx-autoapi", + "codespell", ] [project.urls] diff --git a/src/labthings_fastapi/client/in_server.py b/src/labthings_fastapi/client/in_server.py index b5435bff..17b99327 100644 --- a/src/labthings_fastapi/client/in_server.py +++ b/src/labthings_fastapi/client/in_server.py @@ -143,7 +143,7 @@ class DependencyNameClashError(KeyError): dictionary. This makes the assumption that, if a name is reused, it is reused for the same dependency. - When names are re-used, we check if the values match. If not, this + When names are reused, we check if the values match. If not, this exception is raised. """ diff --git a/src/labthings_fastapi/decorators/__init__.py b/src/labthings_fastapi/decorators/__init__.py index 32bf8307..0cef9b10 100644 --- a/src/labthings_fastapi/decorators/__init__.py +++ b/src/labthings_fastapi/decorators/__init__.py @@ -106,7 +106,7 @@ def thing_action( :param \**kwargs: Keyword arguments are passed to the constructor of `.ActionDescriptor`. - :return: Whether used with or without argumnts, the result is that + :return: Whether used with or without arguments, the result is that the method is wrapped in an `.ActionDescriptor`, so it can be called as usual, but will also be exposed over HTTP. """ diff --git a/src/labthings_fastapi/dependencies/__init__.py b/src/labthings_fastapi/dependencies/__init__.py index e3a66534..cfd52214 100644 --- a/src/labthings_fastapi/dependencies/__init__.py +++ b/src/labthings_fastapi/dependencies/__init__.py @@ -4,7 +4,7 @@ example invoking actions or accessing properties on other `.Thing`\ s or calling methods provided by the server. -:ref:`dependencies` are a `FastAPI concept`_ that is re-used in LabThings to allow +:ref:`dependencies` are a `FastAPI concept`_ that is reused in LabThings to allow :ref:`actions` to request resources in a way that plays nicely with type hints and is easy to intercept for testing. diff --git a/src/labthings_fastapi/dependencies/blocking_portal.py b/src/labthings_fastapi/dependencies/blocking_portal.py index bbc4bb05..a0e6bd08 100644 --- a/src/labthings_fastapi/dependencies/blocking_portal.py +++ b/src/labthings_fastapi/dependencies/blocking_portal.py @@ -39,7 +39,7 @@ def blocking_portal_from_thing_server(request: Request) -> RealBlockingPortal: :param request: The `fastapi.Request` object, supplied by the :ref:`dependencies` mechanism. - :return: the `anyio.from_thread.BlockingPortal` allowing access to te + :return: the `anyio.from_thread.BlockingPortal` allowing access to the `.ThingServer`\ 's event loop. """ portal = find_thing_server(request.app).blocking_portal diff --git a/src/labthings_fastapi/descriptors/action.py b/src/labthings_fastapi/descriptors/action.py index 58ed321c..9bd3941d 100644 --- a/src/labthings_fastapi/descriptors/action.py +++ b/src/labthings_fastapi/descriptors/action.py @@ -249,7 +249,7 @@ def add_to_fastapi(self, app: FastAPI, thing: Thing) -> None: This function creates two functions to handle ``GET`` and ``POST`` requests to the action's endpoint, and adds them to the `fastapi.FastAPI` - aplication. + application. :param app: The `fastapi.FastAPI` app to add the endpoint to. :param thing: The `.Thing` to which the action is attached. Bear in diff --git a/src/labthings_fastapi/descriptors/property.py b/src/labthings_fastapi/descriptors/property.py index c8638a04..d877f33b 100644 --- a/src/labthings_fastapi/descriptors/property.py +++ b/src/labthings_fastapi/descriptors/property.py @@ -208,7 +208,7 @@ def _observers_set(self, obj: Thing): def emit_changed_event(self, obj: Thing, value: Any) -> None: """Notify subscribers that the property has changed. - This function is run when properties are upadated. It must be run from + This function is run when properties are updated. It must be run from within a thread. This could be the `Invocation` thread of a running action, or the property should be updated over via a client/http. It must be run from a thread as it is communicating with the event loop via an `asyncio` blocking diff --git a/src/labthings_fastapi/example_things/__init__.py b/src/labthings_fastapi/example_things/__init__.py index 4d4a1bdd..3c8b045f 100644 --- a/src/labthings_fastapi/example_things/__init__.py +++ b/src/labthings_fastapi/example_things/__init__.py @@ -119,7 +119,7 @@ def action_with_only_kwargs(self, **kwargs: dict) -> None: class ThingWithBrokenAffordances(Thing): - """A Thing that raises exceptions in actions/properites.""" + """A Thing that raises exceptions in actions/properties.""" @thing_action def broken_action(self): diff --git a/src/labthings_fastapi/outputs/blob.py b/src/labthings_fastapi/outputs/blob.py index 08236b66..8043df23 100644 --- a/src/labthings_fastapi/outputs/blob.py +++ b/src/labthings_fastapi/outputs/blob.py @@ -304,7 +304,7 @@ class Blob(BaseModel): separate HTTP request. `.Blob` objects created by a `.ThingClient` contain a URL pointing to the - data, which will be downloaded when it is requred. + data, which will be downloaded when it is required. `.Blob` objects that store their data in a file or in memory will have the ``href`` attribute set to the special value `blob://local`. @@ -457,7 +457,7 @@ def content(self) -> bytes: this property to download the output. This property is read-only. You should also only read it once, as no - guarantees are given about cacheing - reading it many times risks + guarantees are given about caching - reading it many times risks reading the file from disk many times, or re-downloading an artifact. :return: a `bytes` object containing the data. @@ -714,7 +714,7 @@ async def blob_serialisation_context_manager( In order to serialise a `.Blob` to a JSON-serialisable dictionary, we must add it to the `.BlobDataManager` and use that to generate a URL. This - requres that the serialisation code (which may be nested deep within a + requires that the serialisation code (which may be nested deep within a `pydantic.BaseModel`) has access to the `.BlobDataManager` and also the `fastapi.Request.url_for` method. At time of writing, there was not an obvious way to pass these functions in to the serialisation code. diff --git a/src/labthings_fastapi/outputs/mjpeg_stream.py b/src/labthings_fastapi/outputs/mjpeg_stream.py index c4cfb03f..3b0d3300 100644 --- a/src/labthings_fastapi/outputs/mjpeg_stream.py +++ b/src/labthings_fastapi/outputs/mjpeg_stream.py @@ -209,7 +209,7 @@ async def buffer_for_reading(self, i: int) -> AsyncIterator[bytes]: after any ``with`` statement has finished). Using a context manager is intended to allow future versions of this - code to manage access to the ringbuffer (e.g. allowing buffer re-use). + code to manage access to the ringbuffer (e.g. allowing buffer reuse). Currently, buffers are always created as fresh `bytes` objects, so this context manager does not provide additional functionality over `.MJPEGStream.ringbuffer_entry`. diff --git a/src/labthings_fastapi/server/fallback.py b/src/labthings_fastapi/server/fallback.py index c40e9793..388a9f96 100644 --- a/src/labthings_fastapi/server/fallback.py +++ b/src/labthings_fastapi/server/fallback.py @@ -50,7 +50,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: -

LabThings Could't Load

+

LabThings Couldn't Load

Something went wrong when setting up your LabThings server.

Please check your configuration and try again.

More details may be shown below:

diff --git a/src/labthings_fastapi/thing.py b/src/labthings_fastapi/thing.py index c84abb4c..4ef50820 100644 --- a/src/labthings_fastapi/thing.py +++ b/src/labthings_fastapi/thing.py @@ -258,7 +258,7 @@ def thing_state(self) -> Mapping: it requires calls e.g. to a serial instrument, bear in mind it may be called quite often and shouldn't take too long. - Some measure of cacheing here is a nice aim for the future, but not yet + Some measure of caching here is a nice aim for the future, but not yet implemented. """ if self._labthings_thing_state is None: diff --git a/src/labthings_fastapi/thing_description/__init__.py b/src/labthings_fastapi/thing_description/__init__.py index 3774b64e..845167a1 100644 --- a/src/labthings_fastapi/thing_description/__init__.py +++ b/src/labthings_fastapi/thing_description/__init__.py @@ -42,7 +42,7 @@ def is_a_reference(d: JSONSchema) -> bool: def look_up_reference(reference: str, d: JSONSchema) -> JSONSchema: """Look up a reference in a JSONSchema. - JSONSchema allows references, where chunks of JSON may be re-used. + JSONSchema allows references, where chunks of JSON may be reused. Thing Description does not allow references, so we need to resolve them and paste them in-line. @@ -93,7 +93,7 @@ def is_an_object(d: JSONSchema) -> bool: def convert_object(d: JSONSchema) -> JSONSchema: """Convert an object from JSONSchema to Thing Description. - Convert JSONSchema objets to Thing Description datatypes. + Convert JSONSchema objects to Thing Description datatypes. Currently, this deletes the ``additionalProperties`` keyword, which is not supported by Thing Description. diff --git a/tests/test_example_thing.py b/tests/test_example_thing.py index 37036d57..ab6e454c 100644 --- a/tests/test_example_thing.py +++ b/tests/test_example_thing.py @@ -48,12 +48,12 @@ def test_thing_with_broken_affordances(): thing.broken_property() -def test_thing_that_cant_instantiate(): +def test_thing_that_cannot_instantiate(): with pytest.raises(Exception): ThingThatCantInstantiate() -def test_thing_that_cant_start(): +def test_thing_that_cannot_start(): thing = ThingThatCantStart() assert isinstance(thing, ThingThatCantStart) with pytest.raises(Exception): diff --git a/tests/test_fallback.py b/tests/test_fallback.py index 9e285d47..d3cd5f60 100644 --- a/tests/test_fallback.py +++ b/tests/test_fallback.py @@ -54,11 +54,11 @@ def test_fallback_with_server(): def test_fallback_with_log(): - app.log_history = "Fake log conetent" + app.log_history = "Fake log content" with TestClient(app) as client: response = client.get("/") html = response.text assert "Something went wrong" in html assert "No logging info available" not in html assert "

Logging info

" in html - assert "Fake log conetent" in html + assert "Fake log content" in html diff --git a/tests/test_server_cli.py b/tests/test_server_cli.py index f5e97086..dce81b0e 100644 --- a/tests/test_server_cli.py +++ b/tests/test_server_cli.py @@ -136,7 +136,7 @@ def test_invalid_thing(): def test_fallback(): """test the fallback option - startd a dummy server with an error page - + started a dummy server with an error page - it terminates once the server starts. """ config_json = json.dumps( @@ -155,7 +155,7 @@ def test_invalid_config(): check_serve_from_cli(["-c", "non_existent_file.json"]) -def test_thing_that_cant_start(): +def test_thing_that_cannot_start(): """Check it fails for a thing that can't start""" config_json = json.dumps( { From e557f61d0f0eec56ada202e3e80f81a8676a4a82 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Mon, 21 Jul 2025 16:42:45 +0100 Subject: [PATCH 2/2] Update .codespellrc --- .codespellrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.codespellrc b/.codespellrc index 98be2bf7..a8af5d40 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,6 +1,7 @@ [codespell] skip = *.git, + ./.vscode, *.pyc, __pycache__, ./build,