Skip to content

Commit dcfeeac

Browse files
committed
refactor: split venv creation into a separate function
this new function does not change chdir or sets environment
1 parent 3c3c172 commit dcfeeac

File tree

3 files changed

+113
-42
lines changed

3 files changed

+113
-42
lines changed

internal/testutil/cmd.go

Lines changed: 0 additions & 26 deletions
This file was deleted.

libs/python/pythontest/pythontest.go

Lines changed: 96 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,113 @@ package pythontest
22

33
import (
44
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"os/exec"
59
"path/filepath"
10+
"runtime"
611
"strings"
12+
"testing"
713

814
"github.com/databricks/cli/internal/testutil"
915
"github.com/databricks/cli/libs/python"
1016
"github.com/stretchr/testify/require"
1117
)
1218

13-
func RequirePythonVENV(t testutil.TestingT, ctx context.Context, pythonVersion string, checkVersion bool) string {
14-
tmpDir := t.TempDir()
15-
testutil.Chdir(t, tmpDir)
19+
type VenvOpts struct {
20+
// input
21+
PythonVersion string
22+
skipVersionCheck bool
1623

17-
venvName := testutil.RandomName("test-venv-")
18-
testutil.RunCommand(t, "uv", "venv", venvName, "--python", pythonVersion, "--seed")
19-
testutil.InsertVirtualenvInPath(t, filepath.Join(tmpDir, venvName))
24+
// input/output
25+
Dir string
26+
Name string
2027

21-
pythonExe, err := python.DetectExecutable(ctx)
22-
require.NoError(t, err)
23-
require.Contains(t, pythonExe, venvName)
28+
// output:
29+
// Absolute path to venv
30+
EnvPath string
31+
32+
// Absolute path to venv/bin or venv/Scripts, depending on OS
33+
BinPath string
34+
35+
// Absolute path to python binary
36+
PythonExe string
37+
}
38+
39+
func CreatePythonEnv(opts *VenvOpts) error {
40+
if opts == nil || opts.PythonVersion == "" {
41+
return errors.New("PythonVersion must be provided")
42+
}
43+
if opts.Name == "" {
44+
opts.Name = testutil.RandomName("test-venv-")
45+
}
46+
if opts.Dir != "" {
47+
opts.Dir = "."
48+
}
49+
50+
cmd := exec.Command("uv", "venv", opts.Name, "--python", opts.PythonVersion, "--seed", "-q")
51+
cmd.Stdout = os.Stdout
52+
cmd.Stderr = os.Stderr
53+
cmd.Dir = opts.Dir
54+
err := cmd.Run()
55+
if err != nil {
56+
return err
57+
}
58+
59+
opts.EnvPath, err = filepath.Abs(filepath.Join(opts.Dir, opts.Name))
60+
if err != nil {
61+
return err
62+
}
63+
64+
_, err = os.Stat(opts.EnvPath)
65+
if err != nil {
66+
return fmt.Errorf("cannot stat EnvPath %s: %s", opts.EnvPath, err)
67+
}
68+
69+
if runtime.GOOS == "windows" {
70+
// https://github.com/pypa/virtualenv/commit/993ba1316a83b760370f5a3872b3f5ef4dd904c1
71+
opts.BinPath = filepath.Join(opts.EnvPath, "Scripts")
72+
opts.PythonExe = filepath.Join(opts.BinPath, "python.exe")
73+
} else {
74+
opts.BinPath = filepath.Join(opts.EnvPath, "bin")
75+
opts.PythonExe = filepath.Join(opts.BinPath, "python3")
76+
}
77+
78+
_, err = os.Stat(opts.BinPath)
79+
if err != nil {
80+
return fmt.Errorf("cannot stat BinPath %s: %s", opts.BinPath, err)
81+
}
82+
83+
_, err = os.Stat(opts.PythonExe)
84+
if err != nil {
85+
return fmt.Errorf("cannot stat PythonExe %s: %s", opts.PythonExe, err)
86+
}
2487

25-
if checkVersion {
26-
actualVersion := testutil.CaptureCommandOutput(t, pythonExe, "--version")
27-
expectVersion := "Python " + pythonVersion
28-
require.True(t, strings.HasPrefix(actualVersion, expectVersion), "Running %s --version: Expected %v, got %v", pythonExe, expectVersion, actualVersion)
88+
if !opts.skipVersionCheck {
89+
cmd := exec.Command(opts.PythonExe, "--version")
90+
out, err := cmd.CombinedOutput()
91+
if err != nil {
92+
return fmt.Errorf("Failed to run %s --version: %s", opts.PythonExe, err)
93+
}
94+
outString := string(out)
95+
expectVersion := "Python " + opts.PythonVersion
96+
if !strings.HasPrefix(outString, expectVersion) {
97+
return fmt.Errorf("Unexpected output from %s --version: %v (expected %v)", opts.PythonExe, outString, expectVersion)
98+
}
2999
}
30100

31-
return tmpDir
101+
return nil
102+
}
103+
104+
func RequireActivatedPythonEnv(t *testing.T, ctx context.Context, opts *VenvOpts) {
105+
err := CreatePythonEnv(opts)
106+
require.NoError(t, err)
107+
require.DirExists(t, opts.BinPath)
108+
109+
testutil.InsertPathEntry(t, opts.BinPath)
110+
111+
pythonExe, err := python.DetectExecutable(ctx)
112+
require.NoError(t, err)
113+
require.Equal(t, filepath.Dir(pythonExe), filepath.Dir(opts.PythonExe))
32114
}

libs/python/pythontest/pythontest_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,29 @@ package pythontest
33
import (
44
"context"
55
"testing"
6+
7+
"github.com/stretchr/testify/require"
68
)
79

8-
func TestVenv(t *testing.T) {
10+
func TestVenvSuccess(t *testing.T) {
911
// Test at least two version to ensure we capture a case where venv version does not match system one
1012
for _, pythonVersion := range []string{"3.11", "3.12"} {
1113
t.Run(pythonVersion, func(t *testing.T) {
1214
ctx := context.Background()
13-
RequirePythonVENV(t, ctx, pythonVersion, true)
15+
opts := VenvOpts{PythonVersion: pythonVersion}
16+
RequireActivatedPythonEnv(t, ctx, &opts)
17+
require.DirExists(t, opts.EnvPath)
18+
require.DirExists(t, opts.BinPath)
19+
require.FileExists(t, opts.PythonExe)
1420
})
1521
}
1622
}
23+
24+
func TestWrongVersion(t *testing.T) {
25+
require.Error(t, CreatePythonEnv(&VenvOpts{PythonVersion: "4.0"}))
26+
}
27+
28+
func TestMissingVersion(t *testing.T) {
29+
require.Error(t, CreatePythonEnv(nil))
30+
require.Error(t, CreatePythonEnv(&VenvOpts{}))
31+
}

0 commit comments

Comments
 (0)