Repository navigation
Description
Version
pulpcore: 3.102.0
pulp-python: 3.24.1
Installation method: Podman, via this docker-compose:
services:
pulp:
image: pulp/pulp
container_name: pulp
ports:
- "8080:80"
cap_add:
- SYS_ADMIN
security_opt:
- label=disable
devices:
- /dev/fuse
volumes:
- ./settings:/etc/pulp:U
- pulp_storage:/var/lib/pulp
- pgsql:/var/lib/pgsql
- containers:/var/lib/containers
- container_build:/var/lib/pulp/.local/share/containers
restart: unless-stopped
volumes:
pulp_storage:
pgsql:
containers:
container_build:
Describe the bug
Setting up a repository, a corresponding distribution, a remote pointing to pypi, in pull-through caching mode, the setup works with pip, but uv (https://docs.astral.sh/uv/) is unable to resolve dependencies.
To Reproduce
- Do the standard setup of pulp and pulp-cli as described in the tutorials.
- Set up repository, distribution and remote as follows:
pulp python repository create --name orgalorg-repo
pulp python distribution create --name orgalorg-dist --base-path orgalorg --repository orgalorg-repo
pulp python remote create --name pypi --url https://pypi.org/ --policy on_demand
pulp python distribution update --name orgalorg-dist --remote pypi
- Set up a dummy project via uv (e.g.
uv init dummyproject) and in the createdpyproject.tomlwrite
[project]
name = "dummyenv"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = ["requests==2.32.5"]
[[tool.uv.index]]
name = "orgalorg"
url = "http://localhost:8080/pypi/orgalorg/simple"
default = true
Vary the dependencies, for example I have tried requests, requests==2.32.5 and numpy.
-
Run
uv sync -
Observe that
uvfails to resolve dependencies (see later for additional data). -
To ensure things work with pip, destroy the venv (
rm -rf .venv), then
python -m venv .venv
source .venv/bin/activate
pip install requests==2.32.5
Observe that pip does this successfully and running pulp content list (in a venv where pulp-cli has been installed) will display the downloaded package (Note: aborted resolution attempts by uv can also result in some packages being downloaded and cached).
-
Destroy the venv again, and try
uv syncagain, for example withrequests==2.32.5in thedependencies. Again observe failure but now for different reasons (the package is now cached in the pulp server). -
Recreate the venv (
uv venv) if it doesn't exist and runuv pip install requests=2.32.5 --no-deps. Observe that nowuvinstalls the package successfully (but without its dependencies), confirming that this is not a connection problem but a dependency resolution problem.
Expected behavior
uv should be able to resolve dependencies through the proxy.
Additional context
Output when trying to sync project with uv with requests unconstrained:
benzor@orgalorg:~/Projects/Local/dummyenv$ uv sync
× Failed to download and build `requests==1.2.3`
├─▶ Failed to resolve requirements from `setup.py` build
├─▶ No solution found when resolving: `setuptools>=40.8.0`
╰─▶ Because only the following versions of setuptools are available:
setuptools<=40.8.0
setuptools==40.9.0
setuptools==41.0.0
setuptools==41.0.1
setuptools==41.1.0
setuptools==41.2.0
setuptools==41.3.0
setuptools==41.4.0
setuptools==41.5.0
setuptools==41.5.1
setuptools==41.6.0
setuptools==42.0.0
setuptools==42.0.1
setuptools==42.0.2
setuptools==43.0.0
setuptools==44.0.0
setuptools==44.1.0
setuptools==44.1.1
setuptools==45.0.0
setuptools==45.1.0
setuptools==45.2.0
setuptools==45.3.0
setuptools==46.0.0
setuptools==46.1.0
setuptools==46.1.1
setuptools==46.1.2
setuptools==46.1.3
setuptools==46.2.0
setuptools==46.3.0
setuptools==46.3.1
setuptools==46.4.0
setuptools==47.0.0
setuptools==47.1.0
setuptools==47.1.1
setuptools==47.2.0
setuptools==47.3.0
setuptools==47.3.1
setuptools==47.3.2
setuptools==48.0.0
setuptools==49.0.0
setuptools==49.0.1
setuptools==49.1.0
setuptools==49.1.1
setuptools==49.1.2
setuptools==49.1.3
setuptools==49.2.0
setuptools==49.2.1
setuptools==49.3.0
setuptools==49.3.1
setuptools==49.3.2
setuptools==49.4.0
setuptools==49.5.0
setuptools==49.6.0
setuptools==50.0.0
setuptools==50.0.1
setuptools==50.0.2
setuptools==50.0.3
setuptools==50.1.0
setuptools==50.2.0
setuptools==50.3.0
setuptools==50.3.1
setuptools==50.3.2
setuptools==51.0.0
setuptools==51.1.0
setuptools==51.1.0.post20201221
setuptools==51.1.1
setuptools==51.1.2
setuptools==51.2.0
setuptools==51.3.0
setuptools==51.3.1
setuptools==51.3.2
setuptools==51.3.3
setuptools==52.0.0
setuptools==53.0.0
setuptools==53.1.0
setuptools==54.0.0
setuptools==54.1.0
setuptools==54.1.1
setuptools==54.1.2
setuptools==54.1.3
setuptools==54.2.0
setuptools==56.0.0
setuptools==56.1.0
setuptools==56.2.0
setuptools==57.0.0
setuptools==57.1.0
setuptools==57.2.0
setuptools==57.3.0
setuptools==57.4.0
setuptools==57.5.0
setuptools==58.0.0
setuptools==58.0.1
setuptools==58.0.2
setuptools==58.0.3
setuptools==58.0.4
setuptools==58.1.0
setuptools==58.2.0
setuptools==58.3.0
setuptools==58.4.0
setuptools==58.5.0
setuptools==58.5.1
setuptools==58.5.2
setuptools==58.5.3
setuptools==59.0.1
setuptools==59.1.0
setuptools==59.1.1
setuptools==59.2.0
setuptools==59.3.0
setuptools==59.4.0
setuptools==59.5.0
setuptools==59.6.0
setuptools==59.7.0
setuptools==59.8.0
setuptools==60.0.0
setuptools==60.0.1
setuptools==60.0.2
setuptools==60.0.3
setuptools==60.0.4
setuptools==60.0.5
setuptools==60.1.0
setuptools==60.1.1
setuptools==60.2.0
setuptools==60.3.0
setuptools==60.3.1
setuptools==60.4.0
setuptools==60.5.0
setuptools==60.6.0
setuptools==60.7.0
setuptools==60.7.1
setuptools==60.8.0
setuptools==60.8.1
setuptools==60.8.2
setuptools==60.9.0
setuptools==60.9.1
setuptools==60.9.2
setuptools==60.9.3
setuptools==60.10.0
setuptools==61.0.0
setuptools==61.1.0
setuptools==61.1.1
setuptools==61.2.0
setuptools==61.3.0
setuptools==61.3.1
setuptools==62.0.0
setuptools==62.1.0
setuptools==62.2.0
setuptools==62.3.0
setuptools==62.3.1
setuptools==62.3.2
setuptools==62.3.3
setuptools==62.3.4
setuptools==62.4.0
setuptools==62.5.0
setuptools==62.6.0
setuptools==63.0.0
setuptools==63.1.0
setuptools==63.2.0
setuptools==63.3.0
setuptools==63.4.0
setuptools==63.4.1
setuptools==63.4.2
setuptools==63.4.3
setuptools==64.0.0
setuptools==64.0.1
setuptools==64.0.2
setuptools==64.0.3
setuptools==65.0.0
setuptools==65.0.1
setuptools==65.0.2
setuptools==65.1.0
setuptools==65.1.1
setuptools==65.2.0
setuptools==65.3.0
setuptools==65.4.0
setuptools==65.4.1
setuptools==65.5.0
setuptools==65.5.1
setuptools==65.6.0
setuptools==65.6.1
setuptools==65.6.2
setuptools==65.6.3
setuptools==65.7.0
setuptools==66.0.0
setuptools==66.1.0
setuptools==66.1.1
setuptools==67.0.0
setuptools==67.1.0
setuptools==67.2.0
setuptools==67.3.1
setuptools==67.3.2
setuptools==67.3.3
setuptools==67.4.0
setuptools==67.5.0
setuptools==67.5.1
setuptools==67.6.0
setuptools==67.6.1
setuptools==67.7.0
setuptools==67.7.1
setuptools==67.7.2
setuptools==67.8.0
setuptools==68.0.0
setuptools==68.1.0
setuptools==68.1.2
setuptools==68.2.0
setuptools==68.2.1
setuptools==68.2.2
setuptools==69.0.0
setuptools==69.0.1
setuptools==69.0.2
setuptools==69.0.3
setuptools==69.1.0
setuptools==69.1.1
setuptools==69.2.0
setuptools==69.3.0
setuptools==69.3.1
setuptools==69.4.0
setuptools==69.4.1
setuptools==69.4.2
setuptools==69.5.0
setuptools==69.5.1
setuptools==70.0.0
setuptools==70.1.0
setuptools==70.1.1
setuptools==70.2.0
setuptools==70.3.0
setuptools==71.0.0
setuptools==71.0.1
setuptools==71.0.2
setuptools==71.0.3
setuptools==71.0.4
setuptools==71.1.0
setuptools==72.0.0
setuptools==72.1.0
setuptools==72.2.0
setuptools==73.0.0
setuptools==73.0.1
setuptools==74.0.0
setuptools==74.1.0
setuptools==74.1.1
setuptools==74.1.2
setuptools==74.1.3
setuptools==75.0.0
setuptools==75.1.0
setuptools==75.2.0
setuptools==75.3.0
setuptools==75.3.1
setuptools==75.3.2
setuptools==75.3.3
setuptools==75.4.0
setuptools==75.5.0
setuptools==75.6.0
setuptools==75.7.0
setuptools==75.8.0
setuptools==75.8.1
setuptools==75.8.2
setuptools==75.9.0
setuptools==75.9.1
setuptools==76.0.0
setuptools==76.1.0
setuptools==77.0.1
setuptools==77.0.3
setuptools==78.0.1
setuptools==78.0.2
setuptools==78.1.0
setuptools==78.1.1
setuptools==79.0.0
setuptools==79.0.1
setuptools==80.0.0
setuptools==80.0.1
setuptools==80.1.0
setuptools==80.2.0
setuptools==80.3.0
setuptools==80.3.1
setuptools==80.4.0
setuptools==80.6.0
setuptools==80.7.0
setuptools==80.7.1
setuptools==80.8.0
setuptools==80.9.0
setuptools==80.10.1
setuptools==80.10.2
and setuptools>=40.8.0 has invalid metadata, we can conclude that setuptools>=40.8.0 cannot be used.
And because you require setuptools>=40.8.0, we can conclude that your requirements are unsatisfiable.
hint: Only pre-releases of `setuptools` (e.g., 63.0.0b1) match these build requirements, and
build environments can't enable pre-releases automatically. Add `setuptools>=63.0.0b1` to
`build-system.requires`, `[tool.uv.extra-build-dependencies]`, or supply it via `uv build
--build-constraint`.
hint: Metadata for `setuptools` (v80.10.2) could not be parsed:
Metadata field Name not found
help: `requests` (v1.2.3) was included because `dummyenv` (v0.1.0) depends on `requests`
Output when trying to sync with requests==2.32.5:
benzor@orgalorg:~/Projects/Local/dummyenv$ uv sync
× No solution found when resolving dependencies:
╰─▶ Because requests==2.32.5 has invalid metadata and your project depends on requests==2.32.5, we can
conclude that your project's requirements are unsatisfiable.
hint: Metadata for `requests` (v2.32.5) could not be parsed:
Header cannot start with a space; it is likely an overhanging line from a previous header
Logs in the container when trying to sync with requests==2.32.5:
('pulp [fa62431e7c8547ef92b483ea5742dcb5]: ::1 - - [28/Jan/2026:21:35:42 +0000] "GET /pypi/orgalorg/simple/requests/ HTTP/1.0" 200 99533 "-" "uv/0.9.27 {\"installer\":{\"name\":\"uv\",\"version\":\"0.9.27\",\"subcommand\":[\"sync\"]},\"python\":\"3.14.2\",\"implementation\":{\"name\":\"CPython\",\"version\":\"3.14.2\"},\"distro\":{\"name\":\"Fedora Linux\",\"version\":\"43\",\"id\":\"\",\"libc\":{\"lib\":\"glibc\",\"version\":\"2.42\"}},\"system\":{\"name\":\"Linux\",\"release\":\"6.18.6-200.fc43.x86_64\"},\"cpu\":\"x86_64\",\"openssl_version\":null,\"setuptools_version\":null,\"rustc_version\":null,\"ci\":null}"',)
[2026-01-28 21:35:42 +0000] [485] [ERROR] Error handling request from ::1
Traceback (most recent call last):
File "/usr/local/lib64/python3.11/site-packages/aiohttp/web_protocol.py", line 510, in _handle_request
resp = await request_handler(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib64/python3.11/site-packages/aiohttp/web_app.py", line 569, in _handle
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib64/python3.11/site-packages/aiohttp/web_middlewares.py", line 117, in impl
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulpcore/content/authentication.py", line 27, in guid
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulpcore/content/authentication.py", line 58, in authenticate
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulpcore/content/handler.py", line 291, in stream_content
return await self._match_and_stream(path, request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulpcore/content/handler.py", line 922, in _match_and_stream
return await self._stream_remote_artifact(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulpcore/content/handler.py", line 1348, in _stream_remote_artifact
content_artifacts = await asyncio.shield(
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/asgiref/sync.py", line 504, in __call__
ret = await asyncio.shield(exec_coro)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/asgiref/sync.py", line 559, in thread_handler
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulpcore/content/handler.py", line 1052, in _save_artifact
content = c_type.init_from_artifact_and_relative_path(artifact, rel_path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulp_python/app/models.py", line 217, in init_from_artifact_and_relative_path
data = artifact_to_python_content_data(path.name, artifact, domain=get_domain())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulp_python/app/utils.py", line 252, in artifact_to_python_content_data
metadata = get_project_metadata_from_file(temp_file.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pulp_python/app/utils.py", line 196, in get_project_metadata_from_file
pkg_type_index = [filename.endswith(ext) for ext in extensions].index(True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: True is not in list
Output when trying to sync with requests==2.32.5 after this package has been cached (by installing successfully through pip):
benzor@orgalorg:~/Projects/Local/dummyenv$ uv sync
Using CPython 3.14.2 interpreter at: /usr/bin/python3.14
Creating virtual environment at: .venv
⠧ requests==2.32.5 error: Failed to fetch: `http://orgalorg:8080/pulp/content/orgalorg/requests-2.32.5-py3-none-any.whl.metadata`
Caused by: HTTP status client error (404 Error while fetching from upstream remote(https://pypi.org/requests-2.32.5-py3-none-any.whl.metadata): Not Found) for url (http://orgalorg:8080/pulp/content/orgalorg/requests-2.32.5-py3-none-any.whl.metadata)
And corresponding container logs:
[28/Jan/2026:21:39:19 +0000] "GET /pulp/content/orgalorg/requests-2.32.5-py3-none-any.whl.metadata HTTP/1.0" 404 381 "-" "uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["sync"]},"python":"3.14.2","implementation":{"name":"CPython","version":"3.14.2"},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":{"lib":"glibc","version":"2.42"}},"system":{"name":"Linux","release":"6.18.6-200.fc43.x86_64"},"cpu":"x86_64","openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}"
Notes:
I am quite ignorant when it comes to PyPI specifics and packaging in general besides building wheels, but it seems to me the issue is that uv is trying to get .whl.metadata files. I have no idea what these are (as far as I know, metadata is contained in a wheel itself).
The output of pulp content list is
(pulp-server) benzor@orgalorg:~/Projects/Local/pulp-server$ pulp content list
[
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c068b-217b-769e-9308-779cc829e741/",
"prn": "prn:python.pythonpackagecontent:019c068b-217b-769e-9308-779cc829e741",
"pulp_created": "2026-01-28T21:38:27.324595Z",
"pulp_last_updated": "2026-01-28T21:38:27.324607Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"certifi-2026.1.4-py3-none-any.whl": "/pulp/api/v3/artifacts/019c068b-2175-7cf8-a59e-3609e6cdec82/"
}
},
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c068b-1d85-71ec-9172-55f1a957a019/",
"prn": "prn:python.pythonpackagecontent:019c068b-1d85-71ec-9172-55f1a957a019",
"pulp_created": "2026-01-28T21:38:26.309999Z",
"pulp_last_updated": "2026-01-28T21:38:26.310011Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"urllib3-2.6.3-py3-none-any.whl": "/pulp/api/v3/artifacts/019c068b-1d7f-7434-a594-f1b59069e40e/"
}
},
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c068b-1bb6-7795-9621-541c2b381220/",
"prn": "prn:python.pythonpackagecontent:019c068b-1bb6-7795-9621-541c2b381220",
"pulp_created": "2026-01-28T21:38:25.847351Z",
"pulp_last_updated": "2026-01-28T21:38:25.847360Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"idna-3.11-py3-none-any.whl": "/pulp/api/v3/artifacts/019c068b-1bb1-791e-81e2-59540e0b3196/"
}
},
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c068b-1a8d-7895-928c-92ff922746b6/",
"prn": "prn:python.pythonpackagecontent:019c068b-1a8d-7895-928c-92ff922746b6",
"pulp_created": "2026-01-28T21:38:25.550239Z",
"pulp_last_updated": "2026-01-28T21:38:25.550249Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "/pulp/api/v3/artifacts/019c068b-1a88-7c0d-b8fc-1857d748d2a8/"
}
},
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c068b-1959-7aac-bec0-8bf1e07e6fb1/",
"prn": "prn:python.pythonpackagecontent:019c068b-1959-7aac-bec0-8bf1e07e6fb1",
"pulp_created": "2026-01-28T21:38:25.242970Z",
"pulp_last_updated": "2026-01-28T21:38:25.242982Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"requests-2.32.5-py3-none-any.whl": "/pulp/api/v3/artifacts/019c068b-1953-7093-9f56-8e9f644369f7/"
}
},
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c0678-dbd3-71c0-8f0d-5ef1f9d1b6cd/",
"prn": "prn:python.pythonpackagecontent:019c0678-dbd3-71c0-8f0d-5ef1f9d1b6cd",
"pulp_created": "2026-01-28T21:18:29.844404Z",
"pulp_last_updated": "2026-01-28T21:18:29.844415Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"numpy-1.10.0.post2.tar.gz": "/pulp/api/v3/artifacts/019c0678-db5b-76d2-a581-9a42d978d127/"
}
},
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c0667-1868-77d2-8c37-e5db6ce403c3/",
"prn": "prn:python.pythonpackagecontent:019c0667-1868-77d2-8c37-e5db6ce403c3",
"pulp_created": "2026-01-28T20:59:05.706536Z",
"pulp_last_updated": "2026-01-28T20:59:05.706548Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"requests-1.2.3.tar.gz": "/pulp/api/v3/artifacts/019c0667-1859-70c0-b0e5-f09e55a719ba/"
}
},
{
"pulp_href": "/pulp/api/v3/content/python/packages/019c065a-5d0c-74b2-a669-099544911d20/",
"prn": "prn:python.pythonpackagecontent:019c065a-5d0c-74b2-a669-099544911d20",
"pulp_created": "2026-01-28T20:45:11.310796Z",
"pulp_last_updated": "2026-01-28T20:45:11.310808Z",
"pulp_labels": {},
"vuln_report": null,
"artifacts": {
"orgalorg_greeter-0.1.0-py3-none-any.whl": "/pulp/api/v3/artifacts/019c065a-5caa-76ba-8cd3-bdecdb6ee4e3/",
"orgalorg_greeter-0.1.0-py3-none-any.whl.metadata": "/pulp/api/v3/artifacts/019c065a-5d15-76b8-afd6-385c81a76217/"
}
}
]
Here orgalorg_greeter is a dummy package I have created and uploaded to the index. Although this dummy package has no dependencies (so I am not sure if it accurately tests whether uv can resolve dependencies for packages that are uploaded manually to the index as opposed to proxied), uv has no problem resolving this dependency. We can observe the last line
"artifacts": {
"orgalorg_greeter-0.1.0-py3-none-any.whl": "/pulp/api/v3/artifacts/019c065a-5caa-76ba-8cd3-bdecdb6ee4e3/",
"orgalorg_greeter-0.1.0-py3-none-any.whl.metadata": "/pulp/api/v3/artifacts/019c065a-5d15-76b8-afd6-385c81a76217/"
}
which shows that a .whl.metadata file has been uploaded when I published the wheel via twine.
For the rest of the packages -- which have been cached from the PyPI -- there is no such .whl.metadata file. I suspect uv wants this, cannot find it on our index when cached, and for some reason pulp does not properly get this when proxying. But I'm just guessing.
Activity
crupp2 commentedon Jan 29, 2026
I also ran into this behavior and had to revert to an earlier release. The bug was introduced somewhere since pulpcore 3.94.0 and pulp_python 3.21.0.
gerrod3 commentedon Jan 29, 2026
Good write up! Very useful to pinpoint the issue and yes I believe it has to do with pull-through caching not interacting with the PEP 658 (wheel metadata) files correctly. We just added the support for the metadata files in pulp-python 3.23 and it seems that pip does a good job of falling back to the old behavior when the metadata file isn't served correctly while the new tool uv expects it to be there (not a bad assumption since the PEP is pretty old and we just got around to supporting it).
As for why we now separate out the file, it's for efficiency. The metadata file is much smaller than the whole package so it's faster for tools like uv to download the metadata file (if available) when parsing dependencies to find the correct version to download.
Benzor94 commentedon Jan 29, 2026
Can confirm that falling back to an earlier version works. I used this docker-compose:
pulp statusto check the version of the python plugin, but it would be nicer to be able to choose a plugin version.gerrod3 commentedon Jan 30, 2026
@Benzor94 Our main images are built around the pulpcore version, so the version of the plugins are typically the latest version that supported that pulpcore version at the time of being built. If you want to set the plugin versions you will have to build custom images, e.g.
We have docs about image customization here: https://pulpproject.org/pulp-oci-images/docs/admin/guides/build-your-own-pulp-image/
As for onboarding docs, there is technically a ton (https://pulpproject.org/pulpcore/docs/dev/learn/plugin-concepts/) you could read, but we don't have any specifically for pulp-python. If you are familiar with Django and REST APIs, specifically how data moves between models<-->serializers<-->viewsets, then you'll be able to get a good grasp of what Pulp is doing by playing around with the endpoints and looking at the code. If you have any questions I would recommend joining our matrix channel or asking on discourse (https://pulpproject.org/help/community/get-involved/).
Also, I have the fix for the issue, PR coming soon.
Fix pull-through metadata serving
Fix pull-through metadata serving
Fix pull-through metadata serving
Fix pull-through metadata serving
Fix pull-through metadata serving
Kimahriman commentedon Feb 4, 2026
We've noticed an issue with the pull-through metadata as well and I'm not sure if it's related or a separate issue. We are pulling from a PyPI mirror that doesn't support the metadata serving. When listing the simple page for a package that hasn't been cached yet, it properly shows all versions with no
data-dist-info-metadata. And then if you install something for the first time it works and caches the wheel. But it also registers the metadata sha256, so a subsequent listing of thesimple/<package>/page includes thedata-dist-info-metadataattribute for the versions that have been cached, which cause pip and uv to then query for the whl.metadata file, which gets forwarded to the remote, which doesn't exist, and the install fails immediately without trying any other versions.gerrod3 commentedon Feb 5, 2026
@Kimahriman Another similar issue, but slightly different cause in the codebase. I filed an issue to track it here: #1087
Fix pull-through metadata serving
Fix pull-through metadata serving