Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing an object to C module, in Python

Tags:

python

I ran into a situation with pure python and C python module. To summarize, how can I accept and manipulate python object in C module? My python part will look like this.


    #!/usr/bin/env python

    import os, sys
    from c_hello import *

    class Hello:
        busyHello = _sayhello_obj

    class Man:
        def __init__(self, name):
            self.name = name
        def getName(self):
            return self.name

    h = Hello()
    h.busyHello( Man("John") )

in C, two things need to be resolved. first, how can I receive object? second, how can I call a method from the object?


    static PyObject *
    _sayhello_obj(PyObject *self, PyObject *args)
    {
      PyObject *obj;
      // How can I fill obj?

      char s[1024];
      // How can I fill s, from obj.getName() ?

      printf("Hello, %s\n", s);
      return Py_None;
    }
like image 705
dragonfry Avatar asked Nov 29 '11 08:11

dragonfry


People also ask

Can you pass an object to a function in Python?

Python's language reference for assignment statements states that if the target is an object's attribute that supports assignment, then the object will be asked to perform the assignment on that attribute. If you pass the object as an argument to a function, then its attributes can be modified in place.

What is PyObject in Python?

PyObject is an object structure that you use to define object types for Python. All Python objects share a small number of fields that are defined using the PyObject structure. All other object types are extensions of this type. PyObject tells the Python interpreter to treat a pointer to an object as an object.

How do you convert strings to C in Python?

foo("string") passes a Python str object to a C function which will later assign the string to char *c_ptr .


1 Answers

To extract an argument from an invocation of your method, you need to look at the functions documented in Parsing arguments and building values, such as PyArg_ParseTuple. (That's for if you're only taking positional args! There are others for positional-and-keyword args, etc.)

The object you get back from PyArg_ParseTuple doesn't have it's reference count increased. For simple C functions, you probably don't need to worry about this. If you're interacting with other Python/C functions, or if you're releasing the global interpreter lock (ie. allowing threading), you need to think very carefully about object ownership.

static PyObject *
_sayhello_obj(PyObject *self, PyObject *args)
{
  PyObject *obj = NULL;
  // How can I fill obj?

  static char fmt_string = "O" // For "object"

  int parse_result = PyArg_ParseTuple(args, fmt_string, &obj);

  if(!parse_res)
  {
    // Don't worry about using PyErr_SetString, all the exception stuff should be
    // done in PyArg_ParseTuple()
    return NULL;
  }

  // Of course, at this point you need to do your own verification of whatever
  // constraints might be on your argument.

For calling a method on an object, you need to use either PyObject_CallMethod or PyObject_CallMethodObjArgs, depending on how you construct the argument list and method name. And see my comment in the code about object ownership!

Quick digression just to make sure you're not setting yourself up for a fall later: If you really are just getting the string out to print it, you're better off just getting the object reference and passing it to PyObject_Print. Of course, maybe this is just for illustration, or you know better than I do what you want to do with the data ;)

  char s[1024];
  // How can I fill s, from obj.getName() ?

  // Name of the method
  static char method_name = "getName";
  // No arguments? Score! We just need NULL here
  char method_fmt_string = NULL;

  PyObject *objname = PyObject_CallMethod(obj, obj_method, method_fmt_string);
  // This is really important! What we have here now is a Python object with a newly
  // incremented reference count! This means you own it, and are responsible for
  // decrementing the ref count when you're done. See below.

  // If there's a failure, we'll get NULL
  if(objname == NULL)
  {
    // Again, this should just propagate the exception information
    return NULL;
  }

Now there are a number of functions in the String/Bytes Objects section of the Concrete Objects Layer docs; use whichever works best for you.

But do not forget this bit:

  // Now that we're done with the object we obtained, decrement the reference count
  Py_XDECREF(objname);

  // You didn't mention whether you wanted to return a value from here, so let's just
  // return the "None" singleton.
  // Note: this macro includes the "return" statement!
  Py_RETURN_NONE;
}

Note the use of Py_RETURN_NONE there, and note that it's not return Py_RETURN_NONE!

PS. The structure of this code is dictated to a great extent by personal style (eg. early returns, static char format strings inside the function, initialisation to NULL). Hopefully the important information is clear enough apart from stylistic conventions.

like image 181
detly Avatar answered Oct 04 '22 19:10

detly