I think the testcase may be resulting in a false positive. Is it possible that you forgot to del
the xyz
variable prior to calling fun()
?
Defining a function creates a variable local to the current scope that refers to the function object. For example:
def fun():
x = X()
Creates a function
object that is referenced by the fun
variable within the current scope. If the function is invoked, then (by default) a new local scope is created, where in the object returned from X()
will be referenced by x
within the local scope of the function and not within the caller's frame's locals()
.
Here is an example based on the original code:
#include <boost/python.hpp>
/// @brief Mockup types.
struct X
{
X()
{
// Borrow a reference from the locals dictionary to create a handle.
// If PyEval_GetLocals() returns NULL, then Boost.Python will throw.
namespace python = boost::python;
python::object locals(python::borrowed(PyEval_GetLocals()));
// Inject a reference to the int(42) object as 'xyz' into the
// frame's local variables.
locals["xyz"] = 42;
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<X>("X", python::init<>());
}
Interactive usage that asserts visibility:
>>> import example
>>> def fun():
... assert('xyz' not in locals())
... x = example.X()
... assert('xyz' in locals())
... assert('xyz' not in globals())
...
>>> assert('xyz' not in globals())
>>> fun()
>>> assert('xyz' not in globals())
>>> x = example.X()
>>> assert('xyz' in globals())
>>> del xyz
>>> fun()
>>> assert('xyz' not in globals())
For completeness, a FuncionType
can be constructed with a CodeType
whose co_flags
does not have the newlocals
flag set, causing the frame used for a function's invocation to have its locals()
return the same as globals()
. Here is an interactive usage example demonstrating this:
>>> def fun():
... x = 42
... print "local id in fun:", id(locals())
...
>>> import types
>>> def no_locals(fn):
... func_code = fn.func_code
... return types.FunctionType(
... types.CodeType(
... func_code.co_argcount,
... func_code.co_nlocals,
... func_code.co_stacksize,
... func_code.co_flags & ~2, # disable newlocals
... func_code.co_code,
... func_code.co_consts,
... func_code.co_names,
... func_code.co_varnames,
... func_code.co_filename,
... func_code.co_name,
... func_code.co_firstlineno,
... func_code.co_lnotab),
... globals())
...
>>> id(globals())
3075430164L
>>> assert('x' not in locals())
>>> fun()
local id in fun: 3074819588
>>> assert('x' not in locals())
>>> fun = no_locals(fun) # disable newlocals flag for fun
>>> assert('x' not in locals())
>>> fun()
local id in fun: 3075430164
>>> assert('x' in locals())
>>> x
42
Even after disabling the newlocals
flag, I had to invoke locals()
within fun()
to observe x
being inserted into the global symbol table.