refactor(python): move python-specific logic to own module
This commit is contained in:
parent
a5c7242045
commit
c0d8d31ace
8 changed files with 83 additions and 72 deletions
27
commands.go
27
commands.go
|
@ -5,6 +5,7 @@ import (
|
|||
"slices"
|
||||
cli "v/cli"
|
||||
logger "v/logger"
|
||||
python "v/python"
|
||||
state "v/state"
|
||||
)
|
||||
|
||||
|
@ -58,12 +59,12 @@ func UninstallPython(args []string, flags cli.Flags, currentState state.State) e
|
|||
func InstallPython(args []string, flags cli.Flags, currentState state.State) error {
|
||||
version := args[1]
|
||||
|
||||
return InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
||||
return python.InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
||||
}
|
||||
|
||||
func Use(args []string, flags cli.Flags, currentState state.State) error {
|
||||
version := args[1]
|
||||
if err := ValidateVersion(version); err != nil {
|
||||
if err := python.ValidateVersion(version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -79,7 +80,7 @@ func Use(args []string, flags cli.Flags, currentState state.State) error {
|
|||
|
||||
if !found {
|
||||
logger.InfoLogger.Println("Version not installed. Installing it first.")
|
||||
InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
||||
python.InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
||||
}
|
||||
|
||||
state.WriteState(version)
|
||||
|
@ -88,7 +89,7 @@ func Use(args []string, flags cli.Flags, currentState state.State) error {
|
|||
return nil
|
||||
}
|
||||
func ListVersions(args []string, flags cli.Flags, currentState state.State) error {
|
||||
installedVersions, err := ListInstalledVersions()
|
||||
installedVersions, err := python.ListInstalledVersions()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -108,17 +109,17 @@ func ListVersions(args []string, flags cli.Flags, currentState state.State) erro
|
|||
|
||||
// Which prints out the system path to the executable being used by `python`.
|
||||
func Which(args []string, flags cli.Flags, currentState state.State) error {
|
||||
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
||||
installedVersions, _ := ListInstalledVersions()
|
||||
selectedVersion, _ := python.DetermineSelectedPythonVersion(currentState)
|
||||
installedVersions, _ := python.ListInstalledVersions()
|
||||
isInstalled := slices.Contains(installedVersions, selectedVersion.Version)
|
||||
|
||||
var printedPath string
|
||||
|
||||
if selectedVersion.Source == "system" {
|
||||
_, sysPath := DetermineSystemPython()
|
||||
_, sysPath := python.DetermineSystemPython()
|
||||
printedPath = sysPath + " (system)"
|
||||
} else if isInstalled {
|
||||
tag := VersionStringToStruct(selectedVersion.Version)
|
||||
tag := python.VersionStringToStruct(selectedVersion.Version)
|
||||
printedPath = state.GetStatePath("runtimes", "py-"+selectedVersion.Version, "bin", "python"+tag.MajorMinor())
|
||||
} else {
|
||||
logger.InfoLogger.Printf("The desired version (%s) is not installed.\n", selectedVersion.Version)
|
||||
|
@ -130,7 +131,7 @@ func Which(args []string, flags cli.Flags, currentState state.State) error {
|
|||
if flags.RawOutput {
|
||||
prefix = ""
|
||||
} else {
|
||||
printedPath = Bold(printedPath)
|
||||
printedPath = logger.Bold(printedPath)
|
||||
}
|
||||
|
||||
logger.InfoLogger.Printf("%s%s\n", prefix, printedPath)
|
||||
|
@ -141,12 +142,12 @@ func Which(args []string, flags cli.Flags, currentState state.State) error {
|
|||
// and what configures it. If the version is configured by a file, the file is returned
|
||||
// under "source", if the system Python is used, "system" is returned as a source.
|
||||
func CurrentVersion(args []string, flags cli.Flags, currentState state.State) error {
|
||||
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
||||
installedVersions, _ := ListInstalledVersions()
|
||||
selectedVersion, _ := python.DetermineSelectedPythonVersion(currentState)
|
||||
installedVersions, _ := python.ListInstalledVersions()
|
||||
isInstalled := slices.Contains(installedVersions, selectedVersion.Version)
|
||||
|
||||
if !isInstalled {
|
||||
logger.InfoLogger.Println(Bold(Yellow("WARNING: This version is not installed.")))
|
||||
logger.InfoLogger.Println(logger.Bold(logger.Yellow("WARNING: This version is not installed.")))
|
||||
}
|
||||
|
||||
if flags.RawOutput {
|
||||
|
@ -154,6 +155,6 @@ func CurrentVersion(args []string, flags cli.Flags, currentState state.State) er
|
|||
return nil
|
||||
}
|
||||
|
||||
logger.InfoLogger.Printf("Python version: %s\nSource: %s\n", Bold(selectedVersion.Version), Bold(selectedVersion.Source))
|
||||
logger.InfoLogger.Printf("Python version: %s\nSource: %s\n", logger.Bold(selectedVersion.Version), logger.Bold(selectedVersion.Source))
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@ import (
|
|||
cli "v/cli"
|
||||
logger "v/logger"
|
||||
state "v/state"
|
||||
testutils "v/testutils"
|
||||
)
|
||||
|
||||
func TestListVersionOutputsNoticeIfNoVersionsInstalled(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
os.Mkdir(state.GetStatePath("runtimes"), 0750)
|
||||
var out bytes.Buffer
|
||||
|
@ -28,7 +29,7 @@ func TestListVersionOutputsNoticeIfNoVersionsInstalled(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListVersionOutputsVersionsInstalled(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
os.MkdirAll(state.GetStatePath("runtimes", "py-1.2.3"), 0750)
|
||||
var out bytes.Buffer
|
||||
|
@ -45,7 +46,7 @@ func TestListVersionOutputsVersionsInstalled(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListVersionReturnsErrorOnFailure(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
|
@ -65,7 +66,7 @@ func TestListVersionReturnsErrorOnFailure(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListVersionOutputsVersionSelectedAndWarnsNotInstalled(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
|
@ -81,7 +82,7 @@ func TestListVersionOutputsVersionSelectedAndWarnsNotInstalled(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWhichOutputsVersionSelectedIfInstalled(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
|
@ -99,7 +100,7 @@ func TestWhichOutputsVersionSelectedIfInstalled(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWhichOutputsSystemVersionIfNoneSelected(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
|
@ -116,7 +117,7 @@ func TestWhichOutputsSystemVersionIfNoneSelected(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWhichOutputsVersionWithoutPrefixesIfRawOutput(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
|
|
|
@ -1,26 +1,11 @@
|
|||
package main
|
||||
package exec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func VersionStringToStruct(version string) VersionTag {
|
||||
splitVersion := strings.Split(version, ".")
|
||||
|
||||
return VersionTag{Major: splitVersion[0], Minor: splitVersion[1], Patch: splitVersion[2]}
|
||||
}
|
||||
|
||||
func ValidateVersion(version string) error {
|
||||
if splitVersion := strings.Split(version, "."); len(splitVersion) != 3 {
|
||||
return errors.New("Invalid version string. Expected format 'a.b.c'.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunCommand is a thin wrapper around running command-line calls
|
||||
// programmatically. It abstracts common configuration like routing
|
||||
// output and handling the directory the calls are made from.
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package logger
|
||||
|
||||
const (
|
||||
RESET = "\033[0m"
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package python
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -9,6 +9,7 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
exec "v/exec"
|
||||
logger "v/logger"
|
||||
state "v/state"
|
||||
)
|
||||
|
@ -58,7 +59,7 @@ func downloadSource(version string, skipCache bool) (PackageMetadata, error) {
|
|||
|
||||
client := http.Client{}
|
||||
|
||||
logger.InfoLogger.Println(Bold("Downloading source for Python " + version))
|
||||
logger.InfoLogger.Println(logger.Bold("Downloading source for Python " + version))
|
||||
logger.InfoLogger.SetPrefix(" ")
|
||||
defer logger.InfoLogger.SetPrefix("")
|
||||
|
||||
|
@ -88,7 +89,7 @@ func downloadSource(version string, skipCache bool) (PackageMetadata, error) {
|
|||
}
|
||||
|
||||
func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, error) {
|
||||
logger.InfoLogger.Println(Bold("Building from source"))
|
||||
logger.InfoLogger.Println(logger.Bold("Building from source"))
|
||||
logger.InfoLogger.SetPrefix(" ")
|
||||
defer logger.InfoLogger.SetPrefix("")
|
||||
|
||||
|
@ -96,7 +97,7 @@ func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, er
|
|||
|
||||
logger.InfoLogger.Println("Unpacking source for " + pkgMeta.ArchivePath)
|
||||
|
||||
_, untarErr := RunCommand([]string{"tar", "zxvf", pkgMeta.ArchivePath}, state.GetStatePath("cache"), !verbose)
|
||||
_, untarErr := exec.RunCommand([]string{"tar", "zxvf", pkgMeta.ArchivePath}, state.GetStatePath("cache"), !verbose)
|
||||
|
||||
if untarErr != nil {
|
||||
return pkgMeta, untarErr
|
||||
|
@ -108,14 +109,14 @@ func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, er
|
|||
|
||||
targetDirectory := state.GetStatePath("runtimes", "py-"+pkgMeta.Version)
|
||||
|
||||
_, configureErr := RunCommand([]string{"./configure", "--prefix=" + targetDirectory, "--enable-optimizations"}, unzippedRoot, !verbose)
|
||||
_, configureErr := exec.RunCommand([]string{"./configure", "--prefix=" + targetDirectory, "--enable-optimizations"}, unzippedRoot, !verbose)
|
||||
|
||||
if configureErr != nil {
|
||||
return pkgMeta, configureErr
|
||||
}
|
||||
|
||||
logger.InfoLogger.Println("Building")
|
||||
_, buildErr := RunCommand([]string{"make", "altinstall", "-j4"}, unzippedRoot, !verbose)
|
||||
_, buildErr := exec.RunCommand([]string{"make", "altinstall", "-j4"}, unzippedRoot, !verbose)
|
||||
|
||||
if buildErr != nil {
|
||||
return pkgMeta, buildErr
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package python
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -9,26 +9,11 @@ import (
|
|||
"slices"
|
||||
"testing"
|
||||
state "v/state"
|
||||
testutils "v/testutils"
|
||||
)
|
||||
|
||||
// SetupAndCleanupEnvironment sets up a test directory and
|
||||
// environment variables before the test and returns a cleanup
|
||||
// function that can be deferred to cleanup any changes to the
|
||||
// system.
|
||||
func SetupAndCleanupEnvironment(t *testing.T) func() {
|
||||
os.Setenv("V_ROOT", t.TempDir())
|
||||
|
||||
temporaryWd := t.TempDir()
|
||||
|
||||
os.Chdir(temporaryWd)
|
||||
|
||||
return func() {
|
||||
os.Unsetenv("V_ROOT")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetermineSystemPythonGetsUnshimmedPythonRuntime(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
ioutil.WriteFile(state.GetStatePath("shims", "python"), []byte("#!/bin/bash\necho \"Python 4.5.6\""), 0777)
|
||||
mockSystemPythonPath := t.TempDir()
|
||||
|
@ -49,7 +34,7 @@ func TestDetermineSystemPythonGetsUnshimmedPythonRuntime(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDetermineSelectedPythonVersionUsesPythonVersionFileIfFound(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
// Writing a mock user-defined state.
|
||||
mockState := state.State{GlobalVersion: "1.0.0"}
|
||||
|
@ -69,7 +54,7 @@ func TestDetermineSelectedPythonVersionUsesPythonVersionFileIfFound(t *testing.T
|
|||
}
|
||||
|
||||
func TestDetermineSelectedPythonVersionGetsUserDefinedVersion(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
// Writing a mock user-defined state.
|
||||
mockState := state.State{GlobalVersion: "1.0.0"}
|
||||
|
@ -85,7 +70,7 @@ func TestDetermineSelectedPythonVersionGetsUserDefinedVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDetermineSelectedPythonVersionDefaultsToSystem(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
version, err := DetermineSelectedPythonVersion(state.ReadState())
|
||||
|
||||
|
@ -95,7 +80,7 @@ func TestDetermineSelectedPythonVersionDefaultsToSystem(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSearchForPythonVersionFileFindsFileInCwd(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
temporaryWd := t.TempDir()
|
||||
os.Chdir(temporaryWd)
|
||||
|
@ -109,7 +94,7 @@ func TestSearchForPythonVersionFileFindsFileInCwd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSearchForPythonVersionFileFindsFileInParents(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
temporaryWd := t.TempDir()
|
||||
|
||||
|
@ -126,7 +111,7 @@ func TestSearchForPythonVersionFileFindsFileInParents(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSearchForPythonVersionFileReturnsOnRootIfNoneFound(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
versionFound, found := SearchForPythonVersionFile()
|
||||
|
||||
|
@ -136,7 +121,7 @@ func TestSearchForPythonVersionFileReturnsOnRootIfNoneFound(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListInstalledVersion(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
versions := []string{"1.2.3", "4.5.6", "7.8.9"}
|
||||
|
||||
|
@ -153,7 +138,7 @@ func TestListInstalledVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListInstalledVersionNoVersionsInstalled(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
os.Mkdir(state.GetStatePath("runtimes"), 0750)
|
||||
|
||||
|
@ -165,7 +150,7 @@ func TestListInstalledVersionNoVersionsInstalled(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListInstalledVersionNoRuntimesDir(t *testing.T) {
|
||||
defer SetupAndCleanupEnvironment(t)()
|
||||
defer testutils.SetupAndCleanupEnvironment(t)()
|
||||
|
||||
installedVersions, err := ListInstalledVersions()
|
||||
|
|
@ -1,13 +1,29 @@
|
|||
package main
|
||||
package python
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
exec "v/exec"
|
||||
state "v/state"
|
||||
)
|
||||
|
||||
func VersionStringToStruct(version string) VersionTag {
|
||||
splitVersion := strings.Split(version, ".")
|
||||
|
||||
return VersionTag{Major: splitVersion[0], Minor: splitVersion[1], Patch: splitVersion[2]}
|
||||
}
|
||||
|
||||
func ValidateVersion(version string) error {
|
||||
if splitVersion := strings.Split(version, "."); len(splitVersion) != 3 {
|
||||
return errors.New("Invalid version string. Expected format 'a.b.c'.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SelectedVersion struct {
|
||||
Version string
|
||||
Source string
|
||||
|
@ -84,7 +100,7 @@ func DetermineSelectedPythonVersion(currentState state.State) (SelectedVersion,
|
|||
// DetermineSystemPython returns the unshimmed Python version and path.
|
||||
// It assumes that /bin/python is where system Python lives.
|
||||
func DetermineSystemPython() (string, string) {
|
||||
versionOut, _ := RunCommand([]string{"/bin/python", "--version"}, state.GetStatePath(), true)
|
||||
versionOut, _ := exec.RunCommand([]string{"/bin/python", "--version"}, state.GetStatePath(), true)
|
||||
detectedVersion, _ := strings.CutPrefix(versionOut, "Python")
|
||||
return strings.TrimSpace(detectedVersion), "/bin/python"
|
||||
}
|
22
testutils/setup.go
Normal file
22
testutils/setup.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// SetupAndCleanupEnvironment sets up a test directory and
|
||||
// environment variables before the test and returns a cleanup
|
||||
// function that can be deferred to cleanup any changes to the
|
||||
// system.
|
||||
func SetupAndCleanupEnvironment(t *testing.T) func() {
|
||||
os.Setenv("V_ROOT", t.TempDir())
|
||||
|
||||
temporaryWd := t.TempDir()
|
||||
|
||||
os.Chdir(temporaryWd)
|
||||
|
||||
return func() {
|
||||
os.Unsetenv("V_ROOT")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue