Skip to content

Instantly share code, notes, and snippets.

@suzukimilanpaak
Forked from viveksoundrapandi/.py
Last active May 29, 2020 07:11
Show Gist options
  • Save suzukimilanpaak/90fd244037e03c32b1eafbbba5340677 to your computer and use it in GitHub Desktop.
Save suzukimilanpaak/90fd244037e03c32b1eafbbba5340677 to your computer and use it in GitHub Desktop.
Delegatable in python
# -*- coding: utf-8 -*-
'''
Convenient classes to enable delegate
..codeauthor Tatsuya Suzuki <tatsuya.suzuki@nftlearning.com>
'''
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
# 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