I am writing a Go program. From this Go program, I would like to call a Python function defined in another file and receive the function's return value so I can use it in subsequent processing in my Go program. I am having trouble getting any returned data back in my Go program though. Below is a minimum example of what I thought would work, but apparently doesn't:
gofile.go
package main
import "os/exec"
import "fmt"
func main() {
fmt.Println("here we go...")
program := "python"
arg0 := "-c"
arg1 := fmt.Sprintf("'import pythonfile; print pythonfile.cat_strings(\"%s\", \"%s\")'", "foo", "bar")
cmd := exec.Command(program, arg0, arg1)
fmt.Println("command args:", cmd.Args)
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println("Concatenation failed with error:", err.Error())
return
}
fmt.Println("concatentation length: ", len(out))
fmt.Println("concatenation: ", string(out))
fmt.Println("...done")
}
pythonfile.py
def cat_strings(a, b):
return a + b
If I call go run gofile
I get the following output:
here we go...
command args: [python -c 'import pythonfile; print pythonfile.cat_strings("foo", "bar")']
concatentation length: 0
concatenation:
...done
A few notes:
-c
flag in the Python invocation so I can call the function cat_strings
directly. Assume cat_strings
is part of a Python file full of utility functions that are used by other Python programs, hence why I don't have any if __name__ == __main__
business.print a + b
(instead of return a + b
); see the prior point about the function being part of a set of utility functions that ought to be callable by other Python code.cat_strings
function is fictional and for demonstration purposes; the real function is something I don't want to simply reimplement in Go. I really am interested in how I can call a Python function from Go and get the return value.Simply call a function to pass the output into the second function as a parameter will use the return value in another function python.
But we still can call Python in memory from Go (as a shared library), which is relatively fast. So compared to the shared memory solution from the Ardan Labs blogpost I'd argue it's a tradeoff for more approachability at the cost of reduced efficiency. Let's dive in.
Function returning another function in Python As we know functions are treated as first-class objects in Python, therefore we can return a function from another function. A first-class object is an object that can be assigned to a variable, passed as an argument to a function, or used as a return value in a function.
The return keyword in Python exits a function and tells Python to run the rest of the main program. A return keyword can send a value back to the main program. While values may have been defined in a function, you can send them back to your main program and read them throughout your code.
I managed to have some working code for this by simply removing the quote around the command itself:
package main
import "fmt"
import "os/exec"
func main() {
cmd := exec.Command("python", "-c", "import pythonfile; print pythonfile.cat_strings('foo', 'bar')")
fmt.Println(cmd.Args)
out, err := cmd.CombinedOutput()
if err != nil { fmt.Println(err); }
fmt.Println(string(out))
}
And sure enough, in the source, you have this function (for Windows, at least, I don't know if that works for other OSes):
// EscapeArg rewrites command line argument s as prescribed
// in http://msdn.microsoft.com/en-us/library/ms880421.
// This function returns "" (2 double quotes) if s is empty.
// Alternatively, these transformations are done:
// - every back slash (\) is doubled, but only if immediately
// followed by double quote (");
// - every double quote (") is escaped by back slash (\);
// - finally, s is wrapped with double quotes (arg -> "arg"),
// but only if there is space or tab inside s.
func EscapeArg(s string) string { ...
So your code is ending up passing the following command line call:
$ python -c "'import pythonfile; print pythonfile.cat_strings(\\"foo\\", \\"bar\\")'"
Which, if tested, evaluates to a string and returns nothing, hence the 0-length output.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With