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