// Copyright 2022 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"runtime"
"github.com/go-python/gpython/py"
)
// These gpython py.Object type delcarations are the bridge between gpython and embedded Go types.
var (
PyVacationStopType = py.NewType("Stop", "")
PyVacationType = py.NewType("Vacation", "")
)
// init is where you register your embedded module and attach methods to your embedded class types.
func init() {
// For each of your embedded python types, attach instance methods.
// When an instance method is invoked, the "self" py.Object is the instance.
PyVacationStopType.Dict["Set"] = py.MustNewMethod("Set", VacationStop_Set, 0, "")
PyVacationStopType.Dict["Get"] = py.MustNewMethod("Get", VacationStop_Get, 0, "")
PyVacationType.Dict["add_stops"] = py.MustNewMethod("Vacation.add_stops", Vacation_add_stops, 0, "")
PyVacationType.Dict["num_stops"] = py.MustNewMethod("Vacation.num_stops", Vacation_num_stops, 0, "")
PyVacationType.Dict["get_stop"] = py.MustNewMethod("Vacation.get_stop", Vacation_get_stop, 0, "")
// Bind methods attached at the module (global) level.
// When these are invoked, the first py.Object param (typically "self") is the bound *Module instance.
methods := []*py.Method{
py.MustNewMethod("VacationStop_new", VacationStop_new, 0, ""),
py.MustNewMethod("Vacation_new", Vacation_new, 0, ""),
}
// Register a ModuleImpl instance used by the gpython runtime to instantiate new py.Module when first imported.
py.RegisterModule(&py.ModuleImpl{
Info: py.ModuleInfo{
Name: "mylib_go",
Doc: "Example embedded python module",
},
Methods: methods,
Globals: py.StringDict{
"PY_VERSION": py.String("Python 3.4 (github.com/go-python/gpython)"),
"GO_VERSION": py.String(fmt.Sprintf("%s on %s %s", runtime.Version(), runtime.GOOS, runtime.GOARCH)),
"MYLIB_VERS": py.String("Vacation 1.0 by Fletch F. Fletcher"),
},
OnContextClosed: func(instance *py.Module) {
py.Println(instance, ">\n+++")
},
})
}
// VacationStop is an example Go struct to embed.
type VacationStop struct {
Desc py.String
NumNights py.Int
}
// Type comprises the py.Object interface, allowing a Go struct to be cast as a py.Object.
// Instance methods of an type are then attached to this type object
func (stop *VacationStop) Type() *py.Type {
return PyVacationStopType
}
func (stop *VacationStop) M__str__() (py.Object, error) {
line := fmt.Sprintf(" %-16v | %2v nights", stop.Desc, stop.NumNights)
return py.String(line), nil
}
func (stop *VacationStop) M__repr__() (py.Object, error) {
return stop.M__str__()
}
func VacationStop_new(module py.Object, args py.Tuple) (py.Object, error) {
stop := &VacationStop{}
VacationStop_Set(stop, args)
return stop, nil
}
// VacationStop_Set is an embedded instance method of VacationStop
func VacationStop_Set(self py.Object, args py.Tuple) (py.Object, error) {
stop := self.(*VacationStop)
// Check out other convenience functions in py/util.go
// Also available is py.ParseTuple(args, "si", ...)
err := py.LoadTuple(args, []interface{}{&stop.Desc, &stop.NumNights})
if err != nil {
return nil, err
}
/* Alternative util func is ParseTuple():
var desc, nights py.Object
err := py.ParseTuple(args, "si", &desc, &nights)
if err != nil {
return nil, err
}
stop.Desc = desc.(py.String)
stop.NumNights = desc.(py.Int)
*/
return py.None, nil
}
// VacationStop_Get is an embedded instance method of VacationStop
func VacationStop_Get(self py.Object, args py.Tuple) (py.Object, error) {
stop := self.(*VacationStop)
return py.Tuple{
stop.Desc,
stop.NumNights,
}, nil
}
type Vacation struct {
Stops []*VacationStop
MadeBy string
}
func (v *Vacation) Type() *py.Type {
return PyVacationType
}
func Vacation_new(module py.Object, args py.Tuple) (py.Object, error) {
v := &Vacation{}
// For Module-bound methods, we have easy access to the parent Module
py.LoadAttr(module, "MYLIB_VERS", &v.MadeBy)
ret := py.Tuple{
v,
py.String(v.MadeBy),
}
return ret, nil
}
func Vacation_num_stops(self py.Object, args py.Tuple) (py.Object, error) {
v := self.(*Vacation)
return py.Int(len(v.Stops)), nil
}
func Vacation_get_stop(self py.Object, args py.Tuple) (py.Object, error) {
v := self.(*Vacation)
// Check out other convenience functions in py/util.go
// If you would like to be a contributor for gpython, improving these or adding more is a great place to start!
stopNum, err := py.GetInt(args[0])
if err != nil {
return nil, err
}
if stopNum < 1 || int(stopNum) > len(v.Stops) {
return nil, py.ExceptionNewf(py.IndexError, "invalid stop index")
}
return py.Object(v.Stops[stopNum-1]), nil
}
func Vacation_add_stops(self py.Object, args py.Tuple) (py.Object, error) {
v := self.(*Vacation)
srcStops, ok := args[0].(py.Tuple)
if !ok {
return nil, py.ExceptionNewf(py.TypeError, "expected Tuple, got %T", args[0])
}
for _, arg := range srcStops {
stop, ok := arg.(*VacationStop)
if !ok {
return nil, py.ExceptionNewf(py.TypeError, "expected Stop, got %T", arg)
}
v.Stops = append(v.Stops, stop)
}
return py.None, nil
}