pulp-python 3.27.1__tar.gz → 3.27.3__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.27.1 → pulp_python-3.27.3}/CHANGES.md +18 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/MANIFEST.in +2 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/PKG-INFO +1 -1
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/__init__.py +5 -3
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/management/commands/repair-python-metadata.py +3 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/modelresource.py +1 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/models.py +9 -8
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/pypi/serializers.py +7 -5
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/pypi/views.py +40 -28
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/replica.py +3 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/serializers.py +14 -13
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/tasks/publish.py +2 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/tasks/repair.py +28 -13
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/tasks/sync.py +13 -15
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/tasks/upload.py +7 -6
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/utils.py +58 -15
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/viewsets.py +2 -1
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/pytest_plugin.py +8 -6
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_consume_content.py +0 -1
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_crud_content_unit.py +5 -4
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_crud_publications.py +6 -5
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_crud_remotes.py +3 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_domains.py +6 -5
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_export_import.py +5 -3
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_pypi_apis.py +48 -5
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_pypi_simple_api.py +5 -6
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_rbac.py +5 -4
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_repair.py +2 -1
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_upload.py +5 -3
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/constants.py +4 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python.egg-info/PKG-INFO +1 -1
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pyproject.toml +31 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/COMMITMENT +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/COPYRIGHT +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/LICENSE +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/README.md +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/functest_requirements.txt +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/global_access_conditions.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/management/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/management/commands/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0001_initial.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0001_squashed_0010_update_json_field.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0002_pythonpackagecontent_python_version.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0003_new_sync_filters.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0004_DATA_swap_distribution_model.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0005_pythonpackagecontent_sha256.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0006_pythonrepository_autopublish.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0007_pythonpackagecontent_mv-2-1.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0008_pythonpackagecontent_unique_sha256.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0009_pythondistribution_allow_uploads.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0010_update_json_field.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0011_alter_pythondistribution_distribution_ptr_and_more.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0012_add_domain.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0013_add_rbac_permissions.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0014_pythonpackagecontent_dynamic_and_more.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0015_alter_pythonpackagecontent_options.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0016_pythonpackagecontent_metadata_sha256.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0017_pythonpackagecontent_size.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0018_packageprovenance.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/0019_create_missing_metadata_artifacts.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/migrations/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/provenance.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/pypi/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/settings.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/tasks/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/tasks/vulnerability_report.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/urls.py +2 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/webserver_snippets/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/webserver_snippets/apache.conf +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/app/webserver_snippets/nginx.conf +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_attestations.py +2 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_auto_publish.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_download_content.py +2 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_full_mirror.py +9 -9
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_sync.py +11 -11
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/api/test_vulnerability_report.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/assets/shelf-reader-0.1.tar.gz.publish.attestation +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/assets/shelf_reader-0.1-py2-none-any.whl.publish.attestation +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/functional/utils.py +2 -2
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/unit/__init__.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python/tests/unit/test_models.py +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python.egg-info/SOURCES.txt +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python.egg-info/dependency_links.txt +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python.egg-info/entry_points.txt +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python.egg-info/requires.txt +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/pulp_python.egg-info/top_level.txt +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/setup.cfg +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/test_requirements.txt +0 -0
- {pulp_python-3.27.1 → pulp_python-3.27.3}/unittest_requirements.txt +0 -0
|
@@ -8,6 +8,24 @@
|
|
|
8
8
|
|
|
9
9
|
[//]: # (towncrier release notes start)
|
|
10
10
|
|
|
11
|
+
## 3.27.3 (2026-05-19) {: #3.27.3 }
|
|
12
|
+
|
|
13
|
+
#### Bugfixes {: #3.27.3-bugfix }
|
|
14
|
+
|
|
15
|
+
- Fixed `upload_time` in Simple and JSON APIs to reflect repository addition time instead of content creation time.
|
|
16
|
+
[#1232](https://github.com/pulp/pulp_python/issues/1232)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 3.27.2 (2026-04-14) {: #3.27.2 }
|
|
21
|
+
|
|
22
|
+
#### Bugfixes {: #3.27.2-bugfix }
|
|
23
|
+
|
|
24
|
+
- Fixed "Worker has gone missing" errors during repair_metadata on large repositories (1000+ packages) by reducing peak memory consumption.
|
|
25
|
+
[#1188](https://github.com/pulp/pulp_python/issues/1188)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
11
29
|
## 3.27.1 (2026-04-01) {: #3.27.1 }
|
|
12
30
|
|
|
13
31
|
#### Bugfixes {: #3.27.1-bugfix }
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from gettext import gettext as _
|
|
2
|
+
|
|
1
3
|
from django.db.models.signals import post_migrate
|
|
4
|
+
|
|
2
5
|
from pulpcore.plugin import PulpPluginAppConfig
|
|
3
|
-
from gettext import gettext as _
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class PulpPythonPluginAppConfig(PulpPluginAppConfig):
|
|
@@ -10,7 +12,7 @@ class PulpPythonPluginAppConfig(PulpPluginAppConfig):
|
|
|
10
12
|
|
|
11
13
|
name = "pulp_python.app"
|
|
12
14
|
label = "python"
|
|
13
|
-
version = "3.27.
|
|
15
|
+
version = "3.27.3"
|
|
14
16
|
python_package_name = "pulp-python"
|
|
15
17
|
domain_compatible = True
|
|
16
18
|
|
|
@@ -26,7 +28,7 @@ class PulpPythonPluginAppConfig(PulpPluginAppConfig):
|
|
|
26
28
|
|
|
27
29
|
# TODO: Remove this when https://github.com/pulp/pulpcore/issues/5500 is resolved
|
|
28
30
|
def _populate_pypi_access_policies(sender, apps, verbosity, **kwargs):
|
|
29
|
-
from pulp_python.app.pypi.views import PyPIView, SimpleView, UploadView
|
|
31
|
+
from pulp_python.app.pypi.views import MetadataView, PyPIView, SimpleView, UploadView
|
|
30
32
|
|
|
31
33
|
try:
|
|
32
34
|
AccessPolicy = apps.get_model("core", "AccessPolicy")
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import re
|
|
2
1
|
import os
|
|
3
|
-
|
|
2
|
+
import re
|
|
4
3
|
from gettext import gettext as _
|
|
5
4
|
|
|
6
5
|
from django.conf import settings
|
|
6
|
+
from django.core.management import BaseCommand, CommandError
|
|
7
7
|
|
|
8
8
|
from pulpcore.plugin.util import extract_pk
|
|
9
|
+
|
|
9
10
|
from pulp_python.app.models import PythonPackageContent, PythonRepository
|
|
10
11
|
from pulp_python.app.utils import artifact_to_python_content_data
|
|
11
12
|
|
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import json
|
|
3
3
|
from logging import getLogger
|
|
4
|
+
from pathlib import PurePath
|
|
4
5
|
|
|
5
6
|
from aiohttp.web import json_response
|
|
7
|
+
from django.conf import settings
|
|
6
8
|
from django.contrib.postgres.fields import ArrayField
|
|
7
9
|
from django.core.exceptions import ObjectDoesNotExist
|
|
8
10
|
from django.db import models
|
|
9
|
-
from django.conf import settings
|
|
10
11
|
from django_lifecycle import (
|
|
11
12
|
BEFORE_SAVE,
|
|
12
13
|
hook,
|
|
13
14
|
)
|
|
15
|
+
|
|
14
16
|
from pulpcore.plugin.models import (
|
|
15
17
|
AutoAddObjPermsMixin,
|
|
16
18
|
Content,
|
|
17
|
-
Publication,
|
|
18
19
|
Distribution,
|
|
20
|
+
Publication,
|
|
19
21
|
Remote,
|
|
20
22
|
Repository,
|
|
21
23
|
)
|
|
24
|
+
from pulpcore.plugin.repo_version_utils import remove_duplicates, validate_repo_version
|
|
22
25
|
from pulpcore.plugin.responses import ArtifactResponse
|
|
26
|
+
from pulpcore.plugin.util import get_domain, get_domain_pk
|
|
23
27
|
|
|
24
|
-
from pathlib import PurePath
|
|
25
28
|
from .provenance import Provenance
|
|
26
29
|
from .utils import (
|
|
27
|
-
|
|
30
|
+
PYPI_LAST_SERIAL,
|
|
31
|
+
PYPI_SERIAL_CONSTANT,
|
|
28
32
|
artifact_to_metadata_artifact,
|
|
33
|
+
artifact_to_python_content_data,
|
|
29
34
|
canonicalize_name,
|
|
30
35
|
python_content_to_json,
|
|
31
|
-
PYPI_LAST_SERIAL,
|
|
32
|
-
PYPI_SERIAL_CONSTANT,
|
|
33
36
|
)
|
|
34
|
-
from pulpcore.plugin.repo_version_utils import remove_duplicates, validate_repo_version
|
|
35
|
-
from pulpcore.plugin.util import get_domain_pk, get_domain
|
|
36
37
|
|
|
37
38
|
log = getLogger(__name__)
|
|
38
39
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from gettext import gettext as _
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from django.db.utils import IntegrityError
|
|
5
5
|
from pydantic import TypeAdapter, ValidationError
|
|
6
|
-
from
|
|
7
|
-
|
|
6
|
+
from rest_framework import serializers
|
|
7
|
+
|
|
8
8
|
from pulpcore.plugin.models import Artifact
|
|
9
9
|
from pulpcore.plugin.util import get_domain
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
from pulp_python.app.provenance import Attestation
|
|
12
|
+
from pulp_python.app.utils import DIST_EXTENSIONS, SUPPORTED_METADATA_VERSIONS
|
|
11
13
|
|
|
12
14
|
log = logging.getLogger(__name__)
|
|
13
15
|
|
|
@@ -110,7 +112,7 @@ class PackageUploadSerializer(serializers.Serializer):
|
|
|
110
112
|
attestations = TypeAdapter(list[Attestation]).validate_python(attestations)
|
|
111
113
|
except ValidationError as e:
|
|
112
114
|
raise serializers.ValidationError(
|
|
113
|
-
{"attestations": _("The uploaded attestations are not valid: {}".format(e)
|
|
115
|
+
{"attestations": _("The uploaded attestations are not valid: {}").format(e)}
|
|
114
116
|
)
|
|
115
117
|
|
|
116
118
|
sha256 = data.get("sha256_digest")
|
|
@@ -1,60 +1,61 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
from rest_framework.exceptions import NotAcceptable
|
|
7
|
-
from django.core.exceptions import ObjectDoesNotExist
|
|
8
|
-
from django.shortcuts import redirect
|
|
9
|
-
from datetime import datetime, timezone, timedelta
|
|
2
|
+
from datetime import datetime, timedelta, timezone
|
|
3
|
+
from itertools import chain
|
|
4
|
+
from pathlib import PurePath
|
|
5
|
+
from urllib.parse import urljoin, urlparse, urlunsplit
|
|
10
6
|
|
|
11
7
|
from django.contrib.sessions.models import Session
|
|
8
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
12
9
|
from django.db import transaction
|
|
10
|
+
from django.db.models import OuterRef, Subquery
|
|
13
11
|
from django.db.utils import DatabaseError
|
|
14
12
|
from django.http.response import (
|
|
15
13
|
Http404,
|
|
16
|
-
|
|
17
|
-
HttpResponseForbidden,
|
|
14
|
+
HttpResponse,
|
|
18
15
|
HttpResponseBadRequest,
|
|
16
|
+
HttpResponseForbidden,
|
|
17
|
+
HttpResponseNotFound,
|
|
19
18
|
StreamingHttpResponse,
|
|
20
|
-
HttpResponse,
|
|
21
19
|
)
|
|
20
|
+
from django.shortcuts import redirect
|
|
22
21
|
from drf_spectacular.utils import extend_schema
|
|
23
22
|
from dynaconf import settings
|
|
24
|
-
from itertools import chain
|
|
25
23
|
from packaging.utils import canonicalize_name
|
|
26
|
-
from
|
|
27
|
-
from
|
|
24
|
+
from rest_framework.exceptions import NotAcceptable
|
|
25
|
+
from rest_framework.renderers import BrowsableAPIRenderer, JSONRenderer, TemplateHTMLRenderer
|
|
26
|
+
from rest_framework.response import Response
|
|
27
|
+
from rest_framework.viewsets import ViewSet
|
|
28
28
|
|
|
29
|
-
from pulpcore.plugin.
|
|
29
|
+
from pulpcore.plugin.models import RepositoryContent
|
|
30
30
|
from pulpcore.plugin.tasking import dispatch
|
|
31
31
|
from pulpcore.plugin.util import get_domain, get_url
|
|
32
|
+
from pulpcore.plugin.viewsets import OperationPostponedResponse
|
|
33
|
+
|
|
34
|
+
from pulp_python.app import tasks
|
|
32
35
|
from pulp_python.app.models import (
|
|
36
|
+
PackageProvenance,
|
|
33
37
|
PythonDistribution,
|
|
34
38
|
PythonPackageContent,
|
|
35
39
|
PythonPublication,
|
|
36
|
-
PackageProvenance,
|
|
37
40
|
)
|
|
38
41
|
from pulp_python.app.pypi.serializers import (
|
|
39
|
-
SummarySerializer,
|
|
40
42
|
PackageMetadataSerializer,
|
|
41
43
|
PackageUploadSerializer,
|
|
42
44
|
PackageUploadTaskSerializer,
|
|
45
|
+
SummarySerializer,
|
|
43
46
|
)
|
|
44
47
|
from pulp_python.app.utils import (
|
|
45
|
-
write_simple_index,
|
|
46
|
-
write_simple_index_json,
|
|
47
|
-
write_simple_detail,
|
|
48
|
-
write_simple_detail_json,
|
|
49
|
-
python_content_to_json,
|
|
50
48
|
PYPI_LAST_SERIAL,
|
|
51
49
|
PYPI_SERIAL_CONSTANT,
|
|
52
50
|
get_remote_package_filter,
|
|
53
51
|
get_remote_simple_page,
|
|
52
|
+
python_content_to_json,
|
|
53
|
+
write_simple_detail,
|
|
54
|
+
write_simple_detail_json,
|
|
55
|
+
write_simple_index,
|
|
56
|
+
write_simple_index_json,
|
|
54
57
|
)
|
|
55
58
|
|
|
56
|
-
from pulp_python.app import tasks
|
|
57
|
-
|
|
58
59
|
log = logging.getLogger(__name__)
|
|
59
60
|
|
|
60
61
|
ORIGIN_HOST = settings.CONTENT_ORIGIN if settings.CONTENT_ORIGIN else settings.PYPI_API_HOSTNAME
|
|
@@ -362,13 +363,20 @@ class SimpleView(PackageUploadMixin, ViewSet):
|
|
|
362
363
|
return redirect(urljoin(self.base_content_url, f"{path}/simple/{normalized}/"))
|
|
363
364
|
if content:
|
|
364
365
|
local_packages = content.filter(name__normalize=normalized)
|
|
365
|
-
|
|
366
|
+
repo_added_subquery = RepositoryContent.objects.filter(
|
|
367
|
+
content_id=OuterRef("pk"),
|
|
368
|
+
repository=repo_ver.repository,
|
|
369
|
+
version_removed=None,
|
|
370
|
+
).values("pulp_created")[:1]
|
|
371
|
+
packages = local_packages.annotate(
|
|
372
|
+
repo_added_time=Subquery(repo_added_subquery)
|
|
373
|
+
).values(
|
|
366
374
|
"filename",
|
|
367
375
|
"sha256",
|
|
368
376
|
"metadata_sha256",
|
|
369
377
|
"requires_python",
|
|
370
378
|
"size",
|
|
371
|
-
"
|
|
379
|
+
"repo_added_time",
|
|
372
380
|
"version",
|
|
373
381
|
)
|
|
374
382
|
provenances = PackageProvenance.objects.filter(package__in=local_packages).values_list(
|
|
@@ -378,7 +386,7 @@ class SimpleView(PackageUploadMixin, ViewSet):
|
|
|
378
386
|
p["filename"]: {
|
|
379
387
|
**p,
|
|
380
388
|
"url": urljoin(self.base_content_url, f"{path}/{p['filename']}"),
|
|
381
|
-
"upload_time": p["
|
|
389
|
+
"upload_time": p["repo_added_time"],
|
|
382
390
|
"provenance": (
|
|
383
391
|
self.get_provenance_url(normalized, p["version"], p["filename"])
|
|
384
392
|
if p["filename"] in provenances
|
|
@@ -460,7 +468,11 @@ class MetadataView(PyPIMixin, ViewSet):
|
|
|
460
468
|
if settings.DOMAIN_ENABLED:
|
|
461
469
|
domain = get_domain()
|
|
462
470
|
json_body = python_content_to_json(
|
|
463
|
-
path,
|
|
471
|
+
path,
|
|
472
|
+
package_content,
|
|
473
|
+
version=version,
|
|
474
|
+
domain=domain,
|
|
475
|
+
repository_version=repo_ver,
|
|
464
476
|
)
|
|
465
477
|
if json_body:
|
|
466
478
|
return Response(data=json_body, headers=headers)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
from pulpcore.plugin.replica import Replicator
|
|
2
|
-
|
|
3
1
|
from pulp_glue.python.context import (
|
|
4
2
|
PulpPythonDistributionContext,
|
|
5
3
|
PulpPythonPublicationContext,
|
|
6
4
|
PulpPythonRepositoryContext,
|
|
7
5
|
)
|
|
6
|
+
|
|
7
|
+
from pulpcore.plugin.replica import Replicator
|
|
8
|
+
|
|
8
9
|
from pulp_python.app.models import PythonDistribution, PythonRemote, PythonRepository
|
|
9
10
|
from pulp_python.app.tasks import sync as python_sync
|
|
10
11
|
|
|
@@ -2,25 +2,26 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import tempfile
|
|
4
4
|
from gettext import gettext as _
|
|
5
|
+
|
|
5
6
|
from django.conf import settings
|
|
6
7
|
from django.db.utils import IntegrityError
|
|
7
8
|
from drf_spectacular.utils import extend_schema_serializer
|
|
8
9
|
from packaging.requirements import Requirement
|
|
9
|
-
from rest_framework import serializers
|
|
10
|
-
from pypi_attestations import AttestationError
|
|
11
10
|
from pydantic import TypeAdapter, ValidationError
|
|
11
|
+
from pypi_attestations import AttestationError
|
|
12
|
+
from rest_framework import serializers
|
|
12
13
|
|
|
13
14
|
from pulpcore.plugin import models as core_models
|
|
14
15
|
from pulpcore.plugin import serializers as core_serializers
|
|
15
|
-
from pulpcore.plugin.util import get_domain, get_prn
|
|
16
|
+
from pulpcore.plugin.util import get_current_authenticated_user, get_domain, get_prn
|
|
16
17
|
|
|
17
18
|
from pulp_python.app import models as python_models
|
|
18
19
|
from pulp_python.app.provenance import (
|
|
20
|
+
AnyPublisher,
|
|
19
21
|
Attestation,
|
|
22
|
+
AttestationBundle,
|
|
20
23
|
Provenance,
|
|
21
24
|
verify_provenance,
|
|
22
|
-
AttestationBundle,
|
|
23
|
-
AnyPublisher,
|
|
24
25
|
)
|
|
25
26
|
from pulp_python.app.utils import (
|
|
26
27
|
DIST_EXTENSIONS,
|
|
@@ -210,7 +211,7 @@ class PythonPackageContentSerializer(core_serializers.SingleArtifactContentUploa
|
|
|
210
211
|
required=False,
|
|
211
212
|
allow_blank=True,
|
|
212
213
|
help_text=_(
|
|
213
|
-
"The maintainer's name at a minimum;
|
|
214
|
+
"The maintainer's name at a minimum; additional contact information may be provided."
|
|
214
215
|
),
|
|
215
216
|
)
|
|
216
217
|
maintainer_email = serializers.CharField(
|
|
@@ -359,7 +360,7 @@ class PythonPackageContentSerializer(core_serializers.SingleArtifactContentUploa
|
|
|
359
360
|
else:
|
|
360
361
|
attestations = TypeAdapter(list[Attestation]).validate_python(value)
|
|
361
362
|
except ValidationError as e:
|
|
362
|
-
raise serializers.ValidationError(_("Invalid attestations: {}".format(e))
|
|
363
|
+
raise serializers.ValidationError(_("Invalid attestations: {}").format(e))
|
|
363
364
|
return attestations
|
|
364
365
|
|
|
365
366
|
def handle_attestations(self, filename, sha256, attestations, offline=True):
|
|
@@ -372,7 +373,7 @@ class PythonPackageContentSerializer(core_serializers.SingleArtifactContentUploa
|
|
|
372
373
|
verify_provenance(filename, sha256, provenance, offline=offline)
|
|
373
374
|
except AttestationError as e:
|
|
374
375
|
raise serializers.ValidationError(
|
|
375
|
-
{"attestations": _("Attestations failed verification: {}".format(e)
|
|
376
|
+
{"attestations": _("Attestations failed verification: {}").format(e)}
|
|
376
377
|
)
|
|
377
378
|
return provenance.model_dump(mode="json")
|
|
378
379
|
|
|
@@ -627,13 +628,13 @@ class PackageProvenanceSerializer(core_serializers.NoArtifactContentUploadSerial
|
|
|
627
628
|
data["provenance"] = provenance.model_dump(mode="json")
|
|
628
629
|
except ValidationError as e:
|
|
629
630
|
raise serializers.ValidationError(
|
|
630
|
-
_("The uploaded provenance is not valid: {}".format(e)
|
|
631
|
+
_("The uploaded provenance is not valid: {}").format(e)
|
|
631
632
|
)
|
|
632
633
|
if data.pop("verify"):
|
|
633
634
|
try:
|
|
634
635
|
verify_provenance(data["package"].filename, data["package"].sha256, provenance)
|
|
635
636
|
except AttestationError as e:
|
|
636
|
-
raise serializers.ValidationError(_("Provenance verification failed: {}".format(e))
|
|
637
|
+
raise serializers.ValidationError(_("Provenance verification failed: {}").format(e))
|
|
637
638
|
return data
|
|
638
639
|
|
|
639
640
|
def retrieve(self, validated_data):
|
|
@@ -701,7 +702,7 @@ class PythonRemoteSerializer(core_serializers.RemoteSerializer):
|
|
|
701
702
|
package_types = MultipleChoiceArrayField(
|
|
702
703
|
required=False,
|
|
703
704
|
help_text=_(
|
|
704
|
-
"The package types to sync for Python content. Leave blank to get
|
|
705
|
+
"The package types to sync for Python content. Leave blank to get everypackage type."
|
|
705
706
|
),
|
|
706
707
|
choices=python_models.PACKAGE_TYPES,
|
|
707
708
|
default=list,
|
|
@@ -736,7 +737,7 @@ class PythonRemoteSerializer(core_serializers.RemoteSerializer):
|
|
|
736
737
|
Requirement(pkg)
|
|
737
738
|
except ValueError as ve:
|
|
738
739
|
raise serializers.ValidationError(
|
|
739
|
-
_("includes specifier {} is invalid. {}".format(pkg, ve)
|
|
740
|
+
_("includes specifier {} is invalid. {}").format(pkg, ve)
|
|
740
741
|
)
|
|
741
742
|
return value
|
|
742
743
|
|
|
@@ -747,7 +748,7 @@ class PythonRemoteSerializer(core_serializers.RemoteSerializer):
|
|
|
747
748
|
Requirement(pkg)
|
|
748
749
|
except ValueError as ve:
|
|
749
750
|
raise serializers.ValidationError(
|
|
750
|
-
_("excludes specifier {} is invalid. {}".format(pkg, ve)
|
|
751
|
+
_("excludes specifier {} is invalid. {}").format(pkg, ve)
|
|
751
752
|
)
|
|
752
753
|
return value
|
|
753
754
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from gettext import gettext as _
|
|
2
1
|
import logging
|
|
3
2
|
import os
|
|
3
|
+
from gettext import gettext as _
|
|
4
4
|
|
|
5
5
|
from django.core.files import File
|
|
6
6
|
from packaging.utils import canonicalize_name
|
|
@@ -10,7 +10,7 @@ from pulpcore.plugin.util import get_domain
|
|
|
10
10
|
|
|
11
11
|
from pulp_python.app import models as python_models
|
|
12
12
|
from pulp_python.app.serializers import PythonPublicationSerializer
|
|
13
|
-
from pulp_python.app.utils import
|
|
13
|
+
from pulp_python.app.utils import write_simple_detail, write_simple_index
|
|
14
14
|
|
|
15
15
|
log = logging.getLogger(__name__)
|
|
16
16
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
from collections import defaultdict
|
|
3
4
|
from gettext import gettext as _
|
|
4
5
|
from itertools import groupby
|
|
@@ -6,20 +7,24 @@ from uuid import UUID
|
|
|
6
7
|
|
|
7
8
|
from django.db.models import Prefetch
|
|
8
9
|
from django.db.models.query import QuerySet
|
|
10
|
+
|
|
11
|
+
from pulpcore.plugin.models import ContentArtifact, ProgressReport
|
|
12
|
+
from pulpcore.plugin.util import get_domain
|
|
13
|
+
|
|
9
14
|
from pulp_python.app.models import PythonPackageContent, PythonRepository
|
|
10
15
|
from pulp_python.app.utils import (
|
|
11
|
-
artifact_to_metadata_artifact,
|
|
12
16
|
artifact_to_python_content_data,
|
|
17
|
+
copy_artifact_to_temp_file,
|
|
18
|
+
extract_wheel_metadata,
|
|
13
19
|
fetch_json_release_metadata,
|
|
20
|
+
metadata_content_to_artifact,
|
|
14
21
|
parse_metadata,
|
|
15
22
|
)
|
|
16
|
-
from pulpcore.plugin.models import Artifact, ContentArtifact, ProgressReport
|
|
17
|
-
from pulpcore.plugin.util import get_domain
|
|
18
23
|
|
|
19
24
|
log = logging.getLogger(__name__)
|
|
20
25
|
|
|
21
26
|
|
|
22
|
-
BULK_SIZE =
|
|
27
|
+
BULK_SIZE = 250
|
|
23
28
|
|
|
24
29
|
|
|
25
30
|
def repair(repository_pk: UUID) -> None:
|
|
@@ -118,11 +123,21 @@ def repair_metadata(content: QuerySet[PythonPackageContent]) -> tuple[int, set[s
|
|
|
118
123
|
.first()
|
|
119
124
|
.artifact
|
|
120
125
|
)
|
|
121
|
-
|
|
126
|
+
# Copy artifact to temp file once, extract both content data and metadata
|
|
127
|
+
temp_path = copy_artifact_to_temp_file(main_artifact, package.filename)
|
|
128
|
+
try:
|
|
129
|
+
new_data = artifact_to_python_content_data(
|
|
130
|
+
package.filename, main_artifact, domain, temp_path=temp_path
|
|
131
|
+
)
|
|
132
|
+
metadata_content = (
|
|
133
|
+
extract_wheel_metadata(temp_path) if package.filename.endswith(".whl") else None
|
|
134
|
+
)
|
|
135
|
+
finally:
|
|
136
|
+
os.unlink(temp_path)
|
|
122
137
|
total_metadata_repaired += update_metadata_artifact_if_needed(
|
|
123
138
|
package,
|
|
124
139
|
new_data.get("metadata_sha256"),
|
|
125
|
-
|
|
140
|
+
metadata_content,
|
|
126
141
|
metadata_batch,
|
|
127
142
|
pkgs_metadata_not_repaired,
|
|
128
143
|
)
|
|
@@ -236,7 +251,7 @@ def update_package_if_needed(
|
|
|
236
251
|
def update_metadata_artifact_if_needed(
|
|
237
252
|
package: PythonPackageContent,
|
|
238
253
|
new_metadata_sha256: str | None,
|
|
239
|
-
|
|
254
|
+
metadata_content: bytes | None,
|
|
240
255
|
metadata_batch: list[tuple],
|
|
241
256
|
pkgs_metadata_not_repaired: set[str],
|
|
242
257
|
) -> int:
|
|
@@ -248,7 +263,7 @@ def update_metadata_artifact_if_needed(
|
|
|
248
263
|
Args:
|
|
249
264
|
package: Package to check for metadata changes.
|
|
250
265
|
new_metadata_sha256: The correct metadata_sha256 extracted from the main artifact, or None.
|
|
251
|
-
|
|
266
|
+
metadata_content: Raw metadata bytes extracted from the wheel, or None.
|
|
252
267
|
metadata_batch: List of tuples for batch processing (updated in-place).
|
|
253
268
|
pkgs_metadata_not_repaired: Set of package PKs that failed repair (updated in-place).
|
|
254
269
|
|
|
@@ -265,13 +280,13 @@ def update_metadata_artifact_if_needed(
|
|
|
265
280
|
|
|
266
281
|
# Create missing
|
|
267
282
|
if not cas:
|
|
268
|
-
metadata_batch.append((package,
|
|
283
|
+
metadata_batch.append((package, metadata_content))
|
|
269
284
|
# Fix existing
|
|
270
285
|
elif new_metadata_sha256 != original_metadata_sha256:
|
|
271
286
|
ca = cas.first()
|
|
272
287
|
metadata_artifact = ca.artifact
|
|
273
288
|
if metadata_artifact is None or (metadata_artifact.sha256 != new_metadata_sha256):
|
|
274
|
-
metadata_batch.append((package,
|
|
289
|
+
metadata_batch.append((package, metadata_content))
|
|
275
290
|
|
|
276
291
|
if len(metadata_batch) == BULK_SIZE:
|
|
277
292
|
not_repaired = _process_metadata_batch(metadata_batch)
|
|
@@ -288,7 +303,7 @@ def _process_metadata_batch(metadata_batch: list[tuple]) -> set[str]:
|
|
|
288
303
|
and their corresponding ContentArtifacts.
|
|
289
304
|
|
|
290
305
|
Args:
|
|
291
|
-
metadata_batch: List of (package,
|
|
306
|
+
metadata_batch: List of (package, metadata_content) tuples.
|
|
292
307
|
|
|
293
308
|
Returns:
|
|
294
309
|
Set of package PKs for which metadata artifacts could not be created.
|
|
@@ -296,8 +311,8 @@ def _process_metadata_batch(metadata_batch: list[tuple]) -> set[str]:
|
|
|
296
311
|
not_repaired = set()
|
|
297
312
|
content_artifacts = []
|
|
298
313
|
|
|
299
|
-
for package,
|
|
300
|
-
metadata_artifact =
|
|
314
|
+
for package, metadata_content in metadata_batch:
|
|
315
|
+
metadata_artifact = metadata_content_to_artifact(metadata_content)
|
|
301
316
|
if metadata_artifact:
|
|
302
317
|
ca = ContentArtifact(
|
|
303
318
|
artifact=metadata_artifact,
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
import asyncio
|
|
3
|
-
|
|
4
|
-
from aiohttp import ClientResponseError, ClientError
|
|
5
|
-
from lxml.etree import LxmlError
|
|
6
|
-
from gettext import gettext as _
|
|
2
|
+
import logging
|
|
7
3
|
from functools import partial
|
|
4
|
+
from gettext import gettext as _
|
|
5
|
+
from urllib.parse import urljoin
|
|
8
6
|
|
|
7
|
+
from aiohttp import ClientError, ClientResponseError
|
|
8
|
+
from bandersnatch.configuration import BandersnatchConfig
|
|
9
|
+
from bandersnatch.master import Master
|
|
10
|
+
from bandersnatch.mirror import Mirror
|
|
11
|
+
from lxml.etree import LxmlError
|
|
12
|
+
from packaging.requirements import Requirement
|
|
13
|
+
from pypi_attestations import Provenance
|
|
14
|
+
from pypi_simple import IndexPage
|
|
9
15
|
from rest_framework import serializers
|
|
10
16
|
|
|
11
17
|
from pulpcore.plugin.download import HttpDownloader
|
|
@@ -18,19 +24,11 @@ from pulpcore.plugin.stages import (
|
|
|
18
24
|
)
|
|
19
25
|
|
|
20
26
|
from pulp_python.app.models import (
|
|
27
|
+
PackageProvenance,
|
|
21
28
|
PythonPackageContent,
|
|
22
29
|
PythonRemote,
|
|
23
|
-
PackageProvenance,
|
|
24
30
|
)
|
|
25
|
-
from pulp_python.app.utils import
|
|
26
|
-
from pypi_simple import IndexPage
|
|
27
|
-
from pypi_attestations import Provenance
|
|
28
|
-
|
|
29
|
-
from bandersnatch.mirror import Mirror
|
|
30
|
-
from bandersnatch.master import Master
|
|
31
|
-
from bandersnatch.configuration import BandersnatchConfig
|
|
32
|
-
from packaging.requirements import Requirement
|
|
33
|
-
from urllib.parse import urljoin
|
|
31
|
+
from pulp_python.app.utils import PYPI_LAST_SERIAL, aget_remote_simple_page, parse_metadata
|
|
34
32
|
|
|
35
33
|
logger = logging.getLogger(__name__)
|
|
36
34
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import time
|
|
2
|
-
|
|
3
2
|
from datetime import datetime, timezone
|
|
4
|
-
|
|
3
|
+
|
|
5
4
|
from django.contrib.sessions.models import Session
|
|
5
|
+
from django.db import transaction
|
|
6
6
|
from pydantic import TypeAdapter
|
|
7
|
-
from pulpcore.plugin.models import Artifact, CreatedResource, Content, ContentArtifact
|
|
8
|
-
from pulpcore.plugin.util import get_domain, get_current_authenticated_user, get_prn
|
|
9
7
|
|
|
10
|
-
from
|
|
8
|
+
from pulpcore.plugin.models import Artifact, Content, ContentArtifact, CreatedResource
|
|
9
|
+
from pulpcore.plugin.util import get_current_authenticated_user, get_domain, get_prn
|
|
10
|
+
|
|
11
|
+
from pulp_python.app.models import PackageProvenance, PythonPackageContent, PythonRepository
|
|
11
12
|
from pulp_python.app.provenance import (
|
|
13
|
+
AnyPublisher,
|
|
12
14
|
Attestation,
|
|
13
15
|
AttestationBundle,
|
|
14
|
-
AnyPublisher,
|
|
15
16
|
Provenance,
|
|
16
17
|
verify_provenance,
|
|
17
18
|
)
|