77 "encoding/json"
88 "errors"
99 "fmt"
10+ "io"
1011 "io/fs"
1112 "os"
1213 "path/filepath"
@@ -21,7 +22,7 @@ import (
2122//go:embed python.json
2223var releasesData []byte
2324
24- const uvVersion = "uv==0.1.15 "
25+ const uvVersion = "uv==0.1.24 "
2526
2627type Release struct {
2728 OS string `json:"os,omitempty"`
@@ -52,13 +53,65 @@ func (r *Runtime) Supports(cmd []string) bool {
5253 return runtimeEnv .Matches (cmd , "python" ) || runtimeEnv .Matches (cmd , "python3" )
5354}
5455
56+ func pythonCmd (base string ) string {
57+ if runtime .GOOS == "windows" {
58+ return filepath .Join (base , "python.exe" )
59+ }
60+ return filepath .Join (base , "python3" )
61+ }
62+
63+ func pythonBin (base string ) string {
64+ binDir := filepath .Join (base , "python" )
65+ if runtime .GOOS != "windows" {
66+ binDir = filepath .Join (binDir , "bin" )
67+ }
68+ return binDir
69+ }
70+
71+ func uvBin (binDir string ) string {
72+ if runtime .GOOS == "windows" {
73+ return filepath .Join (binDir , "Scripts" , "uv" )
74+ }
75+ return filepath .Join (binDir , "uv" )
76+ }
77+
5578func (r * Runtime ) installVenv (ctx context.Context , binDir , venvPath string ) error {
5679 log .Infof ("Creating virtualenv in %s" , venvPath )
57- cmd := debugcmd .New (ctx , filepath .Join (binDir , "uv" ), "venv" , "-p" ,
58- filepath .Join (binDir , "python3" ), venvPath )
80+ cmd := debugcmd .New (ctx , uvBin (binDir ), "venv" , "-p" , pythonCmd (binDir ), venvPath )
5981 return cmd .Run ()
6082}
6183
84+ func copyFile (to , from string ) error {
85+ in , err := os .Open (from )
86+ if err != nil {
87+ return err
88+ }
89+ defer in .Close ()
90+
91+ out , err := os .Create (to )
92+ if err != nil {
93+ _ = out .Close ()
94+ return err
95+ }
96+ defer out .Close ()
97+
98+ if _ , err := io .Copy (out , in ); err != nil {
99+ return fmt .Errorf ("copying %s => %s" , from , to )
100+ }
101+
102+ return nil
103+ }
104+
105+ func (r * Runtime ) copyPythonForWindows (binDir string ) error {
106+ for _ , targetBin := range []string {"python3.exe" , "python" + r .ID () + ".exe" } {
107+ err := copyFile (filepath .Join (binDir , targetBin ), filepath .Join (binDir , "python.exe" ))
108+ if err != nil {
109+ return err
110+ }
111+ }
112+ return nil
113+ }
114+
62115func (r * Runtime ) Setup (ctx context.Context , dataRoot , toolSource string , env []string ) ([]string , error ) {
63116 binPath , err := r .getRuntime (ctx , dataRoot )
64117 if err != nil {
@@ -67,6 +120,9 @@ func (r *Runtime) Setup(ctx context.Context, dataRoot, toolSource string, env []
67120
68121 venvPath := filepath .Join (dataRoot , "venv" , hash .ID (binPath , toolSource ))
69122 venvBinPath := filepath .Join (venvPath , "bin" )
123+ if runtime .GOOS == "windows" {
124+ venvBinPath = filepath .Join (venvPath , "Scripts" )
125+ }
70126
71127 // Cleanup failed runs
72128 if err := os .RemoveAll (venvPath ); err != nil {
@@ -80,6 +136,12 @@ func (r *Runtime) Setup(ctx context.Context, dataRoot, toolSource string, env []
80136 newEnv := runtimeEnv .AppendPath (env , venvBinPath )
81137 newEnv = append (newEnv , "VIRTUAL_ENV=" + venvPath )
82138
139+ if runtime .GOOS == "windows" {
140+ if err := r .copyPythonForWindows (venvBinPath ); err != nil {
141+ return nil , err
142+ }
143+ }
144+
83145 if err := r .runPip (ctx , toolSource , binPath , append (env , newEnv ... )); err != nil {
84146 return nil , err
85147 }
@@ -110,7 +172,7 @@ func (r *Runtime) runPip(ctx context.Context, toolSource, binDir string, env []s
110172 for _ , req := range []string {"requirements-gptscript.txt" , "requirements.txt" } {
111173 reqFile := filepath .Join (toolSource , req )
112174 if s , err := os .Stat (reqFile ); err == nil && ! s .IsDir () {
113- cmd := debugcmd .New (ctx , filepath . Join (binDir , "uv" ), "pip" , "install" , "-r" , reqFile )
175+ cmd := debugcmd .New (ctx , uvBin (binDir ), "pip" , "install" , "-r" , reqFile )
114176 cmd .Env = env
115177 return cmd .Run ()
116178 }
@@ -120,9 +182,7 @@ func (r *Runtime) runPip(ctx context.Context, toolSource, binDir string, env []s
120182}
121183
122184func (r * Runtime ) setupUV (ctx context.Context , tmp string ) error {
123- cmd := debugcmd .New (ctx , filepath .Join (tmp , "python" , "bin" , "python3" ),
124- filepath .Join (tmp , "python" , "bin" , "pip" ),
125- "install" , uvVersion )
185+ cmd := debugcmd .New (ctx , pythonCmd (tmp ), "-m" , "pip" , "install" , uvVersion )
126186 return cmd .Run ()
127187}
128188
@@ -133,7 +193,7 @@ func (r *Runtime) getRuntime(ctx context.Context, cwd string) (string, error) {
133193 }
134194
135195 target := filepath .Join (cwd , "python" , hash .ID (url , sha , uvVersion ))
136- binDir := filepath . Join (target , "python" , "bin" )
196+ binDir := pythonBin (target )
137197 if _ , err := os .Stat (target ); err == nil {
138198 return binDir , nil
139199 } else if ! errors .Is (err , fs .ErrNotExist ) {
@@ -152,7 +212,7 @@ func (r *Runtime) getRuntime(ctx context.Context, cwd string) (string, error) {
152212 return "" , err
153213 }
154214
155- if err := r .setupUV (ctx , tmp ); err != nil {
215+ if err := r .setupUV (ctx , pythonBin ( tmp ) ); err != nil {
156216 return "" , err
157217 }
158218
0 commit comments