From 4cf3748ff224d841b7f57ae623428c441109f325 Mon Sep 17 00:00:00 2001 From: David Robertson Date: Wed, 3 Nov 2021 23:32:55 +0000 Subject: [PATCH] Type annotations for `nacl.encoding` Pulled out from #692. After we realised in #693 that we don't need `typing_extensions` to use `SupportsBytes`, I didn't want to pull in `typing_extensions` again for just one use of `Protocol`. (I have no other plans to use `Protocol` in other annotations.) My understanding: doing so means that we can't run `mypy` under Python 3.6 or 3.7 to fully typecheck the package. Maybe that's fine? I guess the downside is that someone using `nacl` in a 3.6 or 3.7 project can't fully benefit from these annotations. What're your thoughts here? (As a compromise, I could do a `try-except` dance to use from `typing_extensions.Protocol`, if it happens to be available.) --- pyproject.toml | 1 + src/nacl/encoding.py | 46 ++++++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2a08b68c..1d1b27f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ ignore_missing_imports = true [[tool.mypy.overrides]] module = [ + "nacl.encoding", "nacl.exceptions", ] disallow_any_unimported = true diff --git a/src/nacl/encoding.py b/src/nacl/encoding.py index 848be630..31bdb11b 100644 --- a/src/nacl/encoding.py +++ b/src/nacl/encoding.py @@ -15,69 +15,87 @@ import base64 import binascii -from typing import SupportsBytes +import sys +from typing import SupportsBytes, Type + +if sys.version_info >= (3, 8): + from typing import Protocol + + class _Encoder(Protocol): + @staticmethod + def encode(data: bytes) -> bytes: + ... + + @staticmethod + def decode(data: bytes) -> bytes: + ... + + # We pass around the encoder classes themselves (rather than an instance). + Encoder = Type[_Encoder] +else: + Encoder = "Encoder" class RawEncoder: @staticmethod - def encode(data): + def encode(data: bytes) -> bytes: return data @staticmethod - def decode(data): + def decode(data: bytes) -> bytes: return data class HexEncoder: @staticmethod - def encode(data): + def encode(data: bytes) -> bytes: return binascii.hexlify(data) @staticmethod - def decode(data): + def decode(data: bytes) -> bytes: return binascii.unhexlify(data) class Base16Encoder: @staticmethod - def encode(data): + def encode(data: bytes) -> bytes: return base64.b16encode(data) @staticmethod - def decode(data): + def decode(data: bytes) -> bytes: return base64.b16decode(data) class Base32Encoder: @staticmethod - def encode(data): + def encode(data: bytes) -> bytes: return base64.b32encode(data) @staticmethod - def decode(data): + def decode(data: bytes) -> bytes: return base64.b32decode(data) class Base64Encoder: @staticmethod - def encode(data): + def encode(data: bytes) -> bytes: return base64.b64encode(data) @staticmethod - def decode(data): + def decode(data: bytes) -> bytes: return base64.b64decode(data) class URLSafeBase64Encoder: @staticmethod - def encode(data): + def encode(data: bytes) -> bytes: return base64.urlsafe_b64encode(data) @staticmethod - def decode(data): + def decode(data: bytes) -> bytes: return base64.urlsafe_b64decode(data) class Encodable: - def encode(self: SupportsBytes, encoder=RawEncoder): + def encode(self: SupportsBytes, encoder: Encoder = RawEncoder) -> bytes: return encoder.encode(bytes(self))