As others have mentioned, simply calling the function is not enough: a return
statement might only be present in a conditional, and thus, specific input would need to be passed in order to execute the return
statement. That, too, is not a good indicator of the presence of a return
, since it could return
None
, causing greater ambiguity. Instead, the inspect
and ast
module can be used:
Test functions:
def appler():
a = "apple"
# `return` is missing
def bananer():
b = "banana"
return b
def deeper_test(val, val1):
if val and val1:
if val+val1 == 10:
return
def gen_func(v):
for i in v:
if isinstance(i, list):
yield from gen_func(i)
else:
yield i
inspect.getsource
returns the entire source of the function as a string, which can then be passed to ast.parse
. From there, the syntax tree can be recursively traversed, searching for the presence of a return
statement:
import inspect, ast
fs = [appler, bananer, deeper_test, gen_func]
def has_return(f_obj):
return isinstance(f_obj, ast.Return) or \
any(has_return(i) for i in getattr(f_obj, 'body', []))
result = {i.__name__:has_return(ast.parse(inspect.getsource(i))) for i in fs}
Output:
{'appler': False, 'bananer': True, 'deeper_test': True, 'gen_func': False}
With a defined validate_funk
:
def validate_funk(f):
if not has_return(ast.parse(inspect.getsource(f))):
raise ValueError(f"function '{f.__name__}' does not contain a `return` statement")
return True
Notes:
- This solution does not require the test functions to be called.
- The solution must be run in a file. If it is run in the shell, an
OSError
will be raised. For the file, see this Github Gist.