// encodeTuple translates a Go array to a Python object. func encodeTuple(array []interface{}) (pyTuple *C.PyObject, err error) { if len(array) == 0 { pyTuple = pyEmptyTuple C.INCREF(pyTuple) } else { pyTuple = C.PyTuple_New(C.Py_ssize_t(len(array))) var ok bool defer func() { if !ok { C.DECREF(pyTuple) pyTuple = nil } }() for i, item := range array { var pyItem *C.PyObject if pyItem, err = encode(item); err != nil { return } C.Tuple_SET_ITEM(pyTuple, C.Py_ssize_t(i), pyItem) } ok = true } return }
func pyObjToInterface(o *C.PyObject) interface{} { if C.myPyString_Check(o) != 0 { return C.GoStringN(C.PyString_AsString(o), C.int(C.PyString_Size(o))) } else if C.myPyInt_Check(o) != 0 { return int64(C.PyInt_AsLong(o)) } else if C.myPyDict_Check(o) != 0 { v := make(map[interface{}]interface{}) items := C.PyDict_Items(o) for i := 0; i < int(C.PyTuple_Size(items)); i++ { item := C.PyTuple_GetItem(items, C.Py_ssize_t(i)) key := C.PyTuple_GetItem(item, 0) value := C.PyTuple_GetItem(item, 1) v[pyObjToInterface(key)] = pyObjToInterface(value) } C.Py_DecRef(items) return v } else if C.myPyTuple_Check(o) != 0 { length := int(C.PyTuple_Size(o)) list := make([]interface{}, length) for i := 0; i < length; i++ { list[i] = pyObjToInterface(C.PyTuple_GetItem(o, C.Py_ssize_t(i))) } return list } return nil }
func (l *List) GetSlice(low, high int64) (*List, error) { ret := C.PyList_GetSlice(c(l), C.Py_ssize_t(low), C.Py_ssize_t(high)) if ret == nil { return nil, exception() } return newList(ret), nil }
func (t *Tuple) GetSlice(low, high int64) (*Tuple, error) { ret := C.PyTuple_GetSlice(c(t), C.Py_ssize_t(low), C.Py_ssize_t(high)) if ret == nil { return nil, exception() } return newTuple(ret), nil }
func (u *Unicode) Find(substr Object, start, end int64, direction int) (int64, bool, error) { ret := C.PyUnicode_Find(c(u), c(substr), C.Py_ssize_t(start), C.Py_ssize_t(end), C.int(direction)) if ret >= 0 { return int64(ret), true, nil } else if ret == -1 { return 0, false, nil } return 0, false, exception() }
// int PySlice_GetIndices(PySliceObject *slice, Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step) // Retrieve the start, stop and step indices from the slice object slice, assuming a sequence of length length. Treats indices greater than length as errors. // // Returns 0 on success and -1 on error with no exception set (unless one of the indices was not None and failed to be converted to an integer, in which case -1 is returned with an exception set). // // You probably do not want to use this function. If you want to use slice objects in versions of Python prior to 2.3, you would probably do well to incorporate the source of PySlice_GetIndicesEx(), suitably renamed, in the source of your extension. // // Changed in version 2.5: This function used an int type for length and an int * type for start, stop, and step. This might require changes in your code for properly supporting 64-bit systems. func PySlice_GetIndices(slice *PySliceObject, length int) (start, stop, step int, err error) { c_start := C.Py_ssize_t(0) c_stop := C.Py_ssize_t(0) c_step := C.Py_ssize_t(0) err = int2err(C.PySlice_GetIndices(slice.ptr, C.Py_ssize_t(length), &c_start, &c_stop, &c_step)) start = int(c_start) stop = int(c_stop) step = int(c_step) return }
// 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 int64) (*Tuple, error) { ret := C.PyTuple_New(C.Py_ssize_t(size)) if ret == nil { return nil, exception() } return newTuple(ret), nil }
// PackTuple returns a new *Tuple which contains the arguments. This tuple is // ready to use. // // Return value: New Reference. func PackTuple(items ...Object) (*Tuple, error) { ret := C.PyTuple_New(C.Py_ssize_t(len(items))) if ret == nil { return nil, exception() } // 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) = c(item) // Move base to point to the next item, by incrementing by step bytes base = unsafe.Pointer(uintptr(base) + step) } return newTuple(ret), nil }
func interfaceToPyObj(o interface{}) *C.PyObject { switch o.(type) { case int: return C.PyInt_FromLong(C.long(o.(int))) case int64: return C.PyInt_FromLong(C.long(o.(int64))) case string: strvalue := C.CString(o.(string)) defer C.free(unsafe.Pointer(strvalue)) return C.PyString_FromStringAndSize(strvalue, C.Py_ssize_t(len(o.(string)))) case map[interface{}]interface{}: dict := C.PyDict_New() for key, value := range o.(map[interface{}]interface{}) { dictAddItem(dict, key, value) } return dict case map[string]string: dict := C.PyDict_New() for key, value := range o.(map[string]string) { dictAddItem(dict, key, value) } return dict case map[string]interface{}: dict := C.PyDict_New() for key, value := range o.(map[string]interface{}) { dictAddItem(dict, key, value) } return dict default: return C.PyNone() } }
// NewList creates a new Python List instance. The created list has initial // length "size". // // Note: If size > 0, then the objects in the returned list are initialised to // nil. Thus you cannot use Abstract API functions, or expose the object to // Python code without first filling in all the created slots with // list.SetItem(). // // Return value: New Reference. func NewList(size int64) (*List, error) { ret := C.PyList_New(C.Py_ssize_t(size)) if ret == nil { return nil, exception() } return newList(ret), nil }
// decodeMapping translates a Python object to a Go map. func decodeMapping(pyMapping *C.PyObject) (mapping map[interface{}]interface{}, err error) { mapping = make(map[interface{}]interface{}) pyItems := C.Mapping_Items(pyMapping) if pyItems == nil { err = getError() return } length := int(C.PyList_Size(pyItems)) for i := 0; i < length; i++ { pyPair := C.PyList_GetItem(pyItems, C.Py_ssize_t(i)) var ( key interface{} value interface{} ) if key, err = decode(C.PyTuple_GetItem(pyPair, 0)); err != nil { return } if value, err = decode(C.PyTuple_GetItem(pyPair, 1)); err != nil { return } mapping[key] = value } return }
// PyObject* PyMarshal_ReadObjectFromString(char *string, Py_ssize_t len) // Return value: New reference. // Return a Python object from the data stream in a character buffer containing len bytes pointed to by string. On error, sets the appropriate exception (EOFError or TypeError) and returns NULL. // // Changed in version 2.5: This function used an int type for len. This might require changes in your code for properly supporting 64-bit systems. func PyMarshal_ReadObjectFromString(str string) *PyObject { //FIXME: use []byte ? c_str := C.CString(str) defer C.free(unsafe.Pointer(c_str)) return togo(C.PyMarshal_ReadObjectFromString(c_str, C.Py_ssize_t(len(str)))) }
// PyObject* PyByteArray_FromStringAndSize(const char *string, Py_ssize_t len) // Create a new bytearray object from string and its length, len. On failure, NULL is returned. func PyByteArray_FromStringAndSize(str string) *PyObject { //FIXME use []byte instead ? c_str := C.CString(str) defer C.free(unsafe.Pointer(c_str)) return togo(C.PyByteArray_FromStringAndSize(c_str, C.Py_ssize_t(len(str)))) }
//export goClassSeqLength func goClassSeqLength(obj unsafe.Pointer) C.Py_ssize_t { ctxt := getClassContext(obj) // Turn the function into something we can call f := (*func(unsafe.Pointer) int64)(unsafe.Pointer(&ctxt.sq_length)) return C.Py_ssize_t((*f)(obj)) }
func NewUnicode(s string) (*Unicode, error) { cs := C.CString(s) defer C.free(unsafe.Pointer(cs)) ret := C.PyUnicode_FromStringAndSize(cs, C.Py_ssize_t(len(s))) if ret == nil { return nil, exception() } return newUnicode(ret), nil }
//export goClassMapLen func goClassMapLen(obj unsafe.Pointer) C.Py_ssize_t { // Get the class context ctxt := getClassContext(obj) // Turn the function into something we can call f := (*func(unsafe.Pointer) int64)(unsafe.Pointer(&ctxt.mp_len)) return C.Py_ssize_t((*f)(obj)) }
//export createThreadCallback func createThreadCallback(pid *C.pthread_t) { runtime.LockOSThread() defer runtime.UnlockOSThread() _gstate := C.start_thread() _cb, _r, _w, _ok := callbacks.Get(pid) defer callbacks.Delete(pid) if !_ok { panic(fmt.Errorf("failed to found thread callback for `%v`", pid)) } // TODO: add special headers for WSGI. _environ := GenerateEnviron(_r) _response := C.run_wsgi_application(_environ) // parse header for i := 0; i < int(C.PyList_Size(_response.headers)); i++ { _h := C.PyList_GetItem(_response.headers, C.Py_ssize_t(i)) _k := C.PyTuple_GetItem(_h, C.Py_ssize_t(0)) _v := C.PyTuple_GetItem(_h, C.Py_ssize_t(1)) _w.Header().Set( PyString_AsString(_k), PyString_AsString(_v), ) } _body := C.GoString(_response.body) if len(_body) < 1 { panic(fmt.Errorf("failed to import python wsgi module.")) } C.end_thread(_gstate) // write body _w.WriteHeader(int(_response.status)) _w.Write([]byte(_body)) _cb() }
// Note This function is not available in 3.x and does not have a PyBytes alias. // PyObject* PyString_Decode(const char *s, Py_ssize_t size, const char *encoding, const char *errors) // Return value: New reference. // Create an object by decoding size bytes of the encoded buffer s using the codec registered for encoding. encoding and errors have the same meaning as the parameters of the same name in the unicode() built-in function. The codec to be used is looked up using the Python codec registry. Return NULL if an exception was raised by the codec. // // Note This function is not available in 3.x and does not have a PyBytes alias. // Changed in version 2.5: This function used an int type for size. This might require changes in your code for properly supporting 64-bit systems. func PyString_Decode(s string, sz int, encoding, errors string) *PyObject { c_s := C.CString(s) defer C.free(unsafe.Pointer(c_s)) c_encoding := C.CString(encoding) defer C.free(unsafe.Pointer(c_encoding)) c_errors := C.CString(errors) defer C.free(unsafe.Pointer(c_errors)) return togo(C.PyString_Decode(c_s, C.Py_ssize_t(sz), c_encoding, c_errors)) }
func GenerateEnviron(r *http.Request) *C.PyObject { _environ := C.PyDict_New() for k, _items := range r.Header { _values_tuple := C.PyTuple_New(C.Py_ssize_t(len(_items))) for i, _item := range _items { C.PyTuple_SetItem( _values_tuple, C.Py_ssize_t(i), PyString_FromString(_item), ) } C.PyDict_SetItem( _environ, PyString_FromString(k), _values_tuple, ) } //_environ = upgrade_to_wsgi(r, _environ) return _environ }
func (o *object) ItemValue(t *Thread, i int) (item interface{}, err error) { t.execute(func() { pyItem := C.PySequence_GetItem(o.pyObject, C.Py_ssize_t(i)) if pyItem == nil { err = getError() return } defer C.DECREF(pyItem) item, err = decode(pyItem) }) return }
// PyObject* PyString_Encode(const char *s, Py_ssize_t size, const char *encoding, const char *errors) // Return value: New reference. // Encode the char buffer of the given size by passing it to the codec registered for encoding and return a Python object. encoding and errors have the same meaning as the parameters of the same name in the string encode() method. The codec to be used is looked up using the Python codec registry. Return NULL if an exception was raised by the codec. // // Note This function is not available in 3.x and does not have a PyBytes alias. // Changed in version 2.5: This function used an int type for size. This might require changes in your code for properly supporting 64-bit systems. func PyString_Encode(s, encoding, errors string) *PyObject { c_s := C.CString(s) defer C.free(unsafe.Pointer(c_s)) c_encoding := C.CString(encoding) defer C.free(unsafe.Pointer(c_encoding)) c_errors := C.CString(errors) defer C.free(unsafe.Pointer(c_errors)) // FIXME should check if len is len of rune or of string return togo(C.PyString_Encode(c_s, C.Py_ssize_t(len(s)), c_encoding, c_errors)) }
func PickleLoads(data string) interface{} { pickle_lock.Lock() _pickle_init() datastr := C.CString(data) str := C.PyString_FromStringAndSize(datastr, C.Py_ssize_t(len(data))) C.free(unsafe.Pointer(datastr)) obj := C.PyObject_CallFunction1(pickle_loads, str) v := pyObjToInterface(obj) C.Py_DecRef(obj) C.Py_DecRef(str) pickle_lock.Unlock() return v }
func PickleLoads(data []byte) interface{} { pickleLock.Lock() if initialized == 0 { pickleInit() } str := C.PyString_FromStringAndSize((*C.char)(unsafe.Pointer(&data[0])), C.Py_ssize_t(len(data))) obj := C.PyObject_CallFunction1(pickleLoads, str) v := pyObjToInterface(obj) C.Py_DecRef(obj) C.Py_DecRef(str) pickleLock.Unlock() return v }
func (o *object) Item(t *Thread, i int) (item Object, err error) { t.execute(func() { pyItem := C.PySequence_GetItem(o.pyObject, C.Py_ssize_t(i)) if pyItem == nil { err = getError() return } defer C.DECREF(pyItem) item = newObject(pyItem) }) return }
func wsgi_callback(r *http.Request, w http.ResponseWriter) { _environ := upgrade_header_to_wsgi(r) _body_request, _ := ioutil.ReadAll(r.Body) _body_c := C.CString(string(_body_request)) defer C.free(unsafe.Pointer(_body_c)) _response := C.run_wsgi_application(_body_c, _environ) defer func() { C.Py_DecRef(_response.body) C.Py_DecRef(_response.headers) C.Py_DecRef(_response.error) }() if _response.headers == nil || int(_response.status) == 0 { w.WriteHeader(500) log_wsgi.Error("failed to run python wsgi module.") return } // parse header for i := 0; i < int(C.PyList_Size(_response.headers)); i++ { _h := C.PyList_GetItem(_response.headers, C.Py_ssize_t(i)) _k := C.PyTuple_GetItem(_h, C.Py_ssize_t(0)) _v := C.PyTuple_GetItem(_h, C.Py_ssize_t(1)) w.Header().Set( PyString_AsString(_k), PyString_AsString(_v), ) } // write body w.WriteHeader(int(_response.status)) if _response.body == nil { return } write_response_body(w, _response.body) }
// int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) // Iterate over all key-value pairs in the dictionary p. 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. // // For example: // // PyObject *key, *value; // Py_ssize_t pos = 0; // // while (PyDict_Next(self->dict, &pos, &key, &value)) { // // do something interesting with the values... // ... // } // The dictionary p should not be mutated during iteration. It is safe (since Python 2.1) to modify the values of the keys as you iterate over the dictionary, but only so long as the set of keys does not change. For example: // // PyObject *key, *value; // Py_ssize_t pos = 0; // // while (PyDict_Next(self->dict, &pos, &key, &value)) { // int i = PyInt_AS_LONG(value) + 1; // PyObject *o = PyInt_FromLong(i); // if (o == NULL) // return -1; // if (PyDict_SetItem(self->dict, key, o) < 0) { // Py_DECREF(o); // return -1; // } // Py_DECREF(o); // } // Changed in version 2.5: This function used an int * type for ppos. This might require changes in your code for properly supporting 64-bit systems. func PyDict_Next(self *PyObject, pos *int, key, value **PyObject) error { if pos == nil { return errors.New("invalid position") } c_pos := C.Py_ssize_t(*pos) c_key := topy(*key) c_val := topy(*value) err := C.PyDict_Next(topy(self), &c_pos, &c_key, &c_val) *pos = int(c_pos) *key = togo(c_key) *value = togo(c_val) return int2err(err) }
// decodeSequence translates a Python object to a Go array. func decodeSequence(pySequence *C.PyObject) (array []interface{}, err error) { length := int(C.PySequence_Size(pySequence)) array = make([]interface{}, length) for i := 0; i < length; i++ { pyValue := C.PySequence_GetItem(pySequence, C.Py_ssize_t(i)) if pyValue == nil { err = getError() return } var value interface{} if value, err = decode(pyValue); err != nil { return } array[i] = value } return }
// PyObject* PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) // Return value: New reference. // New in version 2.2. // // Changed in version 2.5: This function used an int type for nitems. This might require changes in your code for properly supporting 64-bit systems. func PyType_GenericAlloc(self *PyTypeObject, nitems int) *PyObject { return togo(C.PyType_GenericAlloc(self.ptr, C.Py_ssize_t(nitems))) }
// PyObject* PyInt_FromSsize_t(Py_ssize_t ival) // Return value: New reference. // Create a new integer object with a value of ival. If the value is larger than LONG_MAX or smaller than LONG_MIN, a long integer object is returned. // // New in version 2.5. func PyInt_FromSsize_t(val int) *PyObject { return togo(C.PyInt_FromSsize_t(C.Py_ssize_t(val))) }
// PyObject* PyLong_FromSsize_t(Py_ssize_t v) // Return value: New reference. // Return a new PyLongObject object from a C Py_ssize_t, or NULL on failure. // // New in version 2.6. func PyLong_FromSsize_t(v int) *PyObject { return togo(C.PyLong_FromSsize_t(C.Py_ssize_t(v))) }