Skip to content

Commit 530d0db

Browse files
committed
create composed methods for callables. Now can call forward docstring.
signature not yet working
1 parent f755640 commit 530d0db

File tree

1 file changed

+41
-12
lines changed

1 file changed

+41
-12
lines changed

ObjectDelegator/delegator.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from abc import ABC
22
from typing import Union
3+
import inspect
4+
from types import MethodType
35

46

57
# based on https://www.fast.ai/2019/08/06/delegation/
@@ -44,20 +46,26 @@ def set_delegated_members(self, delegation_dict: dict) -> None:
4446
if new_member in previous_delegations:
4547
raise ValueError("cannot delegate two members with the same name.")
4648

47-
for member in delegation_dict.keys(): # verify suggested object names and members are valid attributes
48-
for member_name in delegation_dict[member]:
49-
if not getattr(getattr(self, member), member_name, None):
50-
raise AttributeError("'%s' object has no attribute '%s'" % (member, member_name))
49+
for obj in delegation_dict: # verify suggested object names and members are valid attributes
50+
for member_name in delegation_dict[obj]:
51+
if not getattr(getattr(self, obj), member_name, None):
52+
raise AttributeError("'%s' object has no attribute '%s'" % (obj, member_name))
5153

5254
# now delegate
5355
if not self._delegated_members:
5456
self._delegated_members = delegation_dict
5557
else:
56-
for member in delegation_dict.keys(): # for each object needing delegation
57-
if member not in self._delegated_members.keys(): # if this object didn't have delegation before
58-
self._delegated_members[member] = delegation_dict[member]
58+
for obj in delegation_dict: # for each object needing delegation
59+
if obj not in self._delegated_members.keys(): # if this object didn't have delegation before
60+
self._delegated_members[obj] = delegation_dict[obj]
5961
else: # if it had delegations append the new ones
60-
self._delegated_members[member] += delegation_dict[member]
62+
self._delegated_members[obj] += delegation_dict[obj]
63+
64+
# for the sake autocompletion add composed methods if needed
65+
for obj in delegation_dict:
66+
for delegate in delegation_dict[obj]:
67+
if callable(getattr(getattr(self, obj), delegate)):
68+
self._compose_a_method(obj, delegate)
6169

6270
def get_possible_members_to_delegate(self, required_object: Union[None, str] = None) -> dict:
6371
"""Obtain a dict of members which can be delegated.
@@ -108,14 +116,31 @@ def _list_all_delegated_members(self):
108116
else:
109117
return []
110118

119+
def _compose_a_method(self, obj, delegate):
120+
""" for the sake autocompletion add composed methods for callable delegates """
121+
122+
def new_method(self, *args, **kwargs):
123+
return getattr(getattr(self, obj), delegate)(*args, **kwargs)
124+
125+
new_method.__name__ = delegate
126+
new_method.__doc__ = getattr(getattr(self, obj), delegate).__doc__
127+
new_method.__qualname__ = getattr(getattr(self, obj), delegate).__qualname__
128+
if inspect.isfunction(getattr(getattr(self, obj), delegate)):
129+
new_method.__defaults__ = getattr(getattr(self, obj), delegate).__defaults__
130+
new_method.__kwdefaults__ = getattr(getattr(self, obj), delegate).__kwdefaults__
131+
new_method.__annotations__ = getattr(getattr(self, obj), delegate).__annotations__
132+
new_method.__signature__ = inspect.signature(getattr(getattr(self, obj), delegate))
133+
test = inspect.signature(getattr(getattr(self, obj), delegate))
134+
self.__setattr__(delegate, MethodType(new_method, self))
135+
111136
def __getattr__(self, _attribute):
112137
for member in self._delegated_members.keys():
113138
if _attribute in self._delegated_members[member]:
114139
return getattr(getattr(self, member), _attribute)
115140
raise AttributeError(_attribute)
116141

117142
def __dir__(self):
118-
return dir(type(self)) + list(self.__dict__.keys()) + self._list_all_delegated_members
143+
return list(set(dir(type(self)) + list(self.__dict__.keys()) + self._list_all_delegated_members))
119144

120145

121146
if __name__ == '__main__':
@@ -130,9 +155,9 @@ class Foo:
130155
rabbit = RabbitHole('first rabbit')
131156

132157
# noinspection PyMethodMayBeStatic
133-
def foo(self):
158+
def foo(self, txt: str) -> str:
134159
"""dummy method"""
135-
return 'foo'
160+
return txt
136161

137162

138163
class Bar:
@@ -151,8 +176,12 @@ def __init__(self):
151176

152177
# Do some delegations
153178
master.set_delegated_members({'foo_obj': ['foo', 'foo_property', 'rabbit'], 'bar_obj': ['rabbit_too']})
154-
print(master.foo()) # can delegate methods
179+
print(master.foo.__doc__) # can delegate methods
180+
sig = inspect.signature(master.foo)
181+
182+
print(master.foo('hi there'))
155183
print(master.foo_property) # can delegate properties
184+
156185
print(master.rabbit.down_we_go) # or even other objects
157186
print(master.rabbit_too.down_we_go) # and another rabbit hole instance
158187
# find more objects I can delegate too

0 commit comments

Comments
 (0)