Go API Documentation

github.com/TykTechnologies/tyk/dlpython

No package summary is available.

Package

Files: 5. Third party imports: 0. Imports from organisation: 0. Tests: 0. Benchmarks: 0.

Constants

const (
	pythonPathKey = "PYTHONPATH"
)

Vars

var (
	errEmptyPath		= errors.New("Empty PATH")
	errLibNotFound		= errors.New("Library not found")
	errLibLoad		= errors.New("Couldn't load library")
	errOSNotSupported	= errors.New("OS not supported")

	pythonExpr	= regexp.MustCompile(`(^python3(\.)?(\d+)?(m)?(\-config)?$)`)

	pythonConfigPath	string
	pythonLibraryPath	string

	logger	= log.Get().WithField("prefix", "dlpython")

	paths	= os.Getenv("PATH")
)
var libPath * /*line :178:14*/ _Ctype_char	/*line :178:20*/

Types

dummyPtr

This type doesn't have documentation.

Field name Field type Comment
type

unsafe.Pointer

No comment on field.
type dummyPtr unsafe.Pointer

pyobj

This type doesn't have documentation.

Field name Field type Comment
type

_Ctype_PyObject

No comment on field.
type pyobj /*line :108:12*/ _Ctype_PyObject	/*line :108:22*/

Functions

func FindPythonConfig

FindPythonConfig scans PATH for common python-config locations.

func FindPythonConfig(customVersion string) (selectedVersion string, err error) {
	logger.Debugf("Requested python version: %q", customVersion)

	// Not sure if this can be replaced with os.LookPath:
	if paths == "" {
		return selectedVersion, errEmptyPath
	}

	// Scan python-config binaries:
	pythonConfigBinaries := map[string]string{}

	for _, p := range strings.Split(paths, ":") {
		if !strings.HasSuffix(p, "/bin") {
			continue
		}
		files, err := ioutil.ReadDir(p)
		if err != nil {
			continue
		}
		for _, f := range files {
			name := f.Name()
			fullPath := filepath.Join(p, name)
			matches := pythonExpr.FindAllStringSubmatch(name, -1)
			if len(matches) == 0 {
				continue
			}

			minorVersion := matches[0][3]
			pyMallocBuild := matches[0][4]
			isConfig := matches[0][5]
			version := "3"
			if minorVersion != "" {
				version += "." + minorVersion
			}
			if pyMallocBuild != "" {
				version += pyMallocBuild
			}

			if isConfig == "" {
				continue
			}

			if _, exists := pythonConfigBinaries[version]; !exists {
				pythonConfigBinaries[version] = fullPath
			}
		}
	}

	if len(pythonConfigBinaries) == 0 {
		return selectedVersion, errors.New("No Python installations found")
	}

	for ver, binPath := range pythonConfigBinaries {
		logger.Debugf("Found python-config binary: %s (%s)", ver, binPath)
	}

	if customVersion == "" {
		var availableVersions []string
		for k := range pythonConfigBinaries {
			availableVersions = append(availableVersions, k)
		}
		lastVersion := selectLatestVersion(availableVersions)

		pythonConfigPath = pythonConfigBinaries[lastVersion]
		selectedVersion = lastVersion

		logger.Debug("Using Python version", selectedVersion)
	} else {
		cfgPath, ok := pythonConfigBinaries[customVersion]
		if !ok {
			return selectedVersion, errors.New("No python-config was found for the specified version")
		}
		pythonConfigPath = cfgPath
		selectedVersion = customVersion
	}

	logger.Debugf("Selected Python configuration path: %s", pythonConfigPath)
	if err := getLibraryPathFromCfg(); err != nil {
		return selectedVersion, err
	}
	logger.Debugf("Selected Python library path: %s", pythonLibraryPath)
	return selectedVersion, nil
}

Cognitive complexity: 39, Cyclomatic complexity: 17

Uses: errors.New, filepath.Join, ioutil.ReadDir, strings.HasSuffix, strings.Split.

func GetItem

GetItem wraps PyDict_GetItemString

func GetItem(d unsafe.Pointer, k string) (unsafe.Pointer, error) {
	key := ( /*line :51:9*/ _Cfunc_CString /*line :51:17*/)(k)
	defer func() func() {
		_cgo0 := /*line :52:15*/ unsafe.Pointer(key)
		return func() { _cgoCheckPointer(_cgo0, nil); /*line :52:35*/ _Cfunc_free(_cgo0) }
	}()()
	obj := ToPyObject(d)
	item := PyDict_GetItemString(obj, key)
	if item == nil {
		return nil, errors.New("GetItem failed")
	}
	return unsafe.Pointer(item), nil
}

Cognitive complexity: 4, Cyclomatic complexity: 2

Uses: errors.New, unsafe.Pointer.

func Init

Init will initialize the Python runtime.

func Init() error {
	// Set the library path:
	libPath = ( /*line :183:12*/ _Cfunc_CString /*line :183:20*/)(pythonLibraryPath)
	defer func() func() {
		_cgo0 := /*line :184:15*/ unsafe.Pointer(libPath)
		return func() { _cgoCheckPointer(_cgo0, nil); /*line :184:39*/ _Cfunc_free(_cgo0) }
	}()()

	// Map API calls and initialize runtime:
	err := mapCalls()
	if err != nil {
		return err
	}
	Py_Initialize()
	return nil
}

Cognitive complexity: 4, Cyclomatic complexity: 2

Uses: unsafe.Pointer.

func LoadModuleDict

LoadModuleDict wraps PyModule_GetDict.

func LoadModuleDict(m string) (unsafe.Pointer, error) {
	mod := ( /*line :32:9*/ _Cfunc_CString /*line :32:17*/)(m)
	defer func() func() {
		_cgo0 := /*line :33:15*/ unsafe.Pointer(mod)
		return func() { _cgoCheckPointer(_cgo0, nil); /*line :33:35*/ _Cfunc_free(_cgo0) }
	}()()
	modName := PyUnicode_FromString(mod)
	if modName == nil {
		return nil, errors.New("PyUnicode_FromString failed")
	}
	modObject := PyImport_Import(modName)
	if modObject == nil {
		return nil, errors.New("PyImport_Import failed")
	}
	dict := PyModule_GetDict(modObject)
	if dict == nil {
		return nil, errors.New("PyModule_GetDict failed")
	}
	return unsafe.Pointer(dict), nil
}

Cognitive complexity: 8, Cyclomatic complexity: 4

Uses: errors.New, unsafe.Pointer.

func PyBytesAsString

PyBytesAsString wraps PyBytes_AsString

func PyBytesAsString(o unsafe.Pointer, l int) ([]byte, error) {
	obj := ToPyObject(o)
	cstr := PyBytes_AsString(obj)
	if cstr == nil {
		return nil, errors.New("PyBytes_AsString as string failed")
	}
	str := func() []byte {
		_cgo0 := /*line :157:19*/ unsafe.Pointer(cstr)
		var _cgo1 _Ctype_int = _Ctype_int /*line :157:46*/ (l)
		_cgoCheckPointer(_cgo0, nil)
		return /*line :157:50*/ _Cfunc_GoBytes(_cgo0, _cgo1)
	}()
	b := []byte(str)
	return b, nil
}

Cognitive complexity: 3, Cyclomatic complexity: 2

Uses: errors.New, unsafe.Pointer.

func PyBytesFromString

PyBytesFromString wraps PyBytesFromString

func PyBytesFromString(input []byte) (unsafe.Pointer, error) {
	data := func() _cgo_unsafe.Pointer {
		_cgo0 := /*line :141:19*/ input
		_cgoCheckPointer(_cgo0, nil)
		return /*line :141:25*/ _Cfunc_CBytes(_cgo0)
	}()
	defer func() func() {
		_cgo0 := /*line :142:15*/ unsafe.Pointer(data)
		return func() { _cgoCheckPointer(_cgo0, nil); /*line :142:36*/ _Cfunc_free(_cgo0) }
	}()()
	ret := PyBytes_FromStringAndSize((* /*line :143:37*/ _Ctype_char /*line :143:43*/)(data) /*line :143:52*/, _Ctype_long /*line :143:58*/ (len(input)))
	if ret == nil {
		return nil, errors.New("PyBytesFromString failed")
	}
	return unsafe.Pointer(ret), nil
}

Cognitive complexity: 5, Cyclomatic complexity: 2

Uses: errors.New, unsafe.Pointer.

func PyBytes_AsString

func PyBytes_AsString(arg0 * /*line :118:29*/ _Ctype_PyObject /*line :118:39*/) * /*line :118:42*/ _Ctype_char /*line :118:48*/ {
	return ( /*line :119:9*/ _Cfunc_PyBytes_AsString /*line :119:26*/)(arg0)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyBytes_FromStringAndSize

func PyBytes_FromStringAndSize(arg0 * /*line :114:38*/ _Ctype_char /*line :114:44*/, arg1 /*line :114:51*/ _Ctype_long /*line :114:57*/) * /*line :114:60*/ _Ctype_PyObject /*line :114:70*/ {
	return ( /*line :115:9*/ _Cfunc_PyBytes_FromStringAndSize /*line :115:35*/)(arg0, arg1)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyDecRef

func PyDecRef(o unsafe.Pointer) {
	Py_DecRef(ToPyObject(o))
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyDict_GetItemString

func PyDict_GetItemString(dp * /*line :142:31*/ _Ctype_PyObject /*line :142:41*/, key * /*line :142:48*/ _Ctype_char /*line :142:54*/) * /*line :142:57*/ _Ctype_PyObject /*line :142:67*/ {
	return ( /*line :143:9*/ _Cfunc_PyDict_GetItemString /*line :143:30*/)(dp, key)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyErr_Print

func PyErr_Print() {
	( /*line :163:2*/ _Cfunc_PyErr_Print /*line :163:14*/)()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyEval_InitThreads

func PyEval_InitThreads() {
	( /*line :179:2*/ _Cfunc_PyEval_InitThreads /*line :179:21*/)()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyEval_SaveThread

func PyEval_SaveThread() * /*line :174:27*/ _Ctype_PyThreadState /*line :174:42*/ {
	return ( /*line :175:9*/ _Cfunc_PyEval_SaveThread /*line :175:27*/)()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyGILState_Ensure

func PyGILState_Ensure() /*line :150:26*/ _Ctype_PyGILState_STATE /*line :150:44*/ {
	return ( /*line :151:9*/ _Cfunc_PyGILState_Ensure /*line :151:27*/)()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyGILState_Release

func PyGILState_Release(arg0 /*line :154:30*/ _Ctype_PyGILState_STATE /*line :154:48*/) {
	( /*line :155:2*/ _Cfunc_PyGILState_Release /*line :155:21*/)(arg0)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyImportImport

func PyImportImport(modulePtr unsafe.Pointer) unsafe.Pointer {
	ptr := (* /*line :16:11*/ _Ctype_PyObject /*line :16:21*/)(modulePtr)
	ret := PyImport_Import(ptr)
	return unsafe.Pointer(ret)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: unsafe.Pointer.

func PyImport_Import

func PyImport_Import(name * /*line :182:28*/ _Ctype_PyObject /*line :182:38*/) * /*line :182:41*/ _Ctype_PyObject /*line :182:51*/ {
	return ( /*line :183:9*/ _Cfunc_PyImport_Import /*line :183:25*/)(name)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyIncRef

func PyIncRef(o unsafe.Pointer) {
	Py_IncRef(ToPyObject(o))
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyLongAsLong

PyLongAsLong wraps PyLong_AsLong

func PyLongAsLong(o unsafe.Pointer) int {
	l := PyLong_AsLong(ToPyObject(o))
	return int(l)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyLong_AsLong

func PyLong_AsLong(arg0 * /*line :126:26*/ _Ctype_PyObject /*line :126:36*/) /*line :126:38*/ _Ctype_long /*line :126:44*/ {
	return ( /*line :127:9*/ _Cfunc_PyLong_AsLong /*line :127:23*/)(arg0)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyModule_GetDict

func PyModule_GetDict(arg0 * /*line :146:29*/ _Ctype_PyObject /*line :146:39*/) * /*line :146:42*/ _Ctype_PyObject /*line :146:52*/ {
	return ( /*line :147:9*/ _Cfunc_PyModule_GetDict /*line :147:26*/)(arg0)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyObjectCallObject

PyObjectCallObject wraps PyObject_CallObject

func PyObjectCallObject(o unsafe.Pointer, args unsafe.Pointer) (unsafe.Pointer, error) {
	ret := PyObject_CallObject(ToPyObject(o), ToPyObject(args))
	if ret == nil {
		return nil, errors.New("PyObjectCallObject failed")
	}
	return unsafe.Pointer(ret), nil
}

Cognitive complexity: 2, Cyclomatic complexity: 2

Uses: errors.New, unsafe.Pointer.

func PyObjectGetAttr

PyObjectGetAttr wraps PyObject_GetAttr

func PyObjectGetAttr(o unsafe.Pointer, attr interface{}) (unsafe.Pointer, error) {
	switch attr.(type) {
	case string:
		str := ( /*line :124:10*/ _Cfunc_CString /*line :124:18*/)(attr.(string))
		defer func() func() {
			_cgo0 := /*line :125:16*/ unsafe.Pointer(str)
			return func() { _cgoCheckPointer(_cgo0, nil); /*line :125:36*/ _Cfunc_free(_cgo0) }
		}()()
		pystr := PyUnicode_FromString(str)
		if pystr == nil {
			return nil, errors.New("PyUnicode_FromString failed")
		}
		ret := PyObject_GetAttr(ToPyObject(o), pystr)
		if ret == nil {
			return nil, errors.New("PyObjectGetAttr failed")
		}
		return unsafe.Pointer(ret), nil
	}
	return nil, nil
}

Cognitive complexity: 10, Cyclomatic complexity: 5

Uses: errors.New, unsafe.Pointer.

func PyObject_CallObject

func PyObject_CallObject(callable_object * /*line :186:43*/ _Ctype_PyObject /*line :186:53*/, args * /*line :186:61*/ _Ctype_PyObject /*line :186:71*/) * /*line :186:74*/ _Ctype_PyObject /*line :186:84*/ {
	return ( /*line :187:9*/ _Cfunc_PyObject_CallObject /*line :187:29*/)(callable_object, args)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyObject_GetAttr

func PyObject_GetAttr(arg0 * /*line :110:29*/ _Ctype_PyObject /*line :110:39*/, arg1 * /*line :110:47*/ _Ctype_PyObject /*line :110:57*/) * /*line :110:60*/ _Ctype_PyObject /*line :110:70*/ {
	return ( /*line :111:9*/ _Cfunc_PyObject_GetAttr /*line :111:26*/)(arg0, arg1)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyRunSimpleString

PyRunSimpleString wraps PyRun_SimpleStringFlags

func PyRunSimpleString(s string) {
	cstr := ( /*line :63:10*/ _Cfunc_CString /*line :63:18*/)(s)
	defer func() func() {
		_cgo0 := /*line :64:15*/ unsafe.Pointer(cstr)
		return func() { _cgoCheckPointer(_cgo0, nil); /*line :64:36*/ _Cfunc_free(_cgo0) }
	}()()
	PyRun_SimpleStringFlags(cstr, nil)
}

Cognitive complexity: 2, Cyclomatic complexity: 1

Uses: unsafe.Pointer.

func PyRun_SimpleStringFlags

func PyRun_SimpleStringFlags(arg0 * /*line :158:36*/ _Ctype_char /*line :158:42*/, arg1 unsafe.Pointer) /*line :158:65*/ _Ctype_int /*line :158:70*/ {
	return func() _Ctype_int {
		var _cgo0 *_Ctype_char = /*line :159:35*/ arg0
		_cgo1 := /*line :159:41*/ arg1
		_cgoCheckPointer(_cgo1, nil)
		return /*line :159:46*/ _Cfunc_PyRun_SimpleStringFlags(_cgo0, _cgo1)
	}()
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func PyTupleGetItem

PyTupleGetItem wraps PyTuple_GetItem

func PyTupleGetItem(tup unsafe.Pointer, pos int) (unsafe.Pointer, error) {
	item := PyTuple_GetItem(ToPyObject(tup) /*line :104:43*/, _Ctype_long /*line :104:49*/ (pos))
	if item == nil {
		return nil, errors.New("PyTupleGetItem failed")
	}
	return unsafe.Pointer(item), nil
}

Cognitive complexity: 2, Cyclomatic complexity: 2

Uses: errors.New, unsafe.Pointer.

func PyTupleNew

PyTupleNew wraps PyTuple_New

func PyTupleNew(size int) (unsafe.Pointer, error) {
	tup := PyTuple_New( /*line :70:21*/ _Ctype_long /*line :70:27*/ (size))
	if tup == nil {
		return nil, errors.New("PyTupleNew failed")
	}
	return unsafe.Pointer(tup), nil
}

Cognitive complexity: 2, Cyclomatic complexity: 2

Uses: errors.New, unsafe.Pointer.

func PyTupleSetItem

PyTupleSetItem wraps PyTuple_SetItem

func PyTupleSetItem(tup unsafe.Pointer, pos int, o interface{}) error {
	switch o.(type) {
	case string:
		str := ( /*line :81:10*/ _Cfunc_CString /*line :81:18*/)(o.(string))
		defer func() func() {
			_cgo0 := /*line :82:16*/ unsafe.Pointer(str)
			return func() { _cgoCheckPointer(_cgo0, nil); /*line :82:36*/ _Cfunc_free(_cgo0) }
		}()()
		pystr := PyUnicode_FromString(str)
		if pystr == nil {
			return errors.New("PyUnicode_FromString failed")
		}
		ret := PyTuple_SetItem(ToPyObject(tup) /*line :87:43*/, _Ctype_long /*line :87:49*/ (pos), pystr)
		if ret != 0 {
			return errors.New("PyTuple_SetItem failed")
		}
	default:
		// Assume this is a PyObject
		obj := o.(unsafe.Pointer)
		ret := PyTuple_SetItem(ToPyObject(tup) /*line :94:43*/, _Ctype_long /*line :94:49*/ (pos), ToPyObject(obj))
		if ret != 0 {
			return errors.New("PyTuple_SetItem failed")
		}
	}
	return nil
}

Cognitive complexity: 12, Cyclomatic complexity: 6

Uses: errors.New, unsafe.Pointer.

func PyTuple_GetItem

func PyTuple_GetItem(arg0 * /*line :134:28*/ _Ctype_PyObject /*line :134:38*/, arg1 /*line :134:45*/ _Ctype_long /*line :134:51*/) * /*line :134:54*/ _Ctype_PyObject /*line :134:64*/ {
	return ( /*line :135:9*/ _Cfunc_PyTuple_GetItem /*line :135:25*/)(arg0, arg1)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyTuple_New

func PyTuple_New(size /*line :130:23*/ _Ctype_long /*line :130:29*/) * /*line :130:32*/ _Ctype_PyObject /*line :130:42*/ {
	return ( /*line :131:9*/ _Cfunc_PyTuple_New /*line :131:21*/)(size)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyTuple_SetItem

func PyTuple_SetItem(arg0 * /*line :138:28*/ _Ctype_PyObject /*line :138:38*/, arg1 /*line :138:45*/ _Ctype_long /*line :138:51*/, arg2 * /*line :138:59*/ _Ctype_PyObject /*line :138:69*/) /*line :138:71*/ _Ctype_int /*line :138:76*/ {
	return ( /*line :139:9*/ _Cfunc_PyTuple_SetItem /*line :139:25*/)(arg0, arg1, arg2)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func PyUnicodeFromString

func PyUnicodeFromString(s string) unsafe.Pointer {
	cstr := ( /*line :10:10*/ _Cfunc_CString /*line :10:18*/)(s)
	ret := PyUnicode_FromString(cstr)
	return unsafe.Pointer(ret)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: unsafe.Pointer.

func PyUnicode_FromString

func PyUnicode_FromString(u * /*line :122:30*/ _Ctype_char /*line :122:36*/) * /*line :122:39*/ _Ctype_PyObject /*line :122:49*/ {
	return ( /*line :123:9*/ _Cfunc_PyUnicode_FromString /*line :123:30*/)(u)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func Py_DecRef

func Py_DecRef(object * /*line :194:24*/ _Ctype_PyObject /*line :194:34*/) {
	( /*line :195:2*/ _Cfunc_Py_DecRef /*line :195:12*/)(object)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func Py_IncRef

func Py_IncRef(object * /*line :190:24*/ _Ctype_PyObject /*line :190:34*/) {
	( /*line :191:2*/ _Cfunc_Py_IncRef /*line :191:12*/)(object)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func Py_Initialize

func Py_Initialize() {
	( /*line :167:2*/ _Cfunc_Py_Initialize /*line :167:16*/)()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func Py_IsInitialized

func Py_IsInitialized() /*line :170:25*/ _Ctype_int /*line :170:30*/ {
	return ( /*line :171:9*/ _Cfunc_Py_IsInitialized /*line :171:26*/)()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func SetPythonPath

SetPythonPath is a helper for setting PYTHONPATH.

func SetPythonPath(p []string) {
	mergedPaths := strings.Join(p, ":")
	path := ( /*line :23:10*/ _Cfunc_CString /*line :23:18*/)(mergedPaths)
	defer func() func() {
		_cgo0 := /*line :24:15*/ unsafe.Pointer(path)
		return func() { _cgoCheckPointer(_cgo0, nil); /*line :24:36*/ _Cfunc_free(_cgo0) }
	}()()
	key := ( /*line :25:9*/ _Cfunc_CString /*line :25:17*/)(pythonPathKey)
	defer func() func() {
		_cgo0 := /*line :26:15*/ unsafe.Pointer(key)
		return func() { _cgoCheckPointer(_cgo0, nil); /*line :26:35*/ _Cfunc_free(_cgo0) }
	}()()
	( /*line :27:2*/ _Cfunc_setenv /*line :27:9*/)(key, path, 1)
}

Cognitive complexity: 4, Cyclomatic complexity: 1

Uses: strings.Join, unsafe.Pointer.

func ToPyObject

func ToPyObject(p unsafe.Pointer) * /*line :303:36*/ _Ctype_PyObject /*line :303:46*/ {
	o := (* /*line :304:9*/ _Ctype_PyObject /*line :304:19*/)(p)
	return o
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Private functions

func execPythonConfig

execPythonConfig () ([]byte, error)
References: exec.Command.

func getLibraryPathFromCfg

getLibraryPathFromCfg () error
References: filepath.Join, os.IsNotExist, os.Stat, runtime.GOOS, strings.Contains, strings.Replace, strings.Split.

func mapCalls

mapCalls () error
References: fmt.Errorf, unsafe.Pointer.

func selectLatestVersion

selectLatestVersion (versions []string) string
References: sort.Slice, strconv.Atoi, strings.Split.


Tests

Files: 2. Third party imports: 1. Imports from organisation: 0. Tests: 4. Benchmarks: 0.

Vars

var testVersion = "3.5"

Test functions

TestFindPythonConfig

TestInit

TestMain

References: os.Exit, os.Getenv.

TestVersionSelection

References: assert.Equal.