为什么PyObject_SetAttrString返回-1(Python C-API)

时间:2014-11-12 20:23:03

标签: python python-c-api cpython

我正致力于桥接C ++和Python。

当创建我的自定义类型的新实例时,我需要将某些C ++实例方法注册为正在创建的Python对象的属性。

相关代码流程如下:

// during setup, we fill slots for the underlying PyTypeObject
p = new wrapperFor_PyTypeObject{ sizeof(FinalClass), 0, default_name };

p->set_tp_new(      extension_object_new );
p->set_tp_init(     extension_object_init );
p->set_tp_dealloc(  extension_object_deallocator );
:

感兴趣的是set_tp_init

    static int extension_object_init( PyObject* _self, PyObject* _args, PyObject* _kwds )
    {
        try
        {
            Py::Tuple args{_args};
            Py::Dict kwds = _kwds ? Py::Dict{_kwds} : Py::Dict{};

            PythonClassInstance* self{ reinterpret_cast<PythonClassInstance*>(_self) };

            if( self->m_pycxx_object )
            {
                self->m_pycxx_object->reinit( args, kwds );
                DBG_PRINT( "reinit -" );
            }
            else
                self->m_pycxx_object = new FinalClass{ self, args, kwds };

            // here we force all c++ relevant class instance methods to register as attributes
            FinalClass* f = (FinalClass*)(self->m_pycxx_object);
            f->AddAll();
        }
        catch(...)
        :

    void AddAll()
    {
        auto& methods = FuncMapper<FinalClass>::methods();

        //auto py_method_table = new PyMethodDef[ methods.size() + 1 ]{}; // sentinel must be 0'd, which it is thx to {}

        for( auto& m : methods )
        {
            PyObject* a{ selfPtr() };  // 'this' class derives from PyObject

支持PyObject *                 const char * str = m.first.c_str();

            Object callable{ m.second->ConstructCFunc(this) };  // ConstructCFunc uses PyCFunction_New
            callable.increment_reference_count();
            PyObject* c{ callable.ptr() };                      // extract backing PyObject* pointer

            int ret = PyObject_SetAttrString( a, str, c );

            if( ret == -1 )
                throw AttributeError{ m.first };
        }
    }

我已经以一种非常迂腐的方式声明了所有变量,以确保它们正确地传递到PyObject_SetAttrString,它看起来就是这样。

但是,PyObject_SetAttrString返回-1(错误)。

查看CPython源代码,我无法确定此错误的来源:

int
PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
{
    PyObject *s;
    int res;

    if (Py_TYPE(v)->tp_setattr != NULL)
        return (*Py_TYPE(v)->tp_setattr)(v, (char*)name, w);
    s = PyUnicode_InternFromString(name);
    if (s == NULL)
        return -1;
    res = PyObject_SetAttr(v, s, w);
    Py_XDECREF(s);
    return res;
}

int
PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
{
    PyTypeObject *tp = Py_TYPE(v);
    int err;

    if (!PyUnicode_Check(name)) {
        PyErr_Format(PyExc_TypeError,
                     "attribute name must be string, not '%.200s'",
                     name->ob_type->tp_name);
        return -1;
    }
    Py_INCREF(name);

    PyUnicode_InternInPlace(&name);
    if (tp->tp_setattro != NULL) {
        err = (*tp->tp_setattro)(v, name, value); // <-- SHOULD HIT HERE
        Py_DECREF(name);
        return err;
    }
    if (tp->tp_setattr != NULL) {
        char *name_str = _PyUnicode_AsString(name);
        if (name_str == NULL)
            return -1;
        err = (*tp->tp_setattr)(v, name_str, value);
        Py_DECREF(name);
        return err;
    }
    Py_DECREF(name);
    assert(name->ob_refcnt >= 1);
    if (tp->tp_getattr == NULL && tp->tp_getattro == NULL)
        PyErr_Format(PyExc_TypeError,
                     "'%.100s' object has no attributes "
                     "(%s .%U)",
                     tp->tp_name,
                     value==NULL ? "del" : "assign to",
                     name);
    else
        PyErr_Format(PyExc_TypeError,
                     "'%.100s' object has only read-only attributes "
                     "(%s .%U)",
                     tp->tp_name,
                     value==NULL ? "del" : "assign to",
                     name);
    return -1;
}

有什么我可以尝试将CPython源添加到我的项目并单步执行它吗?

通过查看输入的值,它应该达到我标记的点:

    PyUnicode_InternInPlace(&name);
    if (tp->tp_setattro != NULL) {
        err = (*tp->tp_setattro)(v, name, value); // <-- SHOULD HIT HERE
        Py_DECREF(name);
        return err;
    }

但是我无法看到如何调试到tp_setattro。它是函数表中的一个槽。浏览源代码会显示大量访问。

0 个答案:

没有答案