Skip to content

Commit 3f0cc02

Browse files
gerrod3jobselko
authored andcommitted
Merge pull request #717 from gerrod3/normalize-name-pypi
Fix package name normalization for package pypi json view (cherry picked from commit fc43b4b)
1 parent ad422c2 commit 3f0cc02

File tree

5 files changed

+94
-2
lines changed

5 files changed

+94
-2
lines changed

CHANGES/716.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed package name normalization issue preventing syncing packages with "." or "_" in their names.

pulp_python/app/models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from pathlib import PurePath
1818
from .utils import (
19+
canonicalize_name,
1920
get_project_metadata_from_artifact,
2021
parse_project_metadata,
2122
python_content_to_json,
@@ -84,6 +85,8 @@ def content_handler(self, path):
8485
).latest("pulp_created")
8586
except ObjectDoesNotExist:
8687
return None
88+
if len(path.parts) == 2:
89+
path = PurePath(f"simple/{canonicalize_name(path.parts[1])}")
8790
rel_path = f"{path}/index.html"
8891
try:
8992
ca = (
@@ -100,8 +103,9 @@ def content_handler(self, path):
100103
return ArtifactResponse(ca.artifact, headers=headers)
101104

102105
if name:
106+
normalized = canonicalize_name(name)
103107
package_content = PythonPackageContent.objects.filter(
104-
pk__in=self.publication.repository_version.content, name__iexact=name
108+
pk__in=self.publication.repository_version.content, name__normalize=normalized
105109
)
106110
# TODO Change this value to the Repo's serial value when implemented
107111
headers = {PYPI_LAST_SERIAL: str(PYPI_SERIAL_CONSTANT)}

pulp_python/app/pypi/views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ def retrieve(self, request, path, meta):
278278
elif meta_path.match("*/json"):
279279
name = meta_path.parts[0]
280280
if name:
281-
package_content = content.filter(name__iexact=name)
281+
normalized = canonicalize_name(name)
282+
package_content = content.filter(name__normalize=normalized)
282283
# TODO Change this value to the Repo's serial value when implemented
283284
headers = {PYPI_LAST_SERIAL: str(PYPI_SERIAL_CONSTANT)}
284285
json_body = python_content_to_json(path, package_content, version=version)

pulp_python/tests/functional/api/test_download_content.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,31 @@ def test_full_pulp_to_pulp_sync(self):
152152

153153
repo3 = self._create_repo_and_sync_with_remote(remote)
154154
self.assertEqual(get_content_summary(repo3.to_dict()), PYTHON_MD_FIXTURE_SUMMARY)
155+
156+
157+
def test_pulp2pulp_sync_with_oddities(
158+
python_repo_with_sync,
159+
python_remote_factory,
160+
python_publication_factory,
161+
python_distribution_factory,
162+
python_content_summary,
163+
):
164+
"""Test that Pulp can handle syncing packages with wierd names."""
165+
remote = python_remote_factory(includes=["oslo.utils"], url="https://pypi.org")
166+
repo = python_repo_with_sync(remote)
167+
distro = python_distribution_factory(repository=repo.pulp_href)
168+
summary = python_content_summary(repository_version=repo.latest_version_href)
169+
# Test pulp 2 pulp full sync w/ live pypi apis
170+
remote2 = python_remote_factory(includes=[], url=distro.base_url)
171+
repo2 = python_repo_with_sync(remote2)
172+
summary2 = python_content_summary(repository_version=repo2.latest_version_href)
173+
assert summary2.present["python.python"]["count"] > 0
174+
assert summary.present["python.python"]["count"] == summary2.present["python.python"]["count"]
175+
# Test w/ publication
176+
pub = python_publication_factory(repository=repo)
177+
distro2 = python_distribution_factory(publication=pub.pulp_href)
178+
remote3 = python_remote_factory(includes=[], url=distro2.base_url)
179+
repo3 = python_repo_with_sync(remote3)
180+
summary3 = python_content_summary(repository_version=repo3.latest_version_href)
181+
assert summary3.present["python.python"]["count"] > 0
182+
assert summary.present["python.python"]["count"] == summary3.present["python.python"]["count"]

pulp_python/tests/functional/conftest.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
import uuid
23

34
from pulp_smash.pulp3.utils import gen_distribution, gen_repo
45
from pulp_python.tests.functional.utils import gen_python_remote
@@ -9,6 +10,7 @@
910
DistributionsPypiApi,
1011
PublicationsPypiApi,
1112
RepositoriesPythonApi,
13+
RepositoriesPythonVersionsApi,
1214
RemotesPythonApi,
1315
)
1416

@@ -29,6 +31,12 @@ def python_repo_api_client(python_bindings_client):
2931
return RepositoriesPythonApi(python_bindings_client)
3032

3133

34+
@pytest.fixture
35+
def python_repo_version_api_client(python_bindings_client):
36+
"""Provides the Python Repository Version API client object."""
37+
return RepositoriesPythonVersionsApi(python_bindings_client)
38+
39+
3240
@pytest.fixture
3341
def python_distro_api_client(python_bindings_client):
3442
"""Provides the Python Distribution API client object."""
@@ -55,6 +63,16 @@ def python_publication_api_client(python_bindings_client):
5563

5664
# Object Generation Fixtures
5765

66+
@pytest.fixture
67+
def python_repo_factory(python_repo_api_client, gen_object_with_cleanup):
68+
"""A factory to generate a Python Repository with auto-cleanup."""
69+
def _gen_python_repo(**kwargs):
70+
kwargs.setdefault("name", str(uuid.uuid4()))
71+
return gen_object_with_cleanup(python_repo_api_client, kwargs)
72+
73+
return _gen_python_repo
74+
75+
5876
@pytest.fixture
5977
def python_repo(python_repo_api_client, gen_object_with_cleanup):
6078
"""Creates a Python Repository and deletes it at test cleanup time."""
@@ -92,3 +110,43 @@ def _gen_python_remote(**kwargs):
92110
return gen_object_with_cleanup(python_remote_api_client, body)
93111

94112
yield _gen_python_remote
113+
114+
115+
@pytest.fixture
116+
def python_repo_with_sync(
117+
python_repo_api_client, python_repo_factory, python_remote_factory, monitor_task
118+
):
119+
"""A factory to generate a Python Repository synced with the passed in Remote."""
120+
def _gen_python_repo_sync(remote=None, mirror=False, repository=None, **body):
121+
kwargs = {}
122+
if pulp_domain := body.get("pulp_domain"):
123+
kwargs["pulp_domain"] = pulp_domain
124+
remote = remote or python_remote_factory(**kwargs)
125+
repo = repository or python_repo_factory(**body)
126+
sync_body = {"mirror": mirror, "remote": remote.pulp_href}
127+
monitor_task(python_repo_api_client.sync(repo.pulp_href, sync_body).task)
128+
return python_repo_api_client.read(repo.pulp_href)
129+
130+
yield _gen_python_repo_sync
131+
132+
133+
@pytest.fixture
134+
def python_content_summary(python_repo_api_client, python_repo_version_api_client):
135+
"""Get a summary of the repository version's content."""
136+
def _gen_summary(repository_version=None, repository=None, version=None):
137+
if repository_version is None:
138+
repo_href = get_href(repository)
139+
if version:
140+
repo_ver_href = f"{repo_href}versions/{version}/"
141+
else:
142+
repo_ver_href = python_repo_api_client.read(repo_href).latest_version_href
143+
else:
144+
repo_ver_href = get_href(repository_version)
145+
return python_repo_version_api_client.read(repo_ver_href).content_summary
146+
147+
yield _gen_summary
148+
149+
150+
def get_href(item):
151+
"""Tries to get the href from the given item, whether it is a string or object."""
152+
return item if isinstance(item, str) else item.pulp_href

0 commit comments

Comments
 (0)