From 74982d6dc6514151a39b7ffd02d73d0d73b300bd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 11 Jun 2026 00:01:44 +0000 Subject: [PATCH] Address Copilot review: require python prefix; robust entry_points parsing - parsePythonTag now rejects tags without the 'python' prefix (e.g. '3.12'), which would otherwise produce a bogus .../lib/3.12/site-packages layout - parseConsoleScripts iterates with strings.Split instead of bufio.Scanner, so a long line/large entry_points.txt can't silently truncate the launcher set - tests for both Co-authored-by: Jason Hall --- pymage/internal/cli/cli.go | 9 +++++++-- pymage/internal/cli/cli_test.go | 4 ++++ pymage/internal/wheel/wheel.go | 8 ++++---- pymage/internal/wheel/wheel_test.go | 11 +++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/pymage/internal/cli/cli.go b/pymage/internal/cli/cli.go index 578cf5c..50996de 100644 --- a/pymage/internal/cli/cli.go +++ b/pymage/internal/cli/cli.go @@ -459,9 +459,14 @@ func platformTarget(platform *v1.Platform) wheel.Target { return t } -// parsePythonTag parses "python3.12" into (3, 12). +// parsePythonTag parses "python3.12" into (3, 12). The tag must start with +// "python" because it is also used verbatim as the site-packages lib directory +// (".../lib/python3.12/site-packages"). func parsePythonTag(tag string) (int, int, error) { - s := strings.TrimPrefix(tag, "python") + s, ok := strings.CutPrefix(tag, "python") + if !ok { + return 0, 0, fmt.Errorf("--python %q must look like pythonX.Y", tag) + } majStr, minStr, ok := strings.Cut(s, ".") if !ok { return 0, 0, fmt.Errorf("--python %q must look like pythonX.Y", tag) diff --git a/pymage/internal/cli/cli_test.go b/pymage/internal/cli/cli_test.go index ba69ec7..a467f12 100644 --- a/pymage/internal/cli/cli_test.go +++ b/pymage/internal/cli/cli_test.go @@ -440,6 +440,10 @@ func TestParsePythonTag(t *testing.T) { if _, _, err := parsePythonTag("python3"); err == nil { t.Error("expected error for missing minor version") } + // Must require the "python" prefix (it's also the site-packages lib dir). + if _, _, err := parsePythonTag("3.12"); err == nil { + t.Error("expected error for tag without 'python' prefix") + } } func TestPlatformTargetDefaults(t *testing.T) { diff --git a/pymage/internal/wheel/wheel.go b/pymage/internal/wheel/wheel.go index db80ddb..5940223 100644 --- a/pymage/internal/wheel/wheel.go +++ b/pymage/internal/wheel/wheel.go @@ -14,7 +14,6 @@ package wheel import ( "archive/zip" - "bufio" "fmt" "io" "path" @@ -360,9 +359,10 @@ type entryPoint struct{ module, attr string } func parseConsoleScripts(s string) map[string]entryPoint { out := map[string]entryPoint{} section := "" - sc := bufio.NewScanner(strings.NewReader(s)) - for sc.Scan() { - line := strings.TrimSpace(sc.Text()) + // The file is already fully in memory, so split directly rather than using a + // bufio.Scanner (whose default token limit would silently drop long lines). + for _, raw := range strings.Split(s, "\n") { + line := strings.TrimSpace(raw) if line == "" || strings.HasPrefix(line, "#") { continue } diff --git a/pymage/internal/wheel/wheel_test.go b/pymage/internal/wheel/wheel_test.go index 4516aed..b629caf 100644 --- a/pymage/internal/wheel/wheel_test.go +++ b/pymage/internal/wheel/wheel_test.go @@ -196,6 +196,17 @@ func TestConsoleScriptExtrasStripped(t *testing.T) { } } +func TestParseConsoleScriptsLongLineNotTruncated(t *testing.T) { + // A comment line longer than bufio.Scanner's default 64 KiB token limit + // would have made a Scanner stop early and silently drop later entries. + longComment := "# " + strings.Repeat("x", 200*1024) + content := longComment + "\n[console_scripts]\ntool = pkg.mod:main\n" + eps := parseConsoleScripts(content) + if ep, ok := eps["tool"]; !ok || ep.module != "pkg.mod" || ep.attr != "main" { + t.Fatalf("entry after a very long line was dropped: %+v", eps) + } +} + func TestFilesDeterministic(t *testing.T) { path := writeFixture(t) digest := func() string {