Phffff=blog===
Find Functions by Type in Python
10 Jan 2020
After adding types to my project, I became curious if there was a way to programmatically query my project’s functions based on type. The inspect module has a signature function that returns the annotated type signature, which makes this possible.
In this example I will query from the User
module of my learn
project.
First, I create a list of function names and their signature.
res = []
for f in dir(User):
x = getattr(User, f)
if not callable(x):
continue
try:
res.append((f, inspect.signature(x)))
except:
continue
The value of res
looks like this
[...
('get_setting_value', <Signature (self, name: str) -> Union[str, NoneType]>),
('is_teacher', <Signature (self) -> bool>),
('is_teacher_of_survey', <Signature (self, survey: 'Survey') -> bool>),
('open_surveys', <Signature (self, now: datetime.datetime) -> List[ForwardRef('Survey')]>),
('set_password', <Signature (self, password: str) -> None>),
('set_setting', <Signature (self, name: str, value: str) -> None>),
('started_survey', <Signature (self, *, survey: 'Survey') -> bool>),
('stylize_for_import', <Signature (user: List[str]) -> List[str]>),
('wants_easy_input', <Signature (self) -> bool>),
...]
I can now make a function that, for example, finds all functions that could return a specific type. By “could return” I mean that the type could be wrapped in a list, union, or forwardref. There are other possibilities, like sets and dicts, but I didn’t get that far.
def could_return(t):
ret = []
for name, sig in res:
if type_in(t, sig.return_annotation):
ret.append((name, sig))
return ret
This requires a type_in
helper function, which we can define
by going over a few cases that I’m interested in.
def type_in(t, tt):
if t == tt:
return True
if hasattr(tt, '__origin__'):
if tt.__origin__ == list:
assert len(tt.__args__) == 1
return type_in(t, tt.__args__[0])
if tt.__origin__ == typing.Union:
return any([type_in(t, ttt) for ttt in tt.__args__])
if tt.__class__ == typing.ForwardRef:
return type_in(t, tt.__forward_arg__)
return False
The code is a little awkward since there is no clean type inspection API that I know of.
Now, we can query
>>> could_return(bool)
[...
('is_teacher', <Signature (self) -> bool>),
('is_teacher_of_survey', <Signature (self, survey: 'Survey') -> bool>),
('started_survey', <Signature (self, *, survey: 'Survey') -> bool>),
('wants_easy_input', <Signature (self) -> bool>),
...]
>>> could_return('Survey')
[...
('accessible_surveys', <Signature (self) -> List[ForwardRef('Survey')]>),
...]
>>> could_return(str)
[...
('get_setting_value', <Signature (self, name: str) -> Union[str, NoneType]>),
('stylize_for_import', <Signature (user: List[str]) -> List[str]>),
...]
View on GitHub