From 634ab270f861900476ea6336eca8a3493a891429 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Sat, 16 Sep 2023 11:39:24 +0200 Subject: [PATCH 1/2] Mark the library as being typed --- pyjwt_key_fetcher/fetcher.py | 6 +++--- pyjwt_key_fetcher/http_client.py | 4 ++-- pyjwt_key_fetcher/key.py | 6 +++--- pyjwt_key_fetcher/provider.py | 8 +++++--- pyjwt_key_fetcher/py.typed | 0 pyjwt_key_fetcher/utils.py | 2 +- pyproject.toml | 6 ++++++ 7 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 pyjwt_key_fetcher/py.typed diff --git a/pyjwt_key_fetcher/fetcher.py b/pyjwt_key_fetcher/fetcher.py index 974e548..a5b28f1 100644 --- a/pyjwt_key_fetcher/fetcher.py +++ b/pyjwt_key_fetcher/fetcher.py @@ -12,7 +12,7 @@ class AsyncKeyFetcher: def __init__( self, - valid_issuers: Optional[Iterable] = None, + valid_issuers: Optional[Iterable[str]] = None, http_client: Optional[HTTPClient] = None, cache_ttl: int = 3600, cache_maxsize: int = 32, @@ -56,7 +56,7 @@ def get_kid(token: str) -> str: """ jwt_headers = jwt.get_unverified_header(token) try: - kid = jwt_headers["kid"] + kid: str = jwt_headers["kid"] except KeyError: raise JWTFormatError("Missing 'kid' in header") return kid @@ -83,7 +83,7 @@ def get_issuer(token: str) -> str: """ payload = jwt.decode(token, options={"verify_signature": False}) try: - issuer = payload["iss"] + issuer: str = payload["iss"] except KeyError: raise JWTFormatError("Missing 'iss' in payload") diff --git a/pyjwt_key_fetcher/http_client.py b/pyjwt_key_fetcher/http_client.py index 09220dc..6f970c2 100644 --- a/pyjwt_key_fetcher/http_client.py +++ b/pyjwt_key_fetcher/http_client.py @@ -30,7 +30,7 @@ class DefaultHTTPClient(HTTPClient): A default client implemented using aiohttp. """ - def __init__(self): + def __init__(self) -> None: self.session = aiohttp.ClientSession() async def get_json(self, url: str) -> Dict[str, Any]: @@ -46,7 +46,7 @@ async def get_json(self, url: str) -> Dict[str, Any]: try: async with self.session.get(url) as resp: - data = await resp.json() + data: Dict[str, Any] = await resp.json() if resp.status != 200: raise JWTHTTPFetchError(f"Failed to fetch or decode {url}") except (aiohttp.ClientError, JSONDecodeError) as e: diff --git a/pyjwt_key_fetcher/key.py b/pyjwt_key_fetcher/key.py index 573e7cf..7dbbbdd 100644 --- a/pyjwt_key_fetcher/key.py +++ b/pyjwt_key_fetcher/key.py @@ -4,7 +4,7 @@ from jwt import PyJWK -class Key(collections.abc.Mapping): +class Key(collections.abc.Mapping[str, Any]): """ Wrapper for the JWT key and algorithm. """ @@ -32,10 +32,10 @@ def __repr__(self) -> str: f"{self.__kid}>, algorithms={self.algorithms})" ) - def __getitem__(self, item): + def __getitem__(self, item: str) -> Any: return self.dct.__getitem__(item) - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator[str]: return self.dct.__iter__() def __len__(self) -> int: diff --git a/pyjwt_key_fetcher/provider.py b/pyjwt_key_fetcher/provider.py index e9f8776..e46ff07 100644 --- a/pyjwt_key_fetcher/provider.py +++ b/pyjwt_key_fetcher/provider.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any, Callable, Dict, Optional from uuid import uuid4 import aiocache # type: ignore @@ -12,7 +12,9 @@ from pyjwt_key_fetcher.key import Key -def key_builder(f, *args, **kwargs) -> str: +def key_builder( + f: Callable[..., Any], *args: "Provider", **kwargs: Dict[str, Any] +) -> str: """ Custom key builder for aiocache that uses the self.uuid instead of serializing self to something that contains some memory address that might get reused later, @@ -82,7 +84,7 @@ async def _get_jwks_uri(self) -> str: """ conf = await self.get_configuration() try: - jwks_uri = conf["jwks_uri"] + jwks_uri: str = conf["jwks_uri"] except KeyError as e: raise JWTProviderConfigError("Missing 'jwks_uri' in configuration") from e return jwks_uri diff --git a/pyjwt_key_fetcher/py.typed b/pyjwt_key_fetcher/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyjwt_key_fetcher/utils.py b/pyjwt_key_fetcher/utils.py index 6dea7c9..31228f4 100644 --- a/pyjwt_key_fetcher/utils.py +++ b/pyjwt_key_fetcher/utils.py @@ -15,7 +15,7 @@ def unsigned_int_to_urlsafe_b64(i: int) -> str: Encode unsigned integers as urlsafe base64 strings. """ - def byte_len(n): + def byte_len(n: int) -> int: length = 0 while n > 0: length += 1 diff --git a/pyproject.toml b/pyproject.toml index 07c9d80..56e7bc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,3 +31,9 @@ types-cachetools = "^5.3.0" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.mypy] +exclude = ["pyjwt_key_fetcher/tests"] +strict = true +# enable once aiocache is fully typed +disallow_untyped_decorators = false From ada4094aefe1855ed8efdc760f648715424dcfce Mon Sep 17 00:00:00 2001 From: Joakim Nordling Date: Tue, 19 Sep 2023 13:23:22 +0300 Subject: [PATCH 2/2] Switch to Mapping from typing instead of collections.abc --- pyjwt_key_fetcher/key.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyjwt_key_fetcher/key.py b/pyjwt_key_fetcher/key.py index 7dbbbdd..f53ed25 100644 --- a/pyjwt_key_fetcher/key.py +++ b/pyjwt_key_fetcher/key.py @@ -1,10 +1,9 @@ -import collections.abc -from typing import Any, Dict, Iterator +from typing import Any, Dict, Iterator, Mapping from jwt import PyJWK -class Key(collections.abc.Mapping[str, Any]): +class Key(Mapping[str, Any]): """ Wrapper for the JWT key and algorithm. """