Skip to content

Commit

Permalink
feat: implement @delegated decorator #77
Browse files Browse the repository at this point in the history
  • Loading branch information
proofit404 committed Jul 13, 2022
1 parent c9fad05 commit 916fd1f
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 12 deletions.
25 changes: 23 additions & 2 deletions src/_generics/delegated.py
@@ -1,3 +1,24 @@
def delegated(cls):
class Delegated:
"""Create decorator class in true sense of OOP."""
return cls

def __init__(self, interface):
self.interface = interface

def __call__(self, cls):
"""Create decorator class."""
created_methods = {"__init__": cls.__dict__["__init__"]}
for name in self.interface.__dict__:
if name.startswith("_"):
continue
created_methods[name] = _create_method(name)
return type(cls.__name__, (), created_methods)


def _create_method(name):
def _method(self, *args, **kwargs):
return getattr(self.user, name)(*args, **kwargs)

return _method


delegated = Delegated
2 changes: 1 addition & 1 deletion src/_generics/private.py
Expand Up @@ -20,7 +20,7 @@ def private(cls):
created_methods = _create_class_methods(cls, class_name, methods)
created_methods["__new__"] = _create_new_class_method(cls, class_name, methods)
created_methods["__init__"] = init
return _PrivateType(class_name, (object,), created_methods)
return _PrivateType(class_name, (), created_methods)


def _get_class_name(cls):
Expand Down
12 changes: 10 additions & 2 deletions testing/examples/__init__.py
Expand Up @@ -12,12 +12,20 @@ def e():
return examples.definitions


@pytest.fixture()
def w():
"""Fixture with all possible entity decorators."""
import examples.delegates_definitions

return examples.delegates_definitions


@pytest.fixture()
def s():
"""Fixture with subtyping entity definitions."""
import examples.sybtyping_definitions
import examples.subtyping_definitions

return examples.sybtyping_definitions
return examples.subtyping_definitions


@pytest.fixture()
Expand Down
5 changes: 5 additions & 0 deletions testing/examples/delegates_definitions.py
@@ -0,0 +1,5 @@
class SmartUser:
"""User model decorator."""

def __init__(self, user):
self.user = user
Expand Up @@ -20,3 +20,10 @@ def __init__(self, last_login):
def is_active(self):
"""Calculate user activity status."""
return (datetime.now() - self.last_login).days < 30


class SmartUser:
"""User model decorator."""

def __init__(self, user):
self.user = user
12 changes: 11 additions & 1 deletion tests/test_delegated.py
@@ -1,4 +1,14 @@
"""Tests related to the @delegated decorator."""
from datetime import datetime

from generics import delegated
from generics import private


delegated(None)
def test_define_instance_methods(e, w):
"""Provide access to not redefined methods."""
user_class = private(e.User)
smart_user_class = delegated(user_class)(w.SmartUser)
user = user_class(last_login=datetime(1999, 12, 31))
smart_user = smart_user_class(user)
assert not smart_user.is_active()
16 changes: 10 additions & 6 deletions tests/test_subtyping.py
@@ -1,17 +1,21 @@
"""Tests related to inheritance from interface."""
from datetime import datetime

import pytest

from generics import delegated
from generics import private


pytestmark = pytest.mark.parametrize("f", [private, delegated])
def test_private_allow_inheritance_from_interface(s):
"""Allow inheritance from interface."""
user_class = private(s.User)
user = user_class(last_login=datetime(1999, 12, 31))
assert not user.is_active()


def test_allow_inheritance_from_interface(f, s):
def test_delegated_allow_inheritance_from_interface(s):
"""Allow inheritance from interface."""
user_class = f(s.User)
user_class = private(s.User)
smart_user_class = delegated(s.UserType)(s.SmartUser)
user = user_class(last_login=datetime(1999, 12, 31))
assert not user.is_active()
smart_user = smart_user_class(user)
assert not smart_user.is_active()

0 comments on commit 916fd1f

Please sign in to comment.