pythoncppyy

Revert cppyy automatic mapping of operator() to __getitem__ via C++ pythonization callback


As is also explained in this cppyy issue, an A& operator() on the C++ side is mapped to the python __getitem__.

On the issue it is suggested to add a special pythonization if this is not the wished for result.

An extra constraint in my case would be to add this to the C++ class itself to ensure that this pythonization is always applied.

I'm however having trouble figuring out how to properly do this via the Python C API. (1st time working with the that API so I'm a bit lost)

Minimal Reproducer somewhat contrived but shows the problem:
Note in the example below that struct A is code that I can't modify because that class is defined in library code. So the callback has to be in B.

import cppyy


cppyy.include("Python.h")

cppyy.cppdef(r"""

void myprint(PyObject* py){
    PyObject* tmp = PyObject_Str(py);
    Py_ssize_t size = 0;
    const char* s = PyUnicode_AsUTF8AndSize(tmp, &size);
    std::cout << std::string(s, size) << std::endl;
}

template <typename T>
struct A {
  T& operator[](size_t idx) { return arr[idx]; }
  const T& operator[](size_t idx) const { return arr[idx]; }
  std::array<T, 10> arr{};
};

template <typename T>
struct B : public A<T> {
  B& operator()() { return *this; };

  static void __cppyy_pythonize__( PyObject* klass, const std::string& name){
    std::cout << "Hello from pythonize" << std::endl;

    PyObject* getitem = PyObject_GetAttrString(klass, "__getitem__");

    myprint(getitem);
  }
};


using tmp = B<double>;
""")


t = cppyy.gbl.B['double']
print(t.__getitem__.__doc__)

I can get the __getitem__ function from the PyObject* klass but, as explained in the docs, the callback happens at the very end after all the internal processing of the class.
Thus the __call__ function, which here is B& operator()(), has already been mapped to __getitem__.

Unfortunately, I can't for the life of me figure out how I would undo that mapping and get back that old __getitem__ function. Is that operator[]() function even still accessible via the PyObject* klass ?

Any help/pointers would be much appreciated :)


Solution

  • First, to answer your question, to find the __getitem__ you want, get it from the base class of klass, not from klass directly. You can also do this in Python, rather than adding pythonizations in C++. In fact, doing this in Python is preferred as then you don't have to deal with the C-API.

    However, since the actual bug report is not the one you referenced, but this one, and since the suggestion made there, which you followed here, makes this a classic XY-problem, let me also add that what you really want is to simply do PyObject_DelAttrString(klass, "__getitem__") in your code example.

    Completely aside, the code that is giving you trouble here is from the Gaudi project, the core developers of which are the ones who asked for this automatic mapping in the first place. You may want to take this up with them.