Re: PROPOSAL: A Generic Python Object Interface for Python C Modules

Jim Fulton (jfulton@disqvarsa.er.usgs.GOV)
Thu, 2 Mar 1995 13:32:37 GMT

>>>>> "Mark" == Mark Lutz <lutz@humpback.KaPRE.COM> writes:
In article <9503011841.AA17707@whitetaileddeer.KaPRE.COM> lutz@humpback.KaPRE.COM (Mark Lutz) writes:

> Some more thoughts on your proposal: maybe there should also be
> some standardized interface to object-specific methods.

> For instance, the list 'append' method is unique to lists, and
> not immediately available through the proposed interface. I could
> simulate 'append' by 'setslice', but a standard way to get at such
> object methods would be simpler. Something like:

> PyObject* PyObject_CallMethod(object, method-name, args...)

I like it. I should have included it. I'm for adding it. How about a
family of routines:

PyObject *PyObject_CallMethod(PyObject *callable_object,
PyObject *method,
PyObject *args)

Call the method named m of object o. Special method names,
such as "__add__", "__getitem__", and so on are supported.
Arguments are given by the tuple, args. If no arguments are
needed, then args may be NULL. Returns the result of the
call on success, or NULL on failure. This is the equivalent
of the Python expression: o.method(args).

PyObject *PyObject_CallMethodString(PyObject *callable_object,
char *method,
PyObject *args)

Call the method named m of object o. Special method names,
such as "__add__", "__getitem__", and so on are supported.
Arguments are given by the tuple, args. If no arguments are
needed, then args may be NULL. Returns the result of the
call on success, or NULL on failure. This is the equivalent
of the Python expression: o.method(args).

PyObject* PyObject_CallMethodV(PyObject *o, PyObject *m, ...)

Call the method named m of object o with a variable number of
PyObject pointer arguments, terminated by a NULL
pointer. Returns the result of the call on success, or NULL
on failure. This is the equivalent of the Python expression:
o.method(args).

PyObject* PyObject_CallMethodStringV(PyObject *o, char *m, ...)

Call the method named m of object o with a variable number of
PyObject pointer arguments, terminated by a NULL
pointer. Returns the result of the call on success, or NULL
on failure. This is the equivalent of the Python expression:
o.method(args).

PyObject* PyObject_CallMethodC(PyObject *o, PyObject *m,
char *format, ...)

Call the method named m of object o with a variable number of
C arguments. The C arguments are described by a mkvalue
format string. The format may be NULL, indicating that no
arguments are provided. Returns the result of the call on
success, or NULL on failure. This is the equivalent of the
Python expression: o.method(args).

PyObject* PyObject_CallMethodStringC(PyObject *o, char *m,
char *format, ...)

Call the method named m of object o with a variable number of
C arguments. The C arguments are described by a mkvalue
format string. The format may be NULL, indicating that no
arguments are provided. Returns the result of the call on
success, or NULL on failure. This is the equivalent of the
Python expression: o.method(args).

And, while I'm at it, we might as well have:

PyObject* PyObject_CallObjectV(PyObject *callable_object, ...)

Call a callable Python object, callable_object, with a
variable number of PyObject pointer arguments, terminated by
a NULL pointer. Returns the result of the call on success, or
NULL on failure. This is the equivalent of the Python
expression: apply(o,args).

and

PyObject* PyObject_CallObjectC(PyObject *callable_object,
char *format, ...)

Call a callable Python object, callable_object, with a
variable number of C arguments. The C arguments are described
using a mkvalue-style format string. The format may be NULL,
indicating that no arguments are provided. Returns the
result of the call on success, or NULL on failure. This is
the equivalent of the Python expression: apply(o,args).

Also, to handle conversion of the output of this and other functions
returning PyObject pointers to C, I suggest:

PyArg_ParseTupleD(PyObject *o, char *format, ...)

Parse the object, o, according to the newgetargs style
format, format. Place the result in the C objects pointed
to by the additional arguments. The reference count of
the object, o, is reduced by one. This functions is
equivalent to PyArg_ParseTuple (newgetargs), except that the
object being parsed is "destroyed" after use. Also, if o is
NULL and an error has already been raised, then the function
simply returns NULL, without raising an error. This function
is intended to be used with other functions that return
PyObject pointers to convert the results of these functions
directly to C objects. For example:

PyArg_ParseTupleD(
PyObject_CallMethodStringC(foo,"bar","fi",2.5,9),
"i",&j)

> which would just call the object's tp_getattr method, and
> call the resulting methodobject; like "object.method(args)".
> So if I had a list, I could just:

> newlist = PyObject_CallMethod(oldlist, "append", item)

> instead of:

> PyObject *args = PyList_New(1); // see note below...
> PySequence_SetItem(args, 0, item);
> args = PySequence_Tuple(args);

> PyObject* method = PyObject_AttrString(oldlist, "append");
> newlist = PyObject_CallObject(method, args);
> DECREF(method);
> DECREF(args);

> I'd also like to see:

> - 'Call' functions that allows arguments to be passed in as a
> varargs list (rather than requiring a tuple be built).

See above.

> - Ideally, 'Call' ops also provide conversion from C->Python
> automatically, via a mkvalue format list. This gets close to
> the embedded-call API ideas, but here the function is a python
> object or an object's method, not a user-defined item.

Good idea. See above.

> - A more complete definition. For instance, there's no easy
> way to build a tuple; above, I built a list, set item 0,
> and then convered to a tuple. But Tuple_New seems useless:

> PyObject *args = PyTuple_New(1);
> <how to set a tuple's item here: sq_ass_item not defined?>

Hm, well, maybe you set a tuple's item with PySequence_SetItem:

PySequence_SetItem(args,0,some_value)

Ah, but of course, there is a problem here, since tuple's only
allow C routines to set their items through a separate interface.

So, should PySequence_SetItem treat tuples as a special case, or
should someone who needs to set a tuple item have to use the Tuple
specific interface?

> Of course, I could revert to the tuple's C functions, but
> this defeats the purpose.

Well, as I said in the introduction to my proposal, I think the
Python/C interface should contain type-specific public routines. This
is a different part of the interface than the one I'm proposing (as is
the interface to the interpreter through exec/eval-style calls). The
type-specific functions are currently "documented" in the public
include files. It would be nice to have true documentation for these,
but I don't consider reading header files to be quite the same as
reading the source.

> These functions aren't tuple
> 'methods', so there's no way to get to them as object Attr's.

This is an area where the C interface wants to be different than the
Python interface. In Python, there is no way to set a tuple item.
Given that we said that a goal of the abstract object interface was to
preserve the semantics of the interpreter, I think that
PySequence_SetItem should not work on tuples.

Hopefully, the new CallObject and CallMethod routines proposed above
addresses much of this need.

--
-- Jim Fulton      jfulton@mailqvarsa.er.usgs.gov    (703) 648-5622
                   U.S. Geological Survey, Reston VA  22092 
This message is being posted to obtain or provide technical information
relating to my duties at the U.S. Geological Survey.