PLearn 0.1
Public Types | Public Member Functions | Static Public Member Functions | Static Public Attributes | Protected Attributes | Friends
PLearn::PythonObjectWrapper Class Reference

Very lightweight wrapper over a Python Object that allows conversion to/from C++ types (including those of PLearn) More...

#include <PythonObjectWrapper.h>

Collaboration diagram for PLearn::PythonObjectWrapper:
Collaboration graph
[legend]

List of all members.

Public Types

enum  OwnershipMode { control_ownership, transfer_ownership }
 Ownership mode of the PythonObject. More...
typedef map< const string,
PLPyClass
pypl_classes_t
typedef map< const Object
*, PyObject * > 
wrapped_objects_t

Public Member Functions

 PythonObjectWrapper (OwnershipMode o=control_ownership, bool acquire_gil=true)
 Construct 'None'.
 PythonObjectWrapper (PyObject *pyobj, OwnershipMode o=control_ownership, bool acquire_gil=true)
 Constructor for pre-existing PyObject.
template<class T >
 PythonObjectWrapper (const T &x, OwnershipMode o=control_ownership, bool acquire_gil=true)
 Constructor for general type (forwarded to newPyObject)
 PythonObjectWrapper (const PythonObjectWrapper &other)
 Copy constructor: increment refcount if controlling ownership.
 ~PythonObjectWrapper ()
 Destructor: decrement refcount if controlling ownership.
PythonObjectWrapperoperator= (const PythonObjectWrapper &rhs)
 Assignment operator: manage refcount if necessary.
template<typename T >
 operator T () const
 implicit conversion
void swap (PythonObjectWrapper &other)
 Swap *this with another instance.
PyObject * getPyObject () const
 Return the bare PyObject managed by the wrapper.
bool isNull () const
 Return true if m_object is either NULL or stands for Python's None.
void printDebug () const
 Print out the Python object to stderr for debugging purposes.
template<class T >
as () const
 General version that relies on ConvertFromPyOBject.
template<class T >
asNoTraceback () const
 Conversion that does not print any traceback to stderr if there is a conversion error.

Static Public Member Functions

static PyObject * trampoline (PyObject *self, PyObject *args)
static PyObject * python_del (PyObject *self, PyObject *args)
static PyObject * newCPPObj (PyObject *self, PyObject *args)
static PyObject * refCPPObj (PyObject *self, PyObject *args)
static void gc_collect1 ()
static PyObject * newPyObject ()
 Create a raw PyObject* from various types.
static void initializePython ()
 This function is called by PythonCodeSnippet to carry out initializations related to libnumarray.

Static Public Attributes

static bool VMatAsPtr = false
static bool m_unref_injected = false
static PyMethodDef m_unref_method_def
static PyMethodDef m_newCPPObj_method_def
static PyMethodDef m_refCPPObj_method_def
static pypl_classes_t m_pypl_classes
static wrapped_objects_t m_wrapped_objects
 for wrapped PLearn Objects
static wrapped_objects_t::iterator m_gc_next_object

Protected Attributes

OwnershipMode m_ownership
 Whether we own the PyObject or not.
PyObject * m_object

Friends

class ConvertToPyObject
PStreamoperator>> (PStream &in, PythonObjectWrapper &v)
PStreamoperator<< (PStream &out, const PythonObjectWrapper &v)

Detailed Description

Very lightweight wrapper over a Python Object that allows conversion to/from C++ types (including those of PLearn)

A PythonObjectWrapper provides the ability to manage a Python Object in a fairly lightweight manner. It supports construction from a number of C++ types, which in turn create new Python objects. It also supports the conversion of the Python object back to C++ types. The PythonObjectWrapper can either own or not the Python Object. For owned objects, the Python reference count is increased with each copy or assignment, and decremented with each desctruction.

For safety reasons, the Python Global Interpreter Lock is acquired by default before constructing the Python object. However, this can be controlled by a constructor option (useful when you have already acquired the lock in a bigger context and you have to construct a large number of PythonObjectWrappers).

Definition at line 809 of file PythonObjectWrapper.h.


Member Typedef Documentation

Definition at line 938 of file PythonObjectWrapper.h.

Definition at line 940 of file PythonObjectWrapper.h.


Member Enumeration Documentation

Ownership mode of the PythonObject.

If 'control_ownership', full reference counting is enabled. If 'transfer_ownership', no counting is carried out. The latter is useful when needing to transfer objects to Python functions that will become owners of the object.

Enumerator:
control_ownership 
transfer_ownership 

Definition at line 818 of file PythonObjectWrapper.h.


Constructor & Destructor Documentation

PLearn::PythonObjectWrapper::PythonObjectWrapper ( OwnershipMode  o = control_ownership,
bool  acquire_gil = true 
)

Construct 'None'.

This object is a singleton in Python, but it must be reference-counted just like any other object.

Definition at line 439 of file PythonObjectWrapper.cc.

References control_ownership, m_object, and m_ownership.

Referenced by newCPPObj(), python_del(), refCPPObj(), and trampoline().

    : m_ownership(o),
      m_object(Py_None)
{
    if (m_ownership == control_ownership)
    {Py_XINCREF(m_object);}
}

Here is the caller graph for this function:

PLearn::PythonObjectWrapper::PythonObjectWrapper ( PyObject *  pyobj,
OwnershipMode  o = control_ownership,
bool  acquire_gil = true 
)

Constructor for pre-existing PyObject.

Definition at line 450 of file PythonObjectWrapper.cc.

References control_ownership, m_object, and m_ownership.

    : m_ownership(o),
      m_object(pyobj)
{
    if (m_ownership == control_ownership)
    {Py_XINCREF(m_object);}
}
template<class T >
PLearn::PythonObjectWrapper::PythonObjectWrapper ( const T &  x,
OwnershipMode  o = control_ownership,
bool  acquire_gil = true 
) [inline, explicit]

Constructor for general type (forwarded to newPyObject)

Definition at line 842 of file PythonObjectWrapper.h.

References m_object, and newPyObject().

Here is the call graph for this function:

PLearn::PythonObjectWrapper::PythonObjectWrapper ( const PythonObjectWrapper other)

Copy constructor: increment refcount if controlling ownership.

Definition at line 463 of file PythonObjectWrapper.cc.

References control_ownership, m_object, and m_ownership.

    : m_ownership(other.m_ownership),
      m_object(other.m_object)
{
    if (m_ownership == control_ownership)
    {Py_XINCREF(m_object);}
}
PLearn::PythonObjectWrapper::~PythonObjectWrapper ( )

Destructor: decrement refcount if controlling ownership.

Always acquire the Python Global Interpreter Lock before decrementing.

Definition at line 473 of file PythonObjectWrapper.cc.

References control_ownership, m_object, and m_ownership.

{
    if (m_ownership == control_ownership) {
        // Hack: don't acquire the GIL if we are dealing with Py_None, since
        // this object never moves in memory (no deallocation) and decrementing
        // its refcount should be thread-safe.  It is possible that some empty
        // PythonObjectWrappers exist without build() having been called on
        // them (e.g. type registration for plearn help), i.e. Py_Initialize()
        // has not been called and acquiring the GIL in those cases is iffy.
        if (m_object == Py_None)
            Py_XDECREF(m_object);
        else {
            PythonGlobalInterpreterLock gil;
            Py_XDECREF(m_object);
        }
    }
}

Member Function Documentation

template<class T >
T PLearn::PythonObjectWrapper::as ( ) const [inline]
template<class T >
T PLearn::PythonObjectWrapper::asNoTraceback ( ) const [inline]

Conversion that does not print any traceback to stderr if there is a conversion error.

This should be used in the rare cases where multiple return types are expected from Python and one wants to attempt several (e.g. from more specific to more general).

Definition at line 901 of file PythonObjectWrapper.h.

References m_object.

    {
        return ConvertFromPyObject<T>::convert(m_object, false);
    }
void PLearn::PythonObjectWrapper::gc_collect1 ( ) [static]

Definition at line 664 of file PythonObjectWrapper.cc.

References PLearn::endl(), m_gc_next_object, m_wrapped_objects, PLERROR, and PLearn::removeFromWrappedObjectsSet().

Referenced by PLearn::pythonGlobalFuncTramp(), and trampoline().

{
    DBG_MODULE_LOG << "entering PythonObjectWrapper::gc_collect1()" << endl;

    if(m_gc_next_object == m_wrapped_objects.end())
        m_gc_next_object= m_wrapped_objects.begin();
    if(m_gc_next_object != m_wrapped_objects.end())
    {
        wrapped_objects_t::iterator it= m_gc_next_object;
        ++m_gc_next_object;
        if(it->first->usage() == 1 && it->second->ob_refcnt == 1)
        {
            //Py_DECREF(it->second);
            DBG_MODULE_LOG.clearInOutMaps();
            PyObject* cptr= PyObject_GetAttrString(it->second, const_cast<char*>("_cptr"));
//             DBG_MODULE_LOG << "In PythonObjectWrapper::gc_collect1(), removing object " 
//                            << PythonObjectWrapper(it->second)
//                            << " ; python version of: " << it->first << " (" << (void*)it->first 
//                            << ", " << PyCObject_AsVoidPtr(cptr) << ')'
//                            << endl;
            if(!cptr)
            {
                if(PyErr_Occurred()) PyErr_Print();
                PLERROR("In PythonObjectWrapper::gc_collect1 : cannot get attribute '_cptr' from Python object ");
            }
            Py_DECREF(cptr);
            removeFromWrappedObjectsSet(it->second);
            gc_collect1();
        }
    }
    DBG_MODULE_LOG << "exiting PythonObjectWrapper::gc_collect1()" << endl;
}

Here is the call graph for this function:

Here is the caller graph for this function:

PyObject* PLearn::PythonObjectWrapper::getPyObject ( ) const [inline]
void PLearn::PythonObjectWrapper::initializePython ( ) [static]

This function is called by PythonCodeSnippet to carry out initializations related to libnumarray.

Definition at line 81 of file PythonObjectWrapper.cc.

Referenced by PLearn::PythonCodeSnippet::build_(), PLearn::convertArrayCheck(), PLearn::initPythonExtensionModule(), PLearn::injectPLearnClasses(), and PLearn::ConvertToPyObject< Object * >::newPyObject().

{
    static bool numarray_initialized = false;
    if (! numarray_initialized) {
        // must be in each translation unit that makes use of libnumarray;
        // weird stuff related to table of function pointers that's being
        // initialized into a STATIC VARIABLE of the translation unit!
        import_array();//needed for PyArray_DescFromType (will segfault otherwise)
        import_libnumarray();
        numarray_initialized = true;
    }
}

Here is the caller graph for this function:

bool PLearn::PythonObjectWrapper::isNull ( ) const

Return true if m_object is either NULL or stands for Python's None.

Definition at line 514 of file PythonObjectWrapper.cc.

References m_object.

Referenced by PLearn::PythonCodeSnippet::build_(), PLearn::PythonCodeSnippet::getGlobalObject(), PLearn::PythonCodeSnippet::injectInternal(), PLearn::PythonCodeSnippet::invoke(), PLearn::PythonCodeSnippet::isInvokable(), and PLearn::PythonCodeSnippet::setGlobalObject().

{
    return ! m_object || m_object == Py_None;
}

Here is the caller graph for this function:

PyObject * PLearn::PythonObjectWrapper::newCPPObj ( PyObject *  self,
PyObject *  args 
) [static]

Definition at line 630 of file PythonObjectWrapper.cc.

References PLearn::newObjectFromClassname(), and PythonObjectWrapper().

Referenced by PLearn::injectPLearnClasses().

{
    TVec<PyObject*> args_tvec= 
        PythonObjectWrapper(args).as<TVec<PyObject*> >();
    Object* o= newObjectFromClassname(PyString_AsString(args_tvec[1]));

    //DBG_MODULE_LOG << "In PythonObjectWrapper::newCPPObj() " << PyString_AsString(args_tvec[1]) << " <-> " << (void*)o << '\t' << o << endl;
    //perr << "new o->usage()= " << o->usage() << endl;

    return PyCObject_FromVoidPtr(o, 0);
}

Here is the call graph for this function:

Here is the caller graph for this function:

PyObject * PLearn::PythonObjectWrapper::newPyObject ( ) [static]
template<typename T >
PLearn::PythonObjectWrapper::operator T ( ) const [inline]

implicit conversion

Definition at line 866 of file PythonObjectWrapper.h.

{ return as<T>(); }
PythonObjectWrapper & PLearn::PythonObjectWrapper::operator= ( const PythonObjectWrapper rhs)

Assignment operator: manage refcount if necessary.

Definition at line 492 of file PythonObjectWrapper.cc.

References swap().

{
    if (&rhs != this) {
        PythonObjectWrapper other(rhs);
        swap(other);
    }
    return *this;
}

Here is the call graph for this function:

void PLearn::PythonObjectWrapper::printDebug ( ) const

Print out the Python object to stderr for debugging purposes.

Definition at line 509 of file PythonObjectWrapper.cc.

References m_object.

{
    PyObject_Print(m_object, stderr, Py_PRINT_RAW);
}
PyObject * PLearn::PythonObjectWrapper::python_del ( PyObject *  self,
PyObject *  args 
) [static]

Definition at line 600 of file PythonObjectWrapper.cc.

References m_gc_next_object, m_pypl_classes, m_wrapped_objects, newPyObject(), PLERROR, and PythonObjectWrapper().

Referenced by PLearn::injectPLearnClasses().

{
    TVec<PyObject*> args_tvec=
        PythonObjectWrapper(args).as<TVec<PyObject*> >();

    Object* obj= PythonObjectWrapper(args_tvec[0]);

    string classname= obj->classname();
    pypl_classes_t::iterator clit= m_pypl_classes.find(classname);
    if(clit == m_pypl_classes.end())
        PLERROR("in PythonObjectWrapper::python_del : "
                "deleting obj. for which no python class exists!");
    --clit->second.nref;

    //perr << "delete " << (void*)obj << " : " << (void*)m_wrapped_objects[obj] << endl;
    //perr << "bef.del o->usage()= " << obj->usage() << endl;
    obj->unref();//python no longer references this obj.
    //perr << "aft.del o->usage()= " << obj->usage() << endl;

    //GC
    if(m_gc_next_object->first == obj)
        m_gc_next_object= m_wrapped_objects.end();

    m_wrapped_objects.erase(obj);

    //printWrappedObjects();

    return newPyObject();//None
}

Here is the call graph for this function:

Here is the caller graph for this function:

PyObject * PLearn::PythonObjectWrapper::refCPPObj ( PyObject *  self,
PyObject *  args 
) [static]

Definition at line 642 of file PythonObjectWrapper.cc.

References PLearn::addToWrappedObjectsSet(), PLearn::TVec< T >::length(), m_wrapped_objects, newPyObject(), PythonObjectWrapper(), and PLearn::PPointable::ref().

Referenced by PLearn::injectPLearnClasses().

{
    TVec<PyObject*> args_tvec= 
        PythonObjectWrapper(args).as<TVec<PyObject*> >();
    PyObject* pyo= args_tvec[1];
    Object* o= PythonObjectWrapper(pyo);
    if(args_tvec.length() < 3 || args_tvec[2]==Py_True)
        o->ref();

    //perr << "ref o->usage()= " << o->usage() << endl;
    PythonObjectWrapper::m_wrapped_objects[o]= pyo;
    //checkWrappedObjects(">>>>>>>>>> in refcppobj -> checkWrappedObjects");// debug only
    //perr << "refCPPObj: " << (void*)o << " : " << (void*)pyo << endl;

    //DBG_MODULE_LOG << "In PythonObjectWrapper::refCPPObj() " << PythonObjectWrapper(pyo) << " <-> " << (void*)o << '\t' << o << endl;
    addToWrappedObjectsSet(pyo);//Py_INCREF(pyo);
    
    //printWrappedObjects();///***///***

    return newPyObject();//None
}

Here is the call graph for this function:

Here is the caller graph for this function:

void PLearn::PythonObjectWrapper::swap ( PythonObjectWrapper other)

Swap *this with another instance.

Definition at line 502 of file PythonObjectWrapper.cc.

References m_object, and m_ownership.

Referenced by operator=().

{
    std::swap(this->m_ownership, other.m_ownership);
    std::swap(this->m_object,    other.m_object);
}

Here is the caller graph for this function:

PyObject * PLearn::PythonObjectWrapper::trampoline ( PyObject *  self,
PyObject *  args 
) [static]

Definition at line 521 of file PythonObjectWrapper.cc.

References as(), PLearn::RemoteTrampoline::call(), gc_collect1(), getPyObject(), i, PLearn::PLearnError::message(), PLERROR, PythonObjectWrapper(), PLearn::TVec< T >::size(), PLearn::TVec< T >::subVecSelf(), and PLearn::the_PLearn_python_exception.

Referenced by PLearn::injectPLearnClasses().

{
//     DBG_MODULE_LOG << "PythonObjectWrapper::trampoline(" << PythonObjectWrapper(self)
//                    << ", " << PythonObjectWrapper(args) << ')' << endl;
    PythonGlobalInterpreterLock gil;         // For thread-safety

    //get object and trampoline from self
    PythonObjectWrapper s(self);

    //perr << "refcnt self= " << self->ob_refcnt << endl;

    RemoteTrampoline* tramp=
        dynamic_cast<RemoteTrampoline*>(s.as<PPointable*>());
    if(!tramp)
        PLERROR("in PythonObjectWrapper::trampoline : "
                "can't unwrap RemoteTrampoline.");

    //wrap args
    int size = PyTuple_GET_SIZE(args);
    TVec<PythonObjectWrapper> args_tvec(size);
    for(int i= 0; i < size; ++i)
        args_tvec[i]=
            PythonObjectWrapper(PyTuple_GET_ITEM(args,i));

    // separate self from other params.
    Object* obj= args_tvec[0];
    args_tvec.subVecSelf(1, args_tvec.size()-1);

    //perr << "REMOTE METHOD: " << obj->classname() << "::" << tramp->documentation().name() << endl;

    gc_collect1();

    //call, catch and send any errors to python
    try
    {
        PythonObjectWrapper returned_value= tramp->call(obj, args_tvec);
        PyObject* to_return= returned_value.getPyObject();
        Py_XINCREF(to_return);
        return to_return;
    }
    catch(const PLearnError& e)
    {
        PyErr_SetString(the_PLearn_python_exception, e.message().c_str());
        return 0;
    }
    catch(const std::exception& e)
    {
        PyErr_SetString(PyExc_Exception, e.what());
        return 0;
    }
    catch(...)
    {
        PyErr_SetString(PyExc_Exception,
                        "Caught unknown C++ exception while executing injected function "
                        "inside a PythonObjectWrapper");
        return 0;
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:


Friends And Related Function Documentation

friend class ConvertToPyObject [friend]

Definition at line 948 of file PythonObjectWrapper.h.

PStream& operator<< ( PStream out,
const PythonObjectWrapper v 
) [friend]

Definition at line 1015 of file PythonObjectWrapper.cc.

{
    out << v.getPyObject();
    return out;

    PLERROR("operator<<(PStream&, const PythonObjectWrapper&) : "
            "not supported (yet).");
/*
    PyObject* env= PyDict_New();
    if(0 != PyDict_SetItemString(env, "__builtins__", PyEval_GetBuiltins()))
        PLERROR("in operator<<(PStream&, const PythonObjectWrapper& v) : "
                "cannot insert builtins in env.");
    if(0 != PyDict_SetItemString(env, "the_obj", v.m_object))
        PLERROR("in operator<<(PStream&, const PythonObjectWrapper& v) : "
                "cannot insert the_obj in env.");
    PyObject* res= PyRun_String("\nfrom cPickle import *\nresult= dumps(the_obj)\n", 
                                Py_file_input, env, env);
    if(!res)
    {
        Py_DECREF(env);
        if(PyErr_Occurred()) PyErr_Print();
        PLERROR("in operator<<(PStream&, const PythonObjectWrapper& v) : "
                "cannot pickle python object.");
    }
    Py_DECREF(res);
    string pickle= 
        PythonObjectWrapper(env).as<std::map<string, PythonObjectWrapper> >()["result"];
        Py_DECREF(env);
    string toout= string("PythonObjectWrapper(ownership=") + tostring(v.m_ownership) + ", object=\"" + pickle + "\")";
    out << toout;
*/
    return out; // shut up compiler
}
PStream& operator>> ( PStream in,
PythonObjectWrapper v 
) [friend]

Definition at line 963 of file PythonObjectWrapper.cc.

{
    PLERROR("operator>>(PStream&, PythonObjectWrapper&) : "
            "not supported (yet).");
/*
    string s;
    in >> s;
    string sub= "PythonObjectWrapper(ownership=";
    if(s.substr(0,sub.length()) != sub)
        PLERROR("in operator>>(PStream& in, PythonObjectWrapper& v) : "
                "expected '%s' but got '%s'.",
                sub.c_str(), s.c_str());
    s= s.substr(sub.length());
    v.m_ownership= static_cast<PythonObjectWrapper::OwnershipMode>(s[0]-'0');
    s= s.substr(1);
    sub= ", object=";
    if(s.substr(0,sub.length()) != sub)
        PLERROR("in operator>>(PStream& in, PythonObjectWrapper& v) : "
                "expected '%s' but got '%s'.",
                sub.c_str(), s.c_str());
    s= s.substr(sub.length());
    PStream sin= openString(s, PStream::plearn_ascii, "r");
    string pickle;
    sin >> pickle;

    PyObject* pypickle= PyString_FromString(pickle.c_str());
    PyObject* env= PyDict_New();
    if(0 != PyDict_SetItemString(env, "__builtins__", PyEval_GetBuiltins()))
        PLERROR("in operator>>(PStream&, PythonObjectWrapper& v) : "
                "cannot insert builtins in env.");
    if(0 != PyDict_SetItemString(env, "the_pickle", pypickle))
        PLERROR("in operator>>(PStream&, PythonObjectWrapper& v) : "
                "cannot insert the_pickle in env.");
    Py_DECREF(pypickle);
    PyObject* res= PyRun_String("\nfrom cPickle import *\nresult= loads(the_pickle)\n", 
                                Py_file_input, env, env);
    if(!res)
    {
        Py_DECREF(env);
        if(PyErr_Occurred()) PyErr_Print();
        PLERROR("in operator<<(PStream&, const PythonObjectWrapper& v) : "
                "cannot unpickle python object '%s'.",pickle.c_str());
    }
    Py_DECREF(res);
    v.m_object= 
        PythonObjectWrapper(env).as<std::map<string, PyObject*> >()["result"];
    Py_INCREF(v.m_object);
    Py_DECREF(env);
*/
    return in;
}

Member Data Documentation

PythonObjectWrapper::wrapped_objects_t::iterator PLearn::PythonObjectWrapper::m_gc_next_object [static]
Initial value:

Definition at line 942 of file PythonObjectWrapper.h.

Referenced by gc_collect1(), and python_del().

Definition at line 936 of file PythonObjectWrapper.h.

Referenced by PLearn::injectPLearnClasses().

Whether we own the PyObject or not.

Definition at line 945 of file PythonObjectWrapper.h.

Referenced by PythonObjectWrapper(), swap(), and ~PythonObjectWrapper().

Definition at line 937 of file PythonObjectWrapper.h.

Referenced by PLearn::injectPLearnClasses().

Definition at line 934 of file PythonObjectWrapper.h.

Definition at line 935 of file PythonObjectWrapper.h.

Referenced by PLearn::injectPLearnClasses().


The documentation for this class was generated from the following files:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines