Stricter ruff#180
Conversation
I test for an error condition, and also test using the endpoint functions not via HTTP. It was necessary to add a default value for Thing.path to make sure we raise the right error - this is needed by other checks that are introduced in #180.
898b66a to
486c66a
Compare
|
I've paid a little "coverage tax" so the coverage is now increased. I'm starting to contemplate allowing slight (<0.1%) coverage decreases, as the coverage rule is now making it hard to delete code... |
I think the check for decreased coverage is at times unhelpful because it can add to the temptation to just add tests that quickly touch any line of code rather than add clear specific tests. One thing we may want to consider is diff cover. This will check for missing coverage on new lines, so that it encourages specifically testing new code rather than adding any test to get the coverage up. |
julianstirling
left a comment
There was a problem hiding this comment.
Really useful to have these extra rules.
A few small comments. I'd especially like to reserve no-cover for things that are truly not testable, and the one highlighted in the comments is easily testable
Something like I don't see a neat integration for diff-cover in GitHub, though I guess it doesn't need an integration if it's just passing or failing. |
Added the NotConnectedToServerError exception.
I test for an error condition, and also test using the endpoint functions not via HTTP. It was necessary to add a default value for Thing.path to make sure we raise the right error - this is needed by other checks that are introduced in #180.
FastAPI allows dependencies to be specified either with an annotated type, or by supplying Depends() as the default. The latter form can cause problems, e.g. by masking the fact that a dependency hasn't been injected. I've now changed all occurrences of `Depends()` to use annotated types instead, as required by the `FAST` ruleset in ruff.
Sphinx is configured with a Python script, but this is not part of the package, the examples, or the test code. I've therefore excluded it from the rules. I've also changed the exclusion to be specific to the docs config file, and not the whole docs folder, just in case we add scripts there in the future. This also means any example Python files will be checked.
I've added module docstrings and some full stops, so the docstrings in the example comply with the rules for the module source.
Return type annotations for __init__ are not overly useful, and are not required by `mypy`. However, other magic methods do need an annotation, and it's helpful to have `ruff` check this rather than waiting for `mypy`. I've annotated all remaining `__init__` methods so this linter rule now passes. I also fixed a missing import from the change to Annotated for FastAPI dependencies.
One assertion has simply been deleted (line 437) as it was not clear how it could ever fail. An explicit type check a couple of lines below ensures we will catch any errors here promptly, so robustness is not affected.
I've exempted these from coverage: they are both primarily for the benefit of mypy, as they only guard against weakrefs failing. The objects referenced won't be deleted until after the server shuts down, so these errors should never occur.
The action manager is added to a ThingServer during __init__ so it may never be None. The assertion is therefore not necessary.
I've excluded this from coverage: it's testing against the blocking portal being missing, in a dependency function. This dependency function is not intended to be called by users, and it is only evaluated by FastAPI while the server is running.
This was guarding a weakref to the Thing: this isn't an error we expect, or an error we should handle, so I've excluded the if block from testing. Coverage is unchanged: the assert statement was equivalent to the new block, it just didn't show as uncovered if the asertion passed.
This includes a couple of extra tests to ensure the right error is raised if the functions are called prematurely.
Eval is flagged as potentially insecure. This function shouldn't be run on user input, and already sanitises the string. `blob_type` is also in the process of being phased out in favour of subclassing Blob explicitly.
This will decrease coverage slightly.
Tests are needed to stop coverage decreasing, as otherwise the exception isn't raised, and shows as uncovered. These errors shouldn't occur in normal operation.
I've enabled a Ruff rule to disallow `except Exception:` and removed one instance of that from the codebase. I have left one `except Exception:` in test code (which captures exceptions in a thread for later analysis), and another in the MJPEG stream (which logs exceptions but does not crash the server if they happen in the MJPEG stream). The MJPEG code might be able to be eliminated now that streams stop more gracefully - but I don't want to mess with that until we're in a position to test it more extensively. I've swapped the `logging.error` for `logging.exception` so that the full stack trace is dumped to the log.
Test code that will fail without a path should mock one. I've added a path in 3 places where this was required in the test suite.
Exceptions raised when setting up things were being wrapped in an ExceptionGroup: this is fixed by using pytest.RasisesGroup, but that required updating pytest. The test that checked for errors when a non-Thing was added to the server was failing, because the config was invalid. I've fixed it, but we really ought to use a model here. I've also renamed the import to avoid confusion with `server`, so we now import the server module as `ts`.
This previously used an `assert` so that any types that weren't models were handled in the `except` block. I've changed this so that plain types are wrapped in a return statement after the except: block, which I think is clearer than the previous structure.
TestThing causes a warning in pytest: I've renamed to avoid the confusion.
I was looking for the best place to add a test, and noticed the comments/docstrings here needed improvement.
Previous tests didn't check what happened for bad action IDs or actions with no output. This is now tested.
This adds a proper unit test for model_to_dict that checks all code paths.
pydocstyle in flake8 gets confused by overloads. The same rules in ruff behave correctly, and that's what the original authors recommend. I've removed pydocstyle from the dev dependencies already, so this commit just removes the vestigial #noqa directives.
The set property endpoint function was returning `self.__set__` which always returns `None`. The function is still called but, for clarity, it is not returned. This is not a change in behaviour. I've also improved the comment describing how we annotate the `body` argument of the endpoint.
This checks that any URL specified for the "context" field of a Thing Description is valid.
Error checking code added elsewhere now means Things must have a `path` property or we can't generate the Thing Description. I've added this manually in test code, so we don't need a server.
76ce33b to
0442991
Compare
julianstirling
left a comment
There was a problem hiding this comment.
Thanks for the changes
This PR adds a bunch of Ruff rulesets, notably the
Srules (which enforce no assertions) andFASTrules (which are FastAPI specific, and disallow the use ofDependsas a default value because it can cause confusing behaviour).There are quite a few minor code changes as a result of this, mostly swapping assertions for explicit exceptions. As that changes test coverage, a few tests are added to explicitly test for the error conditions. Swapping
assertforif...raisemeans that a test must be added to cover theraiseline, or coverage will decrease.I've also added
ANNwhich enforces type annotations. This is mostly done by the strictermypyrules, but it's nice having a Ruff check because it's much faster. Irritatingly, this complains about__init__being untyped: I feel like__init__does not need an annotation. However, I can't disable it for__init__without disabling other magic functions as well, so I've added explicit-> Noneannotations throughout the codebase.I added
BLEto disallowexcept Exception:blocks, and spotted that one had crept in with a pull request: this is removed.Changing asserts to exceptions also required a bunch of docstring updates, and proper mocking of
Thing.pathin some tests. I'd like to consolidate some of this code in a future PR.