olivier.va
olivier.va

Reputation: 129

How do I make variables from a Python module available in Go?

I am building a tool in Go which needs to provide a way to resolve variables declared in the global scope of Python scripts. In the future I would like to extend this to Node.js as well. It needs to be cross-platform.

Basically, if someone were to have the following Python code:

#!/usr/bin/env python

hello = "world"
some_var = "one"
another_var = "two"
var_three = some_func()

I would like to have access to these variable keys and values in my Golang code. In case of the function, I would like to have access to the value it returns.

My current idea is to run the script with the Golang exec.Command function and have the variables printed to its stdout in some format (e.g. JSON), which in turn can be parsed with Golang. Thoughts?

Upvotes: 0

Views: 983

Answers (1)

Koala Yeung
Koala Yeung

Reputation: 7873

They are of different runtime environments. Golang cannot directly access variables in Python's runtime. Vica versa. You can, however, program them to pass on variable values through standard I/O or environment variables. The key is to determine the proper format for information exchanges.

For example, if the python script takes arguments as input and print the result, encoded as JSON, to the stdout. Then you can call the script with proper arguments, and decode the stdout as JSON.

Such as:

range.py

import json
import sys

def toNum(str):
    return int(str)

def main(argv):
    # Basically called range() with all the arguments from script call
    print(json.dumps(list(range(*map(toNum, argv)))))

if __name__ == '__main__':
    main(sys.argv[1:])

main.go

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "os/exec"
)

func pythonRange(start, stop, step int) (c []byte, err error) {
    return exec.Command(
        "python3",
        "./range.py",
        fmt.Sprintf("%d", start),
        fmt.Sprintf("%d", stop),
        fmt.Sprintf("%d", step),
    ).Output()
}

func main() {
    var arr []int

    // get the output of the python script
    result, err := pythonRange(1, 10, 1)
    if err != nil {
        log.Fatal(err)
    }

    // decode the stdout of the python script
    // as a json array of integer
    err = json.Unmarshal(result, &arr)
    if err != nil {
        log.Fatal(err)
    }

    // show the result with log.Printf
    log.Printf("%#v", arr)
}

Output global variables

To output global variables in Python as JSON object:

import json

def dump_globals():
    # Basically called range() with all the arguments from script call
    vars = dict()
    for (key, value) in globals().items():
        if key.startswith("__") and key.endswith("__"):
            continue # skip __varname__ variables
        try:
            json.dumps(value) # test if value is json serializable
            vars[key] = value
        except:
            continue
    print(json.dumps(vars))

foo = "foo"
bar = "bar"

dump_globals()

Output:

{"foo": "foo", "bar": "bar"}

You can use a main() similar to the last one for this script:


import (
    "encoding/json"
    "fmt"
    "log"
    "os/exec"
)

func pythonGetVars() (c []byte, err error) {
    return exec.Command(
        "python3",
        "./dump_globals.py",
    ).Output()
}

func main() {
    var vars map[string]interface{}

    // get the output of the python script
    result, err := pythonGetVars()
    if err != nil {
        log.Fatal(err)
    }

    // decode the json object
    err = json.Unmarshal(result, &vars)
    if err != nil {
        log.Fatal(err)
    }

    // show the result with log.Printf
    fmt.Printf("%#v", vars)
}

Output:

map[string]interface {}{"bar":"bar", "foo":"foo"}

Upvotes: 2

Related Questions