Summary
The PyPI JSON Metadata API ({package}/json/, {package}/{version}/json/, served by MetadataView) sends no cache-validation headers and does not support conditional requests. This means every client and edge cache must re-download the full JSON body on each request, even when nothing has changed. Add ETag / If-None-Match → 304 Not Modified support to match pypi.org.
Motivation — pypi.org behavior
I compared against pypi.org (Warehouse). Both its JSON and Simple JSON endpoints standardize on ETag as the cache validator:
| Endpoint |
ETag |
Last-Modified |
If-None-Match→304 |
Cache-Control |
/pypi/{pkg}/json |
✅ |
❌ never sent |
✅ |
max-age=900, public |
/simple/{pkg}/ |
✅ |
❌ never sent |
✅ |
max-age=600, public |
Warehouse never emits Last-Modified; it uses ETag. (pypi.org also answers If-Modified-Since with a 304, but that is the Fastly CDN layer synthesizing it from the cached object — the origin validator is the ETag.)
Current state in pulp_python
- The Simple API (
SimpleView.list/retrieve) already matches Warehouse: it emits ETag and honors If-None-Match→304 via @method_decorator(condition(etag_func=_etag_func)), with the same Cache-Control: max-age=600, public. ✅
- The JSON
MetadataView has no ETag, no Cache-Control, and no conditional-request handling. ❌ ← this is the gap.
Proposed change
- Apply the same
condition(etag_func=_etag_func) decorator (and a cache_control decorator) to MetadataView.retrieve, reusing the existing repo-version-based _etag_func in pulp_python/app/pypi/views.py.
- Add functional tests mirroring
test_simple_cache_etag_conditional_request for the JSON Metadata endpoint (matching ETag → 304, bogus ETag → 200).
Related / out of scope
pulpcore #7929 (pulp/pulpcore#7929) adds Last-Modified / If-Modified-Since / 304 to the content app, which serves package file downloads — pulp_python inherits that automatically. Adding Last-Modified to the PyPI metadata views is not required for pypi.org parity (Warehouse doesn't send it) and is left out of this issue.
Files
pulp_python/app/pypi/views.py
pulp_python/tests/functional/api/test_simple_cache.py
Summary
The PyPI JSON Metadata API (
{package}/json/,{package}/{version}/json/, served byMetadataView) sends no cache-validation headers and does not support conditional requests. This means every client and edge cache must re-download the full JSON body on each request, even when nothing has changed. AddETag/If-None-Match→304 Not Modifiedsupport to match pypi.org.Motivation — pypi.org behavior
I compared against pypi.org (Warehouse). Both its JSON and Simple JSON endpoints standardize on
ETagas the cache validator:ETagLast-ModifiedIf-None-Match→304Cache-Control/pypi/{pkg}/jsonmax-age=900, public/simple/{pkg}/max-age=600, publicWarehouse never emits
Last-Modified; it uses ETag. (pypi.org also answersIf-Modified-Sincewith a 304, but that is the Fastly CDN layer synthesizing it from the cached object — the origin validator is the ETag.)Current state in pulp_python
SimpleView.list/retrieve) already matches Warehouse: it emitsETagand honorsIf-None-Match→304 via@method_decorator(condition(etag_func=_etag_func)), with the sameCache-Control: max-age=600, public. ✅MetadataViewhas noETag, noCache-Control, and no conditional-request handling. ❌ ← this is the gap.Proposed change
condition(etag_func=_etag_func)decorator (and acache_controldecorator) toMetadataView.retrieve, reusing the existing repo-version-based_etag_funcinpulp_python/app/pypi/views.py.test_simple_cache_etag_conditional_requestfor the JSON Metadata endpoint (matching ETag → 304, bogus ETag → 200).Related / out of scope
pulpcore #7929 (pulp/pulpcore#7929) adds
Last-Modified/If-Modified-Since/ 304 to the content app, which serves package file downloads — pulp_python inherits that automatically. AddingLast-Modifiedto the PyPI metadata views is not required for pypi.org parity (Warehouse doesn't send it) and is left out of this issue.Files
pulp_python/app/pypi/views.pypulp_python/tests/functional/api/test_simple_cache.py