Skip to content

Commit 50cdacd

Browse files
authored
fix(python): use version-specific pip URL for older Python versions (#119)
1 parent f123331 commit 50cdacd

2 files changed

Lines changed: 83 additions & 3 deletions

File tree

src/runtimes/python/provider.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"regexp"
1313
goruntime "runtime"
1414
"sort"
15+
"strconv"
1516
"strings"
1617
"time"
1718

@@ -117,7 +118,9 @@ func (p *Provider) installPipIfNeeded(version string) {
117118
pipSpinner.Start()
118119
if err := p.installPip(version); err != nil {
119120
pipSpinner.Warning("Failed to install pip")
120-
ui.Info("You can install it manually by running: python -m ensurepip")
121+
ui.Info("To install pip manually:")
122+
ui.Info(" 1. Download: %s", p.getPipURL(version))
123+
ui.Info(" 2. Run: python get-pip.py")
121124
} else {
122125
pipSpinner.Success("pip installed successfully")
123126
}
@@ -306,8 +309,8 @@ func (p *Provider) installPip(version string) error {
306309
return fmt.Errorf("failed to enable site-packages: %w", err)
307310
}
308311

309-
// Step 2: Download get-pip.py
310-
getPipURL := "https://bootstrap.pypa.io/get-pip.py"
312+
// Step 2: Download get-pip.py (use version-specific URL for older Python)
313+
getPipURL := p.getPipURL(version)
311314
getPipPath := filepath.Join(installPath, "get-pip.py")
312315
if err := download.File(getPipURL, getPipPath); err != nil {
313316
return fmt.Errorf("failed to download get-pip.py: %w", err)
@@ -325,6 +328,22 @@ func (p *Provider) installPip(version string) error {
325328
return nil
326329
}
327330

331+
// getPipURL returns the appropriate get-pip.py URL for the given Python version.
332+
// Older Python versions (3.8 and below) require version-specific URLs since the
333+
// main get-pip.py no longer supports end-of-life Python versions.
334+
func (p *Provider) getPipURL(version string) string {
335+
parts := strings.Split(version, ".")
336+
if len(parts) >= 2 && parts[0] == "3" {
337+
minor, err := strconv.Atoi(parts[1])
338+
if err == nil && minor <= 8 {
339+
// Use version-specific URL for Python 3.8 and below
340+
return fmt.Sprintf("https://bootstrap.pypa.io/pip/%s.%s/get-pip.py", parts[0], parts[1])
341+
}
342+
}
343+
// Default URL for Python 3.9+
344+
return "https://bootstrap.pypa.io/get-pip.py"
345+
}
346+
328347
// enableSitePackages modifies the ._pth file to enable site-packages
329348
func (p *Provider) enableSitePackages(pthFile string) error {
330349
// Read the file

src/runtimes/python/provider_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,64 @@ func TestPythonProvider_InstallPath(t *testing.T) {
8282
}
8383
}
8484
}
85+
86+
// TestPythonProvider_GetPipURL tests the version-specific pip URL selection
87+
func TestPythonProvider_GetPipURL(t *testing.T) {
88+
provider := NewProvider()
89+
90+
tests := []struct {
91+
name string
92+
version string
93+
expectedURL string
94+
}{
95+
{
96+
name: "Python 3.12 uses default URL",
97+
version: "3.12.0",
98+
expectedURL: "https://bootstrap.pypa.io/get-pip.py",
99+
},
100+
{
101+
name: "Python 3.11 uses default URL",
102+
version: "3.11.5",
103+
expectedURL: "https://bootstrap.pypa.io/get-pip.py",
104+
},
105+
{
106+
name: "Python 3.10 uses default URL",
107+
version: "3.10.0",
108+
expectedURL: "https://bootstrap.pypa.io/get-pip.py",
109+
},
110+
{
111+
name: "Python 3.9 uses default URL",
112+
version: "3.9.18",
113+
expectedURL: "https://bootstrap.pypa.io/get-pip.py",
114+
},
115+
{
116+
name: "Python 3.8 uses version-specific URL",
117+
version: "3.8.9",
118+
expectedURL: "https://bootstrap.pypa.io/pip/3.8/get-pip.py",
119+
},
120+
{
121+
name: "Python 3.7 uses version-specific URL",
122+
version: "3.7.12",
123+
expectedURL: "https://bootstrap.pypa.io/pip/3.7/get-pip.py",
124+
},
125+
{
126+
name: "Python 3.6 uses version-specific URL",
127+
version: "3.6.15",
128+
expectedURL: "https://bootstrap.pypa.io/pip/3.6/get-pip.py",
129+
},
130+
{
131+
name: "Python 2.7 uses version-specific URL",
132+
version: "2.7.18",
133+
expectedURL: "https://bootstrap.pypa.io/get-pip.py", // 2.x uses default (not 3.x)
134+
},
135+
}
136+
137+
for _, tt := range tests {
138+
t.Run(tt.name, func(t *testing.T) {
139+
url := provider.getPipURL(tt.version)
140+
if url != tt.expectedURL {
141+
t.Errorf("getPipURL(%q) = %q, want %q", tt.version, url, tt.expectedURL)
142+
}
143+
})
144+
}
145+
}

0 commit comments

Comments
 (0)