Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
path: "pulp-openapi-generator"
- uses: "actions/setup-python@v6"
with:
python-version: "3.11"
python-version: "3.12"
- name: "Install uv"
uses: "astral-sh/setup-uv@v7"
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/create-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:

- uses: "actions/setup-python@v6"
with:
python-version: "3.11"
python-version: "3.12"
- name: "Install uv"
uses: "astral-sh/setup-uv@v7"
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

- uses: "actions/setup-python@v6"
with:
python-version: "3.11"
python-version: "3.12"
- name: "Install uv"
uses: "astral-sh/setup-uv@v7"
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

- uses: "actions/setup-python@v6"
with:
python-version: "3.11"
python-version: "3.12"
- name: "Install uv"
uses: "astral-sh/setup-uv@v7"
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:

- uses: "actions/setup-python@v6"
with:
python-version: "3.11"
python-version: "3.12"
- name: "Install uv"
uses: "astral-sh/setup-uv@v7"
with:
Expand Down
1 change: 1 addition & 0 deletions CHANGES/+bandersnatch-8.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Upgraded Bandersnatch to 8.0. Full-index syncs now list packages from either the JSON or HTML representation of the Simple API.
1 change: 1 addition & 0 deletions CHANGES/+python-3.12.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Updated minimum required python version to >=3.12.
66 changes: 29 additions & 37 deletions pulp_python/app/tasks/sync.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import asyncio
import json
import logging
from functools import partial
from urllib.parse import urljoin, urlparse
from urllib.parse import urlparse

from aiohttp import ClientError, ClientResponseError
from bandersnatch.configuration import BandersnatchConfig
from bandersnatch.master import Master
from bandersnatch.mirror import Mirror
from lxml.etree import LxmlError
from packaging.requirements import Requirement
from pypi_simple import IndexPage
from pypi_simple import ACCEPT_ANY, IndexPage, UnsupportedContentTypeError

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

async def determine_packages_to_sync(self):
"""
Calling this means that includes wasn't specified,
so try to get all of the packages from Mirror (hopefully PyPi)
Called when includes wasn't specified. List all projects from the remote's simple index.
"""
number_xmlrpc_attempts = 3
for attempt in range(number_xmlrpc_attempts):
logger.info("Attempt {} to get package list from {}".format(attempt, self.master.url))
try:
if not self.synced_serial:
logger.info("Syncing all packages.")
# First get the current serial, then start to sync.
all_packages = await self.master.all_packages()
self.packages_to_sync.update(all_packages)
self.target_serial = max(
[self.synced_serial] + [int(v) for v in self.packages_to_sync.values()]
)
else:
logger.info("Syncing based on changelog.")
changed_packages = await self.master.changed_packages(self.synced_serial)
self.packages_to_sync.update(changed_packages)
self.target_serial = max(
[self.synced_serial] + [int(v) for v in self.packages_to_sync.values()]
)
break
except (ClientError, ClientResponseError, LxmlError):
# Retry if XMLRPC endpoint failed, server might not support it.
continue
logger.info("Syncing all packages from %s", self.master.url)
url = f"{self.remote.url.rstrip('/')}/simple/"
downloader = self.remote.get_downloader(url=url)
result = await downloader.run(
extra_data={"request_kwargs": {"headers": {"Accept": ACCEPT_ANY}}}
)

content_type = result.headers.get("Content-Type", "text/html").partition(";")[0].lower()
with open(result.path, "rb") as f:
content = f.read()

if content_type == "application/vnd.pypi.simple.v1+json":
index = IndexPage.from_json_data(json.loads(content))
elif content_type in {"application/vnd.pypi.simple.v1+html", "text/html"}:
index = IndexPage.from_html(content)
else:
raise UnsupportedContentTypeError(url, content_type)

self.packages_to_sync = {project: 0 for project in index.projects}
last_serial = index.last_serial
if last_serial is None:
last_serial = result.headers.get(PYPI_LAST_SERIAL)
if last_serial is None:
Comment thread
jobselko marked this conversation as resolved.
self.target_serial = self.synced_serial
else:
logger.info("Failed to get package list using XMLRPC, trying parse simple page.")
url = urljoin(self.remote.url, "simple/")
downloader = self.remote.get_downloader(url=url)
result = await downloader.run()
with open(result.path) as f:
index = IndexPage.from_html(f.read())
self.packages_to_sync.update({p: 0 for p in index.projects})
self.target_serial = result.headers.get(PYPI_LAST_SERIAL, 0)
self.target_serial = max(self.synced_serial, int(last_serial))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, synced_serial in our code is always 0:

                pmirror = PulpMirror(
                    serial=0,  # Serial currently isn't supported by Pulp


self._filter_packages()
if self.target_serial:
Expand Down
2 changes: 1 addition & 1 deletion pulp_python/tests/functional/api/test_download_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_full_fixtures_to_pulp_sync(
):
"""
This test checks that Pulp can fully sync another Python Package repository that is not
PyPI. This reads the repository's simple page if XMLRPC isn't supported.
PyPI. This lists projects from the remote's Simple API index.
"""
# Repository we are syncing from is the fixtures (default url)
remote = python_remote_factory(includes=[], prereleases=True)
Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ classifiers=[
"Framework :: Django",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
requires-python = ">=3.11"
requires-python = ">=3.12"
dependencies = [
"pulpcore>=3.105.0,<3.130",
"pkginfo>=1.12.0,<1.13.0",
"bandersnatch>=6.6.0,<6.7",
"bandersnatch>=8.0.0,<8.1",
"pypi-simple>=1.8.0,<2.0",
"pypi-attestations==0.0.28", # API is not stable
]
Expand Down
Loading