├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── class.go ├── code.go ├── code_test.go ├── dict.go ├── error.go ├── examples └── gomodule │ └── gomodule.go ├── exc.go ├── exception.go ├── float.go ├── goargs.go ├── gofunction.c ├── gofunction.go ├── gofunction.h ├── gomodule.go ├── gomodule_test.go ├── goregister.go ├── goregister_test.go ├── int.go ├── long.go ├── module.go ├── module_test.go ├── none.go ├── number.go ├── object.go ├── object_test.go ├── python.go ├── pyutil ├── call.go ├── call_test.go ├── var.go └── var_test.go ├── string.go ├── tuple.go └── type.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | *.pyc 6 | 7 | # Folders 8 | _obj 9 | _test 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #CHANGELOG 2 | 3 | ## v1.1.00 4 | 5 | 2013-05-28 Issue [#17](https://github.com/qiniu/py/pull/17): 6 | 7 | - bugfix: Closure add methodDef to fix that gc recycle methodDef 8 | - 支持 Float 类型 9 | 10 | 11 | ## v1.0.01 12 | 13 | 2013-03-10 Issue [#5](https://github.com/qiniu/py/pull/5): 14 | 15 | - 增加 gomodule 样例 16 | - Travis-CI 支持 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | py - Golang bindings to the CPython C-API 2 | == 3 | 4 | **NOTE**: This project is **unmaintained**. Maybe [https://github.com/go-python/cpy3](https://github.com/go-python/cpy3) is a good replacement. 5 | 6 | py is Golang bindings to the CPython C-API. 7 | 8 | py project's homepage is: https://github.com/qiniu/py 9 | 10 | # Install 11 | 12 | ``` 13 | go get github.com/qiniu/py 14 | ``` 15 | 16 | # Example 17 | 18 | ```{go} 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "github.com/qiniu/log" 24 | "github.com/qiniu/py" 25 | ) 26 | 27 | // ------------------------------------------------------------------- 28 | 29 | type FooModule struct { 30 | } 31 | 32 | func (r *FooModule) Py_bar(args *py.Tuple) (ret *py.Base, err error) { 33 | var i int 34 | var s string 35 | err = py.Parse(args, &i, &s) 36 | if err != nil { 37 | return 38 | } 39 | fmt.Println("call foo.bar:", i, s) 40 | return py.IncNone(), nil 41 | } 42 | 43 | func (r *FooModule) Py_bar2(args *py.Tuple) (ret *py.Base, err error) { 44 | var i int 45 | var s []string 46 | err = py.ParseV(args, &i, &s) 47 | if err != nil { 48 | return 49 | } 50 | fmt.Println("call foo.bar2:", i, s) 51 | return py.IncNone(), nil 52 | } 53 | 54 | // ------------------------------------------------------------------- 55 | 56 | const pyCode = ` 57 | 58 | import foo 59 | foo.bar(1, 'Hello') 60 | foo.bar2(1, 'Hello', 'world!') 61 | ` 62 | 63 | func main() { 64 | 65 | gomod, err := py.NewGoModule("foo", "", new(FooModule)) 66 | if err != nil { 67 | log.Fatal("NewGoModule failed:", err) 68 | } 69 | defer gomod.Decref() 70 | 71 | code, err := py.Compile(pyCode, "", py.FileInput) 72 | if err != nil { 73 | log.Fatal("Compile failed:", err) 74 | } 75 | defer code.Decref() 76 | 77 | mod, err := py.ExecCodeModule("test", code.Obj()) 78 | if err != nil { 79 | log.Fatal("ExecCodeModule failed:", err) 80 | } 81 | defer mod.Decref() 82 | } 83 | 84 | // ------------------------------------------------------------------- 85 | ``` 86 | 87 | -------------------------------------------------------------------------------- /class.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline int classCheck(PyObject *o) { return PyClass_Check(o); } 5 | import "C" 6 | import "unsafe" 7 | 8 | type Class struct { 9 | Base 10 | o C.PyClassObject 11 | } 12 | 13 | // ClassType is the Type object that represents the Class type. 14 | var ClassType = (*Type)(unsafe.Pointer(&C.PyClass_Type)) 15 | 16 | func newClass(obj *C.PyObject) *Class { 17 | return (*Class)(unsafe.Pointer(obj)) 18 | } 19 | 20 | func AsClass(o *Base) (v *Class, ok bool) { 21 | if ok = C.classCheck(o.c()) != 0; ok { 22 | v = newClass(o.c()) 23 | } 24 | return 25 | } 26 | 27 | func (t *Class) NewNoArgs() (ret *Base, err error) { 28 | args := NewTuple(0) 29 | defer args.Decref() 30 | return t.New(args, nil) 31 | } 32 | 33 | // Return value: New reference. 34 | // Create a new instance of a specific class. The parameters arg and kw are used as 35 | // the positional and keyword parameters to the object’s constructor. 36 | func (t *Class) New(args *Tuple, kw *Dict) (ret *Base, err error) { 37 | ret1 := C.PyInstance_New(t.c(), args.c(), kw.c()) 38 | return obj2ObjErr(ret1) 39 | } 40 | 41 | func (t *Class) NewObjArgs(args ...*Base) (ret *Base, err error) { 42 | args1 := PackTuple(args...) 43 | defer args1.Decref() 44 | return t.New(args1, nil) 45 | } 46 | 47 | // Return true if klass is a subclass of base. Return false in all other cases. 48 | func (t *Class) IsSubclass(base *Base) bool { 49 | return C.PyClass_IsSubclass(t.c(), base.c()) != 0 50 | } 51 | 52 | -------------------------------------------------------------------------------- /code.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include 5 | 6 | static inline int codeCheck(PyObject *o) { return PyCode_Check(o); } 7 | static inline void decref(PyObject *obj) { Py_DECREF(obj); } 8 | 9 | static inline FILE* openFile(char* name) { 10 | return fopen(name, "r"); 11 | } 12 | 13 | static inline PyObject* compileString(char* text, char* filename, int start) { 14 | return Py_CompileString(text, filename, start); 15 | } 16 | 17 | static PyObject* compileFile(FILE* f, char* name, int start) { 18 | struct _node *n = PyParser_SimpleParseFile(f, name, start); 19 | if (!n) return NULL; 20 | return (PyObject*)PyNode_Compile(n, name); 21 | } 22 | */ 23 | import "C" 24 | import "unsafe" 25 | 26 | // ------------------------------------------------------------------------------------------ 27 | // type StartToken 28 | 29 | type StartToken int 30 | 31 | const ( 32 | EvalInput = StartToken(C.Py_eval_input) // for isolated expressions 33 | 34 | FileInput = StartToken(C.Py_file_input) // for sequences of statements as read from a file or other source; 35 | // to use when compiling arbitrarily long Python source code. 36 | 37 | SingleInput = StartToken(C.Py_single_input) // for a single statement; used for the interactive interpreter loop. 38 | ) 39 | 40 | // ------------------------------------------------------------------------------------------ 41 | // type Code 42 | 43 | type Code struct { 44 | Base 45 | o C.PyCodeObject 46 | } 47 | 48 | // CodeType is the Type object that represents the Code type. 49 | var CodeType = (*Type)(unsafe.Pointer(&C.PyCode_Type)) 50 | 51 | func newCode(obj *C.PyObject) *Code { 52 | return (*Code)(unsafe.Pointer(obj)) 53 | } 54 | 55 | func AsCode(o *Base) (v *Code, ok bool) { 56 | if ok = C.codeCheck(o.c()) != 0; ok { 57 | v = newCode(o.c()) 58 | } 59 | return 60 | } 61 | 62 | func Compile(text, filename string, start StartToken) (*Code, error) { 63 | t := C.CString(text) 64 | defer C.free(unsafe.Pointer(t)) 65 | 66 | fn := C.CString(filename) 67 | defer C.free(unsafe.Pointer(fn)) 68 | 69 | ret := C.compileString(t, fn, C.int(start)) 70 | if ret == nil { 71 | return nil, exception() 72 | } 73 | return newCode(ret), nil 74 | } 75 | 76 | func CompileFile(name string, start StartToken) (*Code, error) { 77 | fn := C.CString(name) 78 | defer C.free(unsafe.Pointer(fn)) 79 | 80 | file, err := C.openFile(fn) 81 | if file == nil { 82 | return nil, err 83 | } 84 | defer C.fclose(file) 85 | 86 | ret := C.compileFile(file, fn, C.int(start)) 87 | if ret == nil { 88 | return nil, exception() 89 | } 90 | return newCode(ret), nil 91 | } 92 | 93 | // Return value: New reference. 94 | func (code *Code) Eval(globals, locals *Base) (*Base, error) { 95 | pyCode := (*C.PyCodeObject)(unsafe.Pointer(code)) 96 | ret := C.PyEval_EvalCode(pyCode, globals.c(), locals.c()) 97 | return obj2ObjErr(ret) 98 | } 99 | 100 | func (code *Code) Run(globals, locals *Base) error { 101 | pyCode := (*C.PyCodeObject)(unsafe.Pointer(code)) 102 | ret := C.PyEval_EvalCode(pyCode, globals.c(), locals.c()) 103 | if ret == nil { 104 | return exception() 105 | } 106 | C.decref(ret) 107 | return nil 108 | } 109 | 110 | // ------------------------------------------------------------------------------------------ 111 | 112 | func Run(text string) error { 113 | 114 | t := C.CString(text) 115 | defer C.free(unsafe.Pointer(t)) 116 | 117 | ret := C.PyRun_SimpleStringFlags(t, nil) 118 | return int2Err(ret) 119 | } 120 | 121 | // Return a dictionary of the builtins in the current execution frame, or the interpreter of 122 | // the thread state if no frame is currently executing. 123 | // 124 | // Return value: Borrowed reference. 125 | func GetBuiltins() *Base { 126 | ret := C.PyEval_GetBuiltins() 127 | return newObject(ret) 128 | } 129 | 130 | // Return a dictionary of the global variables in the current execution frame, 131 | // or NULL if no frame is currently executing. 132 | // 133 | // Return value: Borrowed reference 134 | func GetLocals() *Base { 135 | ret := C.PyEval_GetLocals() 136 | return newObject(ret) 137 | } 138 | 139 | // Return a dictionary of the local variables in the current execution frame, 140 | // or NULL if no frame is currently executing. 141 | // 142 | // Return value: Borrowed reference 143 | func GetGlobals() *Base { 144 | ret := C.PyEval_GetGlobals() 145 | return newObject(ret) 146 | } 147 | 148 | // ------------------------------------------------------------------------------------------ 149 | 150 | -------------------------------------------------------------------------------- /code_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type compileCase struct { 8 | exp string 9 | ret string 10 | start StartToken 11 | } 12 | 13 | var g_compileCases = []compileCase{ 14 | {"1+2", "3", EvalInput}, 15 | {"1+2", "None", SingleInput}, // echo 16 | {"1+2", "None", FileInput}, 17 | } 18 | 19 | func TestCompile(t *testing.T) { 20 | 21 | for _, c := range g_compileCases { 22 | code, err := Compile(c.exp, "", c.start) 23 | if err != nil { 24 | t.Fatal("Compile failed:", err) 25 | } 26 | defer code.Decref() 27 | 28 | globals := NewDict() 29 | defer globals.Decref() 30 | 31 | locals := NewDict() 32 | defer locals.Decref() 33 | 34 | ret, err := code.Eval(globals.Obj(), locals.Obj()) 35 | if err != nil { 36 | t.Fatal("Eval failed:", err) 37 | } 38 | defer ret.Decref() 39 | 40 | if ret.String() != c.ret { 41 | t.Fatal("Eval ret:", ret.String()) 42 | } 43 | } 44 | } 45 | 46 | type evalLocalGlobalsCase struct { 47 | exp string 48 | globals string 49 | locals string 50 | start StartToken 51 | } 52 | 53 | var g_evalLocalGlobalsCases = []evalLocalGlobalsCase{ 54 | {"v=1+2", "{}", "{'v': 3}", FileInput}, 55 | {"v=1+2", "{}", "{'v': 3}", SingleInput}, // echo 56 | // {"v=1+2", "{}", "{'v': 3}", EvalInput}, // compile error 57 | } 58 | 59 | func _TestEvalLocalGlobals(t *testing.T) { 60 | 61 | Initialize() 62 | defer Finalize() 63 | 64 | for _, c := range g_evalLocalGlobalsCases { 65 | code, err := Compile(c.exp, "", c.start) 66 | if err != nil { 67 | t.Fatal("Compile failed:", c.exp, c.start, err) 68 | } 69 | defer code.Decref() 70 | 71 | globals := NewDict() 72 | defer globals.Decref() 73 | 74 | locals := NewDict() 75 | defer locals.Decref() 76 | 77 | err = code.Run(globals.Obj(), locals.Obj()) 78 | if err != nil { 79 | t.Fatal("Run failed:", err) 80 | } 81 | println(globals.String(), locals.String()) 82 | 83 | if locals.String() != c.locals || globals.String() != c.globals { 84 | t.Fatal("Run:", globals.String(), locals.String()) 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /dict.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline int dictCheck(PyObject *o) { return PyDict_Check(o); } 5 | // static inline int dictCheckE(PyObject *o) { return PyDict_CheckExact(o); } 6 | import "C" 7 | import "unsafe" 8 | 9 | // *Dict represents a Python dictionary. In addition to satisfying the Object 10 | // interface, Dict pointers also have a number of methods defined - representing 11 | // the PyDict_XXX functions from the Python C API. 12 | type Dict struct { 13 | Base 14 | o C.PyDictObject 15 | } 16 | 17 | // DictType is the Type object that represents the Dict type. 18 | var DictType = (*Type)(unsafe.Pointer(&C.PyDict_Type)) 19 | 20 | func newDict(obj *C.PyObject) *Dict { 21 | return (*Dict)(unsafe.Pointer(obj)) 22 | } 23 | 24 | // NewDict creates a new empty dictionary. 25 | // 26 | // Return value: New Reference. 27 | func NewDict() *Dict { 28 | ret := C.PyDict_New() 29 | return newDict(ret) 30 | } 31 | 32 | func NewDictProxy(obj *Base) *Dict { 33 | ret := C.PyDictProxy_New(obj.c()) 34 | return newDict(ret) 35 | } 36 | 37 | func AsDict(o *Base) (v *Dict, ok bool) { 38 | if ok = C.dictCheck(o.c()) != 0; ok { 39 | v = newDict(o.c()) 40 | } 41 | return 42 | } 43 | 44 | // CheckExact returns true if d is an actual dictionary object, and not an 45 | // instance of a sub type. 46 | func (d *Dict) CheckExact() bool { 47 | ret := C.dictCheckE(d.c()) 48 | if int(ret) != 0 { 49 | return true 50 | } 51 | return false 52 | } 53 | 54 | // Clear empties the dictionary d of all key-value pairs. 55 | func (d *Dict) Clear() { 56 | C.PyDict_Clear(d.c()) 57 | } 58 | 59 | // Contains Returns true if the dictionary contains the given key. This is 60 | // equivalent to the Python expression "key in d". 61 | func (d *Dict) Contains(key *Base) (bool, error) { 62 | ret := C.PyDict_Contains(d.c(), key.c()) 63 | return int2BoolErr(ret) 64 | } 65 | 66 | // Copy returns a new dictionary that contains the same key-values pairs as d. 67 | // 68 | // Return value: New Reference. 69 | func (d *Dict) Copy() (*Base, error) { 70 | ret := C.PyDict_Copy(d.c()) 71 | return obj2ObjErr(ret) 72 | } 73 | 74 | // SetItem inserts "val" into dictionary d with the key "key". If "key" is not 75 | // hashable, then a TypeError will be returned. 76 | func (d *Dict) SetItem(key, val *Base) error { 77 | ret := C.PyDict_SetItem(d.c(), key.c(), val.c()) 78 | return int2Err(ret) 79 | } 80 | 81 | // SetItemString inserts "val" into dictionary d with the key "key" (or rather, 82 | // with a *String with the value of "key" will be used as the key). If "key" is 83 | // not hashable, then a TypeError will be returned. 84 | func (d *Dict) SetItemString(key string, val *Base) error { 85 | s := C.CString(key) 86 | defer C.free(unsafe.Pointer(s)) 87 | ret := C.PyDict_SetItemString(d.c(), s, val.c()) 88 | return int2Err(ret) 89 | } 90 | 91 | // DelItem removes the entry with the key of "key" from the dictionary d. If 92 | // "key" is not hashable, a TypeError is returned. 93 | func (d *Dict) DelItem(key *Base) error { 94 | ret := C.PyDict_DelItem(d.c(), key.c()) 95 | return int2Err(ret) 96 | } 97 | 98 | // DelItem removes the entry with the key of "key" (or rather, with a *String 99 | // with the value of "key" as the key) from the dictionary d. 100 | func (d *Dict) DelItemString(key string) error { 101 | s := C.CString(key) 102 | defer C.free(unsafe.Pointer(s)) 103 | ret := C.PyDict_DelItemString(d.c(), s) 104 | return int2Err(ret) 105 | } 106 | 107 | // GetItem returns the Object from dictionary d which has the key "key". If 108 | // there is no such Object, then nil is returned (without an error). 109 | // 110 | // Return value: Borrowed Reference. 111 | func (d *Dict) GetItem(key *Base) *Base { 112 | ret := C.PyDict_GetItem(d.c(), key.c()) 113 | return newObject(ret) 114 | } 115 | 116 | // GetItemString returns the Object from dictionary d which has the key "key" 117 | // (or rather, which has a *String with the value of "key" as the key). If 118 | // there is no such Object, then nil is returned (without an error). 119 | // 120 | // Return value: Borrowed Reference. 121 | func (d *Dict) GetItemString(key string) *Base { 122 | s := C.CString(key) 123 | defer C.free(unsafe.Pointer(s)) 124 | ret := C.PyDict_GetItemString(d.c(), s) 125 | return newObject(ret) 126 | } 127 | 128 | /* 129 | // Items returns a *List containing all the items from the dictionary d, as with 130 | // the Python "d.items()". 131 | // 132 | // Return value: New Reference. 133 | func (d *Dict) Items() (*List, error) { 134 | ret := C.PyDict_Items(d.c()) 135 | return newList(ret), exception() 136 | } 137 | 138 | // Keys returns a *List containing all the keys from the dictionary d, as with 139 | // the Python "d.keys()". 140 | // 141 | // Return value: New Reference. 142 | func (d *Dict) Keys() (*List, error) { 143 | ret := C.PyDict_Keys(d.c()) 144 | return newList(ret), exception() 145 | } 146 | 147 | // Values returns a *List containing all the values from the dictionary d, as 148 | // with the Python "d.values()". 149 | // 150 | // Return value: New Reference. 151 | func (d *Dict) Values() (*List, error) { 152 | ret := C.PyDict_Values(d.c()) 153 | return newList(ret), exception() 154 | } 155 | */ 156 | 157 | // Size returns the number of items in the dictionary d. This is equivalent to 158 | // the Python "len(d)". 159 | func (d *Dict) Size() int { 160 | ret := C.PyDict_Size(d.c()) 161 | if ret < 0 { 162 | panic(exception()) 163 | } 164 | return int(ret) 165 | } 166 | 167 | // PyDict_Next 168 | 169 | // Merge merges key values pairs from Object o (which may be a dictionary, or an 170 | // object that supports "o.keys()" and "o[key]") into the dictionary d. If 171 | // override is true then a matching key in d will have it's value replaced by 172 | // the one in o, else the value in d will be left. 173 | func (d *Dict) Merge(o *Base, override bool) error { 174 | over := 0 175 | if override { 176 | over = 1 177 | } 178 | ret := C.PyDict_Merge(d.c(), o.c(), C.int(over)) 179 | return int2Err(ret) 180 | } 181 | 182 | // Update replaces key values pairs in d with those from o. It is equivalent to 183 | // d.Merge(o, true) in Go, or "d.update(o)" in Python. 184 | func (d *Dict) Update(o *Base) error { 185 | ret := C.PyDict_Update(d.c(), o.c()) 186 | return int2Err(ret) 187 | } 188 | 189 | // MergeFromSeq2 merges key values pairs from the Object o (which must be an 190 | // iterable object, where each item is an iterable of length 2 - the key value 191 | // pairs). If override is true then the last key value pair with the same key 192 | // wins, otherwise the first instance does (where an instance already in d 193 | // counts before any in o). 194 | func (d *Dict) MergeFromSeq2(o *Base, override bool) error { 195 | over := 0 196 | if override { 197 | over = 1 198 | } 199 | ret := C.PyDict_MergeFromSeq2(d.c(), o.c(), C.int(over)) 200 | return int2Err(ret) 201 | } 202 | 203 | // Map returns a Go map that contains the values from the Python dictionary, 204 | // indexed by the keys. The keys and values are the same as in the Python 205 | // dictionary, but changes to the Go map are not propogated back to the Python 206 | // dictionary. 207 | // 208 | // Note: the map holds borrowed references 209 | func (d *Dict) Map() map[*Base]*Base { 210 | m := make(map[*Base]*Base, d.Size()) 211 | var p C.Py_ssize_t 212 | var k *C.PyObject 213 | var v *C.PyObject 214 | for int(C.PyDict_Next(d.c(), &p, &k, &v)) != 0 { 215 | key := newObject(k) 216 | value := newObject(v) 217 | m[key] = value 218 | } 219 | return m 220 | } 221 | 222 | // MapString is similar to Map, except that the keys are first converted to 223 | // strings. If the keys are not all Python strings, then an error is returned. 224 | // 225 | // Note: the map holds borrowed references 226 | func (d *Dict) MapString() (map[string]*Base, error) { 227 | m := make(map[string]*Base, d.Size()) 228 | var p DictIter 229 | var k, v *Base 230 | for d.Next(&p, &k, &v) { 231 | s, ok := AsString(k) 232 | if !ok { 233 | return nil, TypeError.Err("%v is not a string", k) 234 | } 235 | m[s.String()] = v 236 | } 237 | return m, nil 238 | } 239 | 240 | type DictIter C.Py_ssize_t 241 | 242 | // Iterate over all key-value pairs in the dictionary d. 243 | // The Py_ssize_t referred to by ppos must be initialized to 0 prior to the first call to this function 244 | // to start the iteration; the function returns true for each pair in the dictionary, and false once all 245 | // pairs have been reported. The parameters pkey and pvalue should either point to PyObject* variables 246 | // that will be filled in with each key and value, respectively, or may be NULL. Any references returned 247 | // through them are borrowed. ppos should not be altered during iteration. Its value represents offsets 248 | // within the internal dictionary structure, and since the structure is sparse, the offsets are not consecutive. 249 | func (d *Dict) Next(pos *DictIter, k, v **Base) bool { 250 | k1 := (**C.PyObject)(unsafe.Pointer(k)) 251 | v1 := (**C.PyObject)(unsafe.Pointer(v)) 252 | return C.PyDict_Next(d.c(), (*C.Py_ssize_t)(pos), k1, v1) != 0 253 | } 254 | 255 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline void incref(PyObject *obj) { Py_INCREF(obj); } 5 | // static inline void decref(PyObject *obj) { Py_DECREF(obj); } 6 | // static inline void xdecref(PyObject *obj) { Py_XDECREF(obj); } 7 | import "C" 8 | import "fmt" 9 | import "syscall" 10 | import "strings" 11 | import "runtime" 12 | import "github.com/qiniu/errors" 13 | 14 | // Error represents a Python exception as a Go struct that implements the 15 | // error interface. It allows Go code to handle Python exceptions in an 16 | // idiomatic Go fashion. 17 | type Error struct { 18 | Kind *Base 19 | Value *Base 20 | tb *C.PyObject 21 | } 22 | 23 | func newError(kind, val *Base, tb *C.PyObject) *Error { 24 | e := &Error{kind, val, tb} 25 | runtime.SetFinalizer(e, (*Error).release) 26 | return e 27 | } 28 | 29 | func (e *Error) release() error { 30 | if e.Kind != nil { 31 | e.Kind.Decref() 32 | e.Value.Decref() 33 | e.Kind = nil 34 | e.Value = nil 35 | if e.tb != nil { 36 | C.decref(e.tb) 37 | e.tb = nil 38 | } 39 | } 40 | return nil 41 | } 42 | 43 | // Error() returns a string representation of the Python exception represented 44 | // by the Error e. This is the same as the final line of the Python output from 45 | // an uncaught exception. 46 | func (e *Error) Error() string { 47 | kind := e.Kind.String() 48 | if strings.HasPrefix(kind, " 0, nil 144 | } 145 | 146 | func int2Err(i C.int) error { 147 | if i < 0 { 148 | return exception() 149 | } 150 | return nil 151 | } 152 | 153 | func obj2ObjErr(obj *C.PyObject) (*Base, error) { 154 | if obj == nil { 155 | return nil, exception() 156 | } 157 | return newObject(obj), nil 158 | } 159 | 160 | -------------------------------------------------------------------------------- /examples/gomodule/gomodule.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/qiniu/log" 6 | "github.com/qiniu/py" 7 | ) 8 | 9 | // ------------------------------------------------------------------- 10 | 11 | type FooModule struct { 12 | } 13 | 14 | func (r *FooModule) Py_bar(args *py.Tuple) (ret *py.Base, err error) { 15 | var i int 16 | var s string 17 | err = py.Parse(args, &i, &s) 18 | if err != nil { 19 | return 20 | } 21 | fmt.Println("call foo.bar:", i, s) 22 | return py.IncNone(), nil 23 | } 24 | 25 | func (r *FooModule) Py_bar2(args *py.Tuple) (ret *py.Base, err error) { 26 | var i int 27 | var s []string 28 | err = py.ParseV(args, &i, &s) 29 | if err != nil { 30 | return 31 | } 32 | fmt.Println("call foo.bar2:", i, s) 33 | return py.IncNone(), nil 34 | } 35 | 36 | // ------------------------------------------------------------------- 37 | 38 | const pyCode = ` 39 | 40 | import foo 41 | foo.bar(1, 'Hello') 42 | foo.bar2(1, 'Hello', 'world!') 43 | ` 44 | 45 | func main() { 46 | 47 | gomod, err := py.NewGoModule("foo", "", new(FooModule)) 48 | if err != nil { 49 | log.Fatal("NewGoModule failed:", err) 50 | } 51 | defer gomod.Decref() 52 | 53 | code, err := py.Compile(pyCode, "", py.FileInput) 54 | if err != nil { 55 | log.Fatal("Compile failed:", err) 56 | } 57 | defer code.Decref() 58 | 59 | mod, err := py.ExecCodeModule("test", code.Obj()) 60 | if err != nil { 61 | log.Fatal("ExecCodeModule failed:", err) 62 | } 63 | defer mod.Decref() 64 | } 65 | 66 | // ------------------------------------------------------------------- 67 | 68 | -------------------------------------------------------------------------------- /exc.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | import "C" 5 | 6 | var ( 7 | BaseException = newException(C.PyExc_BaseException) 8 | Exception = newException(C.PyExc_Exception) 9 | StopIteration = newException(C.PyExc_StopIteration) 10 | GeneratorExit = newException(C.PyExc_GeneratorExit) 11 | StandardError = newException(C.PyExc_StandardError) 12 | ArithmeticError = newException(C.PyExc_ArithmeticError) 13 | LookupError = newException(C.PyExc_LookupError) 14 | AssertionError = newException(C.PyExc_AssertionError) 15 | AttributeError = newException(C.PyExc_AttributeError) 16 | EOFError = newException(C.PyExc_EOFError) 17 | FloatingPointError = newException(C.PyExc_FloatingPointError) 18 | EnvironmentError = newException(C.PyExc_EnvironmentError) 19 | IOError = newException(C.PyExc_IOError) 20 | OSError = newException(C.PyExc_OSError) 21 | ImportError = newException(C.PyExc_ImportError) 22 | IndexError = newException(C.PyExc_IndexError) 23 | KeyError = newException(C.PyExc_KeyError) 24 | KeyboardInterrupt = newException(C.PyExc_KeyboardInterrupt) 25 | MemoryError = newException(C.PyExc_MemoryError) 26 | NameError = newException(C.PyExc_NameError) 27 | OverflowError = newException(C.PyExc_OverflowError) 28 | RuntimeError = newException(C.PyExc_RuntimeError) 29 | NotImplementedError = newException(C.PyExc_NotImplementedError) 30 | SyntaxError = newException(C.PyExc_SyntaxError) 31 | IndentationError = newException(C.PyExc_IndentationError) 32 | TabError = newException(C.PyExc_TabError) 33 | ReferenceError = newException(C.PyExc_ReferenceError) 34 | SystemError = newException(C.PyExc_SystemError) 35 | SystemExit = newException(C.PyExc_SystemExit) 36 | TypeError = newException(C.PyExc_TypeError) 37 | UnboundLocalError = newException(C.PyExc_UnboundLocalError) 38 | UnicodeError = newException(C.PyExc_UnicodeError) 39 | UnicodeEncodeError = newException(C.PyExc_UnicodeEncodeError) 40 | UnicodeDecodeError = newException(C.PyExc_UnicodeDecodeError) 41 | UnicodeTranslateError = newException(C.PyExc_UnicodeTranslateError) 42 | ValueError = newException(C.PyExc_ValueError) 43 | ZeroDivisionError = newException(C.PyExc_ZeroDivisionError) 44 | BufferError = newException(C.PyExc_BufferError) 45 | MemoryErrorInst = newException(C.PyExc_MemoryErrorInst) 46 | RecursionErrorInst = newException(C.PyExc_RecursionErrorInst) 47 | Warning = newException(C.PyExc_Warning) 48 | UserWarning = newException(C.PyExc_UserWarning) 49 | DeprecationWarning = newException(C.PyExc_DeprecationWarning) 50 | PendingDeprecationWarning = newException(C.PyExc_PendingDeprecationWarning) 51 | SyntaxWarning = newException(C.PyExc_SyntaxWarning) 52 | RuntimeWarning = newException(C.PyExc_RuntimeWarning) 53 | FutureWarning = newException(C.PyExc_FutureWarning) 54 | ImportWarning = newException(C.PyExc_ImportWarning) 55 | UnicodeWarning = newException(C.PyExc_UnicodeWarning) 56 | BytesWarning = newException(C.PyExc_BytesWarning) 57 | ) 58 | -------------------------------------------------------------------------------- /exception.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | import "C" 5 | import "unsafe" 6 | 7 | type ExceptionClass struct { 8 | Base 9 | o C.PyBaseExceptionObject 10 | } 11 | 12 | func newException(obj *C.PyObject) *ExceptionClass { 13 | return (*ExceptionClass)(unsafe.Pointer(obj)) 14 | } 15 | 16 | // ErrV returns a new Error of the specified kind, and with the given value. 17 | func (kind *ExceptionClass) ErrV(obj *Base) *Error { 18 | return NewErrorV(&kind.Base, obj) 19 | } 20 | 21 | // Err returns a new Error of the specified kind, and with the value being a 22 | // new String containing the string created the given format and args. 23 | func (kind *ExceptionClass) Err(format string, args ...interface{}) *Error { 24 | return NewError(&kind.Base, format, args...) 25 | } 26 | -------------------------------------------------------------------------------- /float.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline double floatCheck(PyObject *o) { return PyFloat_Check(o); } 5 | import "C" 6 | import "unsafe" 7 | 8 | type Float struct { 9 | Base 10 | NumberProtocol 11 | o C.PyFloatObject 12 | } 13 | 14 | // FloatType is the Type object that represents the Float type. 15 | var FloatType = (*Type)(unsafe.Pointer(&C.PyFloat_Type)) 16 | 17 | func newFloat(obj *C.PyObject) *Float { 18 | return (*Float)(unsafe.Pointer(obj)) 19 | } 20 | 21 | func NewFloat(i float64) *Float { 22 | return newFloat(C.PyFloat_FromDouble(C.double(i))) 23 | } 24 | 25 | func AsFloat(o *Base) (v *Float, ok bool) { 26 | if ok = C.floatCheck(o.c()) != 0; ok { 27 | v = newFloat(o.c()) 28 | } 29 | return 30 | } 31 | 32 | func NewFloatFromString(s string) *Float { 33 | cs := NewString(s) 34 | return newFloat(C.PyFloat_FromString((*C.PyObject)(unsafe.Pointer(cs.Obj())), nil)) 35 | } 36 | 37 | func (f *Float) Float() float64 { 38 | return float64(C.PyFloat_AsDouble(f.c())) 39 | } 40 | -------------------------------------------------------------------------------- /goargs.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import ( 4 | "reflect" 5 | "syscall" 6 | "github.com/qiniu/log" 7 | "github.com/qiniu/errors" 8 | ) 9 | 10 | // ------------------------------------------------------------------------------------------ 11 | 12 | func ToInt(in *Base) (int, bool) { 13 | 14 | l, ok := ToLong(in) 15 | return int(l), ok 16 | } 17 | 18 | func ToLong(in *Base) (int64, bool) { 19 | 20 | if v, ok := AsInt(in); ok { 21 | return int64(v.Int()), true 22 | } 23 | if v, ok := AsLong(in); ok { 24 | return int64(v.Long()), true 25 | } 26 | return 0, false 27 | } 28 | 29 | func ToString(in *Base) (string, bool) { 30 | 31 | if v, ok := AsString(in); ok { 32 | return v.String(), true 33 | } 34 | return "", false 35 | } 36 | 37 | func ToInterface(in *Base) (v interface{}, ok bool) { 38 | 39 | if v, ok = ToLong(in); ok { 40 | return 41 | } else if v, ok = ToString(in); ok { 42 | return 43 | } 44 | return 45 | } 46 | 47 | // ------------------------------------------------------------------------------------------ 48 | 49 | func assignToMap(in *Base, out reflect.Value) (err error) { 50 | 51 | dict, ok := AsDict(in) 52 | if !ok { 53 | err = errors.Info(syscall.EINVAL, "py.AssignTo", "uncompatible type") 54 | return 55 | } 56 | 57 | mapTy := out.Type() 58 | m := reflect.MakeMap(mapTy) 59 | 60 | keyTy := mapTy.Key() 61 | valTy := mapTy.Elem() 62 | 63 | log.Debug("assignToMap:", mapTy, keyTy, valTy) 64 | 65 | var iter DictIter 66 | var k, v *Base 67 | for dict.Next(&iter, &k, &v) { 68 | kout := reflect.New(keyTy) 69 | err = AssignTo(k, kout.Interface()) 70 | if err != nil { 71 | err = errors.Info(err, "py.AssignTo", "assign map key").Detail(err) 72 | return 73 | } 74 | vout := reflect.New(valTy) 75 | err = AssignTo(v, vout.Interface()) 76 | if err != nil { 77 | err = errors.Info(err, "py.AssignTo", "assign map val").Detail(err) 78 | return 79 | } 80 | m.SetMapIndex(kout.Elem(), vout.Elem()) 81 | } 82 | 83 | out.Set(m) 84 | return nil 85 | } 86 | 87 | func assignToComplex(in *Base, out1 reflect.Value) (err error) { 88 | 89 | if out1.Kind() != reflect.Ptr { 90 | err = errors.Info(syscall.EINVAL, "py.AssignTo", "not assignable") 91 | return 92 | } 93 | 94 | out := out1.Elem() 95 | switch out.Kind() { 96 | case reflect.Map: 97 | return assignToMap(in, out) 98 | default: 99 | err = errors.Info(syscall.EINVAL, "py.AssignTo", "unsupported input type", reflect.TypeOf(out)) 100 | return 101 | } 102 | return 103 | } 104 | 105 | func AssignTo(in *Base, out interface{}) (err error) { 106 | 107 | var ok bool 108 | switch v := out.(type) { 109 | case *string: 110 | *v, ok = ToString(in) 111 | case *int64: 112 | *v, ok = ToLong(in) 113 | case *int: 114 | *v, ok = ToInt(in) 115 | case *interface{}: 116 | *v, ok = ToInterface(in) 117 | return 118 | default: 119 | return assignToComplex(in, reflect.ValueOf(out)) 120 | } 121 | if !ok { 122 | err = errors.Info(syscall.EINVAL, "py.AssignTo", "can not convert type", reflect.TypeOf(out)) 123 | } 124 | return 125 | } 126 | 127 | // ------------------------------------------------------------------------------------------ 128 | 129 | func Parse(in *Tuple, out ...interface{}) (err error) { 130 | 131 | n := in.Size() 132 | if n != len(out) { 133 | err = errors.Info(syscall.EINVAL, "py.Parse", "invalid argument count") 134 | return 135 | } 136 | 137 | for i := 0; i < n; i++ { 138 | v2, err2 := in.GetItem(i) 139 | if err2 != nil { 140 | err = errors.Info(err2, "py.Parse", "invalid argument", i+1).Detail(err2) 141 | return 142 | } 143 | err2 = AssignTo(v2, out[i]) 144 | if err2 != nil { 145 | err = errors.Info(err2, "py.Parse", "assign argument failed", i+1).Detail(err2) 146 | return 147 | } 148 | } 149 | return 150 | } 151 | 152 | func ParseV(in *Tuple, out ...interface{}) (err error) { 153 | 154 | n1 := in.Size() 155 | n := len(out) - 1 156 | if n1 < n || n < 0 { 157 | err = errors.Info(syscall.EINVAL, "py.ParseV", "argument count is not enough") 158 | return 159 | } 160 | 161 | slicePtr := reflect.TypeOf(out[n]) 162 | if slicePtr.Kind() != reflect.Ptr { 163 | err = errors.Info(syscall.EINVAL, "py.ParseV", "last argument is not a slice pointer") 164 | return 165 | } 166 | 167 | sliceTy := slicePtr.Elem() 168 | if sliceTy.Kind() != reflect.Slice { 169 | err = errors.Info(syscall.EINVAL, "py.ParseV", "last argument is not a slice pointer") 170 | return 171 | } 172 | 173 | for i := 0; i < n; i++ { 174 | v2, err2 := in.GetItem(i) 175 | if err2 != nil { 176 | err = errors.Info(err2, "py.ParseV", "invalid argument", i+1).Detail(err2) 177 | return 178 | } 179 | err2 = AssignTo(v2, out[i]) 180 | if err2 != nil { 181 | err = errors.Info(err2, "py.ParseV", "assign argument failed", i+1).Detail(err2) 182 | return 183 | } 184 | } 185 | 186 | slice := reflect.MakeSlice(sliceTy, n1-n, n1-n) 187 | for i := n; i < n1; i++ { 188 | v2, err2 := in.GetItem(i) 189 | if err2 != nil { 190 | err = errors.Info(err2, "py.ParseV", "invalid argument", i+1).Detail(err2) 191 | return 192 | } 193 | err2 = AssignTo(v2, slice.Index(i-n).Addr().Interface()) 194 | if err2 != nil { 195 | err = errors.Info(err2, "py.ParseV", "assign argument failed", i+1).Detail(err2) 196 | return 197 | } 198 | } 199 | reflect.ValueOf(out[n]).Elem().Set(slice) 200 | return 201 | } 202 | 203 | // ------------------------------------------------------------------------------------------ 204 | 205 | -------------------------------------------------------------------------------- /gofunction.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "_cgo_export.h" 3 | 4 | int setMethod(PyMethodDef* d, int nin) { 5 | switch (nin) { 6 | case 3: 7 | d->ml_meth = (PyCFunction)goClassCallMethodKwds; 8 | d->ml_flags = METH_VARARGS | METH_KEYWORDS; 9 | break; 10 | case 2: 11 | d->ml_meth = (PyCFunction)goClassCallMethodArgs; 12 | d->ml_flags = METH_VARARGS; 13 | break; 14 | default: 15 | return -1; 16 | } 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /gofunction.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include 5 | #include "gofunction.h" 6 | 7 | static inline void decref(PyObject *obj) { Py_DECREF(obj); } 8 | */ 9 | import "C" 10 | import "unsafe" 11 | import "reflect" 12 | 13 | type Closure struct { // closure = self.method 14 | Self reflect.Value 15 | Method reflect.Value 16 | methodDef C.PyMethodDef 17 | } 18 | 19 | func (closure *Closure) NewFunction(name string, nin int, doc string) *Base { 20 | 21 | d := &closure.methodDef 22 | d.ml_name = C.CString(name) 23 | defer C.free(unsafe.Pointer(d.ml_name)) 24 | 25 | if C.setMethod(d, C.int(nin)) != 0 { 26 | panic("Invalid arguments: nin") 27 | } 28 | if doc != "" { 29 | d.ml_doc = C.CString(doc) 30 | defer C.free(unsafe.Pointer(d.ml_doc)) 31 | } 32 | 33 | ctx := uintptr(unsafe.Pointer(closure)) 34 | self := C.PyLong_FromLongLong(C.longlong(ctx)) 35 | defer C.decref(self) 36 | 37 | f := C.PyCFunction_NewEx(d, self, nil) 38 | return (*Base)(unsafe.Pointer(f)) 39 | } 40 | 41 | //export goClassCallMethodArgs 42 | func goClassCallMethodArgs(obj, args unsafe.Pointer) unsafe.Pointer { 43 | 44 | // Unpack context and self pointer from obj 45 | t := (*C.PyObject)(obj) 46 | closure := (*Closure)(unsafe.Pointer(uintptr(C.PyLong_AsLongLong(t)))) 47 | 48 | // Get args ready to use, by turning it into a pointer of the appropriate 49 | // type 50 | a := (*Tuple)(args) 51 | 52 | in := []reflect.Value{closure.Self, reflect.ValueOf(a)} 53 | out := closure.Method.Call(in) 54 | 55 | err := out[1].Interface() 56 | if err != nil { 57 | Raise(err.(error)) 58 | return nil 59 | } 60 | 61 | ret := out[0].Interface().(*Base) 62 | return unsafe.Pointer(ret) 63 | } 64 | 65 | //export goClassCallMethodKwds 66 | func goClassCallMethodKwds(obj, args, kwds unsafe.Pointer) unsafe.Pointer { 67 | 68 | // Unpack context and self pointer from obj 69 | t := (*C.PyObject)(obj) 70 | closure := (*Closure)(unsafe.Pointer(uintptr(C.PyLong_AsLongLong(t)))) 71 | 72 | // Get args and kwds ready to use, by turning them into pointers of the 73 | // appropriate type 74 | a := (*Tuple)(args) 75 | k := (*Dict)(kwds) 76 | 77 | in := []reflect.Value{closure.Self, reflect.ValueOf(a), reflect.ValueOf(k)} 78 | out := closure.Method.Call(in) 79 | 80 | err := out[1].Interface() 81 | if err != nil { 82 | Raise(err.(error)) 83 | return nil 84 | } 85 | 86 | ret := out[0].Interface().(*Base) 87 | return unsafe.Pointer(ret) 88 | } 89 | 90 | -------------------------------------------------------------------------------- /gofunction.h: -------------------------------------------------------------------------------- 1 | #ifndef QBOX_GOPY_GOFUNCTION_H 2 | #define QBOX_GOPY_GOFUNCTION_H 3 | 4 | int setMethod(PyMethodDef* d, int nin); 5 | 6 | #endif /* _GO_PYTHON_UTILS_H */ 7 | 8 | -------------------------------------------------------------------------------- /gomodule.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline void decref(PyObject *obj) { Py_DECREF(obj); } 5 | import "C" 6 | import "unsafe" 7 | 8 | // ------------------------------------------------------------------------------------------ 9 | 10 | type GoModule struct { 11 | *Module 12 | Ctx RegisterCtx 13 | } 14 | 15 | func NewGoModule(name string, doc string, self interface{}) (mod GoModule, err error) { 16 | 17 | cName := C.CString(name) 18 | defer C.free(unsafe.Pointer(cName)) 19 | 20 | var mdoc *C.char 21 | if doc != "" { 22 | mdoc = C.CString(doc) 23 | defer C.free(unsafe.Pointer(mdoc)) 24 | } 25 | 26 | m := C.Py_InitModule4(cName, nil, mdoc, nil, C.PYTHON_API_VERSION) 27 | if m == nil { 28 | err = exception() 29 | return 30 | } 31 | 32 | mod.Module = (*Module)(unsafe.Pointer(m)) 33 | mod.Ctx = Register(mod.Module.Dict(), name + ".", self) 34 | return 35 | } 36 | 37 | // ------------------------------------------------------------------------------------------ 38 | 39 | -------------------------------------------------------------------------------- /gomodule_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | // ------------------------------------------------------------------------------------------ 9 | 10 | type FooModule struct { 11 | } 12 | 13 | func (r *FooModule) Py_bar(args *Tuple) (ret *Base, err error) { 14 | var i int 15 | var s []string 16 | err = ParseV(args, &i, &s) 17 | if err != nil { 18 | return 19 | } 20 | fmt.Println("call foo.bar:", i, s) 21 | return IncNone(), nil 22 | } 23 | 24 | // ------------------------------------------------------------------------------------------ 25 | 26 | type gomoduleCase struct { 27 | exp string 28 | name string 29 | } 30 | 31 | var g_gomoduleCases = []gomoduleCase{ 32 | { 33 | `import foo 34 | foo.bar(1, 'Hello') 35 | `, "test"}, 36 | } 37 | 38 | func TestGoModule(t *testing.T) { 39 | 40 | gomod, err := NewGoModule("foo", "", new(FooModule)) 41 | if err != nil { 42 | t.Fatal("NewGoModule failed:", err) 43 | } 44 | defer gomod.Decref() 45 | 46 | for _, c := range g_gomoduleCases { 47 | 48 | code, err := Compile(c.exp, "", FileInput) 49 | if err != nil { 50 | t.Fatal("Compile failed:", err) 51 | } 52 | defer code.Decref() 53 | 54 | mod, err := ExecCodeModule(c.name, code.Obj()) 55 | if err != nil { 56 | t.Fatal("ExecCodeModule failed:", err) 57 | } 58 | defer mod.Decref() 59 | } 60 | } 61 | 62 | // ------------------------------------------------------------------------------------------ 63 | 64 | -------------------------------------------------------------------------------- /goregister.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import "reflect" 4 | import "strings" 5 | import "github.com/qiniu/log" 6 | 7 | // ------------------------------------------------------------------------------------------ 8 | 9 | // Note: Methods take the receiver as the first argument, which the want 10 | // signature doesn't include. 11 | func sigMatches(got, want reflect.Type) bool { 12 | 13 | nin := want.NumIn() 14 | if got.NumIn()-1 != nin { 15 | return false 16 | } 17 | 18 | nout := want.NumOut() 19 | if got.NumOut() != nout { 20 | return false 21 | } 22 | 23 | for i := 0; i < nin; i++ { 24 | if got.In(i+1) != want.In(i) { 25 | return false 26 | } 27 | } 28 | 29 | for i := 0; i < nout; i++ { 30 | if got.Out(i) != want.Out(i) { 31 | return false 32 | } 33 | } 34 | return true 35 | } 36 | 37 | // ------------------------------------------------------------------------------------------ 38 | 39 | var typUnaryFunc = reflect.TypeOf(func() (*Base, error)(nil)) 40 | var typBinaryCallFunc = reflect.TypeOf(func(*Tuple) (*Base, error)(nil)) 41 | var typTernaryCallFunc = reflect.TypeOf(func(*Tuple, *Dict) (*Base, error)(nil)) 42 | 43 | type RegisterCtx []*Closure // 只是让对象不被gc 44 | 45 | func Register(dict *Dict, nsprefix string, self interface{}) (ctx RegisterCtx) { 46 | 47 | typ := reflect.TypeOf(self) 48 | selfv := reflect.ValueOf(self) 49 | 50 | nmethod := typ.NumMethod() 51 | 52 | for i := 0; i < nmethod; i++ { 53 | method := typ.Method(i) 54 | mtype := method.Type 55 | mname := method.Name 56 | if mtype.PkgPath() != "" || !strings.HasPrefix(mname, "Py_") { 57 | continue 58 | } 59 | nin := mtype.NumIn() 60 | name := mname[3:] 61 | fullname := nsprefix + name 62 | if nin == 3 && sigMatches(mtype, typTernaryCallFunc) || nin == 2 && sigMatches(mtype, typBinaryCallFunc) { 63 | closure := &Closure{Self: selfv, Method: method.Func} 64 | f := closure.NewFunction(fullname, nin, "") 65 | dict.SetItemString(name, f) 66 | f.Decref() 67 | ctx = append(ctx, closure) 68 | log.Debug("Register", fullname) 69 | } else { 70 | log.Warnf("Invalid signature of method %s, register failed", fullname) 71 | continue 72 | } 73 | } 74 | return 75 | } 76 | 77 | // ------------------------------------------------------------------------------------------ 78 | 79 | -------------------------------------------------------------------------------- /goregister_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import ( 4 | "testing" 5 | "github.com/qiniu/log" 6 | ) 7 | 8 | func init() { 9 | log.SetOutputLevel(0) 10 | } 11 | 12 | // ------------------------------------------------------------------------------------------ 13 | 14 | type Foo struct { 15 | } 16 | 17 | func (r *Foo) Py_foo(args *Tuple) (*Base, error) { 18 | return IncNone(), nil 19 | } 20 | 21 | func (r *Foo) Py_bar(args *Tuple) (*Base) { 22 | return IncNone() 23 | } 24 | 25 | // ------------------------------------------------------------------------------------------ 26 | 27 | func _TestRegister(t *testing.T) { 28 | 29 | dict := NewDict() 30 | defer dict.Decref() 31 | 32 | Register(dict, "", new(Foo)) 33 | } 34 | 35 | // ------------------------------------------------------------------------------------------ 36 | 37 | -------------------------------------------------------------------------------- /int.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Julian Phillips. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | // #include 8 | // static inline int intCheck(PyObject *o) { return PyInt_Check(o); } 9 | import "C" 10 | import "unsafe" 11 | 12 | type Int struct { 13 | Base 14 | NumberProtocol 15 | o C.PyIntObject 16 | } 17 | 18 | // IntType is the Type object that represents the Int type. 19 | var IntType = (*Type)(unsafe.Pointer(&C.PyInt_Type)) 20 | 21 | func newInt(obj *C.PyObject) *Int { 22 | return (*Int)(unsafe.Pointer(obj)) 23 | } 24 | 25 | func NewInt(i int) *Int { 26 | return newInt(C.PyInt_FromLong(C.long(i))) 27 | } 28 | 29 | func NewInt64(i int64) *Int { 30 | return newInt(C.PyInt_FromSsize_t(C.Py_ssize_t(i))) 31 | } 32 | 33 | func AsInt(o *Base) (v *Int, ok bool) { 34 | if ok = C.intCheck(o.c()) != 0; ok { 35 | v = newInt(o.c()) 36 | } 37 | return 38 | } 39 | 40 | func (i *Int) Int() int { 41 | return int(C.PyInt_AsLong(i.c())) 42 | } 43 | 44 | -------------------------------------------------------------------------------- /long.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline long longCheck(PyObject *o) { return PyLong_Check(o); } 5 | import "C" 6 | import "unsafe" 7 | 8 | type Long struct { 9 | Base 10 | NumberProtocol 11 | o C.PyLongObject 12 | } 13 | 14 | // LongType is the Type object that represents the Long type. 15 | var LongType = (*Type)(unsafe.Pointer(&C.PyLong_Type)) 16 | 17 | func newLong(obj *C.PyObject) *Long { 18 | return (*Long)(unsafe.Pointer(obj)) 19 | } 20 | 21 | func NewLong(i int64) *Long { 22 | return newLong(C.PyLong_FromLongLong(C.longlong(i))) 23 | } 24 | 25 | func AsLong(o *Base) (v *Long, ok bool) { 26 | if ok = C.longCheck(o.c()) != 0; ok { 27 | v = newLong(o.c()) 28 | } 29 | return 30 | } 31 | 32 | func (l *Long) Long() int64 { 33 | return int64(C.PyLong_AsLongLong(l.c())) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /module.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline int moduleCheck(PyObject *o) { return PyModule_Check(o); } 5 | // static inline int moduleCheckE(PyObject *o) { return PyModule_CheckExact(o); } 6 | // static inline void decref(PyObject *obj) { Py_DECREF(obj); } 7 | import "C" 8 | import "unsafe" 9 | 10 | type Module struct { 11 | Base 12 | o C.PyObject 13 | } 14 | 15 | // ModuleType is the Type object that represents the Module type. 16 | var ModuleType = (*Type)(unsafe.Pointer(&C.PyModule_Type)) 17 | 18 | func newModule(obj *C.PyObject) *Module { 19 | return (*Module)(unsafe.Pointer(obj)) 20 | } 21 | 22 | func Import(name string) (*Module, error) { 23 | s := C.CString(name) 24 | defer C.free(unsafe.Pointer(s)) 25 | 26 | pyName := C.PyString_FromString(s) 27 | defer C.decref(pyName) 28 | 29 | obj := C.PyImport_Import(pyName) 30 | if obj == nil { 31 | return nil, exception() 32 | } 33 | 34 | return newModule(obj), nil 35 | } 36 | 37 | func ExecCodeModule(name string, code *Base) (*Module, error) { 38 | s := C.CString(name) 39 | defer C.free(unsafe.Pointer(s)) 40 | ret := C.PyImport_ExecCodeModule(s, code.c()) 41 | if ret == nil { 42 | return nil, exception() 43 | } 44 | return newModule(ret), nil 45 | } 46 | 47 | func NewModule(name string) (*Module, error) { 48 | cname := C.CString(name) 49 | defer C.free(unsafe.Pointer(cname)) 50 | 51 | ret := C.PyModule_New(cname) 52 | if ret == nil { 53 | return nil, exception() 54 | } 55 | 56 | return newModule(ret), nil 57 | } 58 | 59 | func AsModule(o *Base) (v *Module, ok bool) { 60 | if ok = C.moduleCheck(o.c()) != 0; ok { 61 | v = newModule(o.c()) 62 | } 63 | return 64 | } 65 | 66 | func (mod *Module) CheckExact() bool { 67 | return C.moduleCheckE(mod.c()) != 0 68 | } 69 | 70 | // Return value: Borrowed reference. 71 | func (mod *Module) Dict() *Dict { 72 | ret := C.PyModule_GetDict(mod.c()) 73 | return newDict(ret) 74 | } 75 | 76 | // Return module‘s __name__ value. If the module does not provide one, or if it is not a string, 77 | // SystemError is raised and NULL is returned. 78 | func (mod *Module) Name() (string, error) { 79 | ret := C.PyModule_GetName(mod.c()) 80 | if ret == nil { 81 | return "", exception() 82 | } 83 | return C.GoString(ret), nil 84 | } 85 | 86 | func (mod *Module) Filename() (string, error) { 87 | ret := C.PyModule_GetFilename(mod.c()) 88 | if ret == nil { 89 | return "", exception() 90 | } 91 | return C.GoString(ret), nil 92 | } 93 | 94 | func (mod *Module) AddObject(name string, obj *Base) error { 95 | if obj == nil { 96 | return AssertionError.Err("ValueError: obj == nil!") 97 | } 98 | 99 | cname := C.CString(name) 100 | defer C.free(unsafe.Pointer(cname)) 101 | 102 | ret := C.PyModule_AddObject(mod.c(), cname, obj.c()) 103 | if ret < 0 { 104 | return exception() 105 | } 106 | 107 | return nil 108 | } 109 | 110 | func (mod *Module) AddIntConstant(name string, value int) error { 111 | cname := C.CString(name) 112 | defer C.free(unsafe.Pointer(cname)) 113 | 114 | ret := C.PyModule_AddIntConstant(mod.c(), cname, C.long(value)) 115 | if ret < 0 { 116 | return exception() 117 | } 118 | 119 | return nil 120 | } 121 | 122 | func (mod *Module) AddStringConstant(name, value string) error { 123 | cname := C.CString(name) 124 | defer C.free(unsafe.Pointer(cname)) 125 | 126 | cvalue := C.CString(value) 127 | defer C.free(unsafe.Pointer(cvalue)) 128 | 129 | ret := C.PyModule_AddStringConstant(mod.c(), cname, cvalue) 130 | if ret < 0 { 131 | return exception() 132 | } 133 | 134 | return nil 135 | } 136 | -------------------------------------------------------------------------------- /module_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type moduleCase struct { 8 | exp string 9 | name, ret, globals string 10 | } 11 | 12 | var g_moduleCases = []moduleCase{ 13 | {` 14 | tbl = 'dn_5m' 15 | def init(cat): 16 | global tbl 17 | tbl = tbl + cat 18 | return True 19 | `, "foo", "True", "dn_5m_stage"}, 20 | } 21 | 22 | func TestModule(t *testing.T) { 23 | 24 | for _, c := range g_moduleCases { 25 | code, err := Compile(c.exp, "", FileInput) 26 | if err != nil { 27 | t.Fatal("Compile failed:", err) 28 | } 29 | defer code.Decref() 30 | 31 | mod, err := ExecCodeModule(c.name, code.Obj()) 32 | if err != nil { 33 | t.Fatal("ExecCodeModule failed:", err) 34 | } 35 | defer mod.Decref() 36 | 37 | arg1 := NewString("_stage") 38 | defer arg1.Decref() 39 | 40 | ret, err := mod.CallMethodObjArgs("init", arg1.Obj()) 41 | if err != nil { 42 | t.Fatal("CallMethodObjArgs failed:", err) 43 | } 44 | defer ret.Decref() 45 | 46 | if ret.String() != c.ret { 47 | t.Fatal("CallMethodObjArgs ret:", ret.String(), c.ret) 48 | } 49 | 50 | globals, _ := mod.GetAttrString("tbl") 51 | defer globals.Decref() 52 | 53 | if globals.String() != c.globals { 54 | t.Fatal("mod.GetAttrString('tbl') ret:", globals.String(), c.globals) 55 | } 56 | 57 | dict := mod.Dict() // don't need Decref 58 | tbl2 := dict.GetItemString("tbl") // don't need Decref 59 | if tbl2.String() != c.globals { 60 | t.Fatal("mod.Dict.GetItemString('tbl') ret:", tbl2.String(), c.globals) 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /none.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #include 5 | 6 | static inline PyObject* incNone() { Py_RETURN_NONE; } 7 | */ 8 | import "C" 9 | 10 | // ------------------------------------------------------------------------------------------ 11 | 12 | func IncNone() *Base { 13 | return newObject(C.incNone()) 14 | } 15 | 16 | // ------------------------------------------------------------------------------------------ 17 | 18 | -------------------------------------------------------------------------------- /number.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Julian Phillips. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | // #include 8 | import "C" 9 | import "unsafe" 10 | 11 | // NumberProtocol is a 0-sized type that can be embedded in concrete types after 12 | // the AbstractObject to provide access to the suite of methods that Python 13 | // calls the "Number Protocol". 14 | type NumberProtocol struct{} 15 | 16 | func cnp(n *NumberProtocol) *C.PyObject { 17 | return (*C.PyObject)(unsafe.Pointer(n)) 18 | } 19 | 20 | // Add returns the result of adding n and obj. The equivalent Python is "n + 21 | // obj". 22 | // 23 | // Return value: New Reference. 24 | func (n *NumberProtocol) Add(obj *Base) (*Base, error) { 25 | ret := C.PyNumber_Add(cnp(n), obj.c()) 26 | return obj2ObjErr(ret) 27 | } 28 | 29 | // Subtract returns the result of subtracting obj from n. The equivalent Python 30 | // is "n - obj". 31 | // 32 | // Return value: New Reference. 33 | func (n *NumberProtocol) Subtract(obj *Base) (*Base, error) { 34 | ret := C.PyNumber_Subtract(cnp(n), obj.c()) 35 | return obj2ObjErr(ret) 36 | } 37 | 38 | // Multiply returns the result of multiplying n by obj. The equivalent Python 39 | // is "n * obj". 40 | // 41 | // Return value: New Reference. 42 | func (n *NumberProtocol) Multiply(obj *Base) (*Base, error) { 43 | ret := C.PyNumber_Multiply(cnp(n), obj.c()) 44 | return obj2ObjErr(ret) 45 | } 46 | 47 | // Divide returns the result of dividing n by obj. The equivalent Python is "n 48 | // / obj". 49 | // 50 | // Return value: New Reference. 51 | func (n *NumberProtocol) Divide(obj *Base) (*Base, error) { 52 | ret := C.PyNumber_Divide(cnp(n), obj.c()) 53 | return obj2ObjErr(ret) 54 | } 55 | 56 | // FloorDivide returns the floor of dividing n obj obj. 57 | // 58 | // Return value: New Reference. 59 | func (n *NumberProtocol) FloorDivide(obj *Base) (*Base, error) { 60 | ret := C.PyNumber_FloorDivide(cnp(n), obj.c()) 61 | return obj2ObjErr(ret) 62 | } 63 | 64 | // TrueDivide returns the ... TODO 65 | // 66 | // Return value: New Reference. 67 | func (n *NumberProtocol) TrueDivide(obj *Base) (*Base, error) { 68 | ret := C.PyNumber_TrueDivide(cnp(n), obj.c()) 69 | return obj2ObjErr(ret) 70 | } 71 | 72 | // Remainder returns the remainder of dividing n by obj. The equivalent Python 73 | // is "n % obj". 74 | // 75 | // Return value: New Reference. 76 | func (n *NumberProtocol) Remainder(obj *Base) (*Base, error) { 77 | ret := C.PyNumber_Remainder(cnp(n), obj.c()) 78 | return obj2ObjErr(ret) 79 | } 80 | 81 | // Divmod returns the result of the Python "divmod(n, obj)". 82 | // 83 | // Return value: New Reference. 84 | func (n *NumberProtocol) Divmod(obj *Base) (*Base, error) { 85 | ret := C.PyNumber_Divmod(cnp(n), obj.c()) 86 | return obj2ObjErr(ret) 87 | } 88 | 89 | // Power returns the result of the Python "pow(n, obj1, obj2)". 90 | // 91 | // Return value: New Reference. 92 | func (n *NumberProtocol) Power(obj1, obj2 *Base) (*Base, error) { 93 | ret := C.PyNumber_Power(cnp(n), obj1.c(), obj2.c()) 94 | return obj2ObjErr(ret) 95 | } 96 | 97 | // Negative returns the negation of n. The equivalent Python is "-n". 98 | // 99 | // Return value: New Reference. 100 | func (n *NumberProtocol) Negative() (*Base, error) { 101 | ret := C.PyNumber_Negative(cnp(n)) 102 | return obj2ObjErr(ret) 103 | } 104 | 105 | // Positive returns the positive of n. The equivalent Python is "+n". 106 | // 107 | // Return value: New Reference. 108 | func (n *NumberProtocol) Positive() (*Base, error) { 109 | ret := C.PyNumber_Positive(cnp(n)) 110 | return obj2ObjErr(ret) 111 | } 112 | 113 | // Absolute returns the absolute value of n. The equivalent Python is "abs(n)". 114 | // 115 | // Return value: New Reference. 116 | func (n *NumberProtocol) Absolute() (*Base, error) { 117 | ret := C.PyNumber_Absolute(cnp(n)) 118 | return obj2ObjErr(ret) 119 | } 120 | 121 | // Invert returns the bitwise negation of n. The equivalent Python is "-n". 122 | // 123 | // Return value: New Reference. 124 | func (n *NumberProtocol) Invert() (*Base, error) { 125 | ret := C.PyNumber_Invert(cnp(n)) 126 | return obj2ObjErr(ret) 127 | } 128 | 129 | // Lshift returns the result of left shifting n by obj. The equivalent Python 130 | // is "n << obj". 131 | // 132 | // Return value: New Reference. 133 | func (n *NumberProtocol) Lshift(obj *Base) (*Base, error) { 134 | ret := C.PyNumber_Lshift(cnp(n), obj.c()) 135 | return obj2ObjErr(ret) 136 | } 137 | 138 | // Rshift returns the result of right shifting n by obj. The equivalent Python 139 | // is "n << obj". 140 | // 141 | // Return value: New Reference. 142 | func (n *NumberProtocol) Rshift(obj *Base) (*Base, error) { 143 | ret := C.PyNumber_Rshift(cnp(n), obj.c()) 144 | return obj2ObjErr(ret) 145 | } 146 | 147 | // And returns the bitwise and of n and obj. The equivalent Python is "n & 148 | // obj". 149 | // 150 | // Return value: New Reference. 151 | func (n *NumberProtocol) And(obj *Base) (*Base, error) { 152 | ret := C.PyNumber_And(cnp(n), obj.c()) 153 | return obj2ObjErr(ret) 154 | } 155 | 156 | // Xor returns the bitwise xor of n and obj. The equivalent Python is "n ^ 157 | // obj". 158 | // 159 | // Return value: New Reference. 160 | func (n *NumberProtocol) Xor(obj *Base) (*Base, error) { 161 | ret := C.PyNumber_Xor(cnp(n), obj.c()) 162 | return obj2ObjErr(ret) 163 | } 164 | 165 | // Or returns the bitwise or of n and obj. The equivalent Python is "n | obj". 166 | // 167 | // Return value: New Reference. 168 | func (n *NumberProtocol) Or(obj *Base) (*Base, error) { 169 | ret := C.PyNumber_Or(cnp(n), obj.c()) 170 | return obj2ObjErr(ret) 171 | } 172 | 173 | // InPlaceAdd returns the result of adding n and obj. This is done in place if 174 | // supported by n. The equivalent Python is "n += obj". 175 | // 176 | // Return value: New Reference. 177 | func (n *NumberProtocol) InPlaceAdd(obj *Base) (*Base, error) { 178 | ret := C.PyNumber_InPlaceAdd(cnp(n), obj.c()) 179 | return obj2ObjErr(ret) 180 | } 181 | 182 | // InPlaceSubtract returns the result of subtracting obj from n. This is done 183 | // in place if supported by n. The equivalent Python is "n -= obj". 184 | // 185 | // Return value: New Reference. 186 | func (n *NumberProtocol) InPlaceSubtract(obj *Base) (*Base, error) { 187 | ret := C.PyNumber_InPlaceSubtract(cnp(n), obj.c()) 188 | return obj2ObjErr(ret) 189 | } 190 | 191 | // InPlaceMultiply returns the result of multiplying n by obj. This is done in 192 | // place if supported by n. The equivalent Python is "n *= obj". 193 | // 194 | // Return value: New Reference. 195 | func (n *NumberProtocol) InPlaceMultiply(obj *Base) (*Base, error) { 196 | ret := C.PyNumber_InPlaceMultiply(cnp(n), obj.c()) 197 | return obj2ObjErr(ret) 198 | } 199 | 200 | // InPlaceDivide returns the result of dividing n by obj. This is done in place 201 | // if supported by n. The equivalent Python is "n /= obj". 202 | // 203 | // Return value: New Reference. 204 | func (n *NumberProtocol) InPlaceDivide(obj *Base) (*Base, error) { 205 | ret := C.PyNumber_InPlaceDivide(cnp(n), obj.c()) 206 | return obj2ObjErr(ret) 207 | } 208 | 209 | // TODO returns the ... 210 | // 211 | // Return value: New Reference. 212 | func (n *NumberProtocol) InPlaceFloorDivide(obj *Base) (*Base, error) { 213 | ret := C.PyNumber_InPlaceFloorDivide(cnp(n), obj.c()) 214 | return obj2ObjErr(ret) 215 | } 216 | 217 | // TODO returns the ... 218 | // 219 | // Return value: New Reference. 220 | func (n *NumberProtocol) InPlaceTrueDivide(obj *Base) (*Base, error) { 221 | ret := C.PyNumber_InPlaceTrueDivide(cnp(n), obj.c()) 222 | return obj2ObjErr(ret) 223 | } 224 | 225 | // InPlaceRemainder returns the remainder of n divided by obj. This is done in 226 | // place if supported by n. The equivalent Python is "n %= obj". 227 | // 228 | // Return value: New Reference. 229 | func (n *NumberProtocol) InPlaceRemainder(obj *Base) (*Base, error) { 230 | ret := C.PyNumber_InPlaceRemainder(cnp(n), obj.c()) 231 | return obj2ObjErr(ret) 232 | } 233 | 234 | // InPlacePower returns the result of the Python "pow(n, obj1, obj2)". This is 235 | // done in place if supported by n. If obj2 is None, then the Python "n **= 236 | // obj" is also equivalent, if obj2 is not None, there is no equivalent in 237 | // Python. 238 | // 239 | // Return value: New Reference. 240 | func (n *NumberProtocol) InPlacePower(obj1, obj2 *Base) (*Base, error) { 241 | ret := C.PyNumber_InPlacePower(cnp(n), obj1.c(), obj2.c()) 242 | return obj2ObjErr(ret) 243 | } 244 | 245 | // InPlaceLshift returns the result of left shifting n by obj. This is done in 246 | // place if supported by n. The equivalent Python is "n <<= obj". 247 | // 248 | // Return value: New Reference. 249 | func (n *NumberProtocol) InPlaceLshift(obj *Base) (*Base, error) { 250 | ret := C.PyNumber_InPlaceLshift(cnp(n), obj.c()) 251 | return obj2ObjErr(ret) 252 | } 253 | 254 | // InPlaceRshift returns the result of right shifting n by obj. This is done in 255 | // place if supported by n. The equivalent Python is "n >>= obj". 256 | // 257 | // Return value: New Reference. 258 | func (n *NumberProtocol) InPlaceRshift(obj *Base) (*Base, error) { 259 | ret := C.PyNumber_InPlaceRshift(cnp(n), obj.c()) 260 | return obj2ObjErr(ret) 261 | } 262 | 263 | // InPlaceAnd returns the bitwise and of n and obj. This is done in place if 264 | // supported by n. The equivalent Python is "n &= obj". 265 | // 266 | // Return value: New Reference. 267 | func (n *NumberProtocol) InPlaceAnd(obj *Base) (*Base, error) { 268 | ret := C.PyNumber_InPlaceAnd(cnp(n), obj.c()) 269 | return obj2ObjErr(ret) 270 | } 271 | 272 | // InPlaceXor returns the bitwise xor of n and obj. This is done in place if 273 | // supported by n. The equivalent Python is "n ^= obj". 274 | // 275 | // Return value: New Reference. 276 | func (n *NumberProtocol) InPlaceXor(obj *Base) (*Base, error) { 277 | ret := C.PyNumber_InPlaceXor(cnp(n), obj.c()) 278 | return obj2ObjErr(ret) 279 | } 280 | 281 | // InPlaceOr returns the bitwise or of n and obj. This is done in place if 282 | // supported by n. The equivalent Python is "n |= obj". 283 | // 284 | // Return value: New Reference. 285 | func (n *NumberProtocol) InPlaceOr(obj *Base) (*Base, error) { 286 | ret := C.PyNumber_InPlaceOr(cnp(n), obj.c()) 287 | return obj2ObjErr(ret) 288 | } 289 | 290 | // PyNumber_Coerce: TODO 291 | 292 | // PyNumber_CoerceEx: TODO 293 | 294 | // PyNumber_Int: TODO 295 | 296 | // PyNumber_Long: TODO 297 | 298 | // PyNumber_Float: TODO 299 | 300 | // PyNumber_Index: TODO 301 | 302 | // PyNumber_ToBase: TODO 303 | 304 | // PyNumber_AsSsize_t: TODO 305 | -------------------------------------------------------------------------------- /object.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline void incref(PyObject *obj) { Py_INCREF(obj); } 5 | // static inline void decref(PyObject *obj) { Py_DECREF(obj); } 6 | import "C" 7 | import "unsafe" 8 | 9 | type Op int 10 | 11 | const ( 12 | LT = Op(C.Py_LT) 13 | LE = Op(C.Py_LE) 14 | EQ = Op(C.Py_EQ) 15 | NE = Op(C.Py_NE) 16 | GT = Op(C.Py_GT) 17 | GE = Op(C.Py_GE) 18 | ) 19 | 20 | // Base is an 0-sized type that can be embedded as the first item in 21 | // concrete types to provide the Object interface functions. 22 | type Base struct {} 23 | 24 | func newObject(obj *C.PyObject) *Base { 25 | return (*Base)(unsafe.Pointer(obj)) 26 | } 27 | 28 | func (obj *Base) c() *C.PyObject { 29 | return (*C.PyObject)(unsafe.Pointer(obj)) 30 | } 31 | 32 | func (obj *Base) Obj() *Base { 33 | return obj 34 | } 35 | 36 | // Init initialises obj. It is equivalent to "obj.__init__(*args, **kw)" in 37 | // Python. 38 | func (obj *Base) Init(args *Tuple, kw *Dict) error { 39 | return obj.Type().Init(obj, args, kw) 40 | } 41 | 42 | // Type returns a pointer to the Type that represents the type of this object in 43 | // Python. 44 | func (obj *Base) Type() *Type { 45 | o := obj.c().ob_type 46 | return newType((*C.PyObject)(unsafe.Pointer(o))) 47 | } 48 | 49 | // Decref decrements obj's reference count, obj may not be nil. 50 | func (obj *Base) Decref() { 51 | C.decref(obj.c()) 52 | } 53 | 54 | // Incref increments obj's reference count, obj may not be nil. 55 | func (obj *Base) Incref() { 56 | C.incref(obj.c()) 57 | } 58 | 59 | // IsTrue returns true if the value of obj is considered to be True. This is 60 | // equivalent to "if obj:" in Python. 61 | func (obj *Base) IsTrue() bool { 62 | ret := C.PyObject_IsTrue(obj.c()) 63 | if ret < 0 { 64 | panic(exception()) 65 | } 66 | return ret != 0 67 | } 68 | 69 | // Not returns true if the value of obj is considered to be False. This is 70 | // equivalent to "if not obj:" in Python. 71 | func (obj *Base) Not() bool { 72 | ret := C.PyObject_Not(obj.c()) 73 | if ret < 0 { 74 | panic(exception()) 75 | } 76 | return ret != 0 77 | } 78 | 79 | // HasAttr returns true if "obj" has the attribute "name". This is equivalent 80 | // to the Python "hasattr(obj, name)". 81 | func (obj *Base) HasAttr(name *Base) bool { 82 | ret := C.PyObject_HasAttr(obj.c(), name.c()) 83 | if ret == 1 { 84 | return true 85 | } 86 | return false 87 | } 88 | 89 | // HasAttrString returns true if "obj" has the attribute "name". This is 90 | // equivalent to the Python "hasattr(obj, name)". 91 | func (obj *Base) HasAttrString(name string) bool { 92 | s := C.CString(name) 93 | defer C.free(unsafe.Pointer(s)) 94 | ret := C.PyObject_HasAttrString(obj.c(), s) 95 | if ret == 1 { 96 | return true 97 | } 98 | return false 99 | } 100 | 101 | // GetAttr returns the attribute of "obj" with the name "name". This is 102 | // equivalent to the Python "obj.name". 103 | // 104 | // Return value: New Reference. 105 | func (obj *Base) GetAttr(name *Base) (*Base, error) { 106 | ret := C.PyObject_GetAttr(obj.c(), name.c()) 107 | return obj2ObjErr(ret) 108 | } 109 | 110 | // Retrieve an attribute named attr_name from object o. Returns the attribute value 111 | // on success, or NULL on failure. This is the equivalent to the Python "obj.name". 112 | // 113 | // Return value: New reference. 114 | func (obj *Base) GetAttrString(name string) (*Base, error) { 115 | s := C.CString(name) 116 | defer C.free(unsafe.Pointer(s)) 117 | ret := C.PyObject_GetAttrString(obj.c(), s) 118 | return obj2ObjErr(ret) 119 | } 120 | 121 | // PyObject_GenericGetAttr : This is an internal helper function - we shouldn't 122 | // need to expose it ... 123 | 124 | // SetAttr sets the attribute of "obj" with the name "name" to "value". This is 125 | // equivalent to the Python "obj.name = value". 126 | func (obj *Base) SetAttr(name, value *Base) error { 127 | ret := C.PyObject_SetAttr(obj.c(), name.c(), value.c()) 128 | return int2Err(ret) 129 | } 130 | 131 | // SetAttrString sets the attribute of "obj" with the name "name" to "value". 132 | // This is equivalent to the Python "obj.name = value". 133 | func (obj *Base) SetAttrString(name string, value *Base) error { 134 | s := C.CString(name) 135 | defer C.free(unsafe.Pointer(s)) 136 | ret := C.PyObject_SetAttrString(obj.c(), s, value.c()) 137 | return int2Err(ret) 138 | } 139 | 140 | // PyObject_GenericSetAttr : This is an internal helper function - we shouldn't 141 | // need to expose it ... 142 | 143 | // DelAttr deletes the attribute with the name "name" from "obj". This is 144 | // equivalent to the Python "del obj.name". 145 | func (obj *Base) DelAttr(name *Base) error { 146 | ret := C.PyObject_SetAttr(obj.c(), name.c(), nil) 147 | return int2Err(ret) 148 | } 149 | 150 | // DelAttrString deletes the attribute with the name "name" from "obj". This is 151 | // equivalent to the Python "del obj.name". 152 | func (obj *Base) DelAttrString(name string) error { 153 | s := C.CString(name) 154 | defer C.free(unsafe.Pointer(s)) 155 | ret := C.PyObject_SetAttrString(obj.c(), s, nil) 156 | return int2Err(ret) 157 | } 158 | 159 | // RichCompare compares "obj" with "obj2" using the specified operation (LE, GE 160 | // etc.), and returns the result. The equivalent Python is "obj op obj2", where 161 | // op is the corresponding Python operator for op. 162 | // 163 | // Return value: New Reference. 164 | func (obj *Base) RichCompare(obj2 *Base, op Op) (*Base, error) { 165 | ret := C.PyObject_RichCompare(obj.c(), obj2.c(), C.int(op)) 166 | return obj2ObjErr(ret) 167 | } 168 | 169 | // RichCompare compares "obj" with "obj2" using the specified operation (LE, GE 170 | // etc.), and returns true or false. The equivalent Python is "obj op obj2", 171 | // where op is the corresponding Python operator for op. 172 | func (obj *Base) RichCompareBool(obj2 *Base, op Op) (bool, error) { 173 | ret := C.PyObject_RichCompareBool(obj.c(), obj2.c(), C.int(op)) 174 | return int2BoolErr(ret) 175 | } 176 | 177 | // PyObject_Cmp : Thanks to multiple return values, we don't need this function 178 | // to be available in Go. 179 | 180 | // Compare returns the result of comparing "obj" and "obj2". This is equivalent 181 | // to the Python "cmp(obj, obj2)". 182 | func (obj *Base) Compare(obj2 *Base) (int, error) { 183 | ret := C.PyObject_Compare(obj.c(), obj2.c()) 184 | return int(ret), exception() 185 | } 186 | 187 | // Repr returns a String representation of "obj". This is equivalent to the 188 | // Python "repr(obj)". 189 | // 190 | // Return value: New Reference. 191 | func (obj *Base) Repr() (*Base, error) { 192 | ret := C.PyObject_Repr(obj.c()) 193 | return obj2ObjErr(ret) 194 | } 195 | 196 | // Str returns a String representation of "obj". This is equivalent to the 197 | // Python "str(obj)". 198 | // 199 | // Return value: New Reference. 200 | func (obj *Base) Str() (*Base, error) { 201 | ret := C.PyObject_Str(obj.c()) 202 | return obj2ObjErr(ret) 203 | } 204 | 205 | func (obj *Base) String() string { 206 | if v, ok := AsString(obj); ok { 207 | return v.String() 208 | } 209 | ret := C.PyObject_Str(obj.c()) 210 | if ret == nil { 211 | return "" 212 | } 213 | defer C.decref(ret) 214 | return ((*String)(unsafe.Pointer(ret))).String() 215 | } 216 | 217 | // Bytes returns a Bytes representation of "obj". This is equivalent to the 218 | // Python "bytes(obj)". In Python 2.x this method is identical to Str(). 219 | // 220 | // Return value: New Reference. 221 | func (obj *Base) Bytes() (*Base, error) { 222 | ret := C.PyObject_Bytes(obj.c()) 223 | return obj2ObjErr(ret) 224 | } 225 | 226 | // PyObject_Unicode : TODO 227 | 228 | // IsInstance returns true if "obj" is an instance of "cls", false otherwise. 229 | // If "cls" is a Type instead of a class, then true will be return if "obj" is 230 | // of that type. If "cls" is a Tuple then true will be returned if "obj" is an 231 | // instance of any of the Objects in the tuple. This is equivalent to the 232 | // Python "isinstance(obj, cls)". 233 | func (obj *Base) IsInstance(cls *Base) (bool, error) { 234 | ret := C.PyObject_IsInstance(obj.c(), cls.c()) 235 | return int2BoolErr(ret) 236 | } 237 | 238 | // IsSubclass retuns true if "obj" is a Subclass of "cls", false otherwise. If 239 | // "cls" is a Tuple, then true is returned if "obj" is a Subclass of any member 240 | // of "cls". This is equivalent to the Python "issubclass(obj, cls)". 241 | func (obj *Base) IsSubclass(cls *Base) (bool, error) { 242 | ret := C.PyObject_IsSubclass(obj.c(), cls.c()) 243 | return int2BoolErr(ret) 244 | } 245 | 246 | // Call calls obj with the given args and kwds. kwds may be nil, args may not 247 | // (an empty Tuple must be used if no arguments are wanted). Returns the result 248 | // of the call, or an Error on failure. This is equivalent to 249 | // "obj(*args, **kwds)" in Python. 250 | // 251 | // Return value: New Reference. 252 | func (obj *Base) Call(args *Tuple, kwds *Dict) (*Base, error) { 253 | ret := C.PyObject_Call(obj.c(), args.c(), kwds.c()) 254 | return obj2ObjErr(ret) 255 | } 256 | 257 | // CallObject calls obj with the given args. args may be nil. Returns the 258 | // result of the call, or an Error on failure. This is equivalent to 259 | // "obj(*args)" in Python. 260 | // 261 | // Return value: New Reference. 262 | func (obj *Base) CallObject(args *Tuple) (*Base, error) { 263 | var a *C.PyObject = nil 264 | if args != nil { 265 | a = args.c() 266 | } 267 | ret := C.PyObject_CallObject(obj.c(), a) 268 | return obj2ObjErr(ret) 269 | } 270 | 271 | func (obj *Base) CallMethodObject(name string, args *Tuple) (*Base, error) { 272 | cname := C.CString(name) 273 | defer C.free(unsafe.Pointer(cname)) 274 | 275 | f := C.PyObject_GetAttrString(obj.c(), cname) 276 | if f == nil { 277 | return nil, AttributeError.Err(name) 278 | } 279 | defer C.decref(f) 280 | 281 | if C.PyCallable_Check(f) == 0 { 282 | return nil, TypeError.Err("attribute of type '%s' is not callable", name) 283 | } 284 | 285 | ret := C.PyObject_CallObject(f, args.c()) 286 | return obj2ObjErr(ret) 287 | } 288 | 289 | func (obj *Base) CallObjArgs(args ...*Base) (*Base, error) { 290 | args1 := PackTuple(args...) 291 | defer args1.Decref() 292 | return obj.CallObject(args1) 293 | } 294 | 295 | func (obj *Base) CallMethodObjArgs(name string, args ...*Base) (*Base, error) { 296 | args1 := PackTuple(args...) 297 | defer args1.Decref() 298 | return obj.CallMethodObject(name, args1) 299 | } 300 | 301 | // PyObject_Hash : TODO 302 | 303 | // PyObject_HashNotImplement : This is an internal function, that we probably 304 | // don't need to export. 305 | 306 | // Length returns the length of the Object. This is equivalent to the Python 307 | // "len(obj)". 308 | func (obj *Base) Length() (int64, error) { 309 | ret := C.PyObject_Length(obj.c()) 310 | return int64(ret), exception() 311 | } 312 | 313 | // Size returns the length of the Object. This is equivalent to the Python 314 | // "len(obj)". 315 | func (obj *Base) Size() (int64, error) { 316 | ret := C.PyObject_Size(obj.c()) 317 | return int64(ret), exception() 318 | } 319 | 320 | // GetItem returns the element of "obj" corresponding to "key". This is 321 | // equivalent to the Python "obj[key]". 322 | // 323 | // Return value: New Reference. 324 | func (obj *Base) GetItem(key *Base) (*Base, error) { 325 | ret := C.PyObject_GetItem(obj.c(), key.c()) 326 | return obj2ObjErr(ret) 327 | } 328 | 329 | // SetItem sets the element of "obj" corresponding to "key" to "value". This is 330 | // equivalent to the Python "obj[key] = value". 331 | func (obj *Base) SetItem(key, value *Base) error { 332 | ret := C.PyObject_SetItem(obj.c(), key.c(), value.c()) 333 | return int2Err(ret) 334 | } 335 | 336 | // DelItem deletes the element from "obj" that corresponds to "key". This is 337 | // equivalent to the Python "del obj[key]". 338 | func (obj *Base) DelItem(key *Base) error { 339 | ret := C.PyObject_DelItem(obj.c(), key.c()) 340 | return int2Err(ret) 341 | } 342 | 343 | // PyObject_AsFileDescriptor : TODO 344 | 345 | func (obj *Base) Dir() (*Base, error) { 346 | ret := C.PyObject_Dir(obj.c()) 347 | return obj2ObjErr(ret) 348 | } 349 | 350 | // PyObject_GetIter : TODO 351 | 352 | -------------------------------------------------------------------------------- /object_test.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestBase(t *testing.T) { 8 | 9 | { 10 | v := NewString("Hello!") 11 | defer v.Decref() 12 | 13 | if v.String() != "Hello!" { 14 | t.Fatal("NewString failed") 15 | } 16 | } 17 | { 18 | v := NewInt(1) 19 | defer v.Decref() 20 | 21 | if v.String() != "1" { 22 | t.Fatal("NewInt failed") 23 | } 24 | } 25 | { 26 | v1 := NewInt(1) 27 | defer v1.Decref() 28 | 29 | v2 := NewString("Hello!") 30 | defer v2.Decref() 31 | 32 | v := PackTuple(v1.Obj(), v2.Obj()) 33 | defer v.Decref() 34 | 35 | if v.String() != "(1, 'Hello!')" { 36 | t.Fatal("NewTuple failed:", v.String()) 37 | } 38 | } 39 | { 40 | v1 := NewInt(1) 41 | defer v1.Decref() 42 | 43 | v2 := NewString("Hello!") 44 | defer v2.Decref() 45 | 46 | v := NewDict() 47 | defer v.Decref() 48 | 49 | v.SetItem(v1.Obj(), v2.Obj()) 50 | 51 | if v.String() != "{1: 'Hello!'}" { 52 | t.Fatal("NewDict failed:", v.String()) 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /python.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | /* 4 | #cgo pkg-config: python-2.7 5 | 6 | #include 7 | 8 | static inline int enterRecursive(char *w) { 9 | return Py_EnterRecursiveCall(w); 10 | } 11 | 12 | static inline void leaveRecursive() { 13 | Py_LeaveRecursiveCall(); 14 | } 15 | */ 16 | import "C" 17 | import "unsafe" 18 | 19 | func init() { 20 | C.Py_Initialize() 21 | } 22 | 23 | func Initialize() { 24 | C.Py_Initialize() 25 | } 26 | 27 | func InitializeEx(initsigs bool) { 28 | if initsigs { 29 | C.Py_InitializeEx(1) 30 | } else { 31 | C.Py_InitializeEx(0) 32 | } 33 | } 34 | 35 | func Finalize() { 36 | C.Py_Finalize() 37 | } 38 | 39 | func AddToPath(dir string) { 40 | p := C.CString("path") 41 | defer C.free(unsafe.Pointer(p)) 42 | 43 | sys_path := C.PySys_GetObject(p) 44 | if sys_path == nil { 45 | return 46 | } 47 | 48 | s := C.CString(dir) 49 | defer C.free(unsafe.Pointer(s)) 50 | 51 | pDir := C.PyString_FromString(s) 52 | if pDir == nil { 53 | return 54 | } 55 | 56 | C.PyList_Append(sys_path, pDir) 57 | } 58 | 59 | func Main(args []string) int { 60 | argv := make([]*C.char, len(args)) 61 | 62 | for i, arg := range args { 63 | argv[i] = C.CString(arg) 64 | defer C.free(unsafe.Pointer(argv[i])) 65 | } 66 | 67 | return int(C.Py_Main(C.int(len(argv)), &argv[0])) 68 | } 69 | 70 | // EnterRecusiveCall marks a point where a recursive Go-level call is about to 71 | // be performed. It returns true if the recursive call is permitted, otherwise 72 | // a Python exception is set and false is returned. where is a string that will 73 | // be appended to the RuntimeError set if the recursion limit has been exceeded 74 | // (e.g. " in instance check"). This function needs to be called if the 75 | // recursive function may not invoke Python code (which automatically tracks 76 | // recursion depth). 77 | func EnterRecursiveCall(where string) bool { 78 | s := C.CString(where) 79 | defer C.free(unsafe.Pointer(s)) 80 | return C.enterRecursive(s) == 0 81 | } 82 | 83 | // LeaveRecursiveCall must be called after a recursive call that was indicated 84 | // by EnterRecursiveCall. 85 | func LeaveRecursiveCall() { 86 | C.leaveRecursive() 87 | } 88 | -------------------------------------------------------------------------------- /pyutil/call.go: -------------------------------------------------------------------------------- 1 | package pyutil 2 | 3 | import ( 4 | "syscall" 5 | "github.com/qiniu/py" 6 | "github.com/qiniu/errors" 7 | ) 8 | 9 | // ------------------------------------------------------------------------------------------ 10 | 11 | func PackEx(cfg *Config, args ...interface{}) (ret *py.Tuple, err error) { 12 | 13 | args1 := py.NewTuple(len(args)) 14 | 15 | for i, arg := range args { 16 | v1, ok1 := NewVarEx(arg, cfg) 17 | if !ok1 { 18 | args1.Decref() 19 | err = errors.Info(syscall.EINVAL, "pyutil.Pack", i+1, arg).Detail(err) 20 | return 21 | } 22 | args1.SetItem(i, v1) 23 | } 24 | return args1, nil 25 | } 26 | 27 | func Pack(args ...interface{}) (ret *py.Tuple, err error) { 28 | 29 | return PackEx(DefaultConfig, args...) 30 | } 31 | 32 | // ------------------------------------------------------------------------------------------ 33 | 34 | func CallEx(cfg *Config, fn *py.Base, args ...interface{}) (ret *py.Base, err error) { 35 | 36 | args1, err := PackEx(cfg, args...) 37 | if err != nil { 38 | err = errors.Info(syscall.EINVAL, "pyutil.Call").Detail(err) 39 | return 40 | } 41 | defer args1.Decref() 42 | 43 | return fn.CallObject(args1) 44 | } 45 | 46 | func Call(fn *py.Base, args ...interface{}) (*py.Base, error) { 47 | 48 | return CallEx(DefaultConfig, fn, args...) 49 | } 50 | 51 | // ------------------------------------------------------------------------------------------ 52 | 53 | func CallMethodEx(cfg *Config, self *py.Base, method string, args ...interface{}) (ret *py.Base, err error) { 54 | 55 | args1, err := PackEx(cfg, args...) 56 | if err != nil { 57 | err = errors.Info(syscall.EINVAL, "pyutil.Call").Detail(err) 58 | return 59 | } 60 | defer args1.Decref() 61 | 62 | return self.CallMethodObject(method, args1) 63 | } 64 | 65 | func CallMethod(self *py.Base, method string, args ...interface{}) (ret *py.Base, err error) { 66 | 67 | return CallMethodEx(DefaultConfig, self, method, args...) 68 | } 69 | 70 | // ------------------------------------------------------------------------------------------ 71 | 72 | func NewInstanceEx(cfg *Config, typ *py.Class, args ...interface{}) (ret *py.Base, err error) { 73 | 74 | args1, err := PackEx(cfg, args...) 75 | if err != nil { 76 | err = errors.Info(syscall.EINVAL, "pyutil.NewInstance").Detail(err) 77 | return 78 | } 79 | defer args1.Decref() 80 | 81 | return typ.New(args1, nil) 82 | } 83 | 84 | func NewInstance(typ *py.Class, args ...interface{}) (ret *py.Base, err error) { 85 | 86 | return NewInstanceEx(DefaultConfig, typ, args...) 87 | } 88 | 89 | // ------------------------------------------------------------------------------------------ 90 | 91 | func NewEx(cfg *Config, mod *py.Base, clsname string, args ...interface{}) (ret *py.Base, err error) { 92 | 93 | o, err := mod.GetAttrString(clsname) 94 | if err != nil { 95 | err = errors.Info(err, "pyutil.New", clsname).Detail(err) 96 | return 97 | } 98 | defer o.Decref() 99 | 100 | ty, ok := py.AsClass(o) 101 | if !ok { 102 | err = errors.Info(syscall.EINVAL, "pyutil.New", o.String(), "is not a class") 103 | return 104 | } 105 | 106 | return NewInstanceEx(cfg, ty, args...) 107 | } 108 | 109 | func New(mod *py.Base, clsname string, args ...interface{}) (ret *py.Base, err error) { 110 | 111 | return NewEx(DefaultConfig, mod, clsname, args...) 112 | } 113 | 114 | // ------------------------------------------------------------------------------------------ 115 | 116 | -------------------------------------------------------------------------------- /pyutil/call_test.go: -------------------------------------------------------------------------------- 1 | package pyutil 2 | 3 | import ( 4 | "testing" 5 | "github.com/qiniu/log" 6 | "github.com/qiniu/errors" 7 | "github.com/qiniu/py" 8 | ) 9 | 10 | type moduleCase struct { 11 | exp string 12 | name, ret, tbl string 13 | } 14 | 15 | var g_moduleCases = []moduleCase{ 16 | {` 17 | class Plugin: 18 | def init(self, cate): 19 | self.tbl = "dn_5m" + cate 20 | `, "foo", "None", "dn_5m_stage"}, 21 | } 22 | 23 | func TestCall(t *testing.T) { 24 | 25 | log.SetOutputLevel(0) 26 | 27 | for _, c := range g_moduleCases { 28 | code, err := py.Compile(c.exp, "", py.FileInput) 29 | if err != nil { 30 | t.Fatal("Compile failed:", err) 31 | } 32 | defer code.Decref() 33 | 34 | mod, err := py.ExecCodeModule(c.name, code.Obj()) 35 | if err != nil { 36 | t.Fatal("ExecCodeModule failed:", err) 37 | } 38 | defer mod.Decref() 39 | 40 | plg, err := New(mod.Obj(), "Plugin") 41 | if err != nil { 42 | t.Fatal("NewPlugin failed:", errors.Detail(err)) 43 | } 44 | 45 | ret, err := CallMethod(plg, "init", "_stage") 46 | if err != nil { 47 | t.Fatal("CallMethod failed:", err) 48 | } 49 | defer ret.Decref() 50 | 51 | if ret.String() != c.ret { 52 | t.Fatal("CallMethod ret:", ret.String(), c.ret) 53 | } 54 | 55 | tbl, _ := plg.GetAttrString("tbl") 56 | if tbl.String() != c.tbl { 57 | t.Fatal("mod.GetAttrString('tbl') ret:", tbl.String(), c.tbl) 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /pyutil/var.go: -------------------------------------------------------------------------------- 1 | package pyutil 2 | 3 | import ( 4 | "strings" 5 | "reflect" 6 | "github.com/qiniu/py" 7 | ) 8 | 9 | // ------------------------------------------------------------------------------------------ 10 | 11 | type Config struct { 12 | Cate string 13 | SliceAsList bool 14 | } 15 | 16 | var DefaultConfig = &Config { 17 | Cate: "json", 18 | } 19 | 20 | // ------------------------------------------------------------------------------------------ 21 | 22 | func tagName(tag string) (string) { 23 | if idx := strings.Index(tag, ","); idx != -1 { 24 | return tag[:idx] 25 | } 26 | return tag 27 | } 28 | 29 | func newStruct(sv reflect.Value, cfg *Config) (ret *py.Base, ok bool) { 30 | 31 | dict := py.NewDict() 32 | 33 | st := sv.Type() 34 | for i := 0; i < sv.NumField(); i++ { 35 | sf := st.Field(i) 36 | tag := sf.Tag.Get(cfg.Cate) 37 | if tag == "" { 38 | return nil, false 39 | } 40 | name := tagName(tag) 41 | val := sv.Field(i) 42 | val1, ok1 := NewVarEx(val.Interface(), cfg) 43 | if !ok1 { 44 | dict.Decref() 45 | return nil, false 46 | } 47 | dict.SetItemString(name, val1) 48 | val1.Decref() 49 | } 50 | 51 | return dict.Obj(), true 52 | } 53 | 54 | func newMap(v reflect.Value, cfg *Config) (ret *py.Base, ok bool) { 55 | 56 | dict := py.NewDict() 57 | 58 | keys := v.MapKeys() 59 | for _, key := range keys { 60 | key1, ok1 := NewVarEx(key.Interface(), cfg) 61 | if !ok1 { 62 | dict.Decref() 63 | return nil, false 64 | } 65 | val1, ok1 := NewVarEx(v.MapIndex(key).Interface(), cfg) 66 | if !ok1 { 67 | key1.Decref() 68 | dict.Decref() 69 | return nil, false 70 | } 71 | dict.SetItem(key1, val1) 72 | key1.Decref() 73 | val1.Decref() 74 | } 75 | 76 | return dict.Obj(), true 77 | } 78 | 79 | func newComplex(val reflect.Value, cfg *Config) (ret *py.Base, ok bool) { 80 | 81 | retry: 82 | switch val.Kind() { 83 | case reflect.Struct: 84 | return newStruct(val, cfg) 85 | case reflect.Map: 86 | return newMap(val, cfg) 87 | case reflect.Ptr, reflect.Interface: 88 | val = val.Elem() 89 | goto retry 90 | } 91 | return nil, false 92 | } 93 | 94 | // ------------------------------------------------------------------------------------------ 95 | 96 | func NewVarEx(val interface{}, cfg *Config) (ret *py.Base, ok bool) { 97 | 98 | switch v := val.(type) { 99 | case int: 100 | return py.NewInt(v).Obj(), true 101 | case int64: 102 | return py.NewLong(v).Obj(), true 103 | case string: 104 | return py.NewString(v).Obj(), true 105 | } 106 | return newComplex(reflect.ValueOf(val), cfg) 107 | } 108 | 109 | func NewVar(val interface{}) (ret *py.Base, ok bool) { 110 | 111 | return NewVarEx(val, DefaultConfig) 112 | } 113 | 114 | // ------------------------------------------------------------------------------------------ 115 | -------------------------------------------------------------------------------- /pyutil/var_test.go: -------------------------------------------------------------------------------- 1 | package pyutil 2 | 3 | import ( 4 | "testing" 5 | "github.com/qiniu/py" 6 | ) 7 | 8 | type Foo struct { 9 | A int `json:"a"` 10 | B string `json:"b"` 11 | } 12 | 13 | func Test(t *testing.T) { 14 | 15 | { 16 | val, ok := NewVar(1) 17 | if !ok { 18 | t.Fatal("NewVar failed") 19 | } 20 | if v, ok := py.AsInt(val); !ok || v.Int() != 1 { 21 | t.Fatal("NewVar failed:", val) 22 | } 23 | } 24 | { 25 | val, ok := NewVar(int64(1)) 26 | if !ok { 27 | t.Fatal("NewVar failed") 28 | } 29 | if v, ok := py.AsLong(val); !ok || v.Long() != 1 { 30 | t.Fatal("NewVar failed:", val) 31 | } 32 | } 33 | { 34 | val, ok := NewVar("Hello") 35 | if !ok { 36 | t.Fatal("NewVar failed") 37 | } 38 | if v, ok := py.AsString(val); !ok || v.String() != "Hello" { 39 | t.Fatal("NewVar failed:", val) 40 | } 41 | } 42 | { 43 | foo := &Foo{ 44 | A: 1, B: "Hello", 45 | } 46 | val, ok := NewVar(foo) 47 | if !ok { 48 | t.Fatal("NewVar failed") 49 | } 50 | if v, ok := py.AsDict(val); !ok || !checkFoo(v, t) { 51 | t.Fatal("NewVar failed:", val) 52 | } 53 | } 54 | { 55 | foo := map[string]interface{}{ 56 | "a": 1, "b": "Hello", 57 | } 58 | val, ok := NewVar(foo) 59 | if !ok { 60 | t.Fatal("NewVar failed") 61 | } 62 | if v, ok := py.AsDict(val); !ok || !checkFoo(v, t) { 63 | t.Fatal("NewVar failed:", val) 64 | } 65 | } 66 | } 67 | 68 | func checkFoo(val *py.Dict, t *testing.T) bool { 69 | 70 | a := val.GetItemString("a") 71 | if a == nil { 72 | t.Fatal("GetItemString a failed") 73 | return false 74 | } 75 | if v, ok := py.AsInt(a); !ok || v.Int() != 1 { 76 | t.Fatal("GetItemString a failed") 77 | } 78 | 79 | b := val.GetItemString("b") 80 | if b == nil { 81 | t.Fatal("GetItemString b failed") 82 | return false 83 | } 84 | if v, ok := py.AsString(b); !ok || v.String() != "Hello" { 85 | t.Fatal("GetItemString b failed") 86 | } 87 | return true 88 | } 89 | 90 | -------------------------------------------------------------------------------- /string.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline int stringCheck(PyObject *o) { return PyString_Check(o); } 5 | import "C" 6 | import "unsafe" 7 | 8 | type String struct { 9 | Base 10 | o C.PyStringObject 11 | } 12 | 13 | // StringType is the Type object that represents the String type. 14 | var StringType = (*Type)(unsafe.Pointer(&C.PyString_Type)) 15 | 16 | func newString(obj *C.PyObject) *String { 17 | return (*String)(unsafe.Pointer(obj)) 18 | } 19 | 20 | func NewString(s string) *String { 21 | cs := C.CString(s) 22 | defer C.free(unsafe.Pointer(cs)) 23 | ret := C.PyString_FromString(cs) 24 | return newString(ret) 25 | } 26 | 27 | func AsString(o *Base) (v *String, ok bool) { 28 | if ok = C.stringCheck(o.c()) != 0; ok { 29 | v = newString(o.c()) 30 | } 31 | return 32 | } 33 | 34 | func (s *String) String() string { 35 | if s == nil { 36 | return "" 37 | } 38 | ret := C.PyString_AsString(s.c()) 39 | return C.GoString(ret) 40 | } 41 | 42 | func (s *String) Format(args *Tuple) (*String, error) { 43 | ret := C.PyString_Format(s.c(), args.c()) 44 | if ret == nil { 45 | return nil, exception() 46 | } 47 | return newString(ret), nil 48 | } 49 | -------------------------------------------------------------------------------- /tuple.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline int tupleCheckE(PyObject *o) { return PyTuple_CheckExact(o); } 5 | // static inline int tupleCheck(PyObject *o) { return PyTuple_Check(o); } 6 | // static inline size_t tupleItemSize() { return sizeof(PyObject *); } 7 | import "C" 8 | import "unsafe" 9 | 10 | type Tuple struct { 11 | Base 12 | o C.PyTupleObject 13 | } 14 | 15 | // TupleType is the Type object that represents the Tuple type. 16 | var TupleType = (*Type)(unsafe.Pointer(&C.PyTuple_Type)) 17 | 18 | func newTuple(obj *C.PyObject) *Tuple { 19 | return (*Tuple)(unsafe.Pointer(obj)) 20 | } 21 | 22 | // NewTuple returns a new *Tuple of the specified size. However the entries are 23 | // all set to NULL, so the tuple should not be shared, especially with Python 24 | // code, until the entries have all been set. 25 | // 26 | // Return value: New Reference. 27 | func NewTuple(size int) *Tuple { 28 | ret := C.PyTuple_New(C.Py_ssize_t(size)) 29 | return newTuple(ret) 30 | } 31 | 32 | func AsTuple(o *Base) (v *Tuple, ok bool) { 33 | if ok = C.tupleCheck(o.c()) != 0; ok { 34 | v = newTuple(o.c()) 35 | } 36 | return 37 | } 38 | 39 | // PackTuple returns a new *Tuple which contains the arguments. This tuple is 40 | // ready to use. 41 | // 42 | // Return value: New Reference. 43 | func PackTuple(items ...*Base) *Tuple { 44 | ret := C.PyTuple_New(C.Py_ssize_t(len(items))) 45 | 46 | // Since the ob_item array has a size of 1, Go won't let us index more than 47 | // a single entry, and if we try and use our own local type definition with 48 | // a flexible array member then cgo converts it to [0]byte which is even 49 | // less useful. So, we resort to pointer manipulation - which is 50 | // unfortunate, as it's messy in Go. 51 | 52 | // base is a pointer to the first item in the array of PyObject pointers. 53 | // step is the size of a PyObject * (i.e. the number of bytes we need to add 54 | // to get to the next item). 55 | base := unsafe.Pointer(&(*C.PyTupleObject)(unsafe.Pointer(ret)).ob_item[0]) 56 | step := uintptr(C.tupleItemSize()) 57 | 58 | for _, item := range items { 59 | item.Incref() 60 | *(**C.PyObject)(base) = item.c() 61 | 62 | // Move base to point to the next item, by incrementing by step bytes 63 | base = unsafe.Pointer(uintptr(base) + step) 64 | } 65 | return newTuple(ret) 66 | } 67 | 68 | func (t *Tuple) CheckExact() bool { 69 | ret := C.tupleCheckE(t.c()) 70 | if int(ret) != 0 { 71 | return true 72 | } 73 | return false 74 | } 75 | 76 | func (t *Tuple) Size() int { 77 | ret := C.PyTuple_Size(t.c()) 78 | if ret < 0 { 79 | panic(exception()) 80 | } 81 | return int(ret) 82 | } 83 | 84 | // Return the object at position pos in the tuple pointed to by p. If pos is out of bounds, 85 | // return NULL and sets an IndexError exception. 86 | // 87 | // Return value: Borrowed reference. 88 | func (t *Tuple) GetItem(pos int) (*Base, error) { 89 | ret := C.PyTuple_GetItem(t.c(), C.Py_ssize_t(pos)) 90 | return obj2ObjErr(ret) 91 | } 92 | 93 | func (t *Tuple) GetSlice(low, high int) (*Tuple, error) { 94 | ret := C.PyTuple_GetSlice(t.c(), C.Py_ssize_t(low), C.Py_ssize_t(high)) 95 | if ret == nil { 96 | return nil, exception() 97 | } 98 | return newTuple(ret), nil 99 | } 100 | 101 | // Insert a reference to object o at position pos of the tuple pointed to by p. Return 0 on success. 102 | // Note This function “steals” a reference to o. 103 | func (t *Tuple) SetItem(pos int, obj *Base) error { 104 | ret := C.PyTuple_SetItem(t.c(), C.Py_ssize_t(pos), obj.c()) 105 | return int2Err(ret) 106 | } 107 | 108 | // _PyTuple_Resize 109 | 110 | // PyTuple_ClearFreeList() 111 | 112 | func (t *Tuple) Slice() []*Base { 113 | l := t.Size() 114 | s := make([]*Base, l) 115 | for i := 0; i < l; i++ { 116 | o, err := t.GetItem(i) 117 | if err != nil { 118 | panic(err) 119 | } 120 | s[i] = o 121 | } 122 | return s 123 | } 124 | 125 | -------------------------------------------------------------------------------- /type.go: -------------------------------------------------------------------------------- 1 | package py 2 | 3 | // #include 4 | // static inline int typeCheck(PyObject *o) { return PyType_Check(o); } 5 | // static inline int typeCheckE(PyObject *o) { return PyType_CheckExact(o); } 6 | // static inline PyObject* typeAlloc(PyObject *t, Py_ssize_t n) { 7 | // return ((PyTypeObject*)t)->tp_alloc((PyTypeObject *)t, n); 8 | // } 9 | // static inline int typeInit(PyObject *t, PyObject *o, PyObject *a, PyObject *k) { 10 | // return ((PyTypeObject*)t)->tp_init(o, a, k); 11 | // } 12 | // static inline PyObject* typeNew(PyObject *t, PyObject *a, PyObject *k) { 13 | // return ((PyTypeObject*)t)->tp_new((PyTypeObject*)t, a, k); 14 | // } 15 | import "C" 16 | import "unsafe" 17 | 18 | type Type struct { 19 | Base 20 | o C.PyTypeObject 21 | } 22 | 23 | // TypeType is the Type object that represents the Type type. 24 | var TypeType = (*Type)(unsafe.Pointer(&C.PyType_Type)) 25 | 26 | func newType(obj *C.PyObject) *Type { 27 | return (*Type)(unsafe.Pointer(obj)) 28 | } 29 | 30 | func AsType(o *Base) (v *Type, ok bool) { 31 | if ok = C.typeCheck(o.c()) != 0; ok { 32 | v = newType(o.c()) 33 | } 34 | return 35 | } 36 | 37 | func (t *Type) NewNoArgs() (ret *Base, err error) { 38 | args := NewTuple(0) 39 | defer args.Decref() 40 | return t.New(args, nil) 41 | } 42 | 43 | func (t *Type) New(args *Tuple, kw *Dict) (ret *Base, err error) { 44 | ret1 := C.typeNew(t.c(), args.c(), kw.c()) 45 | return obj2ObjErr(ret1) 46 | } 47 | 48 | func (t *Type) NewObjArgs(args ...*Base) (ret *Base, err error) { 49 | args1 := PackTuple(args...) 50 | defer args1.Decref() 51 | return t.New(args1, nil) 52 | } 53 | 54 | func (t *Type) Alloc(n int64) (*Base, error) { 55 | ret := C.typeAlloc(t.c(), C.Py_ssize_t(n)) 56 | return obj2ObjErr(ret) 57 | } 58 | 59 | func (t *Type) Init(obj *Base, args *Tuple, kw *Dict) error { 60 | ret := C.typeInit(t.c(), obj.c(), args.c(), kw.c()) 61 | if ret < 0 { 62 | return exception() 63 | } 64 | return nil 65 | } 66 | 67 | // CheckExact returns true when "t" is an actual Type object, and not some form 68 | // of subclass. 69 | func (t *Type) CheckExact() bool { 70 | return C.typeCheckE(t.c()) == 1 71 | } 72 | 73 | // PyType_ClearCache : TODO - ??? 74 | 75 | // Modified should be called after the attributes or base class of a Type have 76 | // been changed. 77 | func (t *Type) Modified() { 78 | C.PyType_Modified(&t.o) 79 | } 80 | 81 | // HasFeature returns true when "t" has the feature in question. 82 | func (t *Type) HasFeature(feature uint32) bool { 83 | return (t.o.tp_flags & C.long(feature)) != 0 84 | } 85 | 86 | // IsGc returns true if the type "t" supports Cyclic Garbage Collection. 87 | func (t *Type) IsGc() bool { 88 | return t.HasFeature(C.Py_TPFLAGS_HAVE_GC) 89 | } 90 | 91 | // IsSubtype returns true if "t" is a subclass of "t2". 92 | func (t *Type) IsSubtype(t2 *Type) bool { 93 | return C.PyType_IsSubtype(&t.o, &t2.o) == 1 94 | } 95 | 96 | // PyType_GenericAlloc : This is an internal function, which we should not need 97 | // to expose. 98 | 99 | // PyType_GenericNew : Another internal function we don't need to expose. 100 | 101 | // PyType_Ready : This function is wrapped (along with a lot of other 102 | // functionality) in the Create method of the Class stuct. 103 | --------------------------------------------------------------------------------