11from abc import ABC
22from 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
121146if __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