Skip to content

Commit

Permalink
⬆️ Drop support for Python 3.8 and add 3.13
Browse files Browse the repository at this point in the history
  • Loading branch information
eigenein committed Oct 14, 2024
1 parent 2d7cfd2 commit bf21a9b
Show file tree
Hide file tree
Showing 31 changed files with 92 additions and 90 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ jobs:
fail-fast: false
matrix:
python-version:
- '3.8'
- '3.9'
- '3.10'
- '3.11'
- '3.12'
- '3.13'

steps:
- name: "🗃 Cache"
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ clean:

.PHONY: install
install:
poetry install --all-extras --with=dev
poetry install --sync --all-extras --with=dev

.PHONY: lint
lint: lint/ruff lint/mypy
Expand Down
10 changes: 2 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions pure_protobuf/_accumulators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Generic, Iterable, List, Optional, Type
from collections.abc import Iterable
from typing import Generic, Optional

from pure_protobuf.interfaces._repr import ReprWithInner
from pure_protobuf.interfaces._vars import MessageT, RecordT
Expand All @@ -21,12 +22,12 @@ def __call__(self, accumulator: Optional[RecordT], other: Iterable[RecordT]) ->
return accumulator


class AccumulateAppend(Accumulate[List[RecordT], RecordT]):
class AccumulateAppend(Accumulate[list[RecordT], RecordT]):
def __call__(
self,
accumulator: Optional[List[RecordT]],
accumulator: Optional[list[RecordT]],
other: Iterable[RecordT],
) -> List[RecordT]:
) -> list[RecordT]:
"""Append all items from the `other` into the accumulator."""
if accumulator is None:
accumulator = []
Expand All @@ -35,12 +36,12 @@ def __call__(


class AccumulateMessages(Accumulate[MessageT, MessageT], ReprWithInner):
inner: Type[MessageT]
inner: type[MessageT]

__slots__ = ("inner",)

# noinspection PyProtocol
def __init__(self, inner: Type[MessageT]) -> None:
def __init__(self, inner: type[MessageT]) -> None:
self.inner = inner

def __call__(self, lhs: Optional[MessageT], rhs: Iterable[MessageT]) -> MessageT:
Expand Down
10 changes: 5 additions & 5 deletions pure_protobuf/_mergers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Generic, List, Optional
from typing import Generic, Optional

from pure_protobuf._accumulators import AccumulateMessages
from pure_protobuf.interfaces._repr import ReprWithInner
Expand All @@ -18,12 +18,12 @@ def __call__(self, _lhs: Optional[FieldT], rhs: Optional[FieldT]) -> Optional[Fi
return rhs


class MergeConcatenate(Merge[List[RecordT]], Generic[RecordT]):
class MergeConcatenate(Merge[list[RecordT]], Generic[RecordT]):
def __call__(
self,
lhs: Optional[List[RecordT]],
rhs: Optional[List[RecordT]],
) -> Optional[List[RecordT]]:
lhs: Optional[list[RecordT]],
rhs: Optional[list[RecordT]],
) -> Optional[list[RecordT]]:
if lhs is None:
return rhs
if rhs is None:
Expand Down
5 changes: 2 additions & 3 deletions pure_protobuf/descriptors/_field.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Generic, Optional, Type, cast
from typing import TYPE_CHECKING, Annotated, Any, Generic, Optional, cast

from typing_extensions import Annotated
from typing_extensions import get_args as get_type_args
from typing_extensions import get_origin as get_type_origin

Expand Down Expand Up @@ -64,7 +63,7 @@ class _FieldDescriptor(Generic[FieldT, RecordT]):
@classmethod
def from_attribute(
cls,
message_type: Type[BaseMessage],
message_type: type[BaseMessage],
attribute_hint: Any,
) -> Optional[_FieldDescriptor[Any, Any]]:
"""
Expand Down
13 changes: 9 additions & 4 deletions pure_protobuf/descriptors/record.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

from collections.abc import ByteString, Mapping
from dataclasses import dataclass
from enum import IntEnum
from typing import TYPE_CHECKING, Any, ByteString, ClassVar, Dict, Generic, Type
from types import GenericAlias
from typing import TYPE_CHECKING, Any, ClassVar, Generic
from urllib.parse import ParseResult

from typing_extensions import Self
Expand Down Expand Up @@ -69,13 +71,13 @@ class RecordDescriptor(Generic[RecordT]):
merge: Merge[RecordT] = MergeLastOneWins()
"""Merge two values of the same field from different messages. Only called in a message merger."""

__PREDEFINED__: ClassVar[Dict[Any, RecordDescriptor]]
__PREDEFINED__: ClassVar[Mapping[Any, RecordDescriptor]]
"""Pre-defined descriptors for primitive types."""

@classmethod
def _from_inner_type_hint(
cls,
message_type: Type[BaseMessage],
message_type: type[BaseMessage],
inner_hint: Any,
) -> RecordDescriptor[Any]:
"""
Expand Down Expand Up @@ -115,7 +117,10 @@ def _from_inner_type_hint(
write=WriteEnum[inner_hint](),
read=ReadMaybePacked[inner_hint](ReadEnum(inner_hint), WireType.VARINT),
)
if issubclass(inner_hint, BaseMessage):
if (
not isinstance(inner_hint, GenericAlias) # TODO: remove with Python 3.9 end-of-life.
and issubclass(inner_hint, BaseMessage)
):
return inner_hint._init_embedded_descriptor()

raise UnsupportedAnnotationError(f"type annotation `{inner_hint!r}` is not supported")
Expand Down
2 changes: 1 addition & 1 deletion pure_protobuf/helpers/_dataclasses.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Backwards compatibility for Python 3.8 and 3.9."""
"""Backwards compatibility with Python 3.9."""

from sys import version_info

Expand Down
6 changes: 3 additions & 3 deletions pure_protobuf/helpers/_typing.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from collections.abc import Iterable
from typing import Any, List, Tuple, Union
from typing import Any, Union

from typing_extensions import TypeGuard, get_args, get_origin

Expand All @@ -22,15 +22,15 @@ def __repr__(self) -> str: # pragma: no cover
DEFAULT = Sentinel()


def extract_repeated(hint: Any) -> Tuple[Any, TypeGuard[List]]:
def extract_repeated(hint: Any) -> tuple[Any, TypeGuard[list]]:
"""Extract a possible repeated flag."""
origin = get_origin(hint)
if isinstance(origin, type) and (origin is Iterable or issubclass(origin, list)):
return get_args(hint)[0], True
return hint, False


def extract_optional(hint: Any) -> Tuple[Any, bool]:
def extract_optional(hint: Any) -> tuple[Any, bool]:
"""Extract a possible optional flag."""
if get_origin(hint) is Union:
cleaned_args = tuple(arg for arg in get_args(hint) if arg is not NoneType)
Expand Down
3 changes: 1 addition & 2 deletions pure_protobuf/helpers/datetime.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from math import modf
from typing import Tuple


def split_seconds(seconds: float) -> Tuple[int, int]:
def split_seconds(seconds: float) -> tuple[int, int]:
"""Split seconds into whole seconds and nanoseconds."""
fraction, whole = modf(seconds)
return int(whole), int(fraction * 1_000_000_000.0)
Expand Down
3 changes: 2 additions & 1 deletion pure_protobuf/helpers/itertools.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Callable, Generic, Iterator, TypeVar
from collections.abc import Iterator
from typing import Callable, Generic, TypeVar

from typing_extensions import ParamSpec

Expand Down
3 changes: 2 additions & 1 deletion pure_protobuf/interfaces/accumulate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from abc import abstractmethod
from typing import Iterable, Optional, Protocol
from collections.abc import Iterable
from typing import Optional, Protocol

from pure_protobuf.interfaces._repr import Repr
from pure_protobuf.interfaces._vars import FieldT, RecordT_contra
Expand Down
3 changes: 2 additions & 1 deletion pure_protobuf/interfaces/read.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from abc import abstractmethod
from typing import IO, Iterator, Protocol
from collections.abc import Iterator
from typing import IO, Protocol

from pure_protobuf.interfaces._repr import Repr
from pure_protobuf.interfaces._vars import RecordT_co
Expand Down
3 changes: 2 additions & 1 deletion pure_protobuf/io/struct_.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Iterator
from struct import Struct
from typing import IO, Generic, Iterator
from typing import IO, Generic

from pure_protobuf.helpers.io import read_checked
from pure_protobuf.interfaces._repr import ReprWithInner
Expand Down
3 changes: 2 additions & 1 deletion pure_protobuf/io/url.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Reading and writing parsed URLs."""

from typing import IO, Iterator
from collections.abc import Iterator
from typing import IO
from urllib.parse import ParseResult, urlparse, urlunparse

from pure_protobuf.interfaces.read import Read
Expand Down
5 changes: 3 additions & 2 deletions pure_protobuf/io/varint.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
"""

from collections.abc import Iterator
from enum import IntEnum
from itertools import count
from sys import byteorder
from typing import IO, Iterator, Type, TypeVar
from typing import IO, TypeVar

from pure_protobuf.exceptions import IncorrectValueError
from pure_protobuf.helpers.io import read_byte_checked
Expand Down Expand Up @@ -127,7 +128,7 @@ class ReadEnum(Read[EnumT]):
__slots__ = ("enum_type",)

# noinspection PyProtocol
def __init__(self, enum_type: Type[EnumT]) -> None:
def __init__(self, enum_type: type[EnumT]) -> None:
self.enum_type = enum_type

def __call__(self, io: IO[bytes]) -> Iterator[EnumT]:
Expand Down
3 changes: 2 additions & 1 deletion pure_protobuf/io/wrappers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Iterable, Iterator
from io import BytesIO
from typing import IO, Generic, Iterable, Iterator, Optional, cast
from typing import IO, Generic, Optional, cast

from pure_protobuf.exceptions import UnexpectedWireTypeError
from pure_protobuf.interfaces._repr import ReprWithInner
Expand Down
13 changes: 7 additions & 6 deletions pure_protobuf/message.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations

from abc import ABC
from collections.abc import Mapping
from io import BytesIO
from typing import IO, Any, ClassVar, Dict, Tuple
from typing import IO, Any, ClassVar

from typing_extensions import Self

Expand Down Expand Up @@ -36,10 +37,10 @@ class BaseMessage(ABC):

__slots__ = ()

__PROTOBUF_FIELDS_BY_NUMBER__: ClassVar[Dict[int, Tuple[str, _FieldDescriptor]]]
__PROTOBUF_FIELDS_BY_NAME__: ClassVar[Dict[str, _FieldDescriptor]]
__PROTOBUF_FIELDS_BY_NUMBER__: ClassVar[dict[int, tuple[str, _FieldDescriptor]]]
__PROTOBUF_FIELDS_BY_NAME__: ClassVar[dict[str, _FieldDescriptor]]

__PROTOBUF_SKIP__: ClassVar[Dict[WireType, Skip]] = {
__PROTOBUF_SKIP__: ClassVar[Mapping[WireType, Skip]] = {
WireType.VARINT: skip_varint,
WireType.I64: skip_fixed_64,
WireType.LEN: skip_bytes,
Expand All @@ -53,7 +54,7 @@ def __init_subclass__(cls) -> None: # noqa: D105
cls.__PROTOBUF_FIELDS_BY_NUMBER__ = {}
cls.__PROTOBUF_FIELDS_BY_NAME__ = {}

type_hints: Dict[str, Any] = get_annotations(cls, eval_str=True)
type_hints: dict[str, Any] = get_annotations(cls, eval_str=True)
for name, hint in type_hints.items():
descriptor = _FieldDescriptor.from_attribute(cls, hint)
if descriptor is not None:
Expand All @@ -67,7 +68,7 @@ def __init_subclass__(cls) -> None: # noqa: D105
def read_from(cls, io: IO[bytes]) -> Self:
"""Read a message from the file."""

values: Dict[str, Any] = {}
values: dict[str, Any] = {}
while True:
try:
tag = Tag.read_from(io)
Expand Down
7 changes: 4 additions & 3 deletions pure_protobuf/one_of.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from typing import Any, Callable, Generic, List, MutableMapping, Optional, Tuple, Type, TypeVar
from collections.abc import MutableMapping
from typing import Any, Callable, Generic, Optional, TypeVar

from pure_protobuf.message import BaseMessage

Expand All @@ -22,7 +23,7 @@ def __init__(self) -> None:
A [`Field`][pure_protobuf.annotations.Field] then should be assigned to the group
via the [`one_of`][pure_protobuf.annotations.Field.one_of] parameter.
"""
self._fields: List[Tuple[int, str]] = []
self._fields: list[tuple[int, str]] = []

def _add_field(self, number: int, name: str) -> None:
self._fields.append((number, name))
Expand All @@ -41,7 +42,7 @@ def _keep_attribute(self, message: BaseMessage, keep_number: int) -> None:
if other_number != keep_number:
super(BaseMessage, message).__setattr__(other_name, None)

def __get__(self, instance: Any, type_: Type[Any]) -> Optional[OneOfT]: # noqa: D105
def __get__(self, instance: Any, type_: type[Any]) -> Optional[OneOfT]: # noqa: D105
if not isinstance(instance, BaseMessage):
# Allows passing the descriptor by reference, and we need to move the descriptor from
# the corresponding annotation.
Expand Down
7 changes: 3 additions & 4 deletions pure_protobuf/well_known.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

from __future__ import annotations

from collections.abc import Mapping
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from io import BytesIO
from typing import Any, Mapping, Optional, Type, cast
from typing import Annotated, Any, Optional, cast
from urllib.parse import ParseResult

from typing_extensions import Annotated

from pure_protobuf.annotations import Field
from pure_protobuf.helpers._dataclasses import KW_ONLY, SLOTS
from pure_protobuf.helpers.datetime import split_seconds, unsplit_seconds
Expand Down Expand Up @@ -112,5 +111,5 @@ def into_message(
locals=locals_,
globals=globals_,
)
class_ = cast(Type[BaseMessage], getattr(module, self.type_url.path))
class_ = cast(type[BaseMessage], getattr(module, self.type_url.path))
return class_.read_from(BytesIO(self.value))
Loading

0 comments on commit bf21a9b

Please sign in to comment.