@@ -24,28 +24,57 @@ def get_scope(func: FunctionType) -> ScopeProxy:
2424 return ScopeProxy (inspect .getmodule (func ).__dict__ | vars (builtins ))
2525
2626
27+ def resolve_attribute_to_object (attribute : str , scope : ScopeProxy | object ) -> object :
28+ """
29+ Resolve a dot-separated attribute string to the actual object it references in the
30+ given scope. For example, if attribute is "os.path.join", this function will
31+ return the actual join function from the os.path module.
32+
33+ Args:
34+ attribute: A dot-separated string representing the attribute to resolve.
35+ scope: The scope in which to resolve the attribute. This can be a ScopeProxy
36+ or any object that supports attribute access.
37+
38+ Returns:
39+ The object that the attribute resolves to in the given scope.
40+ """
41+ obj = None
42+ try :
43+ for attr in attribute .split ("." ):
44+ obj = getattr (obj or scope , attr )
45+ return obj
46+ except AttributeError as e :
47+ raise ValueError (f"Could not find attribute '{ attr } ' of { attribute } " ) from e
48+
49+
2750def resolve_symbol_to_object (
2851 node : ast .expr , # Expecting a Name or Attribute here, and will otherwise TypeError
2952 scope : ScopeProxy | object ,
3053 _chain : list [str ] | None = None ,
3154) -> object :
32- """ """
55+ """
56+ Recursively resolve a symbol in the form of an ast.Name or ast.Attribute to the
57+ actual object it references in the given scope. The _chain parameter is used
58+ internally to keep track of the attribute chain being resolved, and should not
59+ be provided by the caller.
60+
61+ Args:
62+ node: An ast.expr representing the symbol to resolve. Expected to be an
63+ ast.Name or ast.Attribute.
64+ scope: The scope in which to resolve the symbol. This can be a ScopeProxy
65+ or any object that supports attribute access.
66+
67+ Returns:
68+ The object that the symbol resolves to in the given scope.
69+ """
3370 _chain = _chain or []
34- error_suffix = f" while attempting to resolve the symbol chain '{ '.' .join (_chain )} '"
3571 if isinstance (node , ast .Name ):
36- attr = node .id
37- try :
38- obj = getattr (scope , attr )
39- for attr in _chain :
40- obj = getattr (obj , attr )
41- return obj
42- except AttributeError as e :
43- raise ValueError (f"Could not find attribute '{ attr } ' { error_suffix } " ) from e
72+ return resolve_attribute_to_object ("." .join ([node .id ] + _chain ), scope )
4473 elif isinstance (node , ast .Attribute ):
4574 return resolve_symbol_to_object (node .value , scope , [node .attr ] + _chain )
4675 else :
4776 raise TypeError (
48- f"Cannot resolve symbol { node } { error_suffix } . "
49- f"Expected an ast.Name or chain of ast.Attribute and ast.Name, but got "
50- f"{ node } ."
77+ f"Cannot resolve symbol { node } while building the symbol chain "
78+ f"' { '.' . join ( _chain ) } '. Expected an ast.Name or chain of ast.Attribute "
79+ f"and ast.Name, but got { node } ."
5180 )
0 commit comments