forked from jetify-com/devbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpython_pip_planner.go
94 lines (81 loc) · 2.65 KB
/
python_pip_planner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// Copyright 2022 Jetpack Technologies Inc and contributors. All rights reserved.
// Use of this source code is governed by the license in the LICENSE file.
package python
import (
"fmt"
"path/filepath"
"strings"
"go.jetpack.io/devbox/boxcli/usererr"
"go.jetpack.io/devbox/planner/plansdk"
)
// TODO: Doesn't work with libraries like Pandas that have C extensions
// We get error
// ImportError: libstdc++.so.6: cannot open shared object file: No such file or directory
// possible solution is to set $LD_LIBRARY_PATH
// https://nixos.wiki/wiki/Packaging/Quirks_and_Caveats
type PIPPlanner struct{}
// PythonPoetryPlanner implements interface Planner (compile-time check)
var _ plansdk.Planner = (*PIPPlanner)(nil)
func (p *PIPPlanner) Name() string {
return "python.Planner"
}
func (p *PIPPlanner) IsRelevant(srcDir string) bool {
return plansdk.FileExists(filepath.Join(srcDir, "requirements.txt"))
}
func (p *PIPPlanner) GetShellPlan(srcDir string) *plansdk.ShellPlan {
return &plansdk.ShellPlan{
DevPackages: []string{
"python3",
},
ShellInitHook: []string{p.shellInitHook(srcDir)},
}
}
func (p *PIPPlanner) GetBuildPlan(srcDir string) *plansdk.BuildPlan {
plan := &plansdk.BuildPlan{
DevPackages: []string{
"python3",
},
RuntimePackages: []string{
`python3`,
},
}
if err := p.isBuildable(srcDir); err != nil {
return plan.WithError(err)
}
plan.InstallStage = &plansdk.Stage{
Command: "python -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt",
InputFiles: plansdk.AllFiles(),
}
plan.BuildStage = &plansdk.Stage{Command: pipBuildCommand}
plan.StartStage = &plansdk.Stage{
Command: "python ./app.pex",
InputFiles: []string{"app.pex"},
}
return plan
}
func (p *PIPPlanner) isBuildable(srcDir string) error {
if plansdk.FileExists(filepath.Join(srcDir, "setup.py")) {
return nil
}
return usererr.New(
"setup.py not found. Please create a setup.py file to build your project." +
" The distribution name must be a case-insensitive match of the package" +
" (dir) name. Dashes are converted to underscores.",
)
}
func (p *PIPPlanner) shellInitHook(srcDir string) string {
venvPath := filepath.Join(srcDir, ".venv")
venvActivatePath := filepath.Join(srcDir, ".venv", "bin", "activate")
script := strings.TrimSpace(`
echo "Creating/Using virtual environment in %[1]s";
python -m venv "%[1]s";
source "%[2]s";`)
return fmt.Sprintf(script, venvPath, venvActivatePath)
}
var pipBuildCommand = strings.TrimSpace(`
source .venv/bin/activate && \
pip install pex && \
PACKAGE_NAME=$(python setup.py --name | tr '[:upper:]-' '[:lower:]_') && \
pex . -o app.pex -m $PACKAGE_NAME -r requirements.txt
`,
)