Skip to content
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

Mypy tests 3 #730

Merged
merged 8 commits into from Dec 24, 2021
Merged

Mypy tests 3 #730

merged 8 commits into from Dec 24, 2021

Conversation

DMRobertson
Copy link
Contributor

@DMRobertson DMRobertson commented Dec 23, 2021

Also a draft. Branches off from #728; the first new commit is
8cfeced. I'm just sticking this up to get CI to run on the proposed final batch of annotations to tests now.

Also, while I'm at it, I wanted to see if we could quantify how much has changed with all this typing. To do so:

git checkout mypy-tests-3
mypy --html-report out --any-exprs-report out
xdg-open out/index.html
less out/any-exprs.txt

git checkout ee93f9f13e4ce803a707b972d0fbc324439e71fc
git restore --source=mypy-tests-3 pyproject.toml
mypy --html-report out_old --any-exprs-report out_old
xdg-open out_old/index.html
less out_old/any-exprs.txt

TL:DR:

  • Before: 55% of expressions were not Any. Mypy deemed the project as a whole 33% imprecise.
  • After: 83% of expressions are not Any. Project as a whole deemed 12% imprecise.

(These numbers from the any-exprs and html reports, respectively).

I think most of that comes down to cffi in nacl; maybe some bits in tests from json.loads().

If we just look at the higher level stuff outside of nacl.bindings, the numbers are rosier:

$ less out/any-exprs.txt | \grep nacl | \grep -v bindings 
                             nacl      0      19    100.00%
                    nacl.encoding      0      70    100.00%
                  nacl.exceptions      0      27    100.00%
                        nacl.hash      0     105    100.00%
                     nacl.hashlib      0     137    100.00%
                      nacl.public      5     489     98.98%
                      nacl.pwhash      0      85    100.00%
              nacl.pwhash._argon2      0      31    100.00%
              nacl.pwhash.argon2i      0      71    100.00%
             nacl.pwhash.argon2id      0      71    100.00%
               nacl.pwhash.scrypt      1     129     99.22%
                      nacl.secret      2     276     99.28%
                     nacl.signing      6     278     97.84%
                       nacl.utils      0      59    100.00%

@DMRobertson DMRobertson force-pushed the mypy-tests-3 branch 2 times, most recently from a270ea9 to e0eb2ea Compare December 23, 2021 17:41
@DMRobertson
Copy link
Contributor Author

@DMRobertson DMRobertson marked this pull request as ready for review December 24, 2021 01:22
@@ -73,13 +73,15 @@ class crypto_secretstream_xchacha20poly1305_state:

def __init__(self) -> None:
"""Initialize a clean state object."""
self.statebuf = ffi.new(
# NOTE: the members below aren't `bytearray` objects, but cffi `cdata` objects
# which own an array of unsigned chars. Is there a better annotation?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered typing.ByteString here, but

  • that's immutable, but one of the test writes to it
  • the docs suggest that bytes in an annotation is really shorthand for a ByteString anyway.

    class typing.ByteString(Sequence[int])

    A generic version of collections.abc.ByteString.

    This type represents the types bytes, bytearray, and memoryview of byte sequences.

    As a shorthand for this type, bytes can be used to annotate arguments of any of the types mentioned above.

bytearray seemed like the least bad option, but would welcome input here!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cryptography we've chosen to leave these untyped for now with a hope of typing ffi.new itself in the future. I suppose if we do that then we'd catch these cases here, but I'm not hugely comfortable with picking a "closest approximation" type. e.g. you can't call bytes() on this buffer, which you could on a bytearray.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense.

The only place (AFAICS) that the statebuf gets mutated by Python is here, and that's just to copy the data from another ffi unsigned char[].

Perhaps it would be safer to use Sequence[int] here instead of bytearray and type-ignore the part of the test that writes to this buffer. (I stuck a breakpoint in at the snippet linked above, where state_save[0] was the int 68 as opposed to b"\x68".)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, ByteString seems like a better fit: it inherits from Sequence[int], but additionally expresses that the ints are all going to be byte-sized.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had a go at this in bc42daf.

Comment on lines +132 to +133
# Type safety: mypy spots that you can't decrypt with a public key, but we
# want to detect this at runtime too.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was pleased to see this caught by mypy---I'd forgotten that we did this in #706!

We're not trying to annotate cffi here (that seems subtle, hard and
important to get right). Instead, `ByteString` gives us a `len()` which
keeps mypy happy within the project and is slightly more prescriptive
than `Sized` or `Sequence[int]`.
@reaperhulk
Copy link
Member

I'm going to avoid further nitpicking here since we can always find ways to improve in the future. 😁

@reaperhulk reaperhulk merged commit 136d917 into pyca:main Dec 24, 2021
@DMRobertson
Copy link
Contributor Author

For what it's worth, I'm open to nitpicking. Either way, great to see this merged and rounded off!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 25, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants