Run function from a package by reflecting its name - go

Currently I have the package name as a string "Forecast" and I need to reflect this string into a package so that I can call the function Run(). Is there a way to make this reflection?
Why?
Currently I am building a task runner in golang in which all tasks have the function Run() and I receive which task to run by a kafka message "task": "Forecast", so I am trying to avoid a switch like:
switch message.Task {
case "Forecast":
Forecast.Run()
case "SupplyCalculator":
SupplyCalculator.Run()
}
And instead to just reflect the name and call the function, like this (PHP):
$task = new ReflectionClass("\\Task\\{$message->task}");
$task->run();

Packages are not a type in Go.
Given a package foo with a function Run, this works...
v := reflect.ValueOf(foo.Run)
fmt.Println(v.Kind()) // func
But this is a syntax error:
v := reflect.ValueOf(foo)
Instead of trying to use reflection, register the functions ahead of time in a map, and then look up the correct function in that map to call it. You can provide a simple tasks package to do this, with methods like Register and Run.
// tasks.go
package tasks
type TaskFunc func() error // or whatever arguments your tasks take
var taskFuncs = map[string]TaskFunc{}
func Register(name string, fn TaskFunc) {
taskFuncs[name] = fn
}
func Run(name string) error {
if fn, found := taskFuncs[name]; found {
return fn()
}
return fmt.Errorf("Task %q not found", name)
}
// forecast.go
package forecast
import "tasks"
tasks.Register("forecast", Run)
func Run() error {
// ...
}
// main.go
err := tasks.Run(message.Task)

Related

Substitute an interface for another one

Recently, I stumbled upon an interesting article regarding the organization of Go code.
I was highly interested in the third approach: 'Independent Packages'.
It's said packages should NOT depend on each other, but defines their own interface to interact with
other packages.
Now, I face a problem with the approach.
I'm building a CLI applications that's able to execute various commands, one after each other.
It's important that these commands can share data.
First, I have built a contain in which I can register commands.
This resides in the package: commands.
import (
"sync"
)
// PRIVATE: Defines the API for sharing data between different commands.
type context interface {
SetAPIKey(string)
}
// Command defines the API that a command should satisfy to become eligible for being registered in the container
type Command interface {
GetName() string
SetProperty(prop, val string) error
Execute(context) error
}
// Container exposes the API for registration and retrieving of services.
type Container struct {
mutex sync.RWMutex
registeredCommands map[string]Command
}
// Add adds the command `cmd` to the container.
func (c *Container) Add(cmd Command) {
c.withLock(func() {
if c.registeredCommands == nil {
c.registeredCommands = make(map[string]Command)
}
c.registeredCommands[cmd.GetName()] = cmd
})
}
// Get retrieves the command who's name is equal to `name`.
func (c *Container) Get(name string) Command {
return c.withRLock(func() Command {
if c.registeredCommands == nil {
return nil
}
if cmd, ok := c.registeredCommands[name]; ok {
return cmd
}
return nil
})
}
// PRIVATE: Create a lock and execute the function `handler`.
// The lock is being released when the function `handler` is executed.
func (c *Container) withLock(handler func()) {
c.mutex.Lock()
defer c.mutex.Unlock()
handler()
}
// PRIVATE: Create a "read" lock and execute the function `handler`.
// The lock is being released when the function `handler` is executed.
func (c *Container) withRLock(handler func() Command) Command {
c.mutex.RLock()
defer c.mutex.RUnlock()
return handler()
}
Next I have command which implements the Command interface.
NOTE: This is stored in another package: rest.
import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"net/http"
)
// PRIVATE: Defines the API for sharing data between different commands.
type context interface {
SetAPIKey(string)
}
// GetAPIKey is the command that allows the retrieval of a API key using the REST API.
type GetAPIKey struct {
}
// GetName returns the name of the command `cmd`.
func (GetAPIKey) GetName() string {
return "API.REST.getAPIKey"
}
// SetProperty assigns value `val` to property `prop`.
func (cmd *GetAPIKey) SetProperty(prop, val string) error {
return nil
}
// Execute runs the command.
func (cmd GetAPIKey) Execute(ctx context) error {
ctx.SetAPIKey(xmlResponse.Key)
return nil
}
The MAIN entry point of the application is defined as follows:
// PRIVATE: Defines the container in which all the available commands are registered.
var container commands.Container
// PRIVATE: Defines an API for sharing data between different commands.
type context struct {
apiKey string
}
func (ctx *context) SetAPIKey(value string) {
ctx.apiKey = value
}
// PRIVATE: Represents the 'MAIN' entry point for the application.
// This method is executed when the application is started.
func main() {
ctx := &context{}
container := new(commands.Container)
container.Add(&rest.GetAPIKey{})
command := container.Get("API.REST.getAPIKey")
command.SetProperty("baseURI", "")
command.SetProperty("environmentNameOrURL", "")
command.SetProperty("userName", "")
command.SetProperty("password", "")
err := command.Execute(ctx)
if err != nil {
fmt.Println(err)
}
}
However this code does NOT compile.
It yields the following error:
.\app.go:53:16: cannot use &rest.GetAPIKey literal (type *rest.GetAPIKey) as type commands.Command in argument to container.Add:
*rest.GetAPIKey does not implement commands.Command (wrong type for Execute method)
have Execute(rest.context) error
want Execute(commands.context) error
Is there any way to make this code work?
So the context interface is defined in 2 places.
This will not work because the function signatures don't match. You can do:
Make Context a shared interface, so every package can use it. This does not necessarily introduce dependencies between packages using it.
Use context.Context, and expect to see the key in there. This is essentially the same as #1 but using a known predefined type
Use interface{} instead of context, and use type assertion to convert it to the local context type.

Godog pass arguments/state between steps

To comply with the concurrency requirements, I'm wondering how to pass arguments or a state between multiple steps in Godog.
func FeatureContext(s *godog.Suite) {
// This step is called in background
s.Step(`^I work with "([^"]*)" entities`, iWorkWithEntities)
// This step should know about the type of entity
s.Step(`^I run the "([^"]*)" mutation with the arguments:$`, iRunTheMutationWithTheArguments)
The only idea which comes to my mind is to inline the called function:
state := make(map[string]string, 0)
s.Step(`^I work with "([^"]*)" entities`, func(entityName string) error {
return iWorkWithEntities(entityName, state)
})
s.Step(`^I run the "([^"]*)" mutation with the arguments:$`, func(mutationName string, args *messages.PickleStepArgument_PickleTable) error {
return iRunTheMutationWithTheArguments(mutationName, args, state)
})
But this feels a bit like a workaround. Is there any feature in the Godog library itself to pass those information?
I've found good luck using methods instead of functions for the steps. Then, putting state in the struct.
func FeatureContext(s *godog.Suite) {
t := NewTestRunner()
s.Step(`^I work with "([^"]*)" entities`, t.iWorkWithEntities)
}
type TestRunner struct {
State map[string]interface{}
}
func (t *TestRunner) iWorkWithEntities(s string) error {
t.State["entities"] = s
...
}
Godog doesn't currently have a feature like this, but what I've done in the past in general (would need to be tested for concurrency) would be to create a TestContext struct to store data in and create a fresh one before each Scenario.
func FeatureContext(s *godog.Suite) {
config := config.NewConfig()
context := NewTestContext(config)
t := &tester{
TestContext: context,
}
s.BeforeScenario(func(interface{}) {
// reset context between scenarios to avoid
// cross contamination of data
context = NewTestContext(config)
})
}
I have a link to an old example here as well: https://github.com/jaysonesmith/godog-baseline-example
Latest version (v0.12.0+) of godog allows chaining context.Context between hooks and steps.
You can have context.Context as step definition argument and return, test runner will provide context from previous step as input and use returned context to pass to next hooks and steps.
func iEat(ctx context.Context, arg1 int) context.Context {
if v, ok := ctx.Value(eatKey{}).int; ok {
// Eat v from context.
}
// Eat arg1.
return context.WithValue(ctx, eatKey{}, 0)
}
Additional information and examples: https://github.com/cucumber/godog/blob/main/release-notes/v0.12.0.md#contextualized-hooks.

Function pointer as argument with "return interface{}"

I would like to pass a function pointer to a function to "anything".
It's easy to print something that gets passed in from just about anything (as in https://play.golang.org/p/gmOy6JWxGm0):
func printStuff(stuff interface{}) {
fmt.Printf("Testing : %v", stuff)
}
Let's say, though, that I want to do this:
Have multiple structs
Have data loaded from various functions
Have a generic print that calls the function for me
I tried this in a Play (https://play.golang.org/p/l3-OkL6tsMW) and I get the following errors:
./prog.go:35:12: cannot use getStuff1 (type func() SomeObject) as type FuncType in argument to printStuff
./prog.go:36:12: cannot use getStuff2 (type func() SomeOtherObject) as type FuncType in argument to printStuff
In case the Play stuff gets deleted, here's the code I'm trying to figure out how to get to work:
package main
import (
"fmt"
)
type SomeObject struct {
Value string
}
type SomeOtherObject struct {
Value string
}
type FuncType func() interface{}
func getStuff1() SomeObject {
return SomeObject{
Value: "Hello, world!",
}
}
func getStuff2() SomeOtherObject {
return SomeOtherObject{
Value: "Another, hello!",
}
}
func printStuff(toCall FuncType) {
stuff := toCall()
fmt.Printf("Testing : %v", stuff)
}
func main() {
printStuff(getStuff1)
printStuff(getStuff2)
}
What is the secret sauce to get this stuff passed in properly?
Larger Goal Explanation
So what I am trying to accomplish here is reduction of boilerplate code that lives inside a gigantic file. Unfortunately I cannot refactor it further at this point due to other restrictions and I was wondering if this were possible at all considering the error messages and what I had read seemed to dictate otherwise.
There's a large amount of copy-and-paste code that looks like this:
func resendContraDevice(trap *TrapLapse, operation *TrapOperation) {
loaded := contra.Load()
err := trap.SnapBack(operation).send(loaded);
// default error handling
// logging
// boilerplate post-process
}
func resendPolicyDevice(trap *TrapLapse, operation *TrapOperation) {
loaded := policy.Load()
err := trap.SnapBack(operation).send(loaded);
// default error handling
// logging
// boilerplate post-process
}
// etc.
In these, the Load() functions all return a different struct type and they are used elsewhere throughout the application.
I want hoping to get something where I could have:
loaded := fn()
err := trap.SnapBack(operation).send(loaded);
// default error handling
// logging
// boilerplate post-process
Signature for send is, which accepts an interface{} argument:
func (s SnapBack) send(data interface{}) error
I don't know if you have control over the return values of contra.Load() and policy.Load(), for instance, so there may be a better approach, but assuming those cannot be modified, this would allow you to eliminate a lot of boilerplate, without any fancy manipulation:
func boilerplate(tram *TrapLapse, operation *TrapOperation, loader func() interface{}) {
loaded := loader()
err := trap.SnapBack(operation).send(loaded);
// default error handling
// logging
// boilerplate post-process
}
func resendContraDevice(trap *TrapLapse, operation *TrapOperation) {
boilerplate(trap, operation, func() interface{} { return contra.Load() })
}
func resendPolicyDevice(trap *TrapLapse, operation *TrapOperation) {
boilerplate(trap, operation, func() interface{} { return policy.Load() })
}
If there's nothing more complex, you can also simplify this even further:
func boilerplate(tram *TrapLapse, operation *TrapOperation, loaded interface{}) {
err := trap.SnapBack(operation).send(loaded);
// default error handling
// logging
// boilerplate post-process
}
func resendContraDevice(trap *TrapLapse, operation *TrapOperation) {
boilerplate(trap, operation, contra.Load())
}
func resendPolicyDevice(trap *TrapLapse, operation *TrapOperation) {
boilerplate(trap, operation, policy.Load())
}

Casting a string to a func type in GoLang

I have a string which is the name of a function in GoLang.
I want to treat them as function. How should I do this? I tried to achieve it through reflect.* but I didn't find a valid path for my purpose.
I get the name fo handlers in a JSON file, and I want to execute those handlers. Something like this:
{
"students/show" : "ShowStudents",
"students/add" : "AddStudents"
}
Then I want to execute ShowStudents(), but don't know how to treat it like a variable of type func
Your task can be broken down into two steps:
Extract function names
Run those functions (assuming that they are defined somewhere)
For step 1, I would unmarshal the JSON into a map[string]string, something like this:
b, err := ioutil.ReadFile(fname)
mp := make(map[string]string)
json.Unmarshal(b, &mp)
Coming to Step 2. In Go, it's not possible to convert string directly to a function call, but it is possible to enumerate the methods of an object using reflect package. This can be used as a workaround in this case. Instead of writing those functions directly, bind them to a dummy type, something like this:
type T int
func (t T) ShowStudents() {
fmt.Println("Showing Students")
}
func (t T) AddStudents() {
fmt.Println("Adding Students")
}
func main() {
var t T
reflect.ValueOf(t).MethodByName("ShowStudents").Call(nil)
}
Run this example
establish a mapping between the keys in the json file and the functions, then use that to call the functions as they appear in the json
package main
import (
"encoding/json"
"fmt"
)
func AddStudents() {
fmt.Println("woo")
}
func ShowStudents() {
fmt.Println("lots of students")
}
func main() {
js := `{
"students/show" : "ShowStudents",
"students/add" : "AddStudents"
}`
lookup := make(map[string]string)
json.Unmarshal([]byte(js), &lookup)
dispatch := make(map[string]func())
dispatch["students/show"] = ShowStudents
dispatch["students/add"] = AddStudents
for v, _ := range lookup {
print(v)
dispatch[v]()
}
}

Plugin symbol as function return

I'm running into a Go behaviour which I don't understand. My idea is to import a plugin which implements an interface that is out of both packages. If a struct is returned it works fine, but to be sure it implements the interface, I want to return an interface which fails.
Interface definition:
package iface
type IPlugin interface{
SayHello(string)
SayGoodby(string)
WhatsYourName() string
}
The main program looks like this:
package main
import (
"plugin"
"plugin_test/iface"
"errors"
"fmt"
)
//go:generate go build -buildmode=plugin -o ./pg/test.so ./pg/test.go
func main(){
path := "pg/test.so"
plug, err := plugin.Open(path)
if err != nil {
panic(err)
}
sym, err := plug.Lookup("Greeter")
if err != nil {
panic(err)
}
var pg iface.IPlugin
pg, ok := sym.(iface.IPlugin)
if !ok {
panic(errors.New("error binding plugin to interface"))
}
fmt.Printf("You're now connected to: %s \n", pg.WhatsYourName())
pg.SayHello("user")
pg.SayGoodby("user")
}
The plugin (stored in pg/test.go)
package main
import (
"fmt"
"plugin_test/iface"
)
type testpl struct {}
func(pl testpl) SayHello(s string){
fmt.Printf("Plugin says hello to %s \n", s)
}
func(pl testpl) SayGoodby(s string){
fmt.Printf("Plugin says goodby to %s \n", s)
}
func(pl testpl) WhatsYourName() string{
return "my name is Test-Plugin"
}
/* This function works */
func getPlugin() testpl{
return testpl{}
}
/* This function doesn't work */
func getPlugin() iface.IPlugin{
return testpl{}
}
/* This function also doesn't work */
func getPlugin() interface{}{
return testpl{}
}
var Greeter = getPlugin()
I tried every getPlugin function on its own.
The function returning testpl prints the expected output:
You're now connected to: my name is Test-Plugin
Plugin says hello to user
Plugin says goodby to user
The other functions end on sym.(iface.IPlugin)
panic: error binding plugin to interface
goroutine 1 [running]:
main.main()
/home/../../../main.go:27 +0x343
exit status 2
Can someone explain why this isn't possible? Wouldn't it be easier to create a plugin if it did't let you build your plugin in such a case?
What you want is possible, but there is something in the background that prevents it from working.
This is namely that you want to lookup a variable named Greeter from the plugin. Plugin.Lookup() will return a pointer to this variable! If it wouldn't, you could only inspect its value, but you couldn't change it.
You can verify this by simply printing the type of the value stored in sym:
fmt.Printf("%T\n", sym)
In your first case func getPlugin() testpl, output will be:
*main.testpl
In your second case func getPlugin() iface.IPlugin, output will be:
*iface.IPlugin
(Yes, it's a pointer to an interface!)
In your third case func getPlugin() interface{}, output will be:
*interface {}
So your first example works because the value stored in sym is of type *main.testpl, which also implements iface.IPlugin (because main.testpl implements it, so does the pointer type).
Back to your 2nd example: func getPlugin() iface.IPlugin
The value stored in sym is of type *iface.IPlugin. A value of pointer type to interface never satisfies any interfaces (except the empty interface), so attempting to type-assert iface.IPlugin from a value of type *iface.IPlugin will never succeed. You have to type assert *iface.IPlugin type, which you can dereference after to obtain a value of type iface.IPlugin. It could look like this:
pgPtr, ok := sym.(*iface.IPlugin)
if !ok {
panic(errors.New("error binding plugin to interface"))
}
pg := *pgPtr
And now everything works as expected!
To avoid such hassle and confusion, you may implement your plugin to expose a function which returns you the Greeter:
func Greeter() iface.IPlugin { return testpl{} }
And then get rid of the Greeter global var of course. If you do this, you may lookup the Greeter symbol which will be of type:
func() iface.IPlugin
The difference is that looking up a function does not require the plugin package to return a pointer to the value, while in case of a variable it does. A simple function type, no pointer-to-interface-kung-fu. Using it to obtain the greeter would be:
Greeter, err := p.Lookup("Greeter")
if err != nil {
panic(err)
}
greeterFunc, ok := GetFilter.(func() iface.IPlugin)
if !ok {
panic(errors.New("not of expected type"))
}
greeter := greeterFunc()
// And using it:
fmt.Printf("You're now connected to: %s \n", greeter.WhatsYourName())
greeter.SayHello("user")
greeter.SayGoodby("user")
See related / similar question: go 1.8 plugin use custom interface

Resources