diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d3a1c47..6ec8fd61 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,12 +13,12 @@ jobs: name: "Python ${{ matrix.python-version }} on ${{ matrix.platform }}" runs-on: "${{ matrix.platform }}" env: - USING_COVERAGE: '3.7,3.8' + USING_COVERAGE: '3.8' strategy: matrix: platform: ["ubuntu-latest", "windows-latest"] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "pypy-3.8", "pypy-3.9"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.8", "pypy3.9"] steps: - uses: "actions/checkout@v3" @@ -26,6 +26,7 @@ jobs: with: python-version: "${{ matrix.python-version }}" + allow-prereleases: true - name: "Install dependencies" run: | diff --git a/.github/workflows/pypi-package.yml b/.github/workflows/pypi-package.yml index 5d6c6fb6..0e6210aa 100644 --- a/.github/workflows/pypi-package.yml +++ b/.github/workflows/pypi-package.yml @@ -34,7 +34,7 @@ jobs: release-test-pypi: name: Publish in-dev package to test.pypi.org environment: release-test-pypi - if: github.event_name == 'push' && github.ref == 'refs/heads/master' + if: github.repository_owner == 'jpadilla' && github.event_name == 'push' && github.ref == 'refs/heads/master' runs-on: ubuntu-latest needs: build-package @@ -54,7 +54,7 @@ jobs: release-pypi: name: Publish released package to pypi.org environment: release-pypi - if: github.event.action == 'published' + if: github.repository_owner == 'jpadilla' && github.event.action == 'published' runs-on: ubuntu-latest needs: build-package diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 799c149d..fb1c89c3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,13 +3,13 @@ repos: rev: 23.7.0 hooks: - id: black - args: ["--target-version=py37"] + args: ["--target-version=py38"] - repo: https://github.com/asottile/blacken-docs rev: 1.15.0 hooks: - id: blacken-docs - args: ["--target-version=py37"] + args: ["--target-version=py38"] - repo: https://github.com/PyCQA/flake8 rev: 6.1.0 diff --git a/jwt/algorithms.py b/jwt/algorithms.py index ed187152..ece86b4d 100644 --- a/jwt/algorithms.py +++ b/jwt/algorithms.py @@ -3,9 +3,8 @@ import hashlib import hmac import json -import sys from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, ClassVar, NoReturn, Union, cast, overload +from typing import TYPE_CHECKING, Any, ClassVar, Literal, NoReturn, cast, overload from .exceptions import InvalidKeyError from .types import HashlibHash, JWKDict @@ -21,12 +20,6 @@ to_base64url_uint, ) -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - - try: from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend @@ -205,7 +198,7 @@ def to_jwk(key_obj, as_dict: Literal[False] = False) -> str: @staticmethod @abstractmethod - def to_jwk(key_obj, as_dict: bool = False) -> Union[JWKDict, str]: + def to_jwk(key_obj, as_dict: bool = False) -> JWKDict | str: """ Serializes a given key into a JWK """ @@ -283,7 +276,7 @@ def to_jwk(key_obj: str | bytes, as_dict: Literal[False] = False) -> str: ... # pragma: no cover @staticmethod - def to_jwk(key_obj: str | bytes, as_dict: bool = False) -> Union[JWKDict, str]: + def to_jwk(key_obj: str | bytes, as_dict: bool = False) -> JWKDict | str: jwk = { "k": base64url_encode(force_bytes(key_obj)).decode(), "kty": "oct", @@ -363,9 +356,7 @@ def to_jwk(key_obj: AllowedRSAKeys, as_dict: Literal[False] = False) -> str: ... # pragma: no cover @staticmethod - def to_jwk( - key_obj: AllowedRSAKeys, as_dict: bool = False - ) -> Union[JWKDict, str]: + def to_jwk(key_obj: AllowedRSAKeys, as_dict: bool = False) -> JWKDict | str: obj: dict[str, Any] | None = None if hasattr(key_obj, "private_numbers"): @@ -533,7 +524,7 @@ def sign(self, msg: bytes, key: EllipticCurvePrivateKey) -> bytes: return der_to_raw_signature(der_sig, key.curve) - def verify(self, msg: bytes, key: "AllowedECKeys", sig: bytes) -> bool: + def verify(self, msg: bytes, key: AllowedECKeys, sig: bytes) -> bool: try: der_sig = raw_to_der_signature(sig, key.curve) except ValueError: @@ -561,9 +552,7 @@ def to_jwk(key_obj: AllowedECKeys, as_dict: Literal[False] = False) -> str: ... # pragma: no cover @staticmethod - def to_jwk( - key_obj: AllowedECKeys, as_dict: bool = False - ) -> Union[JWKDict, str]: + def to_jwk(key_obj: AllowedECKeys, as_dict: bool = False) -> JWKDict | str: if isinstance(key_obj, EllipticCurvePrivateKey): public_numbers = key_obj.public_key().public_numbers() elif isinstance(key_obj, EllipticCurvePublicKey): @@ -780,7 +769,7 @@ def to_jwk(key: AllowedOKPKeys, as_dict: Literal[False] = False) -> str: ... # pragma: no cover @staticmethod - def to_jwk(key: AllowedOKPKeys, as_dict: bool = False) -> Union[JWKDict, str]: + def to_jwk(key: AllowedOKPKeys, as_dict: bool = False) -> JWKDict | str: if isinstance(key, (Ed25519PublicKey, Ed448PublicKey)): x = key.public_bytes( encoding=Encoding.Raw, diff --git a/jwt/api_jwk.py b/jwt/api_jwk.py index 456c7f4d..af745783 100644 --- a/jwt/api_jwk.py +++ b/jwt/api_jwk.py @@ -60,11 +60,11 @@ def __init__(self, jwk_data: JWKDict, algorithm: str | None = None) -> None: self.key = self.Algorithm.from_jwk(self._jwk_data) @staticmethod - def from_dict(obj: JWKDict, algorithm: str | None = None) -> "PyJWK": + def from_dict(obj: JWKDict, algorithm: str | None = None) -> PyJWK: return PyJWK(obj, algorithm) @staticmethod - def from_json(data: str, algorithm: None = None) -> "PyJWK": + def from_json(data: str, algorithm: None = None) -> PyJWK: obj = json.loads(data) return PyJWK.from_dict(obj, algorithm) @@ -104,16 +104,16 @@ def __init__(self, keys: list[JWKDict]) -> None: ) @staticmethod - def from_dict(obj: dict[str, Any]) -> "PyJWKSet": + def from_dict(obj: dict[str, Any]) -> PyJWKSet: keys = obj.get("keys", []) return PyJWKSet(keys) @staticmethod - def from_json(data: str) -> "PyJWKSet": + def from_json(data: str) -> PyJWKSet: obj = json.loads(data) return PyJWKSet.from_dict(obj) - def __getitem__(self, kid: str) -> "PyJWK": + def __getitem__(self, kid: str) -> PyJWK: for key in self.keys: if key.key_id == kid: return key diff --git a/setup.cfg b/setup.cfg index 44ccf0b3..e5f01712 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,20 +23,18 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Utilities [options] zip_safe = false include_package_data = true -python_requires = >=3.7 +python_requires = >=3.8 packages = find: -install_requires = - typing_extensions; python_version<="3.7" [options.package_data] * = py.typed diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py index 82b92994..b51170b3 100644 --- a/tests/test_api_jwt.py +++ b/tests/test_api_jwt.py @@ -437,9 +437,7 @@ def test_raise_exception_audience_as_bytes(self, jwt): payload = {"some": "payload", "aud": ["urn:me", "urn:someone-else"]} token = jwt.encode(payload, "secret") with pytest.raises(InvalidAudienceError): - jwt.decode( - token, "secret", audience="urn:me".encode(), algorithms=["HS256"] - ) + jwt.decode(token, "secret", audience=b"urn:me", algorithms=["HS256"]) def test_raise_exception_invalid_audience_in_array(self, jwt): payload = { diff --git a/tox.ini b/tox.ini index 07ace117..84940242 100644 --- a/tox.ini +++ b/tox.ini @@ -8,20 +8,20 @@ filterwarnings = [gh-actions] python = - 3.7: py37, docs 3.8: py38, typing 3.9: py39 3.10: py310 - 3.11: py311 - pypy-3.8: pypy3 - pypy-3.9: pypy3 + 3.11: py311, docs + 3.12: py312 + pypy3.8: pypy3 + pypy3.9: pypy3 [tox] envlist = lint typing - py{37,38,39,310,311,py3}-{crypto,nocrypto} + py{38,39,310,311,312,py3}-{crypto,nocrypto} docs pypi-description coverage-report @@ -40,7 +40,7 @@ commands = {envpython} -b -m coverage run -m pytest {posargs} [testenv:docs] -basepython = python3.7 +basepython = python3.11 extras = docs commands = sphinx-build -n -T -W -b html -d {envtmpdir}/doctrees docs docs/_build/html