From 8fcd1530e2dfca733f98c1c555bfd1ed77e19d7d Mon Sep 17 00:00:00 2001 From: Gerrod Ubben Date: Mon, 22 Nov 2021 17:43:18 -0500 Subject: [PATCH] Fix name normalization for package simple detail view fixes: #467 --- CHANGES/467.bugfix | 1 + pulp_python/app/models.py | 9 +++++++++ pulp_python/app/pypi/views.py | 24 ++++++++++++++++++------ 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 CHANGES/467.bugfix diff --git a/CHANGES/467.bugfix b/CHANGES/467.bugfix new file mode 100644 index 000000000..3a5209199 --- /dev/null +++ b/CHANGES/467.bugfix @@ -0,0 +1 @@ +Fixed package name normalization issue preventing installing packages with "." or "_" in their names. diff --git a/pulp_python/app/models.py b/pulp_python/app/models.py index 0f10b2e75..fbefb4c12 100644 --- a/pulp_python/app/models.py +++ b/pulp_python/app/models.py @@ -124,6 +124,14 @@ class Meta: default_related_name = "%(app_label)s_%(model_name)s" +class NormalizeName(models.Transform): + """A transform field to normalize package names according to PEP426.""" + + function = "REGEXP_REPLACE" + template = "LOWER(%(function)s(%(expressions)s, '(\.|_|-)', '-', 'ig'))" # noqa:W605 + lookup_name = "normalize" + + class PythonPackageContent(Content): """ A Content Type representing Python's Distribution Package. @@ -142,6 +150,7 @@ class PythonPackageContent(Content): filename = models.TextField(db_index=True) packagetype = models.TextField(choices=PACKAGE_TYPES) name = models.TextField() + name.register_lookup(NormalizeName) version = models.TextField() sha256 = models.CharField(unique=True, db_index=True, max_length=64) # Optional metadata diff --git a/pulp_python/app/pypi/views.py b/pulp_python/app/pypi/views.py index c3020b7cc..70a3f2439 100644 --- a/pulp_python/app/pypi/views.py +++ b/pulp_python/app/pypi/views.py @@ -19,8 +19,10 @@ ) from drf_spectacular.utils import extend_schema from dynaconf import settings +from itertools import chain from urllib.parse import urljoin from pathlib import PurePath +from packaging.utils import canonicalize_name from pulpcore.plugin.tasking import dispatch from pulp_python.app.models import ( @@ -188,14 +190,24 @@ def list(self, request, path): def retrieve(self, request, path, package): """Retrieves the simple api html page for a package.""" distro, repo_ver, content = self.get_drvc(path) + # Should I redirect if the normalized name is different? + normalized = canonicalize_name(package) if self.should_redirect(distro, repo_version=repo_ver): - # Maybe this name needs to be normalized? - return redirect(urljoin(BASE_CONTENT_URL, f'{path}/simple/{package}/')) - packages = content.filter(name__iexact=package).values_list('filename', 'sha256').iterator() - detail_packages = ((f, urljoin(BASE_CONTENT_URL, f'{path}/{f}'), d) for f, d in packages) - return StreamingHttpResponse( - write_simple_detail(package, detail_packages, streamed=True) + return redirect(urljoin(BASE_CONTENT_URL, f'{path}/simple/{normalized}/')) + packages = ( + content.filter(name__normalize=normalized) + .values_list('filename', 'sha256', 'name') + .iterator() ) + try: + present = next(packages) + except StopIteration: + raise Http404(f"{normalized} does not exist.") + else: + packages = chain([present], packages) + name = present[2] + releases = ((f, urljoin(BASE_CONTENT_URL, f'{path}/{f}'), d) for f, d, _ in packages) + return StreamingHttpResponse(write_simple_detail(name, releases, streamed=True)) @extend_schema(request=PackageUploadSerializer, responses={200: PackageUploadTaskSerializer},