go-python / gpython

gpython is a python interpreter written in go "batteries not included"
BSD 3-Clause "New" or "Revised" License
870 stars 95 forks source link

Set attributes on python objects from Go code #218

Closed ahdekkers closed 1 year ago

ahdekkers commented 1 year ago

Hi,

Say I have the following code:

func init() {
    py.RegisterModule(&py.ModuleImpl{
        Info: py.ModuleInfo{
        Name: "lib",
    },
    Methods: []*py.Method{
        py.MustNewMethod("get_data", getDataFunc, 0, ""),
    },
    })
}

var DataType = py.NewType("Data", "")

type Data struct {
    Items []string
}

func (*Data) Type() *py.Type {
    return DataType
}

func getDataFunc(py.Object, py.Tuple) (py.Object, error) {
    return &Data{Items: []string{"one", "two"}}, nil
}

When I try something like the following in python:

import lib

data = lib.get_data()
print(data.Items)

I get Attribute not found error on Items. How can I go about setting Items as an accessible attribute on the Data object?

I tried variations of DataType.Dict["Items"] = py.NewList() but none of these attempts worked. Any suggestions or guidance in what I may be doing wrong would be greatly appreciated.

ahdekkers commented 1 year ago

For anyone else who has this issue, the solution is actually quite simple. The object (in this case the "Data" struct) needs to implement Igetattribute, which has the method Mgetattribute.

sbinet commented 1 year ago

great that you could address your issue from "first principles" :)

perhaps this warrants a little example, though ? WDYT ?

ahdekkers commented 1 year ago

Sure. Here is the above code with the solution implemented.

func init() {
    py.RegisterModule(&py.ModuleImpl{
        Info: py.ModuleInfo{
        Name: "lib",
    },
    Methods: []*py.Method{
        py.MustNewMethod("get_data", getDataFunc, 0, ""),
    },
    })
}

var DataType = py.NewType("Data", "")

type Data struct {
    Items []string
}

func (*Data) Type() *py.Type {
    return DataType
}

//Here is the solution which fixes the attribute error on data.Items in the python code
func (d *Data) M__getattribute__(key string) (py.Object, error) {
    if key == "Items" {
        //Convert from Go string array to python string array
        var pyItems []py.Object
        for _, item := range d.Items {
            pyItems = append(pyItems, py.String(item))
        }
        //Return d.Items as a python object
        return py.NewListFromItems(pyItems), nil
    }

    return nil, py.ExceptionNewf(py.AttributeError, "'%s' has no attribute '%s'", p.Type().Name, key)
}

func getDataFunc(py.Object, py.Tuple) (py.Object, error) {
    return &Data{Items: []string{"one", "two"}}, nil
}