質問

I am trying to access the python locals from the constructor of a C++ class exported with boost.python, but PyEval_GetLocals() seems to return the global instead of local dict. An example: in C++ I do

class X {
   public:
      X() {
         boost::python::object locals(boost::python::borrowed(PyEval_GetLocals()));
         locals["xyz"]=42
      }
};

BOOST_PYTHON_MODULE(test) {
   class_<X>("X", init<>());
}

If I now do in Python

x = X()
print(xyz)

I get '42' as output (as expected). However, the same happens with

def fun():
    x = X()

print(xyz)

which also prints '42', despite the fact that 'fun()' has created a new scope. I would have expected the 'xyz' name to have gone out of scope again after fun() exits, and thus be left with an undefined 'xyz' by the time I reach the print statement.

What am I doing wrong? Is there any way to get access to the local names from within a C++ object or function?

役に立ちましたか?

解決

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top