From 0412f677062975db28d5ee30d5bec1a1cdb13cd8 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 6 Apr 2025 16:21:02 +0100 Subject: [PATCH 1/8] Add more information to the fallback server * Display the traceback, but this isn't always too helpful * Optionally pass log history for display --- src/labthings_fastapi/server/fallback.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/labthings_fastapi/server/fallback.py b/src/labthings_fastapi/server/fallback.py index db224407..f9cf9109 100644 --- a/src/labthings_fastapi/server/fallback.py +++ b/src/labthings_fastapi/server/fallback.py @@ -1,4 +1,5 @@ import json +from traceback import format_exception from fastapi import FastAPI from fastapi.responses import HTMLResponse from starlette.responses import RedirectResponse @@ -10,6 +11,7 @@ def __init__(self, *args, **kwargs): self.labthings_config = None self.labthings_server = None self.labthings_error = None + self.log_history = None app = FallbackApp() @@ -32,6 +34,9 @@ def __init__(self, *args, **kwargs):

Your configuration:

{{config}}
+

Traceback

+
{{traceback}}
+ {{logginginfo}} """ @@ -40,6 +45,9 @@ def __init__(self, *args, **kwargs): @app.get("/") async def root(): error_message = f"{app.labthings_error!r}" + # use traceback.format_exception to get full traceback as list + # this ends in newlines, but needs joining to be a single string + error_w_trace = "".join(format_exception(app.labthings_error)) things = "" if app.labthings_server: for path, thing in app.labthings_server.things.items(): @@ -49,6 +57,14 @@ async def root(): content = content.replace("{{error}}", error_message) content = content.replace("{{things}}", things) content = content.replace("{{config}}", json.dumps(app.labthings_config, indent=2)) + content = content.replace("{{traceback}}", error_w_trace) + + if app.log_history is None: + logging_info = "

No logging info available

" + else: + logging_info = f"

Logging info

\n
{app.log_history}
" + + content = content.replace("{{logginginfo}}", logging_info) return HTMLResponse(content=content, status_code=500) From bc193853747c4a4a234e3b8ea22ad9774be655a6 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 6 Apr 2025 16:41:13 +0100 Subject: [PATCH 2/8] Add tighter restriction on pydantic version due to FastAPI incompatibility with pydantic 2.11 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b154a054..d223e4a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ classifiers = [ "Operating System :: OS Independent", ] dependencies = [ - "pydantic>=2.0.0", + "pydantic ~= 2.10.6", "numpy>=1.20", "jsonschema", "typing_extensions", From cd02d9f852fe218f6f354cbd360cd188041d1cb0 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 6 Apr 2025 18:49:29 +0100 Subject: [PATCH 3/8] Allow base_coverage job to fail If there is a problem on master then base_coverage will never pass meaning that coverage will never be run. --- .github/workflows/test.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 019b532a..08654ac0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,7 @@ on: jobs: base_coverage: + continue-on-error: true runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -111,11 +112,16 @@ jobs: name: coverage-3.12 - name: Download code coverage report for base branch + id: download-base-coverage + continue-on-error: true uses: actions/download-artifact@v4 with: name: base-coverage.lcov - name: Generate Code Coverage report + # Note, due to continue on error (to make job pass) we need to check the + # Status of the step directly not just use success() or failure() + if: steps.download-base-coverage.outcome == 'success' id: code-coverage uses: barecheck/code-coverage-action@v1 with: @@ -124,4 +130,15 @@ jobs: base-lcov-file: "./base-coverage.lcov" minimum-ratio: 0 send-summary-comment: true - show-annotations: "warning" \ No newline at end of file + show-annotations: "warning" + + - name: Generate Code Coverage report if base job fails + if: steps.download-base-coverage.outcome == 'failure' + id: code-coverage-without-base + uses: barecheck/code-coverage-action@v1 + with: + barecheck-github-app-token: ${{ secrets.BARECHECK_GITHUB_APP_TOKEN }} + lcov-file: "./coverage.lcov" + minimum-ratio: 0 + send-summary-comment: true + show-annotations: "warning" From 50598cebefb937eded05f75a508c12787d68c8e8 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 6 Apr 2025 19:39:05 +0100 Subject: [PATCH 4/8] Use pinned dependencies for base_coverage --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 08654ac0..00f96e92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: python-version: 3.12 - name: Install Dependencies - run: pip install -e .[dev,server] + run: pip install -e . -r dev-requirements.txt - name: Test with pytest run: | From 204e35ad0ea74d73d43398599434c1fa686a5c99 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 6 Apr 2025 21:56:32 +0100 Subject: [PATCH 5/8] Ensure concurrency is used in testing to monitor code run via subprocess --- .coveragerc | 4 ++++ tests/test_server_cli.py | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..b065e6eb --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[run] +concurrency = multiprocessing +parallel = true +sigterm = true diff --git a/tests/test_server_cli.py b/tests/test_server_cli.py index 404b9067..1c09b428 100644 --- a/tests/test_server_cli.py +++ b/tests/test_server_cli.py @@ -119,8 +119,8 @@ def test_serve_with_no_config(): check_serve_from_cli([]) -def test_invalid_thing_and_fallback(): - """Check it fails for invalid things, and test the fallback option""" +def test_invalid_thing(): + """Check it fails for invalid things""" config_json = json.dumps( { "things": { @@ -130,8 +130,20 @@ def test_invalid_thing_and_fallback(): ) with raises(ImportError): check_serve_from_cli(["-j", config_json]) - ## the line below should start a dummy server with an error page - - ## it terminates happily once the server starts. + +def test_fallback(): + """test the fallback option + + startd a dummy server with an error page - + it terminates once the server starts. + """ + config_json = json.dumps( + { + "things": { + "broken": "labthings_fastapi.example_things:MissingThing", + } + } + ) check_serve_from_cli(["-j", config_json, "--fallback"]) From e1e630a9e997ff451b0e3a5940a933b70b386df6 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 6 Apr 2025 23:10:59 +0100 Subject: [PATCH 6/8] Add more complete checks for fallback server --- .coveragerc | 2 +- tests/test_fallback.py | 64 ++++++++++++++++++++++++++++++++++++++++ tests/test_server_cli.py | 1 + 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/test_fallback.py diff --git a/.coveragerc b/.coveragerc index b065e6eb..4f0eb412 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,4 @@ [run] -concurrency = multiprocessing +concurrency = multiprocessing, thread parallel = true sigterm = true diff --git a/tests/test_fallback.py b/tests/test_fallback.py new file mode 100644 index 00000000..9e285d47 --- /dev/null +++ b/tests/test_fallback.py @@ -0,0 +1,64 @@ +from fastapi.testclient import TestClient +from labthings_fastapi.server import server_from_config +from labthings_fastapi.server.fallback import app + + +def test_fallback_empty(): + with TestClient(app) as client: + response = client.get("/") + html = response.text + # test that something when wrong is shown + assert "Something went wrong" in html + assert "No logging info available" in html + + +def test_fallback_with_config(): + app.labthings_config = {"hello": "goodbye"} + with TestClient(app) as client: + response = client.get("/") + html = response.text + assert "Something went wrong" in html + assert "No logging info available" in html + assert '"hello": "goodbye"' in html + + +def test_fallback_with_error(): + app.labthings_error = RuntimeError("Custom error message") + with TestClient(app) as client: + response = client.get("/") + html = response.text + assert "Something went wrong" in html + assert "No logging info available" in html + assert "RuntimeError" in html + assert "Custom error message" in html + + +def test_fallback_with_server(): + config = { + "things": { + "thing1": "labthings_fastapi.example_things:MyThing", + "thing2": { + "class": "labthings_fastapi.example_things:MyThing", + "kwargs": {}, + }, + } + } + app.labthings_server = server_from_config(config) + with TestClient(app) as client: + response = client.get("/") + html = response.text + assert "Something went wrong" in html + assert "No logging info available" in html + assert "thing1/" in html + assert "thing2/" in html + + +def test_fallback_with_log(): + app.log_history = "Fake log conetent" + 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 diff --git a/tests/test_server_cli.py b/tests/test_server_cli.py index 1c09b428..49bd5e23 100644 --- a/tests/test_server_cli.py +++ b/tests/test_server_cli.py @@ -131,6 +131,7 @@ def test_invalid_thing(): with raises(ImportError): check_serve_from_cli(["-j", config_json]) + def test_fallback(): """test the fallback option From 7118d7d342a9db4eb17d11bc15479fd891ea795b Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 6 Apr 2025 23:36:16 +0100 Subject: [PATCH 7/8] Exclude test and doc files from coverage as they are now detected --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 4f0eb412..7280bd89 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,3 +2,4 @@ concurrency = multiprocessing, thread parallel = true sigterm = true +omit = tests/**/*.py, docs/**/*.py From b232bc10ea9f96828594ccf8646f33c33fc67b79 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Tue, 8 Apr 2025 09:00:05 +0100 Subject: [PATCH 8/8] Bump version to 0.0.8 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d223e4a2..c4cc5131 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "labthings-fastapi" -version = "0.0.7" +version = "0.0.8" authors = [ { name="Richard Bowman", email="richard.bowman@cantab.net" }, ]