I am trying to do unit testing for nested functions (function inside a function), I am using code("nested" is function name) from below link which would supply closures and returns a valid function that is callable from tests. It works for simple functions. I am trying to make it work for recursive functions.
As an example: I am trying to get a valid function for "innerfunction" which has an entry in co_freevars as "innerfunction".
I want to get a function (FunctionType I believe) for "innerfunction" as a callable. To get this I need to call FunctionType with a tuple with a FunctionType for "innerfunction". This becomes a recursive "dependency". How can I resolve this dependency for parameters to be sent for "closures"
Function for
def toplevelfunction():
def innerfunction(a):
print ('val of a is ', a)
if a > 0:
innerfunction(a -1)
innerfunction(10)
Original code that I am using:
def freeVar(val):
def nested():
return val
return nested.__closure__[0]
codeAttribute = '__code__' if sys.version_info[0] == 3 else 'func_code'
def nested(outer, innerName, **freeVars):
if isinstance(outer, (types.FunctionType, types.MethodType)):
outer = outer.__getattribute__(codeAttribute)
for const in outer.co_consts:
if isinstance(const, types.CodeType) and const.co_name == innerName:
return types.FunctionType(const, globals(), None, None, tuple(
freeVar(freeVars[name]) for name in const.co_freevars))
https://code.activestate.com/recipes/580716-unit-testing-nested-functions/
How to add support for closures so below works:
func = nested(toplevelfunction, 'innerfunction')
func(5)
would return error need a closure of length 1. Adding a closure referring to "const" shows up that it is of CodeType and not FunctionType. Adding a closure value to refer itself seems tricky after reading through the documentation.
I do find innerfunction as:
{code} <code object innerfunction at 0x104b9bc00, file "/<filedirectory>/handle_results.py", line 44>
co_argcount = {int} 1
co_cellvars = {tuple} <class 'tuple'>: ()
co_code = {bytes} b"t\x00\x00d\x01\x00|\x00\x00\x83\x02\x00\x01|\x00\x00d\x02\x00k\x04\x00r'\x00\x88\x00\x00|\x00\x00d\x03\x00\x18\x83\x01\x00\x01d\x00\x00S"
co_consts = {tuple} <class 'tuple'>: (None, 'val of a is ', 0, 1)
co_filename = {str} '/<fildirectory>/handle_results.py'
co_firstlineno = {int} 44
co_flags = {int} 19
co_freevars = {tuple} <class 'tuple'>: ('innerfunction ',)
co_kwonlyargcount = {int} 0
co_lnotab = {bytes} b'\x00\x01\r\x01\x0c\x01'
co_name = {str} 'innerfunction '
co_names = {tuple} <class 'tuple'>: ('print',)
co_nlocals = {int} 1
co_stacksize = {int} 3
co_varnames = {tuple} <class 'tuple'>: ('a',)
toplevelfunctionto return the inner function, rather than returningNoneand discarding the function it creates.toplevelfunctiondoesn't do anything useful. You don't have an explicitreturnstatement, so it implicitly returnsNone. No references toinnerfunctionexist aftertoplevelfunctionexits. (Even if you did addreturn innerfunctiontotoplevelfunction, it's not a very interesting closure, because there are no other local variables intoplevelfunctionfor it to close over.)innerfunctionin your edit still is not a closure, becausetoplevelfunctiondoes not return it. It's just a function defined and used in the scope oftop levelfunction.