Skip to content

Commit

Permalink
OPT: add __slots__ to more dataclasses ⚡️
Browse files Browse the repository at this point in the history
  • Loading branch information
eigenein committed Nov 16, 2023
1 parent a968ea3 commit 05dfbc0
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 18 deletions.
18 changes: 11 additions & 7 deletions combadge/core/markers/method.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
from __future__ import annotations

from abc import ABC
from dataclasses import dataclass
from inspect import BoundArguments
from typing import Any, Callable, Generic

# noinspection PyUnresolvedReferences
from typing_extensions import override

from combadge._helpers.dataclasses import SLOTS
from combadge.core.typevars import BackendRequestT, FunctionT


@dataclass(**SLOTS)
class MethodMarker(ABC, Generic[BackendRequestT, FunctionT]):
"""Method marker that modifies an entire request based on all the call arguments."""

__slots__ = ()

def mark(self, what: FunctionT) -> FunctionT:
"""
Mark the function with itself.
Expand Down Expand Up @@ -58,15 +59,13 @@ def ensure_markers(in_: Any) -> list[MethodMarker]:
return marks


@dataclass(**SLOTS)
class WrapWith(Generic[FunctionT], MethodMarker[Any, FunctionT]): # noqa: D101
__slots__ = ("_decorator",)

def __init__(self, decorator: Callable[[FunctionT], FunctionT]) -> None: # noqa: D107
self._decorator = decorator
decorator: Callable[[FunctionT], FunctionT]

@override
def wrap(self, what: FunctionT) -> FunctionT: # noqa: D102
return self._decorator(what)
return self.decorator(what)


def wrap_with(decorator: Callable[[Any], Any]) -> Callable[[FunctionT], FunctionT]:
Expand All @@ -77,5 +76,10 @@ def wrap_with(decorator: Callable[[Any], Any]) -> Callable[[FunctionT], Function
>>> @wrap_with(functools.cache)
>>> def service_method(self, ...) -> ...:
>>> ...
Question: Why can I not just use a decorator directly?
The decorator needs to wrap a method **implementation** and a service definition is just an **interface**.
If you put it directly onto an abstract method, it would wrap only this abstract method,
but **not** the actual implementation which is produced by [binding][binding].
"""
return WrapWith(decorator).mark
5 changes: 3 additions & 2 deletions combadge/core/markers/parameter.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any, Generic

from combadge._helpers.dataclasses import SLOTS
from combadge.core.markers.base import AnnotatedMarker
from combadge.core.typevars import BackendRequestT


@dataclass(**SLOTS)
class ParameterMarker(AnnotatedMarker, Generic[BackendRequestT], ABC):
"""Parameter marker: it modifies a request with a call-time argument."""

__slots__ = ()

@abstractmethod
def __call__(self, request: BackendRequestT, value: Any) -> None:
"""Update the request according to the marker and the actual argument."""
12 changes: 4 additions & 8 deletions combadge/core/markers/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,35 @@
# noinspection PyUnresolvedReferences
from typing_extensions import override

from combadge._helpers.dataclasses import SLOTS
from combadge.core.markers.base import AnnotatedMarker

_InputPayloadT = TypeVar("_InputPayloadT")
_OutputPayloadT = TypeVar("_OutputPayloadT")


@dataclass(**SLOTS)
class ResponseMarker(AnnotatedMarker, ABC):
"""Response marker: it transforms or contructs a response."""

__slots__ = ()

@abstractmethod
def __call__(self, response: Any, payload: Any) -> Any:
"""Transform the response."""


@dataclass
@dataclass(**SLOTS)
class Map(ResponseMarker):
"""Map a payload to a dictionary under the specified key."""

key: Any
"""Key under which the response will be mapped."""

__slots__ = ("key",)

@override
def __call__(self, response: Any, payload: Any) -> Dict[Any, Any]: # noqa: D102
return {self.key: payload}


@dataclass
@dataclass(**SLOTS)
class Extract(ResponseMarker):
"""
Extract a value from the specified key.
Expand All @@ -54,8 +52,6 @@ class Extract(ResponseMarker):
key: Any
"""Key which will be extracted from the payload."""

__slots__ = ("key",)

@override
def __call__(self, response: Any, payload: Mapping[Any, Any]) -> Any: # noqa: D102
return payload[self.key]
Expand Down
2 changes: 1 addition & 1 deletion combadge/support/http/markers/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ def __call__(self, request: ContainsHeaders, value: Any) -> None: # noqa: D102
request.headers.append((self.name, value))


@dataclass(init=False, **SLOTS)
class Path(Generic[FunctionT], MethodMarker[ContainsUrlPath, FunctionT]): # noqa: D101
_factory: Callable[[BoundArguments], str]
__slots__ = ("_factory",)

def __init__(self, path_or_factory: str | Callable[[BoundArguments], str]) -> None: # noqa: D107
if callable(path_or_factory):
Expand Down

0 comments on commit 05dfbc0

Please sign in to comment.