ensurePythonDependenciesPip uses the standard library venv + pip workflow.
(dir, venvPath string, w io.Writer)
| 1023 | |
| 1024 | // ensurePythonDependenciesPip uses the standard library venv + pip workflow. |
| 1025 | func ensurePythonDependenciesPip(dir, venvPath string, w io.Writer) error { |
| 1026 | fmt.Fprintf(w, "Creating venv at %s…\n", venvPath) |
| 1027 | c := exec.Command("python3", "-m", "venv", venvPath) |
| 1028 | c.Dir = dir |
| 1029 | c.Stdout = w |
| 1030 | c.Stderr = os.Stderr |
| 1031 | if err := c.Run(); err != nil { |
| 1032 | return fmt.Errorf("creating venv: %w", err) |
| 1033 | } |
| 1034 | |
| 1035 | venvPython := filepath.Join(venvPath, "bin", "python3") |
| 1036 | |
| 1037 | // Prefer pyproject.toml, fall back to requirements.txt for legacy projects. |
| 1038 | pyprojectFile := filepath.Join(dir, "pyproject.toml") |
| 1039 | reqFile := filepath.Join(dir, "requirements.txt") |
| 1040 | |
| 1041 | switch { |
| 1042 | case fileExists(pyprojectFile): |
| 1043 | fmt.Fprintln(w, "Installing Python dependencies from pyproject.toml…") |
| 1044 | // Non-package mode projects cannot be installed with `pip install -e .`. |
| 1045 | // Extract the dependency list and install packages directly instead. |
| 1046 | if isNonPackageMode(pyprojectFile) { |
| 1047 | deps := parsePyprojectDeps(pyprojectFile) |
| 1048 | if len(deps) == 0 { |
| 1049 | return nil |
| 1050 | } |
| 1051 | args := append([]string{"-m", "pip", "install", "-q"}, deps...) |
| 1052 | c = exec.Command(venvPython, args...) |
| 1053 | } else { |
| 1054 | c = exec.Command(venvPython, "-m", "pip", "install", "-e", ".", "-q") |
| 1055 | } |
| 1056 | c.Dir = dir |
| 1057 | c.Stdout = w |
| 1058 | c.Stderr = os.Stderr |
| 1059 | if err := c.Run(); err != nil { |
| 1060 | return fmt.Errorf("installing dependencies from pyproject.toml with pip: %w", err) |
| 1061 | } |
| 1062 | case fileExists(reqFile): |
| 1063 | fmt.Fprintln(w, "Installing Python dependencies…") |
| 1064 | c = exec.Command(venvPython, "-m", "pip", "install", "-r", "requirements.txt", "-q") |
| 1065 | c.Dir = dir |
| 1066 | c.Stdout = w |
| 1067 | c.Stderr = os.Stderr |
| 1068 | if err := c.Run(); err != nil { |
| 1069 | return fmt.Errorf("installing dependencies with pip: %w", err) |
| 1070 | } |
| 1071 | } |
| 1072 | return nil |
| 1073 | } |
| 1074 | |
| 1075 | // isNonPackageMode reports whether a pyproject.toml declares a non-installable |
| 1076 | // project. This covers: |
no test coverage detected