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

Add support for Python 3.12 #910

Merged
merged 4 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ 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"
- uses: "actions/setup-python@v4"

with:
python-version: "${{ matrix.python-version }}"
allow-prereleases: true

- name: "Install dependencies"
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pypi-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 7 additions & 18 deletions jwt/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
"""
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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"):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 5 additions & 5 deletions jwt/api_jwk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions tests/test_api_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
12 changes: 6 additions & 6 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down