From 736771594ff0e051f8d8924def66b9e5c188d49f Mon Sep 17 00:00:00 2001 From: David Robertson Date: Wed, 3 Nov 2021 21:12:32 +0000 Subject: [PATCH] Type annotations for `nacl.exceptions` (#694) Following on from #693, I've pulled out the changes in #692 which relate to `nacl.exceptions` (and applied a suggestion of @Dreamsorcerer in the process). I picked this module because it was a leaf: it doesn't import any other things from `nacl`. The code changes are small; instead, I think we'll have more to say about the mypy config changes. I've turned on a bunch of extra checks for `nacl.exceptions` only. I'd hoped that I could enable strict mode on a module-by-module basis, but that doesn't seem to be possible. So instead, I went through the [mypy documentation](https://mypy.readthedocs.io/en/stable/config_file.html) and turned on every check that sounded good for type safety. (At work, I've found disallowing the propagation of Any, and `disallow_untyped_defs` the most useful: they help ensure that mypy is actually able to do type checking in the first place!) If you're happy with this list of checks, my plan would be to take on other modules and add them one-by-one to the "strict" section in `pyproject.toml`. In the long run I'd like to have everything in `pynacl` under this list, at which point we can drop the per-module override and have a single set of package-wide configuration for mypy. --- pyproject.toml | 30 +++++++++++++++++++++++++++++- src/nacl/exceptions.py | 3 ++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0d0bd5ca..2a08b68c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ target-version = ["py36"] show_error_codes = true check_untyped_defs = true no_implicit_reexport = true +warn_redundant_casts = true files = ["src/nacl"] @@ -22,4 +23,31 @@ files = ["src/nacl"] module = [ "nacl._sodium", ] -ignore_missing_imports = true \ No newline at end of file +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = [ + "nacl.exceptions", +] +disallow_any_unimported = true +disallow_any_expr = true +disallow_any_decorated = true +disallow_any_explicit = true +disallow_any_generics = true +disallow_subclassing_any = true + +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +# check_untyped_defs enabled globally +disallow_untyped_decorators = true + +no_implicit_optional = true + +warn_unused_ignores = true +warn_no_return = true +warn_return_any = true +warn_unreachable = true + +# no-implicit-reexport enabled globally +strict_equality = true diff --git a/src/nacl/exceptions.py b/src/nacl/exceptions.py index c5d097e4..e321df7b 100644 --- a/src/nacl/exceptions.py +++ b/src/nacl/exceptions.py @@ -18,6 +18,7 @@ # `builtins` namespace, so mypy can distinguish between (e.g.) # `nacl.exceptions.RuntimeError` and `builtins.RuntimeError`. import builtins +from typing import Type class CryptoError(Exception): @@ -66,7 +67,7 @@ class UnavailableError(RuntimeError): pass -def ensure(cond, *args, **kwds): +def ensure(cond: bool, *args: object, **kwds: Type[Exception]) -> None: """ Return if a condition is true, otherwise raise a caller-configurable :py:class:`Exception`