feat: support .python-version to set version (#12)
* feat: add support for defining version via .python-version * test: coverage for .python-version support * fix: double-printing due to untrimmed .python-version read
This commit is contained in:
parent
d6bce67296
commit
3ccf410f17
3 changed files with 110 additions and 4 deletions
|
@ -104,8 +104,10 @@ func Where(args []string, flags Flags, currentState State) error {
|
||||||
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
||||||
|
|
||||||
var printedPath string
|
var printedPath string
|
||||||
|
|
||||||
if selectedVersion == "SYSTEM" {
|
if selectedVersion == "SYSTEM" {
|
||||||
_, printedPath = DetermineSystemPython()
|
_, sysPath := DetermineSystemPython()
|
||||||
|
printedPath = fmt.Sprintf("%s (system)", sysPath)
|
||||||
} else {
|
} else {
|
||||||
tag := VersionStringToStruct(selectedVersion)
|
tag := VersionStringToStruct(selectedVersion)
|
||||||
printedPath = GetStatePath("runtimes", fmt.Sprintf("py-%s", selectedVersion), "bin", fmt.Sprintf("python%s", tag.MajorMinor()))
|
printedPath = GetStatePath("runtimes", fmt.Sprintf("py-%s", selectedVersion), "bin", fmt.Sprintf("python%s", tag.MajorMinor()))
|
||||||
|
|
|
@ -1,17 +1,52 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SearchForPythonVersionFile crawls up to the system root to find any
|
||||||
|
// .python-version file that could set the current version.
|
||||||
|
func SearchForPythonVersionFile() (string, bool) {
|
||||||
|
currentPath, _ := os.Getwd()
|
||||||
|
var versionFound string
|
||||||
|
for {
|
||||||
|
content, err := ioutil.ReadFile(path.Join(currentPath, ".python-version"))
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
versionFound = strings.TrimSpace(string(content))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPath := path.Dir(currentPath)
|
||||||
|
|
||||||
|
if currentPath == nextPath {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPath = nextPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionFound, versionFound != ""
|
||||||
|
}
|
||||||
|
|
||||||
// DetermineSelectedPythonVersion returns the Python runtime version that should be
|
// DetermineSelectedPythonVersion returns the Python runtime version that should be
|
||||||
// used according to v.
|
// used according to v.
|
||||||
//
|
//
|
||||||
// By default, 'SYSTEM' is returned, which signals that the non-v-managed Python
|
// First, v will look in the current directory and all its parents for a .python-version
|
||||||
// runtime is used.
|
// file that would indicate which version is preferred. If none are found, the global
|
||||||
|
// user-defined version (via `v use <version>`) is used. If there is none, the system
|
||||||
|
// Python version is used.
|
||||||
func DetermineSelectedPythonVersion(currentState State) (string, error) {
|
func DetermineSelectedPythonVersion(currentState State) (string, error) {
|
||||||
|
pythonFileVersion, pythonFileVersionFound := SearchForPythonVersionFile()
|
||||||
|
|
||||||
|
if pythonFileVersionFound {
|
||||||
|
return pythonFileVersion, nil
|
||||||
|
}
|
||||||
|
|
||||||
if len(currentState.GlobalVersion) != 0 {
|
if len(currentState.GlobalVersion) != 0 {
|
||||||
return currentState.GlobalVersion, nil
|
return currentState.GlobalVersion, nil
|
||||||
}
|
}
|
||||||
|
@ -27,8 +62,11 @@ func DetermineSystemPython() (string, string) {
|
||||||
pathWithoutShims := slices.DeleteFunc(strings.Split(currentPathEnv, ":"), func(element string) bool {
|
pathWithoutShims := slices.DeleteFunc(strings.Split(currentPathEnv, ":"), func(element string) bool {
|
||||||
return element == GetStatePath("shims")
|
return element == GetStatePath("shims")
|
||||||
})
|
})
|
||||||
|
|
||||||
// FIXME: This should be set through RunCommand instead.
|
// FIXME: This should be set through RunCommand instead.
|
||||||
os.Setenv("PATH", strings.Join(pathWithoutShims, ":"))
|
os.Setenv("PATH", strings.Join(pathWithoutShims, ":"))
|
||||||
|
defer os.Setenv("PATH", currentPathEnv)
|
||||||
|
|
||||||
whichOut, _ := RunCommand([]string{"which", "python"}, GetStatePath(), true)
|
whichOut, _ := RunCommand([]string{"which", "python"}, GetStatePath(), true)
|
||||||
versionOut, _ := RunCommand([]string{"python", "--version"}, GetStatePath(), true)
|
versionOut, _ := RunCommand([]string{"python", "--version"}, GetStatePath(), true)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,10 @@ import (
|
||||||
func SetupAndCleanupEnvironment(t *testing.T) func() {
|
func SetupAndCleanupEnvironment(t *testing.T) func() {
|
||||||
os.Setenv("V_ROOT", t.TempDir())
|
os.Setenv("V_ROOT", t.TempDir())
|
||||||
|
|
||||||
|
temporaryWd := t.TempDir()
|
||||||
|
|
||||||
|
os.Chdir(temporaryWd)
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
os.Unsetenv("V_ROOT")
|
os.Unsetenv("V_ROOT")
|
||||||
}
|
}
|
||||||
|
@ -43,6 +47,26 @@ func TestDetermineSystemPythonGetsUnshimmedPythonRuntime(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDetermineSelectedPythonVersionUsesPythonVersionFileIfFound(t *testing.T) {
|
||||||
|
defer SetupAndCleanupEnvironment(t)()
|
||||||
|
|
||||||
|
// Writing a mock user-defined state.
|
||||||
|
mockState := State{GlobalVersion: "1.0.0"}
|
||||||
|
statePath := GetStatePath("state.json")
|
||||||
|
stateData, _ := json.Marshal(mockState)
|
||||||
|
ioutil.WriteFile(statePath, stateData, 0750)
|
||||||
|
|
||||||
|
temporaryWd := t.TempDir()
|
||||||
|
os.Chdir(temporaryWd)
|
||||||
|
ioutil.WriteFile(path.Join(temporaryWd, ".python-version"), []byte("1.2.3"), 0750)
|
||||||
|
|
||||||
|
version, err := DetermineSelectedPythonVersion(ReadState())
|
||||||
|
|
||||||
|
if err != nil || version != "1.2.3" {
|
||||||
|
t.Errorf("Expected version to be %s, got %s instead.", "1.2.3", version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDetermineSelectedPythonVersionGetsUserDefinedVersion(t *testing.T) {
|
func TestDetermineSelectedPythonVersionGetsUserDefinedVersion(t *testing.T) {
|
||||||
defer SetupAndCleanupEnvironment(t)()
|
defer SetupAndCleanupEnvironment(t)()
|
||||||
|
|
||||||
|
@ -60,7 +84,7 @@ func TestDetermineSelectedPythonVersionGetsUserDefinedVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDetermineSelectedPythonVersionDefaultsToSystem(t *testing.T) {
|
func TestDetermineSelectedPythonVersionDefaultsToSystem(t *testing.T) {
|
||||||
defer SetupAndCleanupEnvironment(t)
|
defer SetupAndCleanupEnvironment(t)()
|
||||||
|
|
||||||
version, err := DetermineSelectedPythonVersion(ReadState())
|
version, err := DetermineSelectedPythonVersion(ReadState())
|
||||||
|
|
||||||
|
@ -68,3 +92,45 @@ func TestDetermineSelectedPythonVersionDefaultsToSystem(t *testing.T) {
|
||||||
t.Errorf("Expected version to be 'SYSTEM', got %s instead.", version)
|
t.Errorf("Expected version to be 'SYSTEM', got %s instead.", version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSearchForPythonVersionFileFindsFileInCwd(t *testing.T) {
|
||||||
|
defer SetupAndCleanupEnvironment(t)()
|
||||||
|
|
||||||
|
temporaryWd := t.TempDir()
|
||||||
|
os.Chdir(temporaryWd)
|
||||||
|
ioutil.WriteFile(path.Join(temporaryWd, ".python-version"), []byte("1.2.3"), 0750)
|
||||||
|
|
||||||
|
versionFound, found := SearchForPythonVersionFile()
|
||||||
|
|
||||||
|
if versionFound != "1.2.3" || !found {
|
||||||
|
t.Errorf("Expected \"1.2.3\", found %s", versionFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearchForPythonVersionFileFindsFileInParents(t *testing.T) {
|
||||||
|
defer SetupAndCleanupEnvironment(t)()
|
||||||
|
|
||||||
|
temporaryWd := t.TempDir()
|
||||||
|
|
||||||
|
ioutil.WriteFile(path.Join(temporaryWd, ".python-version"), []byte("1.2.3"), 0750)
|
||||||
|
os.Mkdir(path.Join(temporaryWd, "child"), 0750)
|
||||||
|
os.Chdir(path.Join(temporaryWd, "child"))
|
||||||
|
|
||||||
|
versionFound, found := SearchForPythonVersionFile()
|
||||||
|
|
||||||
|
if versionFound != "1.2.3" || !found {
|
||||||
|
t.Errorf("Expected \"1.2.3\", found %s", versionFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearchForPythonVersionFileReturnsOnRootIfNoneFound(t *testing.T) {
|
||||||
|
defer SetupAndCleanupEnvironment(t)()
|
||||||
|
|
||||||
|
versionFound, found := SearchForPythonVersionFile()
|
||||||
|
|
||||||
|
if versionFound != "" || found {
|
||||||
|
t.Errorf("Did not expect any result, found %s.", versionFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue