Skip to content

Commit 6e09898

Browse files
committed
Use Project-URL metadata field to get a PyPI package's homepage URL
1 parent a739c78 commit 6e09898

File tree

1 file changed

+41
-4
lines changed

1 file changed

+41
-4
lines changed

routers/api/packages/pypi/pypi.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"regexp"
1111
"sort"
1212
"strings"
13+
"unicode"
1314

1415
packages_model "code.gitea.io/gitea/models/packages"
1516
packages_module "code.gitea.io/gitea/modules/packages"
@@ -139,9 +140,28 @@ func UploadPackageFile(ctx *context.Context) {
139140
return
140141
}
141142

142-
projectURL := ctx.Req.FormValue("home_page")
143-
if !validation.IsValidURL(projectURL) {
144-
projectURL = ""
143+
// Ensure ctx.Req.Form exists.
144+
_ = ctx.Req.ParseForm()
145+
146+
// TODO: Home-page is a deprecated metadata field. Remove this form lookup once it's no longer apart of the spec.
147+
homepageURL := ctx.Req.FormValue("home_page")
148+
if len(homepageURL) == 0 {
149+
projectURLs := ctx.Req.Form["project_urls"]
150+
for _, url := range projectURLs {
151+
label, url, found := strings.Cut(url, ",")
152+
if !found {
153+
continue
154+
}
155+
if normalizeLabel(label) != "homepage" {
156+
continue
157+
}
158+
homepageURL = strings.TrimSpace(url)
159+
break
160+
}
161+
}
162+
163+
if !validation.IsValidURL(homepageURL) {
164+
homepageURL = ""
145165
}
146166

147167
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
@@ -160,7 +180,7 @@ func UploadPackageFile(ctx *context.Context) {
160180
Description: ctx.Req.FormValue("description"),
161181
LongDescription: ctx.Req.FormValue("long_description"),
162182
Summary: ctx.Req.FormValue("summary"),
163-
ProjectURL: projectURL,
183+
ProjectURL: homepageURL,
164184
License: ctx.Req.FormValue("license"),
165185
RequiresPython: ctx.Req.FormValue("requires_python"),
166186
},
@@ -189,6 +209,23 @@ func UploadPackageFile(ctx *context.Context) {
189209
ctx.Status(http.StatusCreated)
190210
}
191211

212+
// Normalizes a Project-URL label.
213+
// See https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
214+
func normalizeLabel(label string) string {
215+
var builder strings.Builder
216+
217+
// "A label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result
218+
// to lowercase."
219+
for _, r := range label {
220+
if unicode.IsPunct(r) || unicode.IsSpace(r) {
221+
continue
222+
}
223+
builder.WriteRune(unicode.ToLower(r))
224+
}
225+
226+
return builder.String()
227+
}
228+
192229
func isValidNameAndVersion(packageName, packageVersion string) bool {
193230
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
194231
}

0 commit comments

Comments
 (0)