encukou / py3c

A Python 2/3 compatibility layer for C extensions
http://py3c.readthedocs.org
MIT License
59 stars 20 forks source link

Add Py_FindMethod to docs #22

Closed ghost closed 3 years ago

fbergmann commented 6 years ago

I just came across this myself, it would be great if there would be a macro for Py_FindMethod as well, or at least if a workaround would be described.

encukou commented 6 years ago

Hi, PyObject_GenericGetAttr should work in most cases. Describing this in depth is on my radar, but I'm swamped by other work at the moment.

fbergmann commented 6 years ago

Ok .. figured it (mostly) out ... not quite as dir(customtype) still lists an error, but i can call the methods again. the trick was to manually iterate through the method dictionary until the right PyMethodDef was encountered and then call an return PyCFunction_New with the method definition and self.

Since PyObject_GenericGetAttr didn't know about my method definitions it just returned an element not found.

encukou commented 6 years ago

Is the code you're working on open-source, by any chance? It would be nice to have a real-world example.

fbergmann commented 6 years ago

It will be, once it is all working. In the mean time here the relevant code snippet:


static PyObject * ds_getattro(PyObject *self, PyObject *nameobj)
{
  // try to resolve by name
  char* name = PyBytes_AsString(nameobj);

  if (name != NULL && strcmp(name, "len") == 0)
    return Py_BuildValue("i", getLength((tds *)self));
  if (name != NULL && strcmp(name, "__members__") == 0)
    return Py_BuildValue("[s]", "len");

#if PY_MAJOR_VERSION >= 3
  {
    int i;
  // try to find handler manually
  for (i = 0; DataStream_methods[i].ml_name != NULL; ++i) {
    if (strcmp(name, DataStream_methods[i].ml_name) == 0) {
      PyObject *result = PyCFunction_New(&DataStream_methods[i], self);
      if (result == NULL)
        result = Py_None;
      Py_INCREF(result);
      return result;
    }
  }
  }

  // use generic which will give us an error
  return PyObject_GenericGetAttr((PyObject *)self, nameobj);

#else
  return Py_FindMethod(DataStream_methods, (PyObject *)self, name);
#endif
}

like i said, i could not yet find why dir will no longer print all available methods, when it used to work in python 2.

davvid commented 3 years ago

I ran into a similar situation. What worked really well was having an ifdef that activates on Python3 that sets the tp_methods field of the type object struct to point to the PyMethodDef array (DataStream_methods in your example).

That should eliminate the need for custom code in getattro for looking up the methods -- tp_methods will do it for you. This also fixed the dir(...) situation in my scenario.

Maybe py3c should not add Py_FindMethod support, but if there's some useful information in this issue then perhaps it can be folded into the docs.

[ I realize I'm responding to an issue from 2018 but figured this might be helpful for someone else as well. cheers! ]

encukou commented 3 years ago

It's still an open issue :) My priorities did shift; I'll probably not get to adding this myself, but I'm happy to review pull requests.