-
Notifications
You must be signed in to change notification settings - Fork 4
Better errors for bad return values #333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b92d8ad
e155711
420350e
ddad49d
b93623f
fa1bc94
58ddb29
5e6b1a7
e6c8631
c5c8704
05c757a
26852c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,8 @@ | |||||
| # An __all__ for this module is less than helpful, unless we have an | ||||||
| # automated check that everything's included. | ||||||
|
|
||||||
| from collections.abc import Callable | ||||||
|
|
||||||
|
|
||||||
| class NotConnectedToServerError(RuntimeError): | ||||||
| """The Thing is not connected to a server. | ||||||
|
|
@@ -254,6 +256,74 @@ class NoInvocationContextError(RuntimeError): | |||||
| """ | ||||||
|
|
||||||
|
|
||||||
| class CausedByUserCodeError(Exception): | ||||||
| """A mixin to allow exceptions to refer to downstream code.""" | ||||||
|
|
||||||
| def _append_to_args(self, message: str) -> None: | ||||||
| """Add a message to the exception's arguments. | ||||||
|
|
||||||
| :param message: the message to append. | ||||||
| """ | ||||||
| if len(self.args) == 1: | ||||||
| # If there's only one string, assume it's a message and append | ||||||
| self.args = (self.args[0] + "\n" + message,) | ||||||
| else: | ||||||
| # If there are multiple arguments, add this as a further one | ||||||
| self.args += (message,) | ||||||
|
|
||||||
| def set_source_function(self, func: Callable) -> None: | ||||||
| """Add the location of a user-supplied function to the error message. | ||||||
|
|
||||||
| :param func: the function that caused this error. | ||||||
| """ | ||||||
| code = func.__code__ | ||||||
| self._append_to_args( | ||||||
| f"This was likely caused by function '{code.co_name}' " | ||||||
| f"at {code.co_filename}:{code.co_firstlineno}" | ||||||
| ) | ||||||
|
|
||||||
| def set_source_class(self, cls: type, attr: str | None = None) -> None: | ||||||
| """Add a reference to a class (and optionally attribute). | ||||||
|
|
||||||
| :param cls: the class that caused this error. | ||||||
| :param attr: the attribute name that caused this error. | ||||||
| """ | ||||||
| self._append_to_args( | ||||||
| f"This was likely caused by '{cls.__module__}.{cls.__qualname__}.{attr}" | ||||||
| if attr | ||||||
| else "'." | ||||||
| ) | ||||||
|
|
||||||
|
|
||||||
| class InvalidReturnValueError(CausedByUserCodeError, RuntimeError): | ||||||
| r"""The return value from a method cannot be serialised by LabThings. | ||||||
|
|
||||||
| This error is raised when an action returns a value that can't be serialised. | ||||||
| This usually means that either it doesn't match the declared return type of | ||||||
| the function, or the declared return type permits un-serialisable values. | ||||||
|
|
||||||
| If an action's return type is missing or `Any`\ , it's possible to return a | ||||||
| value that can't be serialised, which will cause this error. | ||||||
|
|
||||||
| The solution is usually to ensure that the return type of your action is | ||||||
| either a simple type that can be serialised to JSON, or a Pydantic model. | ||||||
| You should also check that the function's return value matches the declared | ||||||
| type, ideally by regularly running a type checker like `mypy` on your code. | ||||||
| """ | ||||||
|
|
||||||
|
|
||||||
| class UnserializableTypeError(CausedByUserCodeError, TypeError): | ||||||
| r"""A type has been specified that can't be serialized to JSON. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Just to be consistent with the docstring for InvalidReturnValueError |
||||||
|
|
||||||
| This error generally means a property or action has a type that cannot be | ||||||
| serialized to JSON. This might be an instance of a custom class, or another | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Just to be consistent with the docstring for InvalidReturnValueError |
||||||
| datatype that doesn't have a ready representation using JSON-compatible types. | ||||||
|
|
||||||
| This error can often be fixed using `pydantic` annotations, or by using simple | ||||||
| Python types instead of custom ones. | ||||||
| """ | ||||||
|
|
||||||
|
|
||||||
| class LogConfigurationError(RuntimeError): | ||||||
| """There is a problem with logging configuration. | ||||||
|
|
||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.