-
-
Save suzukimilanpaak/90fd244037e03c32b1eafbbba5340677 to your computer and use it in GitHub Desktop.
Delegatable in python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
''' | |
Convenient classes to enable delegate | |
..codeauthor Tatsuya Suzuki <[email protected]> | |
''' | |
import copy | |
import logging as log | |
from typing import Sequence, Any, Dict, Tuple | |
# def delegatable(cls): | |
# def __new__(cls, *args, **kwargs): | |
# instance = super().__new__(cls) | |
# instance.delegates = {} | |
# return instance | |
# @property | |
# def delegates(self) -> Dict[str, Tuple[str]]: | |
# return self._delegates | |
# @delegates.setter | |
# def delegates(self, delegates: Dict[str, Tuple[str]]) -> Dict[str, Tuple[str]]: | |
# self._delegates = delegates | |
# return self.delegates | |
# def delegate(self, *funcs, to: str) -> Sequence: | |
# self.delegates.update({to: funcs}) | |
# return self.delegates | |
# def __getattr__(self, name) -> Any: | |
# log.debug(f'delegates={self._delegates}') | |
# for to, funcs in self._delegates.items(): | |
# funcs = (funcs,) if type(funcs) is str else funcs | |
# for func in funcs: | |
# # Check if func is in any of the delegates | |
# if name == func and hasattr(getattr(self, to), func): | |
# # Delegate the call | |
# return getattr(getattr(self, to), func) | |
# raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") # noqa: E501 | |
class MetaDelegatable(type): | |
def _get_delegates(cls) -> Dict[str, Tuple[str]]: | |
return cls._delegates | |
def _set_delegates(cls, delegates: Dict[str, Tuple[str]]) -> Dict[str, Tuple[str]]: | |
cls._delegates = delegates | |
return cls._delegates | |
def delegate(cls, *funcs: Sequence[str], to: str) -> Dict[str, Tuple[str]]: | |
cls._delegates.update({to: funcs}) | |
return cls._delegates | |
def __getattr__(self, name) -> Any: | |
log.debug(f'delegates={self._delegates}') | |
for to, funcs in self._delegates.items(): | |
funcs = (funcs,) if type(funcs) is str else funcs | |
for func in funcs: | |
# Check if func is in any of the delegates | |
if name == func and hasattr(getattr(self, to), func): | |
# Delegate the call | |
return getattr(getattr(self, to), func) | |
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") # noqa: E501 | |
def __prepare__(name, bases, **kwds): | |
_dict_ = {} | |
_dict_['_delegates'] = {} | |
_dict_['delegates'] = property(MetaDelegatable._get_delegates, MetaDelegatable._set_delegates) | |
_dict_['delegate'] = classmethod(MetaDelegatable.delegate) | |
_dict_['__getattr__'] = MetaDelegatable.__getattr__ | |
return _dict_ | |
def __new__(cls, name, bases, attrs): | |
attrs['_delegates'] = {} | |
attrs['delegates'] = property(cls._get_delegates, cls._set_delegates) | |
attrs['delegate'] = cls.delegate | |
attrs['__getattr__'] = cls.__getattr__ | |
instance = super().__new__(cls, name, bases, attrs) | |
return instance | |
class Delegatable: | |
def __new__(cls, *args, **kwargs): | |
instance = super().__new__(cls) | |
instance.delegates = {} | |
return instance | |
@property | |
def delegates(self) -> Dict[str, Tuple[str]]: | |
return self._delegates | |
@delegates.setter | |
def delegates(self, delegates: Dict[str, Tuple[str]]) -> Dict[str, Tuple[str]]: | |
self._delegates = delegates | |
return self.delegates | |
def delegate(self, *funcs, to: str) -> Sequence: | |
self.delegates.update({to: funcs}) | |
return self.delegates | |
def __getattr__(self, name) -> Any: | |
log.debug(f'delegates={self._delegates}') | |
for to, funcs in self._delegates.items(): | |
funcs = (funcs,) if type(funcs) is str else funcs | |
for func in funcs: | |
# Check if func is in any of the delegates | |
if name == func and hasattr(getattr(self, to), func): | |
# Delegate the call | |
return getattr(getattr(self, to), func) | |
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") # noqa: E501 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# testing_requirements.txt | |
# ``` | |
# pytest-describe=1.0.0 | |
# ``` | |
# testing_requirements.txt | |
# ``` | |
# pytest-describe=1.0.0 | |
# ``` | |
import pytest | |
from chalicelib.core.extension import Delegatable, MetaDelegatable | |
# @delegatable | |
# class DecoQueue: | |
# def __init__(self, name='default_queue'): | |
# self._name = name | |
# self.q = [] | |
# self.s = ',' | |
# @property | |
# def name(self): | |
# return self._name | |
class MetaQueue(metaclass=MetaDelegatable): | |
delegate("append", "pop", "copy", to="q") | |
def __init__(self, name='default_queue'): | |
self._name = name | |
self.q = [] | |
self.s = ',' | |
@property | |
def name(self): | |
return self._name | |
class Parent: | |
pass | |
class Queue(Parent, Delegatable): | |
def __init__(self, name='default_queue'): | |
self._name = name | |
self.q = [] | |
@property | |
def name(self): | |
return self._name | |
def describe_MetaDelegatable(): | |
def describe_delegate(): | |
def when_delegated_function_called(): | |
def it_returns_value_of_the_delegated_function(): | |
# delegate("append", "pop", "copy", to="q") | |
que = MetaQueue('') | |
que.append(1) | |
que.append(2) | |
que.pop(0) | |
assert que.q == [2] | |
def when_undelegated_function_called(): | |
def it_deosnt_polute_another_property(): | |
que = MetaQueue('sub_queue') | |
que.delegate("append", "pop", "copy", to="q") | |
que.append('a') | |
assert que.name == 'sub_queue' | |
def it_overwrites_all_delegates(): | |
que = MetaQueue('sub_queue') | |
que.delegate("append", "pop", "copy", to="q") | |
que.delegates = {"s": ("join")} | |
with pytest.raises(AttributeError) as exc_info: | |
que.append('a') | |
assert "'MetaQueue' object has no attribute 'append'" == str(exc_info.value) # noqa: E501 | |
def when_undefined_function_called(): | |
def it_raises_error_when_undefined_attr_is_called(): | |
with pytest.raises(Exception): | |
MetaQueue('').undefined(1) | |
def describe_delegates(): | |
def when_delegated_function_called(): | |
def it_sets_delegates(): | |
que = MetaQueue('') | |
que.delegates = {"s": "join"} | |
assert que.delegates == {"s": "join"} | |
def it_returns_value_of_the_delegated_function(): | |
que = MetaQueue('') | |
que.delegates = {"s": "join"} | |
actual = que.join(['a', 'b']) | |
assert actual == 'a,b' | |
def describe_Delegatable(): | |
def describe_delegate(): | |
def when_delegated_function_called(): | |
def it_returns_value_of_the_delegated_function(): | |
que = Queue('') | |
que.delegate("append", "pop", to="q") | |
que.append(1) | |
que.append(2) | |
que.pop(0) | |
assert que.q == [2] | |
def when_undelegated_function_called(): | |
def it_deosnt_polute_another_property(): | |
que = Queue('sub_queue') | |
que.delegate("append", "pop", to="q") | |
que.append(1) | |
assert que.name == 'sub_queue' | |
def when_undefined_function_called(): | |
def it_raises_error_when_undefined_attr_is_called(): | |
with pytest.raises(Exception): | |
Queue('').undefined(1) | |
def describe_delegates(): | |
def when_delegated_function_called(): | |
def it_sets_delegates(): | |
que = MetaQueue('') | |
que.delegates = {"s": "join"} | |
assert que.delegates == {"s": "join"} | |
def it_returns_value_of_the_delegated_function(): | |
que = MetaQueue('') | |
que.delegates = {"s": "join"} | |
actual = que.join(['a', 'b']) | |
assert actual == 'a,b' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment