pulp-python 3.30.0__py3-none-any.whl → 3.30.2__py3-none-any.whl

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.
@@ -12,7 +12,7 @@ class PulpPythonPluginAppConfig(PulpPluginAppConfig):
12
12
 
13
13
  name = "pulp_python.app"
14
14
  label = "python"
15
- version = "3.30.0"
15
+ version = "3.30.2"
16
16
  python_package_name = "pulp-python"
17
17
  domain_compatible = True
18
18
 
@@ -7,6 +7,7 @@ from urllib.parse import urljoin, urlparse, urlunsplit
7
7
  from django.contrib.sessions.models import Session
8
8
  from django.core.exceptions import ObjectDoesNotExist
9
9
  from django.db import transaction
10
+ from django.db.models import OuterRef, Subquery
10
11
  from django.db.utils import DatabaseError
11
12
  from django.http.response import (
12
13
  Http404,
@@ -25,6 +26,7 @@ from rest_framework.renderers import BrowsableAPIRenderer, JSONRenderer, Templat
25
26
  from rest_framework.response import Response
26
27
  from rest_framework.viewsets import ViewSet
27
28
 
29
+ from pulpcore.plugin.models import RepositoryContent
28
30
  from pulpcore.plugin.tasking import dispatch
29
31
  from pulpcore.plugin.util import get_domain, get_url
30
32
  from pulpcore.plugin.viewsets import OperationPostponedResponse
@@ -366,13 +368,20 @@ class SimpleView(PackageUploadMixin, ViewSet):
366
368
  return redirect(urljoin(self.base_content_url, f"{path}/simple/{normalized}/"))
367
369
  if content is not None:
368
370
  local_packages = content.filter(name_normalized=normalized)
369
- packages = local_packages.values(
371
+ repo_added_subquery = RepositoryContent.objects.filter(
372
+ content_id=OuterRef("pk"),
373
+ repository=repo_ver.repository,
374
+ version_removed=None,
375
+ ).values("pulp_created")[:1]
376
+ packages = local_packages.annotate(
377
+ repo_added_time=Subquery(repo_added_subquery)
378
+ ).values(
370
379
  "filename",
371
380
  "sha256",
372
381
  "metadata_sha256",
373
382
  "requires_python",
374
383
  "size",
375
- "pulp_created",
384
+ "repo_added_time",
376
385
  "version",
377
386
  )
378
387
  provenances = PackageProvenance.objects.filter(package__in=local_packages).values_list(
@@ -382,7 +391,7 @@ class SimpleView(PackageUploadMixin, ViewSet):
382
391
  p["filename"]: {
383
392
  **p,
384
393
  "url": urljoin(self.base_content_url, f"{path}/{p['filename']}"),
385
- "upload_time": p["pulp_created"],
394
+ "upload_time": p["repo_added_time"],
386
395
  "provenance": (
387
396
  self.get_provenance_url(normalized, p["version"], p["filename"])
388
397
  if p["filename"] in provenances
@@ -464,7 +473,11 @@ class MetadataView(PyPIMixin, ViewSet):
464
473
  if settings.DOMAIN_ENABLED:
465
474
  domain = get_domain()
466
475
  json_body = python_content_to_json(
467
- path, package_content, version=version, domain=domain
476
+ path,
477
+ package_content,
478
+ version=version,
479
+ domain=domain,
480
+ repository_version=repo_ver,
468
481
  )
469
482
  if json_body:
470
483
  return Response(data=json_body, headers=headers)
pulp_python/app/utils.py CHANGED
@@ -11,6 +11,7 @@ from datetime import timezone
11
11
  import pkginfo
12
12
  from aiohttp.client_exceptions import ClientError
13
13
  from django.conf import settings
14
+ from django.db.models import OuterRef, Subquery
14
15
  from django.db.utils import IntegrityError
15
16
  from jinja2 import Template
16
17
  from packaging.requirements import Requirement
@@ -19,7 +20,7 @@ from packaging.version import InvalidVersion, parse
19
20
  from pypi_simple import ACCEPT_JSON_PREFERRED, ProjectPage
20
21
 
21
22
  from pulpcore.plugin.exceptions import TimeoutException
22
- from pulpcore.plugin.models import Artifact, Remote
23
+ from pulpcore.plugin.models import Artifact, Remote, RepositoryContent
23
24
  from pulpcore.plugin.util import get_domain
24
25
 
25
26
  log = logging.getLogger(__name__)
@@ -359,7 +360,9 @@ def fetch_json_release_metadata(name: str, version: str, remotes: set[Remote]) -
359
360
  raise Exception(f"Failed to fetch {url} from any remote.")
360
361
 
361
362
 
362
- def python_content_to_json(base_path, content_query, version=None, domain=None):
363
+ def python_content_to_json(
364
+ base_path, content_query, version=None, domain=None, repository_version=None
365
+ ):
363
366
  """
364
367
  Converts a QuerySet of PythonPackageContent into the PyPi JSON format
365
368
  https://www.python.org/dev/peps/pep-0566/
@@ -371,6 +374,13 @@ def python_content_to_json(base_path, content_query, version=None, domain=None):
371
374
 
372
375
  Returns None if version is specified but not found within content_query
373
376
  """
377
+ if repository_version:
378
+ repo_added_subquery = RepositoryContent.objects.filter(
379
+ content_id=OuterRef("pk"),
380
+ repository=repository_version.repository,
381
+ version_removed=None,
382
+ ).values("pulp_created")[:1]
383
+ content_query = content_query.annotate(repo_added_time=Subquery(repo_added_subquery))
374
384
  full_metadata = {"last_serial": 0} # For now the serial field isn't supported by Pulp
375
385
  latest_content = latest_content_version(content_query, version)
376
386
  if not latest_content:
@@ -515,8 +525,10 @@ def python_content_to_download_info(content, base_path, domain=None):
515
525
  "python_version": content.python_version,
516
526
  "requires_python": content.requires_python or None,
517
527
  "size": content.size,
518
- "upload_time": str(content.pulp_created),
519
- "upload_time_iso_8601": str(content.pulp_created.isoformat()),
528
+ "upload_time": str(getattr(content, "repo_added_time", None) or content.pulp_created),
529
+ "upload_time_iso_8601": str(
530
+ (getattr(content, "repo_added_time", None) or content.pulp_created).isoformat()
531
+ ),
520
532
  "url": url,
521
533
  "yanked": False,
522
534
  "yanked_reason": None,
@@ -260,7 +260,7 @@ class PythonBlocklistEntryViewSet(
260
260
  parent_lookup_kwargs = {"repository_pk": "repository__pk"}
261
261
  serializer_class = python_serializers.PythonBlocklistEntrySerializer
262
262
  queryset = python_models.PythonBlocklistEntry.objects.all()
263
- filterset_fields = {"name": ["exact"], "version": ["exact"], "filename": ["exact"]}
263
+ filterset_fields = {"name": ["exact"], "version": ["exact", "isnull"], "filename": ["exact"]}
264
264
  ordering = ("-pulp_created",)
265
265
 
266
266
  DEFAULT_ACCESS_POLICY = {
@@ -1,4 +1,6 @@
1
1
  import subprocess
2
+ import time
3
+ from datetime import datetime
2
4
  from urllib.parse import urljoin
3
5
 
4
6
  import pytest
@@ -6,6 +8,7 @@ import requests
6
8
 
7
9
  from pulp_python.tests.functional.constants import (
8
10
  PYPI_SERIAL_CONSTANT,
11
+ PYPI_SIMPLE_V1_JSON,
9
12
  PYTHON_EGG_FILENAME,
10
13
  PYTHON_EGG_SHA256,
11
14
  PYTHON_MD_PROJECT_SPECIFIER,
@@ -13,6 +16,8 @@ from pulp_python.tests.functional.constants import (
13
16
  PYTHON_WHEEL_FILENAME,
14
17
  PYTHON_WHEEL_SHA256,
15
18
  SHELF_PYTHON_JSON,
19
+ TWINE_WHEEL_FILENAME,
20
+ TWINE_WHEEL_URL,
16
21
  )
17
22
  from pulp_python.tests.functional.utils import ensure_metadata
18
23
 
@@ -328,3 +333,41 @@ def assert_download_info(expected, received, message="Failed to match"):
328
333
  matched = True
329
334
  break
330
335
  assert matched is True, message
336
+
337
+
338
+ @pytest.mark.parallel
339
+ def test_upload_time_reflects_repo_addition(
340
+ monitor_task,
341
+ python_bindings,
342
+ python_content_factory,
343
+ python_distribution_factory,
344
+ python_repo_factory,
345
+ ):
346
+ """
347
+ Test that upload_time reflects repository addition time instead of content creation time.
348
+ Checks both Simple and JSON APIs.
349
+ """
350
+ content = python_content_factory(TWINE_WHEEL_FILENAME, url=TWINE_WHEEL_URL)
351
+ content_created = datetime.fromisoformat(
352
+ str(python_bindings.ContentPackagesApi.read(content.pulp_href).pulp_created)
353
+ )
354
+ time.sleep(2)
355
+
356
+ repo = python_repo_factory()
357
+ body = {"add_content_units": [content.pulp_href]}
358
+ monitor_task(python_bindings.RepositoriesPythonApi.modify(repo.pulp_href, body).task)
359
+ distro = python_distribution_factory(repository=repo)
360
+
361
+ # Simple API
362
+ headers = {"Accept": PYPI_SIMPLE_V1_JSON}
363
+ resp = requests.get(f"{urljoin(distro.base_url, 'simple/')}twine", headers=headers)
364
+ assert resp.status_code == 200
365
+ simple_time = datetime.fromisoformat(resp.json()["files"][0]["upload-time"])
366
+ assert simple_time > content_created
367
+
368
+ # JSON API
369
+ json_resp = requests.get(urljoin(distro.base_url, "pypi/twine/json"))
370
+ assert json_resp.status_code == 200
371
+ json_time = datetime.fromisoformat(json_resp.json()["urls"][0]["upload_time"])
372
+ assert json_time > content_created
373
+ assert json_time == simple_time
@@ -5,6 +5,9 @@ import requests
5
5
 
6
6
  from pulp_python.tests.functional.constants import (
7
7
  PYPI_SERIAL_CONSTANT,
8
+ PYPI_SIMPLE_V1_HTML,
9
+ PYPI_SIMPLE_V1_JSON,
10
+ PYPI_TEXT_HTML,
8
11
  PYTHON_SM_FIXTURE_CHECKSUMS,
9
12
  PYTHON_SM_FIXTURE_RELEASES,
10
13
  PYTHON_SM_PROJECT_SPECIFIER,
@@ -27,10 +30,6 @@ from pulp_python.tests.functional.utils import ensure_simple
27
30
 
28
31
  API_VERSION = "1.1"
29
32
 
30
- PYPI_TEXT_HTML = "text/html"
31
- PYPI_SIMPLE_V1_HTML = "application/vnd.pypi.simple.v1+html"
32
- PYPI_SIMPLE_V1_JSON = "application/vnd.pypi.simple.v1+json"
33
-
34
33
 
35
34
  @pytest.mark.parallel
36
35
  def test_simple_html_index_api(
@@ -382,3 +382,7 @@ VULNERABILITY_REPORT_TEST_PACKAGES = [
382
382
  ]
383
383
 
384
384
  PYPI_SERIAL_CONSTANT = 1000000000
385
+
386
+ PYPI_TEXT_HTML = "text/html"
387
+ PYPI_SIMPLE_V1_HTML = "application/vnd.pypi.simple.v1+html"
388
+ PYPI_SIMPLE_V1_JSON = "application/vnd.pypi.simple.v1+json"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pulp-python
3
- Version: 3.30.0
3
+ Version: 3.30.2
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
@@ -1,6 +1,6 @@
1
1
  pulp_python/__init__.py,sha256=GIuTLoBTc-07dSLJUh8xrZPRz8x-jJ61pfR0J1IjnzI,65
2
2
  pulp_python/pytest_plugin.py,sha256=LNnLjOkeEu2X4gJi614bHVmbsHyEwooHYIeecr96Qy4,8606
3
- pulp_python/app/__init__.py,sha256=y_CC6G_mwMgeCLPJVhbg_XELv0W3VVqwiJh9dEizSWA,2490
3
+ pulp_python/app/__init__.py,sha256=dNk1wMkVYLDT0BZk8FFSe7wAVZ09UxjvaJTOk0zuWx0,2490
4
4
  pulp_python/app/global_access_conditions.py,sha256=MZJtyoVsr-4hRaty6mKDqh3caOHd5UKJjEWLV2crOLs,1080
5
5
  pulp_python/app/modelresource.py,sha256=4SFAdqk6lozi_cZz4uqDIqhqPAZF-7l5jJwPn-xGZFs,1249
6
6
  pulp_python/app/models.py,sha256=Y7MDTl2nKz1dz2S4KQIQQ4oWcYgBA4TyDVxJGBYPHY4,17705
@@ -9,8 +9,8 @@ pulp_python/app/replica.py,sha256=qiWRP7tM_v4yP_XLIQfumfGolru-Jt6ZA0KVb-9g2cA,18
9
9
  pulp_python/app/serializers.py,sha256=Wg83g7LE9Qkf1S7hkkvG2_EJUi7D12l8UyCA1wmwf-I,33819
10
10
  pulp_python/app/settings.py,sha256=Cyc_p6U0HQjKpyrRL6JFrK3R7RMQJ9MAgNMJCfzPEiA,255
11
11
  pulp_python/app/urls.py,sha256=M2xjQ0j47BwQVpi75QCa5eUnQDcroKv3Cee7UrQ3QcA,1387
12
- pulp_python/app/utils.py,sha256=3XNXNtpr9-DgiKBh5fLGvQYMmtqqZubsNC9vsreumYY,26742
13
- pulp_python/app/viewsets.py,sha256=EyeuNNvklB74tSyL4MRfTnh4a5KVv2kUPgmeTUd6oSY,34209
12
+ pulp_python/app/utils.py,sha256=f9sCzZ58Sf7EBNqzBG0NBGtWE6gE2eIcnoSTAhdVIIg,27303
13
+ pulp_python/app/viewsets.py,sha256=yN1gHaT9vosqTbEP6emyMzoitliTzeHZTEqBVyn_2vA,34219
14
14
  pulp_python/app/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  pulp_python/app/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  pulp_python/app/management/commands/repair-python-metadata.py,sha256=knvTPcPwyfgCH0-KNNCK1Mj0mQKzXUdYEwo-v6F94XU,4548
@@ -40,7 +40,7 @@ pulp_python/app/migrations/0022_pythonblocklistentry.py,sha256=EbtjZuN65myTAHVWr
40
40
  pulp_python/app/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  pulp_python/app/pypi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  pulp_python/app/pypi/serializers.py,sha256=rE0Cci2IMg4NbNWlVdo-8zPaJCAUq2Re57q_T3jMt0Y,5030
43
- pulp_python/app/pypi/views.py,sha256=ksvj34QMHEXnevzsPNTeiGjrNrUpfGvLJ8zH_P4Faqg,21126
43
+ pulp_python/app/pypi/views.py,sha256=D2lsHijKp3ZJemt-iwWxjJEin5rAkc0pZzi9kZyQgwk,21651
44
44
  pulp_python/app/tasks/__init__.py,sha256=lTFpVvpDKbqv9RC0b2RYU8Bo6svDjrA-djt16pADFr8,284
45
45
  pulp_python/app/tasks/publish.py,sha256=bjsJzqJbLu7TF5rLb-UsZMmlNnc_LKw-sdHX9Gcatbw,4334
46
46
  pulp_python/app/tasks/repair.py,sha256=5InzdbjW8y3AC4Vj2PsNLm3wGGTr8D3LcfPw_WA2Fks,12257
@@ -52,7 +52,7 @@ pulp_python/app/webserver_snippets/apache.conf,sha256=3frHSl2YV_8pJPscaFxMVo7Hmx
52
52
  pulp_python/app/webserver_snippets/nginx.conf,sha256=gMqZGFefsTJVVx9YRxpHVS7NMEll9CzOseYdtLr3Avc,344
53
53
  pulp_python/tests/__init__.py,sha256=4Yz43a8s-KyhdHFb5eEhIIvH72807Y84uAHnG5bO5y0,31
54
54
  pulp_python/tests/functional/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- pulp_python/tests/functional/constants.py,sha256=SvzlxGQz1I-p3JEBSqNz9qoAYfq5yhGsqtYoJpCOioU,13819
55
+ pulp_python/tests/functional/constants.py,sha256=4e_zssE9G1bSNNJxYo7VgwvojUL_NocG5mUH996KBJs,13969
56
56
  pulp_python/tests/functional/utils.py,sha256=GWnXrsQGZd0Ngewa_9yce58Qc3z7FbrlRvqao_8ZML0,5901
57
57
  pulp_python/tests/functional/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
58
  pulp_python/tests/functional/api/test_attestations.py,sha256=cyZl4jCL-Q5Qu7XpxLc5_YmCzyc1Ctfl1IvSJ6YPQQU,8640
@@ -66,8 +66,8 @@ pulp_python/tests/functional/api/test_domains.py,sha256=BnWBwid_RA0xenBBHwvkT5_2
66
66
  pulp_python/tests/functional/api/test_download_content.py,sha256=5IuaHXyLakPkjm5sLTxtTl7Dq0yl7N36HpObnIa0Sks,4950
67
67
  pulp_python/tests/functional/api/test_export_import.py,sha256=rHns9wdaeP-vtfW_qoGHU9EVuJ4YuWE0-rjl_8otAgU,4530
68
68
  pulp_python/tests/functional/api/test_full_mirror.py,sha256=eU5HzgBBFOnX1q4m8ELx-t-I3U4TWXL-LNrlc2BcWVc,12045
69
- pulp_python/tests/functional/api/test_pypi_apis.py,sha256=kvKp29yfv9ZqbMs7knYO48mLXt7ZEVIdZLUEjgeztzc,11691
70
- pulp_python/tests/functional/api/test_pypi_simple_api.py,sha256=td_I42mAHVzdXbHsngRbwrNYIsnEewKXA1NC6TfNZZY,7251
69
+ pulp_python/tests/functional/api/test_pypi_apis.py,sha256=mhST0GV-uDrVPsBWYRGaJx1pp9htzbYiUGuIGOI0s_s,13218
70
+ pulp_python/tests/functional/api/test_pypi_simple_api.py,sha256=tOUl8WXgJkMoxGyLbA-WorCxIRKfGWM-27_dK_Jc0Q4,7171
71
71
  pulp_python/tests/functional/api/test_rbac.py,sha256=-xNWqvKKU-v1uC6tCRGBK0aWaff25K9C-AfzQkdHhhI,10513
72
72
  pulp_python/tests/functional/api/test_repair.py,sha256=4FR7jx_LA2K-pnRJXKkhq-1uUoWXST1leZ9XdyhGM2U,12909
73
73
  pulp_python/tests/functional/api/test_sync.py,sha256=TTHR1CpZeRoD97RKxj2pZPsP0OS9ibYKGPOh1WfKISg,13801
@@ -77,9 +77,9 @@ pulp_python/tests/functional/assets/shelf-reader-0.1.tar.gz.publish.attestation,
77
77
  pulp_python/tests/functional/assets/shelf_reader-0.1-py2-none-any.whl.publish.attestation,sha256=muTQ8dqYSSdx76DlaPjB1REcNIS-aak-Na0TkASxu8M,10426
78
78
  pulp_python/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  pulp_python/tests/unit/test_models.py,sha256=TBI0yKsrdbnJSPeBFfxSqhXK7zaNvR6qg5JehGH3Pds,229
80
- pulp_python-3.30.0.dist-info/licenses/LICENSE,sha256=2ylvL381vKOhdO-w6zkrOxe9lLNBhRQpo9_0EbHC_HM,18046
81
- pulp_python-3.30.0.dist-info/METADATA,sha256=qoGeUKqhylMVAFH-ZVkJ-Bzmj1-opwBmZGX5bTmL1wY,1744
82
- pulp_python-3.30.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
83
- pulp_python-3.30.0.dist-info/entry_points.txt,sha256=HvqLEXjw_dS5jqAwnE5JiRZFE6f-y5SRtitKLPml2To,115
84
- pulp_python-3.30.0.dist-info/top_level.txt,sha256=X0hXgXc_bpbiKqVrkt8jD5_QEiQviKbHDwveQcOcJjo,12
85
- pulp_python-3.30.0.dist-info/RECORD,,
80
+ pulp_python-3.30.2.dist-info/licenses/LICENSE,sha256=2ylvL381vKOhdO-w6zkrOxe9lLNBhRQpo9_0EbHC_HM,18046
81
+ pulp_python-3.30.2.dist-info/METADATA,sha256=HGJ7fVTZdNPmd0urUEK5YT7sY-gfac00-0nRkeiGHS4,1744
82
+ pulp_python-3.30.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
83
+ pulp_python-3.30.2.dist-info/entry_points.txt,sha256=HvqLEXjw_dS5jqAwnE5JiRZFE6f-y5SRtitKLPml2To,115
84
+ pulp_python-3.30.2.dist-info/top_level.txt,sha256=X0hXgXc_bpbiKqVrkt8jD5_QEiQviKbHDwveQcOcJjo,12
85
+ pulp_python-3.30.2.dist-info/RECORD,,