pulp-python 3.31.1__tar.gz → 3.32.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pulp_python-3.31.1 → pulp_python-3.32.0}/CHANGES.md +54 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/PKG-INFO +2 -2
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/__init__.py +1 -1
- pulp_python-3.32.0/pulp_python/app/cache.py +32 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/pypi/views.py +24 -3
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/tasks/sync.py +1 -1
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_attestations.py +48 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_crud_content_unit.py +0 -2
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_pypi_apis.py +4 -3
- pulp_python-3.32.0/pulp_python/tests/functional/api/test_simple_cache.py +101 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python.egg-info/PKG-INFO +2 -2
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python.egg-info/SOURCES.txt +2 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python.egg-info/requires.txt +1 -1
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pyproject.toml +4 -4
- {pulp_python-3.31.1 → pulp_python-3.32.0}/COMMITMENT +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/COPYRIGHT +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/LICENSE +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/MANIFEST.in +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/README.md +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/functest_requirements.txt +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/exceptions.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/global_access_conditions.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/management/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/management/commands/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/management/commands/repair-python-metadata.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0001_initial.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0001_squashed_0010_update_json_field.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0002_pythonpackagecontent_python_version.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0003_new_sync_filters.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0004_DATA_swap_distribution_model.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0005_pythonpackagecontent_sha256.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0006_pythonrepository_autopublish.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0007_pythonpackagecontent_mv-2-1.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0008_pythonpackagecontent_unique_sha256.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0009_pythondistribution_allow_uploads.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0010_update_json_field.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0011_alter_pythondistribution_distribution_ptr_and_more.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0012_add_domain.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0013_add_rbac_permissions.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0014_pythonpackagecontent_dynamic_and_more.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0015_alter_pythonpackagecontent_options.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0016_pythonpackagecontent_metadata_sha256.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0017_pythonpackagecontent_size.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0018_packageprovenance.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0019_create_missing_metadata_artifacts.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0020_pythonpackagecontent_name_normalized.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0021_pythonrepository_upload_duplicate_filenames.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0022_pythonblocklistentry.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/modelresource.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/models.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/provenance.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/pypi/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/pypi/serializers.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/replica.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/serializers.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/settings.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/tasks/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/tasks/publish.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/tasks/repair.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/tasks/upload.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/tasks/vulnerability_report.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/urls.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/utils.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/viewsets.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/webserver_snippets/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/webserver_snippets/apache.conf +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/webserver_snippets/nginx.conf +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/pytest_plugin.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_auto_publish.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_blocklist.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_consume_content.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_crud_publications.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_crud_remotes.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_domains.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_download_content.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_export_import.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_full_mirror.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_pypi_simple_api.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_rbac.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_repair.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_sync.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_upload.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_vulnerability_report.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/assets/shelf-reader-0.1.tar.gz.publish.attestation +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/assets/shelf_reader-0.1-py2-none-any.whl.publish.attestation +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/constants.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/utils.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/unit/__init__.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/unit/test_models.py +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python.egg-info/dependency_links.txt +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python.egg-info/entry_points.txt +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python.egg-info/top_level.txt +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/setup.cfg +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/test_requirements.txt +0 -0
- {pulp_python-3.31.1 → pulp_python-3.32.0}/unittest_requirements.txt +0 -0
|
@@ -8,6 +8,28 @@
|
|
|
8
8
|
|
|
9
9
|
[//]: # (towncrier release notes start)
|
|
10
10
|
|
|
11
|
+
## 3.32.0 (2026-07-21) {: #3.32.0 }
|
|
12
|
+
|
|
13
|
+
#### Features {: #3.32.0-feature }
|
|
14
|
+
|
|
15
|
+
- Added server-side caching and HTTP cache headers (`Cache-Control`, `ETag`) to Simple API responses.
|
|
16
|
+
[#1254](https://github.com/pulp/pulp_python/issues/1254)
|
|
17
|
+
|
|
18
|
+
#### Bugfixes {: #3.32.0-bugfix }
|
|
19
|
+
|
|
20
|
+
- Fixed a bug where the `python_version` field was not parsed correctly when Pulp workers had hyphens in their name.
|
|
21
|
+
- Fixed provenance sync failing when syncing Pulp-created attestations that use a `Pulp User` publisher.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 3.31.2 (2026-07-15) {: #3.31.2 }
|
|
26
|
+
|
|
27
|
+
#### Bugfixes {: #3.31.2-bugfix }
|
|
28
|
+
|
|
29
|
+
- Fixed provenance sync failing when syncing Pulp-created attestations that use a `Pulp User` publisher.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
11
33
|
## 3.31.1 (2026-06-30) {: #3.31.1 }
|
|
12
34
|
|
|
13
35
|
#### Bugfixes {: #3.31.1-bugfix }
|
|
@@ -127,6 +149,22 @@
|
|
|
127
149
|
|
|
128
150
|
---
|
|
129
151
|
|
|
152
|
+
## 3.27.6 (2026-07-16) {: #3.27.6 }
|
|
153
|
+
|
|
154
|
+
#### Bugfixes {: #3.27.6-bugfix }
|
|
155
|
+
|
|
156
|
+
- Fixed provenance sync failing when syncing Pulp-created attestations that use a `Pulp User` publisher.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 3.27.5 (2026-06-30) {: #3.27.5 }
|
|
161
|
+
|
|
162
|
+
#### Bugfixes {: #3.27.5-bugfix }
|
|
163
|
+
|
|
164
|
+
- Fixed a bug where the `python_version` field was not parsed correctly when Pulp workers had hyphens in their name.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
130
168
|
## 3.27.4 (2026-06-05) {: #3.27.4 }
|
|
131
169
|
|
|
132
170
|
#### Bugfixes {: #3.27.4-bugfix }
|
|
@@ -365,6 +403,14 @@
|
|
|
365
403
|
|
|
366
404
|
---
|
|
367
405
|
|
|
406
|
+
## 3.19.4 (2026-06-30) {: #3.19.4 }
|
|
407
|
+
|
|
408
|
+
#### Bugfixes {: #3.19.4-bugfix }
|
|
409
|
+
|
|
410
|
+
- Fixed a bug where the `python_version` field was not parsed correctly when Pulp workers had hyphens in their name.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
368
414
|
## 3.19.3 (2026-05-04) {: #3.19.3 }
|
|
369
415
|
|
|
370
416
|
#### Bugfixes {: #3.19.3-bugfix }
|
|
@@ -488,6 +534,14 @@ No significant changes.
|
|
|
488
534
|
|
|
489
535
|
---
|
|
490
536
|
|
|
537
|
+
## 3.13.7 (2026-06-30) {: #3.13.7 }
|
|
538
|
+
|
|
539
|
+
#### Bugfixes {: #3.13.7-bugfix }
|
|
540
|
+
|
|
541
|
+
- Fixed a bug where the `python_version` field was not parsed correctly when Pulp workers had hyphens in their name.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
491
545
|
## 3.13.6 (2026-04-01) {: #3.13.6 }
|
|
492
546
|
|
|
493
547
|
#### Bugfixes {: #3.13.6-bugfix }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pulp-python
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.32.0
|
|
4
4
|
Summary: pulp-python plugin for the Pulp Project
|
|
5
5
|
Author-email: Pulp Team <pulp-list@redhat.com>
|
|
6
6
|
Project-URL: Homepage, https://pulpproject.org
|
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: pulpcore<3.
|
|
23
|
+
Requires-Dist: pulpcore<3.130,>=3.105.0
|
|
24
24
|
Requires-Dist: pkginfo<1.13.0,>=1.12.0
|
|
25
25
|
Requires-Dist: bandersnatch<6.7,>=6.6.0
|
|
26
26
|
Requires-Dist: pypi-simple<2.0,>=1.8.0
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from pulpcore.plugin.cache import CacheKeys, SyncContentCache
|
|
2
|
+
from pulpcore.plugin.util import cache_key
|
|
3
|
+
|
|
4
|
+
ACCEPT_HEADER_KEY = "accept_header"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PythonApiCache(SyncContentCache):
|
|
8
|
+
"""
|
|
9
|
+
Cache for the Simple API.
|
|
10
|
+
|
|
11
|
+
Adds Accept header to the cache key so HTML and JSON responses are cached separately.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, base_key=None):
|
|
15
|
+
keys = (CacheKeys.path, CacheKeys.method, ACCEPT_HEADER_KEY)
|
|
16
|
+
super().__init__(base_key=base_key, keys=keys)
|
|
17
|
+
|
|
18
|
+
def make_key(self, request):
|
|
19
|
+
all_keys = {
|
|
20
|
+
CacheKeys.path: request.path,
|
|
21
|
+
CacheKeys.method: request.method,
|
|
22
|
+
ACCEPT_HEADER_KEY: request.headers.get("accept", ""),
|
|
23
|
+
}
|
|
24
|
+
return ":".join(all_keys[k] for k in self.keys)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def find_base_path_cached(request, cached):
|
|
28
|
+
"""
|
|
29
|
+
Resolve the distribution base_path for use as the Redis cache base_key.
|
|
30
|
+
"""
|
|
31
|
+
path = request.resolver_match.kwargs["path"]
|
|
32
|
+
return cache_key(path)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import hashlib
|
|
1
2
|
import logging
|
|
2
3
|
from datetime import datetime, timedelta, timezone
|
|
3
4
|
from itertools import chain
|
|
@@ -15,9 +16,11 @@ from django.http.response import (
|
|
|
15
16
|
HttpResponseBadRequest,
|
|
16
17
|
HttpResponseForbidden,
|
|
17
18
|
HttpResponseNotFound,
|
|
18
|
-
StreamingHttpResponse,
|
|
19
19
|
)
|
|
20
20
|
from django.shortcuts import redirect
|
|
21
|
+
from django.utils.decorators import method_decorator
|
|
22
|
+
from django.views.decorators.cache import cache_control
|
|
23
|
+
from django.views.decorators.http import condition
|
|
21
24
|
from drf_spectacular.utils import extend_schema
|
|
22
25
|
from dynaconf import settings
|
|
23
26
|
from packaging.utils import canonicalize_name
|
|
@@ -31,6 +34,7 @@ from pulpcore.plugin.util import get_domain, get_url
|
|
|
31
34
|
from pulpcore.plugin.viewsets import OperationPostponedResponse
|
|
32
35
|
|
|
33
36
|
from pulp_python.app import tasks
|
|
37
|
+
from pulp_python.app.cache import PythonApiCache, find_base_path_cached
|
|
34
38
|
from pulp_python.app.models import (
|
|
35
39
|
PackageProvenance,
|
|
36
40
|
PythonDistribution,
|
|
@@ -65,6 +69,17 @@ PYPI_SIMPLE_V1_HTML = "application/vnd.pypi.simple.v1+html"
|
|
|
65
69
|
PYPI_SIMPLE_V1_JSON = "application/vnd.pypi.simple.v1+json"
|
|
66
70
|
|
|
67
71
|
|
|
72
|
+
def _etag_func(request, path, **kwargs):
|
|
73
|
+
"""Compute unquoted ETag for the condition decorator. Returns None if no repo."""
|
|
74
|
+
try:
|
|
75
|
+
distro = PyPIMixin.get_distribution(path)
|
|
76
|
+
repo_ver = PyPIMixin.get_repository_version(distro)
|
|
77
|
+
except Http404:
|
|
78
|
+
return None
|
|
79
|
+
raw = f"{repo_ver.number}:{repo_ver.pulp_created.isoformat()}"
|
|
80
|
+
return hashlib.sha256(raw.encode()).hexdigest()[:16]
|
|
81
|
+
|
|
82
|
+
|
|
68
83
|
class PyPISimpleHTMLRenderer(TemplateHTMLRenderer):
|
|
69
84
|
media_type = PYPI_SIMPLE_V1_HTML
|
|
70
85
|
|
|
@@ -298,6 +313,9 @@ class SimpleView(PackageUploadMixin, ViewSet):
|
|
|
298
313
|
)
|
|
299
314
|
|
|
300
315
|
@extend_schema(summary="Get index simple page")
|
|
316
|
+
@method_decorator(cache_control(max_age=600, public=True))
|
|
317
|
+
@method_decorator(condition(etag_func=_etag_func))
|
|
318
|
+
@PythonApiCache(base_key=find_base_path_cached)
|
|
301
319
|
def list(self, request, path):
|
|
302
320
|
"""Gets the simple api html page for the index."""
|
|
303
321
|
repo_version, content = self.get_rvc()
|
|
@@ -316,9 +334,9 @@ class SimpleView(PackageUploadMixin, ViewSet):
|
|
|
316
334
|
index_data = write_simple_index_json(names)
|
|
317
335
|
return Response(index_data, headers=headers)
|
|
318
336
|
else:
|
|
319
|
-
index_data = write_simple_index(names
|
|
337
|
+
index_data = write_simple_index(names)
|
|
320
338
|
kwargs = {"content_type": media_type, "headers": headers}
|
|
321
|
-
return
|
|
339
|
+
return HttpResponse(index_data, **kwargs)
|
|
322
340
|
|
|
323
341
|
def pull_through_package_simple(self, package, path, remote):
|
|
324
342
|
"""Gets the package's simple page from remote."""
|
|
@@ -355,6 +373,9 @@ class SimpleView(PackageUploadMixin, ViewSet):
|
|
|
355
373
|
}
|
|
356
374
|
|
|
357
375
|
@extend_schema(operation_id="pypi_simple_package_read", summary="Get package simple page")
|
|
376
|
+
@method_decorator(cache_control(max_age=600, public=True))
|
|
377
|
+
@method_decorator(condition(etag_func=_etag_func))
|
|
378
|
+
@PythonApiCache(base_key=find_base_path_cached)
|
|
358
379
|
def retrieve(self, request, path, package):
|
|
359
380
|
"""Retrieves the simple api html/json page for a package."""
|
|
360
381
|
repo_ver, content = self.get_rvc()
|
|
@@ -9,7 +9,6 @@ from bandersnatch.master import Master
|
|
|
9
9
|
from bandersnatch.mirror import Mirror
|
|
10
10
|
from lxml.etree import LxmlError
|
|
11
11
|
from packaging.requirements import Requirement
|
|
12
|
-
from pypi_attestations import Provenance
|
|
13
12
|
from pypi_simple import IndexPage
|
|
14
13
|
|
|
15
14
|
from pulpcore.plugin.download import HttpDownloader
|
|
@@ -28,6 +27,7 @@ from pulp_python.app.models import (
|
|
|
28
27
|
PythonPackageContent,
|
|
29
28
|
PythonRemote,
|
|
30
29
|
)
|
|
30
|
+
from pulp_python.app.provenance import Provenance
|
|
31
31
|
from pulp_python.app.utils import PYPI_LAST_SERIAL, aget_remote_simple_page, parse_metadata
|
|
32
32
|
|
|
33
33
|
logger = logging.getLogger(__name__)
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_attestations.py
RENAMED
|
@@ -154,6 +154,54 @@ def test_attestation_sync_upload(python_bindings, twine_package, download_python
|
|
|
154
154
|
assert att_bundle["publisher"]["kind"] == "Pulp User"
|
|
155
155
|
|
|
156
156
|
|
|
157
|
+
@pytest.mark.parallel
|
|
158
|
+
def test_sync_pulp_created_provenance(
|
|
159
|
+
domain_factory,
|
|
160
|
+
python_bindings,
|
|
161
|
+
twine_package,
|
|
162
|
+
python_repo_factory,
|
|
163
|
+
python_distribution_factory,
|
|
164
|
+
python_remote_factory,
|
|
165
|
+
python_repo_with_sync,
|
|
166
|
+
python_content_summary,
|
|
167
|
+
monitor_task,
|
|
168
|
+
):
|
|
169
|
+
"""Test syncing provenance across domains from a Pulp upload."""
|
|
170
|
+
source_domain = domain_factory()
|
|
171
|
+
attestations = get_attestations(twine_package.provenance_url)
|
|
172
|
+
source_repo = python_repo_factory(pulp_domain=source_domain.name)
|
|
173
|
+
body = {
|
|
174
|
+
"relative_path": twine_package.filename,
|
|
175
|
+
"file_url": twine_package.url,
|
|
176
|
+
"attestations": json.dumps(attestations),
|
|
177
|
+
"repository": source_repo.pulp_href,
|
|
178
|
+
}
|
|
179
|
+
task = python_bindings.ContentPackagesApi.create(pulp_domain=source_domain.name, **body).task
|
|
180
|
+
monitor_task(task)
|
|
181
|
+
|
|
182
|
+
source_distro = python_distribution_factory(
|
|
183
|
+
repository=source_repo, pulp_domain=source_domain.name
|
|
184
|
+
)
|
|
185
|
+
dest_domain = domain_factory()
|
|
186
|
+
remote = python_remote_factory(
|
|
187
|
+
url=source_distro.base_url,
|
|
188
|
+
provenance=True,
|
|
189
|
+
includes=[f"twine=={twine_package.version}"],
|
|
190
|
+
pulp_domain=dest_domain.name,
|
|
191
|
+
)
|
|
192
|
+
dest_repo = python_repo_with_sync(remote=remote, pulp_domain=dest_domain.name)
|
|
193
|
+
|
|
194
|
+
summary = python_content_summary(repository_version=dest_repo.latest_version_href)
|
|
195
|
+
assert summary.present["python.provenance"]["count"] == 1
|
|
196
|
+
|
|
197
|
+
provs = python_bindings.ContentProvenanceApi.list(
|
|
198
|
+
repository_version=dest_repo.latest_version_href,
|
|
199
|
+
pulp_domain=dest_domain.name,
|
|
200
|
+
)
|
|
201
|
+
assert provs.count == 1
|
|
202
|
+
assert provs.results[0].provenance["attestation_bundles"][0]["publisher"]["kind"] == "Pulp User"
|
|
203
|
+
|
|
204
|
+
|
|
157
205
|
def test_attestation_twine_upload(
|
|
158
206
|
pulpcore_bindings,
|
|
159
207
|
python_content_summary,
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_crud_content_unit.py
RENAMED
|
@@ -203,7 +203,6 @@ def test_package_creation_with_metadata(
|
|
|
203
203
|
)
|
|
204
204
|
|
|
205
205
|
|
|
206
|
-
@pytest.mark.parallel
|
|
207
206
|
def test_disallow_package_substitution(
|
|
208
207
|
monitor_task,
|
|
209
208
|
python_bindings,
|
|
@@ -272,7 +271,6 @@ def test_disallow_package_substitution(
|
|
|
272
271
|
assert repo.latest_version_href.endswith("/2/")
|
|
273
272
|
|
|
274
273
|
|
|
275
|
-
@pytest.mark.parallel
|
|
276
274
|
def test_package_substitution_allowed_by_default(
|
|
277
275
|
monitor_task,
|
|
278
276
|
python_bindings,
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_pypi_apis.py
RENAMED
|
@@ -245,8 +245,9 @@ def test_simple_redirect_with_publications(
|
|
|
245
245
|
assert response.url == str(urljoin(pulp_content_url, f"{distro.base_path}/simple/"))
|
|
246
246
|
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
def test_pypi_json(
|
|
249
|
+
delete_orphans_pre, python_remote_factory, python_repo_with_sync, python_distribution_factory
|
|
250
|
+
):
|
|
250
251
|
"""Checks the data of `pypi/{package_name}/json` endpoint."""
|
|
251
252
|
remote = python_remote_factory(policy="immediate")
|
|
252
253
|
repo = python_repo_with_sync(remote)
|
|
@@ -260,8 +261,8 @@ def test_pypi_json(python_remote_factory, python_repo_with_sync, python_distribu
|
|
|
260
261
|
assert_pypi_json(response.json())
|
|
261
262
|
|
|
262
263
|
|
|
263
|
-
@pytest.mark.parallel
|
|
264
264
|
def test_pypi_json_content_app(
|
|
265
|
+
delete_orphans_pre,
|
|
265
266
|
python_remote_factory,
|
|
266
267
|
python_repo_with_sync,
|
|
267
268
|
python_publication_factory,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from urllib.parse import urljoin
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from pulp_python.tests.functional.constants import (
|
|
7
|
+
PYPI_SIMPLE_V1_HTML,
|
|
8
|
+
PYPI_SIMPLE_V1_JSON,
|
|
9
|
+
PYTHON_SM_PROJECT_SPECIFIER,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def skip_without_cache(pulp_settings):
|
|
15
|
+
"""
|
|
16
|
+
Skip test if server-side caching is not enabled.
|
|
17
|
+
"""
|
|
18
|
+
if not pulp_settings.CACHE_ENABLED:
|
|
19
|
+
pytest.skip("CACHE_ENABLED is not set")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def synced_distro(
|
|
24
|
+
skip_without_cache,
|
|
25
|
+
python_remote_factory,
|
|
26
|
+
python_repo_with_sync,
|
|
27
|
+
python_distribution_factory,
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Sync a repo and create a distribution for cache tests.
|
|
31
|
+
"""
|
|
32
|
+
remote = python_remote_factory(includes=PYTHON_SM_PROJECT_SPECIFIER)
|
|
33
|
+
repo = python_repo_with_sync(remote)
|
|
34
|
+
return python_distribution_factory(repository=repo)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.mark.parallel
|
|
38
|
+
def test_simple_cache_hit_miss_and_headers(synced_distro):
|
|
39
|
+
"""
|
|
40
|
+
First request is a MISS, second is a HIT. Cache headers are present and stable.
|
|
41
|
+
"""
|
|
42
|
+
index_url = urljoin(synced_distro.base_url, "simple/")
|
|
43
|
+
detail_url = f"{index_url}aiohttp"
|
|
44
|
+
|
|
45
|
+
for url in [index_url, detail_url]:
|
|
46
|
+
r1 = requests.get(url)
|
|
47
|
+
assert r1.status_code == 200
|
|
48
|
+
assert r1.headers["X-PULP-CACHE"] == "MISS"
|
|
49
|
+
assert r1.headers["Cache-Control"] == "max-age=600, public"
|
|
50
|
+
assert r1.headers["ETag"].startswith('"') and r1.headers["ETag"].endswith('"')
|
|
51
|
+
|
|
52
|
+
r2 = requests.get(url)
|
|
53
|
+
assert r2.status_code == 200
|
|
54
|
+
assert r2.headers["X-PULP-CACHE"] == "HIT"
|
|
55
|
+
assert r2.headers["Cache-Control"] == r1.headers["Cache-Control"]
|
|
56
|
+
assert r2.headers["ETag"] == r1.headers["ETag"]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.parallel
|
|
60
|
+
def test_simple_cache_separate_accept_headers(synced_distro):
|
|
61
|
+
"""
|
|
62
|
+
HTML and JSON responses are cached separately.
|
|
63
|
+
"""
|
|
64
|
+
url = urljoin(synced_distro.base_url, "simple/")
|
|
65
|
+
|
|
66
|
+
for header in [PYPI_SIMPLE_V1_HTML, PYPI_SIMPLE_V1_JSON]:
|
|
67
|
+
r = requests.get(url, headers={"Accept": header})
|
|
68
|
+
assert r.status_code == 200
|
|
69
|
+
assert r.headers["X-PULP-CACHE"] == "MISS"
|
|
70
|
+
|
|
71
|
+
for header in [PYPI_SIMPLE_V1_HTML, PYPI_SIMPLE_V1_JSON]:
|
|
72
|
+
r = requests.get(url, headers={"Accept": header})
|
|
73
|
+
assert r.status_code == 200
|
|
74
|
+
assert r.headers["X-PULP-CACHE"] == "HIT"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parallel
|
|
78
|
+
def test_simple_cache_etag_conditional_request(synced_distro):
|
|
79
|
+
"""
|
|
80
|
+
Matching If-None-Match returns 304, non-matching returns 200.
|
|
81
|
+
"""
|
|
82
|
+
url = urljoin(synced_distro.base_url, "simple/")
|
|
83
|
+
|
|
84
|
+
r1 = requests.get(url)
|
|
85
|
+
assert r1.status_code == 200
|
|
86
|
+
etag = r1.headers["ETag"]
|
|
87
|
+
cache_control = r1.headers["Cache-Control"]
|
|
88
|
+
|
|
89
|
+
r2 = requests.get(url, headers={"If-None-Match": etag})
|
|
90
|
+
assert r2.status_code == 304
|
|
91
|
+
assert r2.headers["ETag"] == etag
|
|
92
|
+
assert r2.headers["Cache-Control"] == cache_control
|
|
93
|
+
assert "X-PULP-CACHE" not in r2.headers
|
|
94
|
+
assert len(r2.content) == 0
|
|
95
|
+
|
|
96
|
+
r3 = requests.get(url, headers={"If-None-Match": '"old"'})
|
|
97
|
+
assert r3.status_code == 200
|
|
98
|
+
assert r3.headers["ETag"] == etag
|
|
99
|
+
assert r3.headers["Cache-Control"] == cache_control
|
|
100
|
+
assert r3.headers["X-PULP-CACHE"] == "HIT"
|
|
101
|
+
assert len(r3.content) > 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pulp-python
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.32.0
|
|
4
4
|
Summary: pulp-python plugin for the Pulp Project
|
|
5
5
|
Author-email: Pulp Team <pulp-list@redhat.com>
|
|
6
6
|
Project-URL: Homepage, https://pulpproject.org
|
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: pulpcore<3.
|
|
23
|
+
Requires-Dist: pulpcore<3.130,>=3.105.0
|
|
24
24
|
Requires-Dist: pkginfo<1.13.0,>=1.12.0
|
|
25
25
|
Requires-Dist: bandersnatch<6.7,>=6.6.0
|
|
26
26
|
Requires-Dist: pypi-simple<2.0,>=1.8.0
|
|
@@ -17,6 +17,7 @@ pulp_python.egg-info/entry_points.txt
|
|
|
17
17
|
pulp_python.egg-info/requires.txt
|
|
18
18
|
pulp_python.egg-info/top_level.txt
|
|
19
19
|
pulp_python/app/__init__.py
|
|
20
|
+
pulp_python/app/cache.py
|
|
20
21
|
pulp_python/app/exceptions.py
|
|
21
22
|
pulp_python/app/global_access_conditions.py
|
|
22
23
|
pulp_python/app/modelresource.py
|
|
@@ -87,6 +88,7 @@ pulp_python/tests/functional/api/test_pypi_apis.py
|
|
|
87
88
|
pulp_python/tests/functional/api/test_pypi_simple_api.py
|
|
88
89
|
pulp_python/tests/functional/api/test_rbac.py
|
|
89
90
|
pulp_python/tests/functional/api/test_repair.py
|
|
91
|
+
pulp_python/tests/functional/api/test_simple_cache.py
|
|
90
92
|
pulp_python/tests/functional/api/test_sync.py
|
|
91
93
|
pulp_python/tests/functional/api/test_upload.py
|
|
92
94
|
pulp_python/tests/functional/api/test_vulnerability_report.py
|
|
@@ -7,7 +7,7 @@ build-backend = 'setuptools.build_meta'
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "pulp-python"
|
|
10
|
-
version = "3.
|
|
10
|
+
version = "3.32.0"
|
|
11
11
|
description = "pulp-python plugin for the Pulp Project"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
authors = [
|
|
@@ -26,7 +26,7 @@ classifiers=[
|
|
|
26
26
|
]
|
|
27
27
|
requires-python = ">=3.11"
|
|
28
28
|
dependencies = [
|
|
29
|
-
"pulpcore>=3.105.0,<3.
|
|
29
|
+
"pulpcore>=3.105.0,<3.130",
|
|
30
30
|
"pkginfo>=1.12.0,<1.13.0",
|
|
31
31
|
"bandersnatch>=6.6.0,<6.7",
|
|
32
32
|
"pypi-simple>=1.8.0,<2.0",
|
|
@@ -79,7 +79,7 @@ ignore = [
|
|
|
79
79
|
[tool.bumpversion]
|
|
80
80
|
# This section is managed by the plugin template. Do not edit manually.
|
|
81
81
|
|
|
82
|
-
current_version = "3.
|
|
82
|
+
current_version = "3.32.0"
|
|
83
83
|
commit = false
|
|
84
84
|
tag = false
|
|
85
85
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<alpha>0a)?(?P<patch>\\d+)(\\.(?P<release>[a-z]+))?"
|
|
@@ -125,7 +125,6 @@ filename = "./pyproject.toml"
|
|
|
125
125
|
search = "version = \"{current_version}\""
|
|
126
126
|
replace = "version = \"{new_version}\""
|
|
127
127
|
|
|
128
|
-
|
|
129
128
|
[tool.black]
|
|
130
129
|
line-length = 100
|
|
131
130
|
|
|
@@ -149,6 +148,7 @@ extend-select = [
|
|
|
149
148
|
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
|
150
149
|
# This section is managed by the plugin template. Do not edit manually.
|
|
151
150
|
"distutils".msg = "The 'distutils' module has been deprecated since Python 3.9."
|
|
151
|
+
"pulpcore.app.settings".msg = "Always import 'settings' from 'django.conf' instead."
|
|
152
152
|
"pulpcore.app".msg = "The 'pulpcore' apis must only be consumed via 'pulpcore.plugin'."
|
|
153
153
|
|
|
154
154
|
[tool.ruff.lint.isort]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0003_new_sync_filters.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0010_update_json_field.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0013_add_rbac_permissions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0018_packageprovenance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/app/migrations/0022_pythonblocklistentry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_auto_publish.py
RENAMED
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_blocklist.py
RENAMED
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_consume_content.py
RENAMED
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_crud_publications.py
RENAMED
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_crud_remotes.py
RENAMED
|
File without changes
|
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_download_content.py
RENAMED
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_export_import.py
RENAMED
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_full_mirror.py
RENAMED
|
File without changes
|
{pulp_python-3.31.1 → pulp_python-3.32.0}/pulp_python/tests/functional/api/test_pypi_simple_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|