traefik / yaegi

Yaegi is Another Elegant Go Interpreter
https://pkg.go.dev/github.com/traefik/yaegi
Apache License 2.0
6.94k stars 343 forks source link

Cannot convert interp.valueInterface to a function #1528

Open dcalsky opened 1 year ago

dcalsky commented 1 year ago

The following program sample.go triggers an unexpected result

package main

import (
    "fmt"
    "github.com/traefik/yaegi/interp"
    "github.com/traefik/yaegi/stdlib"
    "go/build"
    "log"
    "os"
    "yaegi/pluginpkg"
)

func main() {
    i := interp.New(interp.Options{
        GoPath: build.Default.GOPATH,
    })
    i.Use(stdlib.Symbols)

    // Load plugin code from file
    pluginPath := "plugins/plugin1.go"
    pluginCode, err := os.ReadFile(pluginPath)
    if err != nil {
        log.Fatalf("Error reading plugin file: %v", err)
    }

    // Evaluate the plugin code
    _, err = i.Eval(string(pluginCode))
    if err != nil {
        log.Fatalf("Error evaluating plugin code: %v", err)
    }
    // Load the NewPlugin function from the plugin
    newPluginSymbol, err := i.Eval("plugins.NewPlugin")

    if err != nil {
        log.Fatalf("Error looking up NewPlugin symbol: %v", err)
    }
    fmt.Printf("NewPlugin type: %T\n", newPluginSymbol.Interface())
    // Assert the function type
    newPluginFunc, ok := newPluginSymbol.Interface().(func() pluginpkg.Plugin)
    if !ok {
        log.Fatalf("Error: NewPlugin symbol is not a func() pluginpkg.Plugin") // <- an exception occurred over here
    }
}

Expected result

expect there is no error

Got

don't use yaegi cli

Yaegi Version

0.15.0

Additional Notes

go.mod

module yaegi

go 1.20

require github.com/traefik/yaegi v0.15.0

plugins/plugin1.go

package plugins

import (
    "fmt"
    "yaegi/pluginpkg"
)

type Plugin1 struct{}

func (p *Plugin1) Initialize() error {
    // Initialization logic
    return nil
}

func (p *Plugin1) Execute(args interface{}) (interface{}, error) {
    // Plugin logic
    fmt.Println("Plugin1 executed")
    return nil, nil
}

func (p *Plugin1) GetName() string {
    return "Plugin1"
}

func NewPlugin() pluginpkg.Plugin {
    return &Plugin1{}
}

pluginpkg/plugin.go: a common interface for main.go and plugin1.go

package pluginpkg

type Plugin interface {
    Initialize() error
    Execute(args interface{}) (interface{}, error)
    GetName() string
}

Output:

NewPlugin type: func() interp.valueInterface
2023/03/21 21:52:31 Error: NewPlugin symbol is not a func() pluginpkg.Plugin
mvertes commented 1 year ago

I'm not sure there is an issue here (a bug in yaegi). It seems to be a question about how integrate the interpreter with the runtime. If you need to share data structures between the runtime and the interpreter, the best starting point is to look at on how the runtime symbols are exported to the interpreter, for example using yaegi extract, and looking at the stdlib wrappers.

Regarding exporting interfaces, unfortunately more boilerplate is required due to the necessity of work around some Go reflect limitations.

dcalsky commented 1 year ago

I'm not sure there is an issue here (a bug in yaegi). It seems to be a question about how integrate the interpreter with the runtime. If you need to share data structures between the runtime and the interpreter, the best starting point is to look at on how the runtime symbols are exported to the interpreter, for example using yaegi extract, and looking at the stdlib wrappers.

Regarding exporting interfaces, unfortunately more boilerplate is required due to the necessity of work around some Go reflect limitations.

OK, I'm going to try yaegi extract