What is the proper way to define attributes in a PyCXX extension

212 Views Asked by At

I wonder what the proper way is to define attributes in a python extension generated with PyCxx. Currently I have created a subclass of Py::PythonClass as in the official example. I've added behaviors().supportGetattro(); in the initialization function and created a simple override of

Py::Object getattro( const Py::String &name_ )
{
    std::string name( name_.as_std_string( "utf-8" ) );

    if( name == "name" )
    {
        return m_name;
    }
    else
    {
        return genericGetAttro( name_ );
    }
}

So far so good. In python I get the proper value with obj.name, but the only thing that makes me unsatified is that when calling dir(obj) the name attribute does not get listed. How can I change that?

2

There are 2 best solutions below

1
On BEST ANSWER

Barry Scott, developer of PyCXX kindly provided a simple solution. The trick is that python asks for the value of __members__ in getattr( const char *_name ) when calling dir(). In this case one can return a Py::List object containing the attribute names as strings.

0
On

For Python 3 dir is using the __dict__ member in getattr( const char *_name ) (among other things). It is expected to be a Py:Dict linking the attribute names to their values.

The dir function keeps only the attribute name for it return value.

Here is the code of dir() to source the answer.

static PyObject *
object_dir(PyObject *self, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *dict = NULL;
    PyObject *itsclass = NULL;

    /* Get __dict__ (which may or may not be a real dict...) */
    dict = _PyObject_GetAttrId(self, &PyId___dict__);
    if (dict == NULL) {
        PyErr_Clear();
        dict = PyDict_New();
    }
    else if (!PyDict_Check(dict)) {
        Py_DECREF(dict);
        dict = PyDict_New();
    }
    else {
        /* Copy __dict__ to avoid mutating it. */
        PyObject *temp = PyDict_Copy(dict);
        Py_DECREF(dict);
        dict = temp;
    }

    if (dict == NULL)
        goto error;

    /* Merge in attrs reachable from its class. */
    itsclass = _PyObject_GetAttrId(self, &PyId___class__);
    if (itsclass == NULL)
        /* XXX(tomer): Perhaps fall back to obj->ob_type if no
                       __class__ exists? */
        PyErr_Clear();
    else if (merge_class_dict(dict, itsclass) != 0)
        goto error;

    result = PyDict_Keys(dict);
    /* fall through */
error:
    Py_XDECREF(itsclass);
    Py_XDECREF(dict);
    return result;
}