Skip to content

Commit

Permalink
Overeager pip normalization (#206)
Browse files Browse the repository at this point in the history
* Fleshing out pipFlags comment

* Filter pip freeze based on normalized form

pip freeze output is actually pip's internal canonicalized form, which
can be different from the normalized package name format.

Example: `discord.py` gets normalized into `discord-py`,
but `pip show discord-py` still outputs `Name: discord.py`.

* Avoid normalizing pip canonical package name

Names in requirements.txt are the package as tracked in package metadata, not necessarily the normalized name of the package.

Example: pip install discord-py will add `discord.py` to the output of `pip freeze`.
  • Loading branch information
blast-hardcheese authored Jan 2, 2024
1 parent ecd256c commit c35c3ca
Showing 1 changed file with 21 additions and 12 deletions.
33 changes: 21 additions & 12 deletions internal/backends/python/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,15 +388,24 @@ func makePythonPipBackend(python string) api.LanguageBackend {
util.Die("failed to run freeze: %s", err.Error())
}

// As we walk through the output of pip freeze,
// compare the package metadata name to the normalized
// pkgs that we are trying to install, to see which we
// want to track in `requirements.txt`.
normalizedPkgs := make(map[api.PkgName]bool)
for name := range pkgs {
normalizedPkgs[normalizePackageName(name)] = true
}

var toAppend []string
for _, line := range strings.Split(string(outputB), "\n") {
for _, canonicalSpec := range strings.Split(string(outputB), "\n") {
var name api.PkgName
matches := matchPackageAndSpec.FindSubmatch(([]byte)(line))
matches := matchPackageAndSpec.FindSubmatch(([]byte)(canonicalSpec))
if len(matches) > 0 {
name = normalizePackageName(api.PkgName(string(matches[1])))
}
if _, exists := pkgs[name]; exists {
toAppend = append(toAppend, line)
if normalizedPkgs[name] {
toAppend = append(toAppend, canonicalSpec)
}
}

Expand Down Expand Up @@ -434,20 +443,20 @@ func makePythonPipBackend(python string) api.LanguageBackend {
util.RunCmd([]string{"pip", "install", "-r", "requirements.txt"})
},
ListSpecfile: func() map[api.PkgName]api.PkgSpec {
flags, rawPkgs, err := ListRequirementsTxt("requirements.txt")
flags, pkgs, err := ListRequirementsTxt("requirements.txt")
if err != nil {
util.Die("%s", err.Error())
}

normalizedPkgs := make(map[api.PkgName]api.PkgSpec)
for name, spec := range rawPkgs {
normalizedPkgs[normalizePackageName(name)] = spec
}

// Stash the seen flags into a module global
// Stash the seen flags into a module global.
// This isn't great, but the expectation is that ListSpecfile
// is called before we run `Add`.
pipFlags = flags

return normalizedPkgs
// NB: We rely on requirements.txt being populated with the
// Python package _metadata_ name, not the PEP-503/PEP-508
// normalized version.
return pkgs
},
GuessRegexps: pythonGuessRegexps,
Guess: func(ctx context.Context) (map[api.PkgName]bool, bool) { return guess(ctx, python) },
Expand Down

0 comments on commit c35c3ca

Please sign in to comment.