Skip to content

Commit

Permalink
Merge pull request #95 from ewjoachim/modern_annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
ewjoachim committed May 29, 2022
2 parents abc0ec9 + 28592dc commit e304de0
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 242 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ jobs:
strategy:
matrix:
include:
- python_version: 3.6
- python_version: "3.7"
script: tests
- python_version: 3.7
- python_version: "3.8"
script: tests
- python_version: 3.8
- python_version: "3.9"
script: tests
- python_version: 3.9
- python_version: "3.10"
script: tests

name: "py${{ matrix.python_version }} / ${{ matrix.script }}"
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ PyPIToken: Manipulate PyPI API tokens
:alt: Contributor Covenant


PyPIToken is an open-source Python 3.6+ library for generating and manipulating
PyPIToken is an open-source Python 3.7+ library for generating and manipulating
PyPI tokens.

PyPI tokens are very powerful, as that they are based on Macaroons_. They allow
Expand Down
10 changes: 6 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config

# -- Path setup --------------------------------------------------------------
from __future__ import annotations

import datetime
import pathlib
import sys

# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
Expand All @@ -14,9 +19,6 @@
# import sys
# sys.path.insert(0, os.path.abspath('.'))

import datetime
import pathlib
import sys

# -- Project information -----------------------------------------------------
project = "PyPIToken"
Expand Down
360 changes: 181 additions & 179 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pypitoken/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from .exceptions import (
InvalidRestriction,
LoaderError,
Expand Down
3 changes: 3 additions & 0 deletions pypitoken/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations


class PyPITokenException(Exception):
"""
The base exception for all exceptions raised
Expand Down
76 changes: 38 additions & 38 deletions pypitoken/token.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from __future__ import annotations

import dataclasses
import datetime
import functools
import json
import time
from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, Union
from typing import Any, Iterable, TypeVar

import jsonschema
import pymacaroons
Expand Down Expand Up @@ -43,15 +45,15 @@ class Restriction:
"""

@staticmethod
def _get_schema() -> Dict:
def _get_schema() -> dict:
"""
Return a jsonschema Dict object used to validate the format
of a json restriction.
"""
raise NotImplementedError

@classmethod
def _load_value(cls: Type[T], value: Dict) -> T:
def _load_value(cls: type[T], value: dict) -> T:
"""
Create a Restriction from the JSON value stored in the caveat
Expand All @@ -75,7 +77,7 @@ def _load_value(cls: Type[T], value: Dict) -> T:
return cls(**cls._extract_kwargs(value=value)) # type: ignore

@staticmethod
def _get_subclasses() -> List[Type["Restriction"]]:
def _get_subclasses() -> list[type[Restriction]]:
"""
List all subclasses of Restriction that we want to match against
"""
Expand All @@ -94,7 +96,7 @@ def _json_load_caveat(caveat: str) -> Any:
return value

@classmethod
def load(cls, caveat: Dict) -> "Restriction":
def load(cls, caveat: dict) -> Restriction:
"""
Create a Restriction from a raw caveat restriction JSON object.
Expand All @@ -118,7 +120,7 @@ def load(cls, caveat: Dict) -> "Restriction":
)

@classmethod
def load_json(cls, caveat: str) -> "Restriction":
def load_json(cls, caveat: str) -> Restriction:
"""
Create a Restriction from a raw caveat restriction JSON string.
Expand All @@ -135,15 +137,15 @@ def load_json(cls, caveat: str) -> "Restriction":
return cls.load(caveat=caveat_obj)

@classmethod
def _extract_kwargs(cls, value: Dict) -> Dict:
def _extract_kwargs(cls, value: dict) -> dict:
"""
Receive the parsed JSON value of a caveat for which the schema has been
validated. Return the instantiation kwargs (``__init__`` parameters).
"""
raise NotImplementedError

@classmethod
def from_parameters(cls: Type[T], **kwargs) -> Optional[T]:
def from_parameters(cls: type[T], **kwargs) -> T | None:
"""
Contructs an instance from the parameters passed to `Token.restrict`
"""
Expand All @@ -156,7 +158,7 @@ def restriction_parameters(cls):
)

@classmethod
def restrictions_from_parameters(cls, **kwargs) -> Iterable["Restriction"]:
def restrictions_from_parameters(cls, **kwargs) -> Iterable[Restriction]:
"""
Contructs an iterable of Restriction subclass instances from the parameters
passed to `Token.restrict`
Expand All @@ -182,7 +184,7 @@ def check(self, context: Context) -> None:
"""
raise NotImplementedError

def dump(self) -> Dict:
def dump(self) -> dict:
"""
Transform a restriction into a JSON-compatible dict object
"""
Expand All @@ -202,7 +204,7 @@ class NoopRestriction(Restriction):
"""

@staticmethod
def _get_schema() -> Dict:
def _get_schema() -> dict:
return {
"type": "object",
"properties": {
Expand All @@ -214,18 +216,18 @@ def _get_schema() -> Dict:
}

@classmethod
def _extract_kwargs(cls, value: Dict) -> Dict:
def _extract_kwargs(cls, value: dict) -> dict:
return {}

def check(self, context: Context) -> None:
# Always passes
return

def dump(self) -> Dict:
def dump(self) -> dict:
return {"version": 1, "permissions": "user"}

@classmethod
def from_parameters(cls, **kwargs) -> Optional["NoopRestriction"]:
def from_parameters(cls, **kwargs) -> NoopRestriction | None:
if not kwargs:
return cls()
return None
Expand All @@ -242,10 +244,10 @@ class ProjectsRestriction(Restriction):
Normalized project names this token may upload to.
"""

projects: List[str]
projects: list[str]

@staticmethod
def _get_schema() -> Dict:
def _get_schema() -> dict:
return {
"type": "object",
"properties": {
Expand All @@ -264,10 +266,10 @@ def _get_schema() -> Dict:
}

@classmethod
def _extract_kwargs(cls, value: Dict) -> Dict:
def _extract_kwargs(cls, value: dict) -> dict:
return {"projects": value["permissions"]["projects"]}

def dump(self) -> Dict:
def dump(self) -> dict:
return {"version": 1, "permissions": {"projects": self.projects}}

def check(self, context: Context) -> None:
Expand All @@ -282,9 +284,9 @@ def check(self, context: Context) -> None:
@classmethod
def from_parameters(
cls,
projects: Optional[List[str]] = None,
projects: list[str] | None = None,
**kwargs,
) -> Optional["ProjectsRestriction"]:
) -> ProjectsRestriction | None:
if projects is not None:
return cls(projects=projects)
return None
Expand All @@ -308,7 +310,7 @@ class DateRestriction(Restriction):
not_after: int

@staticmethod
def _get_schema() -> Dict:
def _get_schema() -> dict:
return {
"type": "object",
"properties": {
Expand All @@ -320,13 +322,13 @@ def _get_schema() -> Dict:
}

@classmethod
def _extract_kwargs(cls, value: Dict) -> Dict:
def _extract_kwargs(cls, value: dict) -> dict:
return {
"not_before": value["nbf"],
"not_after": value["exp"],
}

def dump(self) -> Dict:
def dump(self) -> dict:
return {"nbf": self.not_before, "exp": self.not_after}

def check(self, context: Context) -> None:
Expand All @@ -342,10 +344,10 @@ def check(self, context: Context) -> None:
@classmethod
def from_parameters(
cls,
not_before: Optional[Union[datetime.datetime, int]] = None,
not_after: Optional[Union[datetime.datetime, int]] = None,
not_before: datetime.datetime | int | None = None,
not_after: datetime.datetime | int | None = None,
**kwargs,
) -> Optional["DateRestriction"]:
) -> DateRestriction | None:
if not_before or not_after:
if not (not_before and not_after):
raise exceptions.InvalidRestriction(
Expand All @@ -360,7 +362,7 @@ def from_parameters(
return None

@staticmethod
def timestamp_from_parameter(param: Union[datetime.datetime, int]) -> int:
def timestamp_from_parameter(param: datetime.datetime | int) -> int:
if isinstance(param, int):
return param
# https://docs.python.org/3/library/datetime.html#determining-if-an-object-is-aware-or-naive
Expand Down Expand Up @@ -425,7 +427,7 @@ def identifier(self) -> str:
return self._macaroon.identifier.decode("utf-8")

@classmethod
def load(cls, raw: str) -> "Token":
def load(cls, raw: str) -> Token:
"""
Deserialize a `Token` from a raw string.
Warning: does NOT check for the Token validity, you need to call `check`
Expand Down Expand Up @@ -467,10 +469,10 @@ def create(
cls,
domain: str,
identifier: str,
key: Union[str, bytes],
key: str | bytes,
prefix: str = PREFIX,
version: int = pymacaroons.MACAROON_V2,
) -> "Token":
) -> Token:
"""
Create a token. Initially, it has no restruction, but they can be added
with restrict.
Expand Down Expand Up @@ -509,7 +511,7 @@ def create(
token = cls(prefix=prefix, macaroon=macaroon)
return token

def restrict(self, **kwargs) -> "Token":
def restrict(self, **kwargs) -> Token:
"""
Modifies the token in-place to add restrictions to it. This can be called by
PyPI as well as by anyone, adding restrictions to new or existing tokens.
Expand Down Expand Up @@ -564,9 +566,7 @@ def dump(self) -> str:
"""
return f"{self.prefix}-{self._macaroon.serialize()}"

def check(
self, key: Union[str, bytes], project: str, now: Optional[int] = None
) -> None:
def check(self, key: str | bytes, project: str, now: int | None = None) -> None:
"""
Raises pypitoken.ValidationError if the token is invalid.
Expand All @@ -592,12 +592,12 @@ def check(
"""
verifier = pymacaroons.Verifier()

context_kwargs: Dict[str, Any] = {"project": project}
context_kwargs: dict[str, Any] = {"project": project}
if now:
context_kwargs["now"] = now
context = Context(**context_kwargs)

errors: List[Exception] = []
errors: list[Exception] = []

verifier.satisfy_general(
functools.partial(self._check_caveat, context=context, errors=errors)
Expand All @@ -616,7 +616,7 @@ def check(
) from exc

@staticmethod
def _check_caveat(caveat: str, context: Context, errors: List[Exception]) -> bool:
def _check_caveat(caveat: str, context: Context, errors: list[Exception]) -> bool:
"""
This method follows the pymacaroon Verifier.satisfy_general API, except
that it takes a context parameter. It's expected to be used with
Expand Down Expand Up @@ -654,7 +654,7 @@ def _check_caveat(caveat: str, context: Context, errors: List[Exception]) -> boo
return True

@property
def restrictions(self) -> List[Restriction]:
def restrictions(self) -> list[Restriction]:
"""
Return a list of restrictions associated to this `Token`. This can be used
to get a better insight on what this `Token` contains.
Expand Down
8 changes: 5 additions & 3 deletions pypitoken/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

import inspect
from typing import Callable, List
from typing import Callable


def merge_parameters(*callables: Callable) -> List[inspect.Parameter]:
def merge_parameters(*callables: Callable) -> list[inspect.Parameter]:
"""
Return a list of Parameters object matching every parameters from input callables.
"""
Expand All @@ -14,7 +16,7 @@ def merge_parameters(*callables: Callable) -> List[inspect.Parameter]:
]


def replace_signature(method: Callable, parameters: List[inspect.Parameter]) -> None:
def replace_signature(method: Callable, parameters: list[inspect.Parameter]) -> None:
"""
On the received method, keep the self parameter. Replace the other parameters
with the list reciend in ``parameters``.
Expand Down

0 comments on commit e304de0

Please sign in to comment.