@@ -19,12 +19,7 @@ def from_obj(cls, obj, skip_attrs=None):
1919 if isinstance (obj , ObjectConfig ):
2020 return obj
2121 else :
22- return cls (obj , set (skip_attrs ) if skip_attrs else set (), str (id (obj )))
23-
24- def getattr (self , attr ):
25- if attr in self .skip_attrs :
26- return
27- return getattr (self .obj , attr , None )
22+ return cls (obj , skip_attrs or set (), str (id (obj )))
2823
2924
3025class WrapSearchResult :
@@ -88,29 +83,74 @@ def wrapper(*args, **kwargs):
8883 return wrapper
8984
9085
91- def search_callable (attr , * configs ) -> Generator [WrapSearchResult , None , None ]:
92- if callable (attr ) or isinstance (attr , property ):
93- yield CallableSearchResult (attr , attr , None )
94- else :
95- for config in configs :
96- func = config .getattr (attr )
97- if func is None :
98- continue
99- if not callable (func ):
100- yield AttributeCallableSearchResult (attr , config .obj , config .resolver_id )
86+ def _search_callable_attr_is_property (
87+ attr , configs : tuple [ObjectConfig ]
88+ ) -> "WrapSearchResult | None" :
89+ # if the attr is a property, we'll try to find the object that has the
90+ # property on the configs
91+ attr_name = attr .fget .__name__
92+ for obj , _skip_attrs , resolver_id in configs :
93+ func = getattr (type (obj ), attr_name , None )
94+ if func is not None and func is attr :
95+ return AttributeCallableSearchResult (attr_name , obj , resolver_id )
96+ return None
10197
102- if getattr (func , "_is_sm_event" , False ):
103- yield EventSearchResult (attr , func , config .resolver_id )
10498
105- yield CallableSearchResult (attr , func , config .resolver_id )
99+ def _search_callable_attr_is_callable (attr , configs : tuple [ObjectConfig ]) -> WrapSearchResult :
100+ # if the attr is an unbounded method, we'll try to find the bounded method
101+ # on the configs
102+ if not hasattr (attr , "__self__" ):
103+ for obj , _skip_attrs , resolver_id in configs :
104+ func = getattr (obj , attr .__name__ , None )
105+ if func is not None and func .__func__ is attr :
106+ return CallableSearchResult (attr .__name__ , func , resolver_id )
106107
108+ return CallableSearchResult (attr , attr , None )
109+
110+
111+ def _search_callable_in_configs (
112+ attr , configs : tuple [ObjectConfig ]
113+ ) -> Generator [WrapSearchResult , None , None ]:
114+ for obj , skip_attrs , resolver_id in configs :
115+ if attr in skip_attrs :
116+ continue
117+
118+ if not hasattr (obj , attr ):
119+ continue
120+
121+ func = getattr (obj , attr )
122+ if not callable (func ):
123+ yield AttributeCallableSearchResult (attr , obj , resolver_id )
124+
125+ if getattr (func , "_is_sm_event" , False ):
126+ yield EventSearchResult (attr , func , resolver_id )
127+
128+ yield CallableSearchResult (attr , func , resolver_id )
107129
108- def resolver_factory (* objects ):
109- """Factory that returns a configured resolver."""
110130
111- objects = [ObjectConfig .from_obj (obj ) for obj in objects ]
131+ def search_callable (attr , configs : tuple ) -> Generator [WrapSearchResult , None , None ]: # noqa: C901
132+ if isinstance (attr , property ):
133+ result = _search_callable_attr_is_property (attr , configs )
134+ if result is not None :
135+ yield result
136+ return
137+
138+ if callable (attr ):
139+ yield _search_callable_attr_is_callable (attr , configs )
140+ return
141+
142+ yield from _search_callable_in_configs (attr , configs )
143+
144+
145+ def resolver_factory (objects : tuple [ObjectConfig ]):
146+ """Factory that returns a configured resolver."""
112147
113148 def wrapper (attr ) -> Generator [WrapSearchResult , None , None ]:
114- yield from search_callable (attr , * objects )
149+ yield from search_callable (attr , objects )
115150
116151 return wrapper
152+
153+
154+ def resolver_factory_from_objects (* objects : tuple [Any ]):
155+ configs = tuple (ObjectConfig .from_obj (o ) for o in objects )
156+ return resolver_factory (configs )
0 commit comments