Skip to content

Commit f598a5f

Browse files
committed
Bump bandersnatch to 8.0
Generated-by: OpenAI Codex
1 parent a74ccaf commit f598a5f

4 files changed

Lines changed: 32 additions & 39 deletions

File tree

‎CHANGES/+bandersnatch-8.feature‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Upgraded Bandersnatch to 8.0. Full-index syncs now list packages from either the JSON or HTML representation of the Simple API.

‎pulp_python/app/tasks/sync.py‎

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import asyncio
2+
import json
23
import logging
34
from functools import partial
4-
from urllib.parse import urljoin, urlparse
5+
from urllib.parse import urlparse
56

6-
from aiohttp import ClientError, ClientResponseError
77
from bandersnatch.configuration import BandersnatchConfig
88
from bandersnatch.master import Master
99
from bandersnatch.mirror import Mirror
10-
from lxml.etree import LxmlError
1110
from packaging.requirements import Requirement
12-
from pypi_simple import IndexPage
11+
from pypi_simple import ACCEPT_ANY, IndexPage, UnsupportedContentTypeError
1312

1413
from pulpcore.plugin.download import HttpDownloader
1514
from pulpcore.plugin.exceptions import SyncError
@@ -169,41 +168,34 @@ def __init__(self, serial, master, workers, deferred_download, python_stage, pro
169168

170169
async def determine_packages_to_sync(self):
171170
"""
172-
Calling this means that includes wasn't specified,
173-
so try to get all of the packages from Mirror (hopefully PyPi)
171+
Called when includes wasn't specified. List all projects from the remote's simple index.
174172
"""
175-
number_xmlrpc_attempts = 3
176-
for attempt in range(number_xmlrpc_attempts):
177-
logger.info("Attempt {} to get package list from {}".format(attempt, self.master.url))
178-
try:
179-
if not self.synced_serial:
180-
logger.info("Syncing all packages.")
181-
# First get the current serial, then start to sync.
182-
all_packages = await self.master.all_packages()
183-
self.packages_to_sync.update(all_packages)
184-
self.target_serial = max(
185-
[self.synced_serial] + [int(v) for v in self.packages_to_sync.values()]
186-
)
187-
else:
188-
logger.info("Syncing based on changelog.")
189-
changed_packages = await self.master.changed_packages(self.synced_serial)
190-
self.packages_to_sync.update(changed_packages)
191-
self.target_serial = max(
192-
[self.synced_serial] + [int(v) for v in self.packages_to_sync.values()]
193-
)
194-
break
195-
except (ClientError, ClientResponseError, LxmlError):
196-
# Retry if XMLRPC endpoint failed, server might not support it.
197-
continue
173+
logger.info("Syncing all packages from %s", self.master.url)
174+
url = f"{self.remote.url.rstrip('/')}/simple/"
175+
downloader = self.remote.get_downloader(url=url)
176+
result = await downloader.run(
177+
extra_data={"request_kwargs": {"headers": {"Accept": ACCEPT_ANY}}}
178+
)
179+
180+
content_type = result.headers.get("Content-Type", "text/html").partition(";")[0].lower()
181+
with open(result.path, "rb") as f:
182+
content = f.read()
183+
184+
if content_type == "application/vnd.pypi.simple.v1+json":
185+
index = IndexPage.from_json_data(json.loads(content))
186+
elif content_type in {"application/vnd.pypi.simple.v1+html", "text/html"}:
187+
index = IndexPage.from_html(content)
188+
else:
189+
raise UnsupportedContentTypeError(url, content_type)
190+
191+
self.packages_to_sync = {project: 0 for project in index.projects}
192+
last_serial = index.last_serial
193+
if last_serial is None:
194+
last_serial = result.headers.get(PYPI_LAST_SERIAL)
195+
if last_serial is None:
196+
self.target_serial = self.synced_serial
198197
else:
199-
logger.info("Failed to get package list using XMLRPC, trying parse simple page.")
200-
url = urljoin(self.remote.url, "simple/")
201-
downloader = self.remote.get_downloader(url=url)
202-
result = await downloader.run()
203-
with open(result.path) as f:
204-
index = IndexPage.from_html(f.read())
205-
self.packages_to_sync.update({p: 0 for p in index.projects})
206-
self.target_serial = result.headers.get(PYPI_LAST_SERIAL, 0)
198+
self.target_serial = max(self.synced_serial, int(last_serial))
207199

208200
self._filter_packages()
209201
if self.target_serial:

‎pulp_python/tests/functional/api/test_download_content.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def test_full_fixtures_to_pulp_sync(
4848
):
4949
"""
5050
This test checks that Pulp can fully sync another Python Package repository that is not
51-
PyPI. This reads the repository's simple page if XMLRPC isn't supported.
51+
PyPI. This lists projects from the remote's Simple API index.
5252
"""
5353
# Repository we are syncing from is the fixtures (default url)
5454
remote = python_remote_factory(includes=[], prereleases=True)

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ requires-python = ">=3.12"
2727
dependencies = [
2828
"pulpcore>=3.105.0,<3.130",
2929
"pkginfo>=1.12.0,<1.13.0",
30-
"bandersnatch>=6.6.0,<6.7",
30+
"bandersnatch>=8.0.0,<8.1",
3131
"pypi-simple>=1.8.0,<2.0",
3232
"pypi-attestations==0.0.28", # API is not stable
3333
]

0 commit comments

Comments
 (0)