From: John Nagle on
I'm doing something with CPython introspection, and I'm trying
to determine whether a function is a closure. Consider

def foo(x) :
global fbar
def bar(y) :
pass
fbar = bar # export closure


foo(0)

We now have "fbar" as a reference to a closure.

"inspect" can tell us some things about foo and fbar:

>>> inspect.getargspec(foo)
ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)
>>> inspect.getargspec(fbar)
ArgSpec(args=['y'], varargs=None, keywords=None, defaults=None)

No difference there.

>>> fbar.func_globals
{'fbar': <function bar at 0x021FD470>,
'__builtins__': <module '__builtin__' (built-in)>,
'inspect': <module 'inspect' from 'C:\python26\lib\inspect.pyc'>,
'm':
[('__call__', <method-wrapper '__call__' of function object at 0x021FD470>),
('__class__', <type 'function'>), ('__closure__', None),
('__code__', <code object bar at 021F1C38, file "<stdin>", line 3>),
('__defaults__', None),
('__delattr__', <method-wrapper '__delattr__' of function object at
0x021FD470>),
('__dict__', {}),
('__doc__', None), ('__format__', <built-in method __format__ of
function object at 0x021FD470>),
('__get__', <method-wrapper '__get__' of function object at 0x021FD470>),
('__getattribute__', <method-wrapper '__getattribute__' of function
object at 0x021FD470>),
('__globals__', {...}),
('__hash__', <method-wrapper '__hash__' of function object at 0x021FD470>),
('__init__', <method-wrapper '__init__' of function object at 0x021FD470>),
('__module__', '__main__'),
('__name__', 'bar'),
('__new__', <built-in method __new__ of type object at 0x1E1FACF0>),
('__reduce__', <built-in method __reduce__ of function object at
0x021FD470>),
('__reduce_ex__', <built-in method __reduce_ex__ of function object at
0x021FD470>),
('__repr__', <method-wrapper '__repr__' of function object at 0x021FD470>),
('__setattr__', <method-wrapper '__setattr__' of function object at
0x021FD470>),
('__sizeof__', <built-in method __sizeof__ of function object at
0x021FD470>),
('__str__', <method-wrapper '__str__' of function object at 0x021FD470>),
('__subclasshook__', <built-in method __subclasshook__ of type object at
0x1E1FACF0>),
('func_closure', None),
('func_code', <code object bar at 021F1C38, file "<stdin>", line 3>),
('func_defaults', None),
('func_dict', {}),
('func_doc', None),
('func_globals', {...}),
('func_name', 'bar')],
'__package__': None, '__name__': '__main__',
'foo': <function foo at 0x02223C70>,
'__doc__': None}
>>>

No indication there that "fbar" is a closure inside "foo". There
are "__closure__" and "__func_closure__" entries in there, but
they are both None. A non-closure function has the same entries.

So how can I detect a closure?

John Nagle
From: Steven D'Aprano on
On Mon, 14 Jun 2010 20:46:28 -0700, John Nagle wrote:

> So how can I detect a closure?

I *think* you do it through the co_flags attribute of the code object.
This is in Python 2.5:


>>> def f(x):
.... def g():
.... return x
.... return g
....
>>>
>>> closure = f(42)
>>> closure()
42
>>> closure.func_code.co_flags
19
>>> f.func_code.co_flags
3


although this doesn't seem to be documented, at least not here:

http://docs.python.org/reference/datamodel.html



--
Steven
From: Ian Kelly on
On Mon, Jun 14, 2010 at 9:46 PM, John Nagle <nagle(a)animats.com> wrote:
> No indication there that "fbar" is a closure inside "foo".  There
> are "__closure__" and "__func_closure__" entries in there, but
> they are both None.  A non-closure function has the same entries.
>
> So how can I detect a closure?

Maybe because it has no non-local references, so it's not really a closure?

>>> def foo(x):
.... global fbar
.... def bar(y):
.... pass
.... fbar = bar
....
>>> foo(0)
>>> fbar.func_closure
>>>
>>> def foo(x):
.... global fbar
.... def bar(y):
.... x
.... fbar = bar
....
>>> foo(0)
>>> fbar.func_closure
(<cell at 0x7ffb7dbd8210: int object at 0xa9f2b0>,)

Cheers,
Ian
From: John Nagle on
On 6/14/2010 9:33 PM, Steven D'Aprano wrote:
> On Mon, 14 Jun 2010 20:46:28 -0700, John Nagle wrote:
>
>> So how can I detect a closure?
>
> I *think* you do it through the co_flags attribute of the code object.
> This is in Python 2.5:
>
> although this doesn't seem to be documented, at least not here:
>
> http://docs.python.org/reference/datamodel.html

Got it. Check

f.func_closure

for a non-null value. For a closure, the value will be a Cell object.

The value of "func_closure" in f.func_globals is None, but that's the
wrong place to look, apparently.

John Nagle