Skip to content
This repository was archived by the owner on Jul 24, 2023. It is now read-only.

Commit dcbfa20

Browse files
committed
object: implement PyObject_Call{Function,Method}
tentatively implement PyObject_CallFunction and PyObject_CallMethod. Fixes #27.
1 parent a81d1b0 commit dcbfa20

File tree

4 files changed

+278
-7
lines changed

4 files changed

+278
-7
lines changed

go-python.c

+103
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,109 @@ _gopy_PyObject_DelAttrString(PyObject *o, const char *attr_name) {
1212
return PyObject_DelAttrString(o,attr_name);
1313
}
1414

15+
PyObject*
16+
_gopy_PyObject_CallFunction(PyObject *o, int len, char* pyfmt, void *cargs) {
17+
void ** args = (void**)cargs;
18+
19+
if (len > _gopy_max_varargs) {
20+
PyErr_Format(
21+
PyExc_RuntimeError,
22+
"python: maximum number of varargs (%d) exceeded (%d)",
23+
_gopy_max_varargs,
24+
len
25+
);
26+
return NULL;
27+
}
28+
29+
switch (len) {
30+
case 0:
31+
return PyObject_CallFunction(o, pyfmt);
32+
33+
case 1:
34+
return PyObject_CallFunction(o, pyfmt, args[0]);
35+
36+
case 2:
37+
return PyObject_CallFunction(o, pyfmt, args[0], args[1]);
38+
39+
case 3:
40+
return PyObject_CallFunction(o, pyfmt, args[0], args[1], args[2]);
41+
42+
case 4:
43+
return PyObject_CallFunction(o, pyfmt, args[0], args[1], args[2], args[3]);
44+
45+
case 5:
46+
return PyObject_CallFunction(o, pyfmt, args[0], args[1], args[2], args[3], args[4]);
47+
48+
case 6:
49+
return PyObject_CallFunction(o, pyfmt, args[0], args[1], args[2], args[3], args[4], args[5]);
50+
51+
case 7:
52+
return PyObject_CallFunction(o, pyfmt, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
53+
54+
case 8:
55+
return PyObject_CallFunction(o, pyfmt, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
56+
57+
default:
58+
PyErr_Format(PyExc_RuntimeError, "python: invalid number of arguments (%d)", len);
59+
return NULL;
60+
61+
}
62+
63+
return NULL;
64+
}
65+
66+
PyObject*
67+
_gopy_PyObject_CallMethod(PyObject *o, char *method, int len, char* pyfmt, void *cargs) {
68+
void ** args = (void**)cargs;
69+
70+
if (len > _gopy_max_varargs) {
71+
PyErr_Format(
72+
PyExc_RuntimeError,
73+
"python: maximum number of varargs (%d) exceeded (%d)",
74+
_gopy_max_varargs,
75+
len
76+
);
77+
return NULL;
78+
}
79+
80+
switch (len) {
81+
case 0:
82+
return PyObject_CallMethod(o, method, pyfmt);
83+
84+
case 1:
85+
return PyObject_CallMethod(o, method, pyfmt, args[0]);
86+
87+
case 2:
88+
return PyObject_CallMethod(o, method, pyfmt, args[0], args[1]);
89+
90+
case 3:
91+
return PyObject_CallMethod(o, method, pyfmt, args[0], args[1], args[2]);
92+
93+
case 4:
94+
return PyObject_CallMethod(o, method, pyfmt, args[0], args[1], args[2], args[3]);
95+
96+
case 5:
97+
return PyObject_CallMethod(o, method, pyfmt, args[0], args[1], args[2], args[3], args[4]);
98+
99+
case 6:
100+
return PyObject_CallMethod(o, method, pyfmt, args[0], args[1], args[2], args[3], args[4], args[5]);
101+
102+
case 7:
103+
return PyObject_CallMethod(o, method, pyfmt, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
104+
105+
case 8:
106+
return PyObject_CallMethod(o, method, pyfmt, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
107+
108+
default:
109+
PyErr_Format(PyExc_RuntimeError, "python: invalid number of arguments (%d)", len);
110+
return NULL;
111+
112+
}
113+
114+
return NULL;
115+
}
116+
117+
15118
/* --- dict --- */
16119

17120
int _gopy_PyDict_Check(PyObject *o) {

go-python.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package python
2+
3+
// #include "go-python.h"
4+
import "C"
5+
6+
import (
7+
"fmt"
8+
"unsafe"
9+
)
10+
11+
// pyfmt returns the python format string for a given go value
12+
func pyfmt(v interface{}) (unsafe.Pointer, string) {
13+
switch v := v.(type) {
14+
case bool:
15+
return unsafe.Pointer(&v), "b"
16+
17+
// case byte:
18+
// return unsafe.Pointer(&v), "b"
19+
20+
case int8:
21+
return unsafe.Pointer(&v), "b"
22+
23+
case int16:
24+
return unsafe.Pointer(&v), "h"
25+
26+
case int32:
27+
return unsafe.Pointer(&v), "i"
28+
29+
case int64:
30+
return unsafe.Pointer(&v), "k"
31+
32+
case int:
33+
switch unsafe.Sizeof(int(0)) {
34+
case 4:
35+
return unsafe.Pointer(&v), "i"
36+
case 8:
37+
return unsafe.Pointer(&v), "k"
38+
}
39+
40+
case uint8:
41+
return unsafe.Pointer(&v), "B"
42+
43+
case uint16:
44+
return unsafe.Pointer(&v), "H"
45+
46+
case uint32:
47+
return unsafe.Pointer(&v), "I"
48+
49+
case uint64:
50+
return unsafe.Pointer(&v), "K"
51+
52+
case uint:
53+
switch unsafe.Sizeof(uint(0)) {
54+
case 4:
55+
return unsafe.Pointer(&v), "I"
56+
case 8:
57+
return unsafe.Pointer(&v), "K"
58+
}
59+
60+
case float32:
61+
return unsafe.Pointer(&v), "f"
62+
63+
case float64:
64+
return unsafe.Pointer(&v), "d"
65+
66+
case complex64:
67+
return unsafe.Pointer(&v), "D"
68+
69+
case complex128:
70+
return unsafe.Pointer(&v), "D"
71+
72+
case string:
73+
cstr := C.CString(v)
74+
return unsafe.Pointer(cstr), "s"
75+
76+
case *PyObject:
77+
return unsafe.Pointer(v.topy()), "O"
78+
79+
}
80+
81+
panic(fmt.Errorf(
82+
"python: unknown type (%#T)",
83+
v,
84+
))
85+
}

go-python.h

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include <stdlib.h>
1111
#include <string.h>
1212

13+
/* go-python */
14+
#define _gopy_max_varargs 8 /* maximum number of varargs accepted by go-python */
15+
1316
/* --- object --- */
1417

1518
int
@@ -18,6 +21,12 @@ _gopy_PyObject_DelAttr(PyObject *o, PyObject *attr_name);
1821
int
1922
_gopy_PyObject_DelAttrString(PyObject *o, const char *attr_name);
2023

24+
PyObject*
25+
_gopy_PyObject_CallFunction(PyObject *o, int len, char* types, void *args);
26+
27+
PyObject*
28+
_gopy_PyObject_CallMethod(PyObject *o, char *method, int len, char* types, void *args);
29+
2130
/* --- dict --- */
2231

2332
int

object.go

+81-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import "C"
66
import (
77
"fmt"
88
"os"
9+
"strings"
910
"unsafe"
1011
)
1112

@@ -301,18 +302,91 @@ func (self *PyObject) CallObject(args *PyObject) *PyObject {
301302
// Return value: New reference.
302303
// Call a callable Python object callable, with a variable number of C arguments. The C arguments are described using a Py_BuildValue() 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(callable, args) or callable(*args). Note that if you only pass PyObject * args, PyObject_CallFunctionObjArgs() is a faster alternative.
303304
func (self *PyObject) CallFunction(format string, args ...interface{}) *PyObject {
304-
//FIXME
305-
panic("not implemented")
306-
return nil
305+
if len(args) > int(C._gopy_max_varargs) {
306+
panic(fmt.Errorf(
307+
"gopy: maximum number of varargs (%d) exceeded (%d)",
308+
int(C._gopy_max_varargs),
309+
len(args),
310+
))
311+
}
312+
313+
types := make([]string, 0, len(args))
314+
cargs := make([]unsafe.Pointer, 0, len(args))
315+
316+
for _, arg := range args {
317+
ptr, typ := pyfmt(arg)
318+
types = append(types, typ)
319+
cargs = append(cargs, ptr)
320+
if typ == "s" {
321+
defer func(ptr unsafe.Pointer) {
322+
C.free(ptr)
323+
}(ptr)
324+
}
325+
}
326+
327+
if len(args) <= 0 {
328+
o := C._gopy_PyObject_CallFunction(self.ptr, 0, nil, nil)
329+
return togo(o)
330+
}
331+
332+
pyfmt := C.CString(strings.Join(types, ""))
333+
defer C.free(unsafe.Pointer(pyfmt))
334+
o := C._gopy_PyObject_CallFunction(
335+
self.ptr,
336+
C.int(len(args)),
337+
pyfmt,
338+
unsafe.Pointer(&cargs[0]),
339+
)
340+
341+
return togo(o)
342+
307343
}
308344

309345
// PyObject* PyObject_CallMethod(PyObject *o, char *method, char *format, ...)
310346
// Return value: New reference.
311347
// Call the method named method of object o with a variable number of C arguments. The C arguments are described by a Py_BuildValue() format string that should produce a tuple. 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). Note that if you only pass PyObject * args, PyObject_CallMethodObjArgs() is a faster alternative.
312-
func (self *PyObject) CallMethod(format string, args ...interface{}) *PyObject {
313-
//FIXME
314-
panic("not implemented")
315-
return nil
348+
func (self *PyObject) CallMethod(method string, args ...interface{}) *PyObject {
349+
if len(args) > int(C._gopy_max_varargs) {
350+
panic(fmt.Errorf(
351+
"gopy: maximum number of varargs (%d) exceeded (%d)",
352+
int(C._gopy_max_varargs),
353+
len(args),
354+
))
355+
}
356+
357+
cmethod := C.CString(method)
358+
defer C.free(unsafe.Pointer(cmethod))
359+
360+
types := make([]string, 0, len(args))
361+
cargs := make([]unsafe.Pointer, 0, len(args))
362+
363+
for _, arg := range args {
364+
ptr, typ := pyfmt(arg)
365+
types = append(types, typ)
366+
cargs = append(cargs, ptr)
367+
if typ == "s" {
368+
defer func(ptr unsafe.Pointer) {
369+
C.free(ptr)
370+
}(ptr)
371+
}
372+
}
373+
374+
if len(args) <= 0 {
375+
o := C._gopy_PyObject_CallMethod(self.ptr, cmethod, 0, nil, nil)
376+
return togo(o)
377+
}
378+
379+
pyfmt := C.CString(strings.Join(types, ""))
380+
defer C.free(unsafe.Pointer(pyfmt))
381+
o := C._gopy_PyObject_CallMethod(
382+
self.ptr,
383+
cmethod,
384+
C.int(len(args)),
385+
pyfmt,
386+
unsafe.Pointer(&cargs[0]),
387+
)
388+
389+
return togo(o)
316390
}
317391

318392
/*

0 commit comments

Comments
 (0)