Skip to content

Commit

Permalink
Merge pull request #10 from volfpeter/bound-method-wrapper-memo
Browse files Browse the repository at this point in the history
Bound method wrapper memo
  • Loading branch information
volfpeter authored Mar 29, 2024
2 parents e832114 + 29d87bf commit 99e54e0
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 14 deletions.
26 changes: 15 additions & 11 deletions motorhead/bound_method_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,26 @@ class BoundMethodWrapper(Generic[TOwner, TParams, TConfig]):
this method produces.
"""

__slots__ = (
"_config",
"_func",
)
__slots__ = ("_config", "_wrapped", "_exec", "_owner")

exception: Callable[[str], Exception] | None = None

def __init__(
self,
func: Callable[Concatenate[TOwner, TParams], Coroutine[None, None, None]],
wrapped: Callable[Concatenate[TOwner, TParams], Coroutine[None, None, None]],
config: TConfig,
) -> None:
"""
Initialization.
Arguments:
func: The wrapped method.
wrapped: The wrapped method.
config: Wrapper configuration.
"""
self._config = config
self._func = func
self._wrapped = wrapped
self._exec: Callable[TParams, Coroutine[None, None, None]] | None = None
self._owner: TOwner | None = None

@property
def config(self) -> TConfig:
Expand All @@ -63,19 +62,24 @@ def name(self) -> str:
"""
The (qualified) name of the wrapped method.
"""
return self._func.__qualname__
return self._wrapped.__qualname__

def __get__(
self, owner: TOwner, obj_type: type[TOwner] | None = None
) -> Callable[TParams, Coroutine[None, None, None]]:
"""
Descriptor implementation that makes the wrapper work as a bound method of its owner.
"""
if owner is self._owner and self._exec is not None:
return self._exec

async def do(*args: TParams.args, **kwargs: TParams.kwargs) -> None:
async def exec(*args: TParams.args, **kwargs: TParams.kwargs) -> None:
return await self(owner, *args, **kwargs)

return do
self._exec = exec
self._owner = owner

return exec

async def __call__(self, owner: TOwner, *args: TParams.args, **kwargs: TParams.kwargs) -> None:
"""
Expand All @@ -89,6 +93,6 @@ async def __call__(self, owner: TOwner, *args: TParams.args, **kwargs: TParams.k
*kwargs: The wrapped method's keyword arguments.
"""
try:
await self._func(owner, *args, **kwargs)
await self._wrapped(owner, *args, **kwargs)
except Exception as e:
raise e if self.exception is None else self.exception(f"Method failed: {self.name}") from e
2 changes: 1 addition & 1 deletion motorhead/delete_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,6 @@ def delete_rule(
def decorator(
func: Callable[[TOwner, AgnosticClientSession, Sequence[ObjectId]], Coroutine[None, None, None]], /
) -> "DeleteRule[TOwner]":
return DeleteRule(func=func, config=config)
return DeleteRule(wrapped=func, config=config)

return decorator
2 changes: 1 addition & 1 deletion motorhead/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ def decorator(
func: Callable[[TOwner, TInsertOrUpdate, ClauseOrMongoQuery | None], Coroutine[None, None, None]],
/,
) -> "Validator[TOwner, TInsertOrUpdate]":
return Validator(func=func, config=config)
return Validator(wrapped=func, config=config)

return decorator
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ tracker = "https://github.com/volfpeter/motorhead/issues"

[tool.poetry]
name = "motorhead"
version = "0.2402.1"
version = "0.2403.0"
description = "Async MongoDB with vanilla Pydantic v2+ - made easy."
authors = ["Peter Volf <[email protected]>"]
readme = "README.md"
Expand Down
10 changes: 10 additions & 0 deletions tests/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@ async def test_delete_rules_and_validators(
person_service: PersonService,
person_service_with_rules: PersonServiceWithRules,
) -> None:
assert (
person_service_with_rules._dr_never_delete_root
is person_service_with_rules._dr_never_delete_root
)
assert (
person_service_with_rules._v_root_case_insensitive
is person_service_with_rules._v_root_case_insensitive
)
assert person_service_with_rules._v_root_unique is person_service_with_rules._v_root_unique

with pytest.raises(ValidationError, match="PersonServiceWithRules._v_root_case_insensitive"):
async with make_person(person_service_with_rules, name="Root"):
...
Expand Down

0 comments on commit 99e54e0

Please sign in to comment.