I also wanted to do this in Jython. But the way shown in the accepted answer doesn't work there, because the co_consts
isn't available on a code object. (Also, there doesn't seem to be any other way to query a code object to get at the code objects of nested functions.)
But of course, the code objects are there somewhere, we have the source and full access, so it's only a matter of finding an easy way within a reasonable amount of time. So here's one way that works. (Hold on to your seats.)
Suppose we have code like this in module mod
:
def outer():
def inner():
print "Inner"
First get the code object of the outer function directly:
code = mod.outer.__code__
In Jython, this is an instance of PyTableCode
, and, by reading the source, we find that the actual functions are implemented in a Java-class made out of your given script, which is referenced by the code object's funcs
field. (All these classes made out of scripts are subclasses of PyFunctionTable
, hence that's the declared type of funcs
.) This isn't visible from within Jython, as a result of magic machinery which is a designer's way of saying that you're accessing these things at your own risk.
So we need to dive into the Java for a moment. A class like this does the trick:
import java.lang.reflect.Field;
public class Getter {
public static Object getFuncs(Object o)
throws NoSuchFieldException, IllegalAccessException {
Field f = o.getClass().getDeclaredField("funcs");
f.setAccessible(true);
return f.get(o);
}
}
Back in Jython:
>>> import Getter
>>> funcs = Getter.getFuncs(mod.outer.__code__)
>>> funcs
mod$py@1bfa3a2
Now, this funcs
object has all of the functions declared anywhere in the Jython script (those nested arbitrarily, within classes, etc.). Also, it has fields holding the corresponding code objects.
>>> fields = funcs.class.getDeclaredFields()
In my case, the code object corresponding to the nested function happens to be the last one:
>>> flast = fields[-1]
>>> flast
static final org.python.core.PyCode mod$py.inner$24
To get the code object of interest:
>>> flast.setAccessible(True)
>>> inner_code = flast.get(None) #"None" because it's a static field.
>>> dir(inner_code)
co_argcount co_filename co_flags co_freevars co_name co_nlocals co_varnames
co_cellvars co_firstlineno
And the rest is the same as the accepted answer, i.e. check co_freevars
, (which is there in Jython, unlike co_consts
).
A good thing about this approach is that you enumerate exactly all code objects that are declared anywhere within the source code file: functions, methods, generators, whether or not they are nested under anything or under each other. There is nowhere else for them to hide.