Skip to content

Commit 744f7bc

Browse files
committed
Bump bandersnatch to 8.0 and bump required python_version to >=3.12
Generated-by: cursor-grok-4.6
1 parent fae9eb3 commit 744f7bc

5 files changed

Lines changed: 30 additions & 35 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 via the PEP 691 Simple JSON API, falling back to HTML `/simple/` when JSON is unavailable.

CHANGES/+python-3.12.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Updated minimum required python version to >=3.12.

pulp_python/app/tasks/sync.py

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
from functools import partial
44
from urllib.parse import urljoin, urlparse
55

6-
from aiohttp import ClientError, ClientResponseError
6+
from aiohttp import ClientError
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
1211
from pypi_simple import IndexPage
1312

@@ -169,41 +168,36 @@ 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
172+
via the PEP 691 Simple JSON API, falling back to HTML /simple/.
174173
"""
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
198-
else:
199-
logger.info("Failed to get package list using XMLRPC, trying parse simple page.")
174+
logger.info("Syncing all packages from %s", self.master.url)
175+
try:
176+
simple_index = await self.master.fetch_simple_index()
177+
if not isinstance(simple_index, dict) or "projects" not in simple_index:
178+
raise ValueError("Simple JSON index is missing a projects list")
179+
for project in simple_index["projects"]:
180+
name = project.get("name")
181+
if name is None:
182+
continue
183+
# _last-serial is a PyPI extension; default to 0 when absent
184+
self.packages_to_sync[name] = project.get("_last-serial", 0)
185+
self.target_serial = max(
186+
[self.synced_serial or 0] + [int(v) for v in self.packages_to_sync.values()]
187+
)
188+
except (ClientError, ValueError, TypeError, AttributeError) as exc:
189+
logger.info(
190+
"Failed to list packages via Simple JSON API (%s); "
191+
"falling back to HTML simple index.",
192+
exc,
193+
)
200194
url = urljoin(self.remote.url, "simple/")
201195
downloader = self.remote.get_downloader(url=url)
202196
result = await downloader.run()
203197
with open(result.path) as f:
204198
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)
199+
self.packages_to_sync.update({p: 0 for p in index.projects})
200+
self.target_serial = result.headers.get(PYPI_LAST_SERIAL, 0)
207201

208202
self._filter_packages()
209203
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 via the Simple JSON API, falling back to HTML /simple/.
5252
"""
5353
# Repository we are syncing from is the fixtures (default url)
5454
remote = python_remote_factory(includes=[], prereleases=True)

pyproject.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@ classifiers=[
2020
"Framework :: Django",
2121
"Programming Language :: Python",
2222
"Programming Language :: Python :: 3",
23-
"Programming Language :: Python :: 3.11",
2423
"Programming Language :: Python :: 3.12",
2524
"Programming Language :: Python :: 3.13",
2625
]
27-
requires-python = ">=3.11"
26+
requires-python = ">=3.12"
2827
dependencies = [
2928
"pulpcore>=3.105.0,<3.130",
3029
"pkginfo>=1.12.0,<1.13.0",
31-
"bandersnatch>=6.6.0,<6.7",
30+
"bandersnatch>=8.0.0,<8.1",
3231
"pypi-simple>=1.8.0,<2.0",
3332
"pypi-attestations==0.0.28", # API is not stable
3433
]

0 commit comments

Comments
 (0)