pulp-python 3.7.3__tar.gz → 3.9.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.
Files changed (73) hide show
  1. {pulp-python-3.7.3 → pulp-python-3.9.0}/CHANGES.rst +68 -0
  2. {pulp-python-3.7.3 → pulp-python-3.9.0}/PKG-INFO +1 -1
  3. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/__init__.py +1 -1
  4. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/models.py +46 -59
  5. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/pypi/views.py +10 -2
  6. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/serializers.py +13 -11
  7. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/tasks/sync.py +14 -18
  8. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/viewsets.py +1 -0
  9. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_crud_content_unit.py +3 -7
  10. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_download_content.py +3 -3
  11. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_sync.py +47 -0
  12. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python.egg-info/PKG-INFO +1 -1
  13. pulp-python-3.9.0/pulp_python.egg-info/requires.txt +4 -0
  14. {pulp-python-3.7.3 → pulp-python-3.9.0}/pyproject.toml +2 -1
  15. pulp-python-3.9.0/requirements.txt +4 -0
  16. {pulp-python-3.7.3 → pulp-python-3.9.0}/setup.py +1 -1
  17. pulp-python-3.7.3/pulp_python.egg-info/requires.txt +0 -5
  18. pulp-python-3.7.3/requirements.txt +0 -5
  19. {pulp-python-3.7.3 → pulp-python-3.9.0}/COMMITMENT +0 -0
  20. {pulp-python-3.7.3 → pulp-python-3.9.0}/COPYRIGHT +0 -0
  21. {pulp-python-3.7.3 → pulp-python-3.9.0}/LICENSE +0 -0
  22. {pulp-python-3.7.3 → pulp-python-3.9.0}/MANIFEST.in +0 -0
  23. {pulp-python-3.7.3 → pulp-python-3.9.0}/README.md +0 -0
  24. {pulp-python-3.7.3 → pulp-python-3.9.0}/functest_requirements.txt +0 -0
  25. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/__init__.py +0 -0
  26. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0001_initial.py +0 -0
  27. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0002_pythonpackagecontent_python_version.py +0 -0
  28. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0003_new_sync_filters.py +0 -0
  29. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0004_DATA_swap_distribution_model.py +0 -0
  30. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0005_pythonpackagecontent_sha256.py +0 -0
  31. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0006_pythonrepository_autopublish.py +0 -0
  32. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0007_pythonpackagecontent_mv-2-1.py +0 -0
  33. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0008_pythonpackagecontent_unique_sha256.py +0 -0
  34. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0009_pythondistribution_allow_uploads.py +0 -0
  35. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/0010_update_json_field.py +0 -0
  36. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/migrations/__init__.py +0 -0
  37. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/pypi/__init__.py +0 -0
  38. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/pypi/serializers.py +0 -0
  39. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/settings.py +0 -0
  40. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/tasks/__init__.py +0 -0
  41. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/tasks/publish.py +0 -0
  42. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/tasks/upload.py +0 -0
  43. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/urls.py +0 -0
  44. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/utils.py +0 -0
  45. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/webserver_snippets/__init__.py +0 -0
  46. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/webserver_snippets/apache.conf +0 -0
  47. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/app/webserver_snippets/nginx.conf +0 -0
  48. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/__init__.py +0 -0
  49. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/__init__.py +0 -0
  50. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/__init__.py +0 -0
  51. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_auto_publish.py +0 -0
  52. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_consume_content.py +0 -0
  53. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_crud_publications.py +0 -0
  54. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_crud_remotes.py +0 -0
  55. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_full_mirror.py +0 -0
  56. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/api/test_pypi_apis.py +0 -0
  57. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/conftest.py +0 -0
  58. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/constants.py +0 -0
  59. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/functional/utils.py +0 -0
  60. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/unit/__init__.py +0 -0
  61. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/unit/test_models.py +0 -0
  62. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/upgrade/__init__.py +0 -0
  63. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/upgrade/post/__init__.py +0 -0
  64. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/upgrade/post/test_publish.py +0 -0
  65. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/upgrade/pre/__init__.py +0 -0
  66. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python/tests/upgrade/pre/test_publish.py +0 -0
  67. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python.egg-info/SOURCES.txt +0 -0
  68. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python.egg-info/dependency_links.txt +0 -0
  69. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python.egg-info/entry_points.txt +0 -0
  70. {pulp-python-3.7.3 → pulp-python-3.9.0}/pulp_python.egg-info/top_level.txt +0 -0
  71. {pulp-python-3.7.3 → pulp-python-3.9.0}/setup.cfg +0 -0
  72. {pulp-python-3.7.3 → pulp-python-3.9.0}/test_requirements.txt +0 -0
  73. {pulp-python-3.7.3 → pulp-python-3.9.0}/unittest_requirements.txt +0 -0
@@ -13,6 +13,60 @@ Changelog
13
13
 
14
14
  .. towncrier release notes start
15
15
 
16
+ 3.9.0 (2023-03-17)
17
+ ==================
18
+
19
+
20
+ Features
21
+ --------
22
+
23
+ - Added version filter to package list endpoint.
24
+ `#577 <https://github.com/pulp/pulp_python/issues/577>`__
25
+ - Allow duplicate uploads to return existing packages instead of erring.
26
+ `#590 <https://github.com/pulp/pulp_python/issues/590>`__
27
+
28
+
29
+ Bugfixes
30
+ --------
31
+
32
+ - Fixed pull-through caching ignoring remote proxy settings.
33
+ `#553 <https://github.com/pulp/pulp_python/issues/553>`__
34
+ - Changed includes and excludes openapi schema to report as array of strings instead of object.
35
+ `#576 <https://github.com/pulp/pulp_python/issues/576>`__
36
+ - Fixed syncing ignoring remote proxy.
37
+ `#581 <https://github.com/pulp/pulp_python/issues/581>`__
38
+ - Fixed duplicate operationID for generated PyPI simple endpoints schema.
39
+ `#594 <https://github.com/pulp/pulp_python/issues/594>`__
40
+
41
+
42
+ ----
43
+
44
+
45
+ 3.8.0 (2022-12-19)
46
+ ==================
47
+
48
+
49
+ Bugfixes
50
+ --------
51
+
52
+ - Fixed syncing failing when using bandersnatch 5.3.0
53
+ `#554 <https://github.com/pulp/pulp_python/issues/554>`__
54
+ - Prevent .netrc file from being read on syncs.
55
+ `#566 <https://github.com/pulp/pulp_python/issues/566>`__
56
+ - Fix 500 error when pip installing using object storage.
57
+ `#572 <https://github.com/pulp/pulp_python/issues/572>`__
58
+
59
+
60
+ Improved Documentation
61
+ ----------------------
62
+
63
+ - Documented ``pulp_python`` specific settings.
64
+ `#571 <https://github.com/pulp/pulp_python/issues/571>`__
65
+
66
+
67
+ ----
68
+
69
+
16
70
  3.7.3 (2022-10-06)
17
71
  ==================
18
72
 
@@ -78,6 +132,20 @@ Misc
78
132
  ----
79
133
 
80
134
 
135
+ 3.6.1 (2022-08-19)
136
+ ==================
137
+
138
+
139
+ Bugfixes
140
+ --------
141
+
142
+ - Fixed syncing failing when using bandersnatch 5.3.0
143
+ `#554 <https://github.com/pulp/pulp_python/issues/554>`__
144
+
145
+
146
+ ----
147
+
148
+
81
149
  3.6.0 (2021-12-15)
82
150
  ==================
83
151
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pulp-python
3
- Version: 3.7.3
3
+ Version: 3.9.0
4
4
  Summary: pulp-python plugin for the Pulp Project
5
5
  Home-page: https://www.pulpproject.org
6
6
  Author: Pulp Project Developers
@@ -8,5 +8,5 @@ class PulpPythonPluginAppConfig(PulpPluginAppConfig):
8
8
 
9
9
  name = "pulp_python.app"
10
10
  label = "python"
11
- version = "3.7.3"
11
+ version = "3.9.0"
12
12
  python_package_name = "pulp-python"
@@ -5,15 +5,14 @@ from django.contrib.postgres.fields import ArrayField
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
6
  from django.db import models
7
7
  from django.conf import settings
8
- from yarl import URL
9
-
10
8
  from pulpcore.plugin.models import (
11
9
  Content,
12
10
  Publication,
13
11
  Distribution,
14
12
  Remote,
15
- Repository
13
+ Repository,
16
14
  )
15
+ from pulpcore.plugin.responses import ArtifactResponse
17
16
 
18
17
  from pathlib import PurePath
19
18
  from .utils import (
@@ -21,7 +20,7 @@ from .utils import (
21
20
  parse_project_metadata,
22
21
  python_content_to_json,
23
22
  PYPI_LAST_SERIAL,
24
- PYPI_SERIAL_CONSTANT
23
+ PYPI_SERIAL_CONSTANT,
25
24
  )
26
25
  from pulpcore.plugin.repo_version_utils import remove_duplicates, validate_repo_version
27
26
 
@@ -39,10 +38,12 @@ PACKAGE_TYPES = (
39
38
  ("sdist", "sdist"),
40
39
  )
41
40
 
42
- PLATFORMS = (("windows", "windows"),
43
- ("macos", "macos"),
44
- ("freebsd", "freebsd"),
45
- ("linux", "linux"))
41
+ PLATFORMS = (
42
+ ("windows", "windows"),
43
+ ("macos", "macos"),
44
+ ("freebsd", "freebsd"),
45
+ ("linux", "linux"),
46
+ )
46
47
 
47
48
 
48
49
  class PythonDistribution(Distribution):
@@ -50,7 +51,7 @@ class PythonDistribution(Distribution):
50
51
  Distribution for 'Python' Content.
51
52
  """
52
53
 
53
- TYPE = 'python'
54
+ TYPE = "python"
54
55
 
55
56
  allow_uploads = models.BooleanField(default=True)
56
57
 
@@ -76,47 +77,31 @@ class PythonDistribution(Distribution):
76
77
  # Temporary fix for PublishedMetadata not being properly served from remote storage
77
78
  # https://github.com/pulp/pulp_python/issues/413
78
79
  if settings.DEFAULT_FILE_STORAGE != "pulpcore.app.models.storage.FileSystem":
79
- try:
80
- publication = self.publication or Publication.objects.filter(
81
- repository_version=self.repository.latest_version()).latest("pulp_created")
82
- except ObjectDoesNotExist:
83
- return None
84
- rel_path = f"{path}/index.html"
85
- try:
86
- ca = publication.published_artifact.select_related(
87
- "content_artifact",
88
- "content_artifact__artifact",
89
- ).get(relative_path=rel_path).content_artifact
90
- except ObjectDoesNotExist:
91
- return None
92
- file = ca.artifact.file
93
- content_disposition = f"attachment%3Bfilename={ca.relative_path}"
94
- if settings.DEFAULT_FILE_STORAGE == "storages.backends.azure_storage.AzureStorage":
95
- parameters = {
96
- "content_disposition": content_disposition,
97
- "content_type": "text/html"
98
- }
99
- elif settings.DEFAULT_FILE_STORAGE == "storages.backends.s3boto3.S3Boto3Storage":
100
- parameters = {
101
- "ResponseContentDisposition": content_disposition,
102
- "ResponseContentType": "text/html"
103
- }
104
- else:
105
- raise NotImplementedError()
106
-
107
- url = URL(file.storage.url(file.name, parameters=parameters), encoded=True)
108
- # Trick the content app to stream the metadata from the remote storage
109
- remote = PythonRemote(name="Redirect", url=str(url), policy="streamed")
110
- remote.get_remote_artifact_url = lambda *args, **kwargs: str(url)
111
- setattr(self, "publication", None)
112
- setattr(self, "repository", None)
113
- setattr(self, "remote", remote)
114
- return None
80
+ if self.publication or self.repository:
81
+ try:
82
+ publication = self.publication or Publication.objects.filter(
83
+ repository_version=self.repository.latest_version()
84
+ ).latest("pulp_created")
85
+ except ObjectDoesNotExist:
86
+ return None
87
+ rel_path = f"{path}/index.html"
88
+ try:
89
+ ca = (
90
+ publication.published_artifact.select_related(
91
+ "content_artifact",
92
+ "content_artifact__artifact",
93
+ )
94
+ .get(relative_path=rel_path)
95
+ .content_artifact
96
+ )
97
+ except ObjectDoesNotExist:
98
+ return None
99
+ headers = {"Content-Type": "text/html"}
100
+ return ArtifactResponse(ca.artifact, headers=headers)
115
101
 
116
102
  if name:
117
103
  package_content = PythonPackageContent.objects.filter(
118
- pk__in=self.publication.repository_version.content,
119
- name__iexact=name
104
+ pk__in=self.publication.repository_version.content, name__iexact=name
120
105
  )
121
106
  # TODO Change this value to the Repo's serial value when implemented
122
107
  headers = {PYPI_LAST_SERIAL: str(PYPI_SERIAL_CONSTANT)}
@@ -150,7 +135,7 @@ class PythonPackageContent(Content):
150
135
 
151
136
  PROTECTED_FROM_RECLAIM = False
152
137
 
153
- TYPE = 'python'
138
+ TYPE = "python"
154
139
  repo_key_fields = ("filename",)
155
140
  # Required metadata
156
141
  filename = models.TextField(db_index=True)
@@ -190,9 +175,9 @@ class PythonPackageContent(Content):
190
175
  path = PurePath(relative_path)
191
176
  metadata = get_project_metadata_from_artifact(path.name, artifact)
192
177
  data = parse_project_metadata(vars(metadata))
193
- data['packagetype'] = metadata.packagetype
194
- data['version'] = metadata.version
195
- data['filename'] = path.name
178
+ data["packagetype"] = metadata.packagetype
179
+ data["version"] = metadata.version
180
+ data["filename"] = path.name
196
181
  data["sha256"] = artifact.sha256
197
182
  return PythonPackageContent(**data)
198
183
 
@@ -206,11 +191,11 @@ class PythonPackageContent(Content):
206
191
  e.g. <PythonPackageContent: shelf-reader [version] (whl)>
207
192
 
208
193
  """
209
- return '<{obj_name}: {name} [{version}] ({type})>'.format(
194
+ return "<{obj_name}: {name} [{version}] ({type})>".format(
210
195
  obj_name=self._meta.object_name,
211
196
  name=self.name,
212
197
  version=self.version,
213
- type=self.packagetype
198
+ type=self.packagetype,
214
199
  )
215
200
 
216
201
  class Meta:
@@ -223,7 +208,7 @@ class PythonPublication(Publication):
223
208
  A Publication for PythonContent.
224
209
  """
225
210
 
226
- TYPE = 'python'
211
+ TYPE = "python"
227
212
 
228
213
  class Meta:
229
214
  default_related_name = "%(app_label)s_%(model_name)s"
@@ -238,16 +223,18 @@ class PythonRemote(Remote):
238
223
  prereleases (models.BooleanField): Whether to sync pre-release versions of packages.
239
224
  """
240
225
 
241
- TYPE = 'python'
226
+ TYPE = "python"
242
227
  DEFAULT_DOWNLOAD_CONCURRENCY = 10
243
228
  prereleases = models.BooleanField(default=False)
244
229
  includes = models.JSONField(default=list)
245
230
  excludes = models.JSONField(default=list)
246
- package_types = ArrayField(models.CharField(max_length=15, blank=True),
247
- choices=PACKAGE_TYPES, default=list)
231
+ package_types = ArrayField(
232
+ models.CharField(max_length=15, blank=True), choices=PACKAGE_TYPES, default=list
233
+ )
248
234
  keep_latest_packages = models.IntegerField(default=0)
249
- exclude_platforms = ArrayField(models.CharField(max_length=10, blank=True),
250
- choices=PLATFORMS, default=list)
235
+ exclude_platforms = ArrayField(
236
+ models.CharField(max_length=10, blank=True), choices=PLATFORMS, default=list
237
+ )
251
238
 
252
239
  def get_remote_artifact_url(self, relative_path=None, request=None):
253
240
  """Get url for remote_artifact"""
@@ -201,12 +201,20 @@ class SimpleView(ViewSet, PackageUploadMixin):
201
201
  return link.text, d_url, value if digest == 'sha256' else ''
202
202
 
203
203
  url = remote.get_remote_artifact_url(f'simple/{package}/')
204
- response = requests.get(url, stream=True)
204
+ kwargs = {}
205
+ if proxy_url := remote.proxy_url:
206
+ if remote.proxy_username or remote.proxy_password:
207
+ parsed_proxy = urlparse(proxy_url)
208
+ netloc = f"{remote.proxy_username}:{remote.proxy_password}@{parsed_proxy.netloc}"
209
+ proxy_url = urlunsplit((parsed_proxy.scheme, netloc, "", "", ""))
210
+ kwargs["proxies"] = {"http": proxy_url, "https": proxy_url}
211
+
212
+ response = requests.get(url, stream=True, **kwargs)
205
213
  links = parse_links_stream_response(response)
206
214
  packages = (parse_url(link) for link in links)
207
215
  return StreamingHttpResponse(write_simple_detail(package, packages, streamed=True))
208
216
 
209
- @extend_schema(summary="Get package simple page")
217
+ @extend_schema(operation_id="pypi_simple_package_read", summary="Get package simple page")
210
218
  def retrieve(self, request, path, package):
211
219
  """Retrieves the simple api html page for a package."""
212
220
  distro, repo_ver, content = self.get_drvc(path)
@@ -226,20 +226,21 @@ class PythonPackageContentSerializer(core_serializers.SingleArtifactContentUploa
226
226
  "The uploaded artifact's sha256 checksum does not match the one provided"
227
227
  )}
228
228
  )
229
- sha256 = artifact.sha256
230
- if sha256 and python_models.PythonPackageContent.objects.filter(sha256=sha256).exists():
231
- raise serializers.ValidationError(detail={"sha256": _('This field must be unique')})
232
229
 
233
230
  _data = parse_project_metadata(vars(metadata))
234
231
  _data['packagetype'] = metadata.packagetype
235
232
  _data['version'] = metadata.version
236
233
  _data['filename'] = filename
237
- _data['sha256'] = sha256
234
+ _data['sha256'] = artifact.sha256
238
235
 
239
236
  data.update(_data)
240
237
 
241
238
  return data
242
239
 
240
+ def retrieve(self, validated_data):
241
+ content = python_models.PythonPackageContent.objects.filter(sha256=validated_data["sha256"])
242
+ return content.first()
243
+
243
244
  class Meta:
244
245
  fields = core_serializers.SingleArtifactContentUploadSerializer.Meta.fields + (
245
246
  'filename', 'packagetype', 'name', 'version', 'sha256', 'metadata_version', 'summary',
@@ -278,20 +279,21 @@ class PythonRemoteSerializer(core_serializers.RemoteSerializer):
278
279
  A Serializer for PythonRemote.
279
280
  """
280
281
 
281
- includes = serializers.JSONField(
282
+ includes = serializers.ListField(
283
+ child=serializers.CharField(allow_blank=False),
282
284
  required=False,
283
- default=list,
285
+ allow_empty=True,
284
286
  help_text=_(
285
- "A JSON list containing project specifiers for Python packages to include."
287
+ "A list containing project specifiers for Python packages to include."
286
288
  ),
287
289
  )
288
- excludes = serializers.JSONField(
290
+ excludes = serializers.ListField(
291
+ child=serializers.CharField(allow_blank=False),
289
292
  required=False,
290
- default=list,
293
+ allow_empty=True,
291
294
  help_text=_(
292
- "A JSON list containing project specifiers for Python packages to exclude."
295
+ "A list containing project specifiers for Python packages to exclude."
293
296
  ),
294
-
295
297
  )
296
298
  prereleases = serializers.BooleanField(
297
299
  required=False,
@@ -3,7 +3,7 @@ import logging
3
3
  from aiohttp import ClientResponseError, ClientError
4
4
  from lxml.etree import LxmlError
5
5
  from gettext import gettext as _
6
- from os import environ
6
+ from os import environ, path
7
7
 
8
8
  from rest_framework import serializers
9
9
 
@@ -26,7 +26,7 @@ from bandersnatch.mirror import Mirror
26
26
  from bandersnatch.master import Master
27
27
  from bandersnatch.configuration import BandersnatchConfig
28
28
  from packaging.requirements import Requirement
29
- from urllib.parse import urljoin
29
+ from urllib.parse import urljoin, urlsplit, urlunsplit
30
30
 
31
31
  logger = logging.getLogger(__name__)
32
32
 
@@ -111,14 +111,22 @@ class PythonBanderStage(Stage):
111
111
  """
112
112
  If includes is specified, then only sync those,else try to sync all other packages
113
113
  """
114
+ # Prevent bandersnatch from reading actual .netrc file, set to nonexistent file
115
+ # See discussion on https://github.com/pulp/pulp_python/issues/581
116
+ environ["NETRC"] = f"{path.curdir}/.fake-netrc"
114
117
  # TODO Change Bandersnatch internal API to take proxy settings in from config parameters
115
- if self.remote.proxy_url:
116
- environ['http_proxy'] = self.remote.proxy_url
117
- environ['https_proxy'] = self.remote.proxy_url
118
+ if proxy_url := self.remote.proxy_url:
119
+ if self.remote.proxy_username or self.remote.proxy_password:
120
+ parsed_proxy = urlsplit(proxy_url)
121
+ creds = f"{self.remote.proxy_username}:{self.remote.proxy_password}"
122
+ netloc = f"{creds}@{parsed_proxy.netloc}"
123
+ proxy_url = urlunsplit((parsed_proxy.scheme, netloc, "", "", ""))
124
+ environ['http_proxy'] = proxy_url
125
+ environ['https_proxy'] = proxy_url
118
126
  # Bandersnatch includes leading slash when forming API urls
119
127
  url = self.remote.url.rstrip("/")
120
128
  # local & global timeouts defaults to 10secs and 5 hours
121
- async with PulpMaster(url) as master:
129
+ async with Master(url) as master:
122
130
  deferred_download = self.remote.policy != Remote.IMMEDIATE
123
131
  workers = self.remote.download_concurrency or self.remote.DEFAULT_DOWNLOAD_CONCURRENCY
124
132
  async with ProgressReport(
@@ -140,18 +148,6 @@ class PythonBanderStage(Stage):
140
148
  await pmirror.synchronize(packages_to_sync)
141
149
 
142
150
 
143
- class PulpMaster(Master):
144
- """
145
- Temporary subclass of bandersnatch.Master until features are in bandersnatch.
146
- """
147
-
148
- async def __aenter__(self) -> "Master":
149
- """Ensure Pulp does not try to read the .netrc file."""
150
- await super().__aenter__()
151
- self.session._trust_env = False
152
- return self
153
-
154
-
155
151
  class PulpMirror(Mirror):
156
152
  """
157
153
  Pulp Mirror Class to perform syncing using Bandersnatch
@@ -101,6 +101,7 @@ class PythonPackageContentFilter(core_viewsets.ContentFilter):
101
101
  'filename': ['exact', 'in', 'contains'],
102
102
  'keywords': ['in', 'contains'],
103
103
  'sha256': ['exact', 'in'],
104
+ 'version': ['exact', 'gt', 'lt', 'gte', 'lte']
104
105
  }
105
106
 
106
107
 
@@ -146,7 +146,7 @@ class ContentUnitTestCase(TestCaseUsingBindings, TestHelpersMixin):
146
146
  """
147
147
  1) upload file
148
148
  2) upload the same file again
149
- 3) this should fail/send an error
149
+ 3) this should return first unit
150
150
  """
151
151
  delete_orphans()
152
152
  response = self.do_upload()
@@ -154,12 +154,8 @@ class ContentUnitTestCase(TestCaseUsingBindings, TestHelpersMixin):
154
154
  content_unit = self.content_api.read(created_resources[0])
155
155
  self.check_package_data(content_unit.to_dict())
156
156
 
157
- with self.assertRaises(PulpTaskError) as cm:
158
- monitor_task(self.do_upload().task)
159
- task_report = cm.exception.task.to_dict()
160
- msg = "This field must be unique"
161
- self.assertTrue("sha256" in task_report["error"]["description"])
162
- self.assertTrue(msg in task_report["error"]["description"])
157
+ created_resources = monitor_task(self.do_upload().task).created_resources
158
+ self.assertEqual(content_unit.pulp_href, created_resources[0])
163
159
 
164
160
  def test_08_upload_same_filename_different_artifact(self):
165
161
  """
@@ -124,7 +124,7 @@ class PublishPyPIJSON(TestCaseUsingBindings, TestHelpersMixin):
124
124
  This test checks that Pulp can fully sync another Python Package repository that is not
125
125
  PyPI. This reads the repository's simple page if XMLRPC isn't supported.
126
126
  """
127
- remote = self._create_remote(includes="", prereleases=True)
127
+ remote = self._create_remote(includes=[], prereleases=True)
128
128
  repo = self._create_repo_and_sync_with_remote(remote)
129
129
  self.assertEqual(get_content_summary(repo.to_dict()), PYTHON_LG_FIXTURE_SUMMARY)
130
130
 
@@ -138,14 +138,14 @@ class PublishPyPIJSON(TestCaseUsingBindings, TestHelpersMixin):
138
138
  # Test using live generated simple pages
139
139
  distro = self._create_distribution_from_repo(repo)
140
140
 
141
- remote = self._create_remote(includes="", url=distro.base_url)
141
+ remote = self._create_remote(includes=[], url=distro.base_url)
142
142
  repo2 = self._create_repo_and_sync_with_remote(remote)
143
143
  self.assertEqual(get_content_summary(repo2.to_dict()), PYTHON_MD_FIXTURE_SUMMARY)
144
144
 
145
145
  # Now test using publication simple pages
146
146
  pub = self._create_publication(repo)
147
147
  distro2 = self._create_distribution_from_publication(pub)
148
- remote = self._create_remote(includes="", url=distro2.base_url, prereleases=True)
148
+ remote = self._create_remote(includes=[], url=distro2.base_url, prereleases=True)
149
149
 
150
150
  repo3 = self._create_repo_and_sync_with_remote(remote)
151
151
  self.assertEqual(get_content_summary(repo3.to_dict()), PYTHON_MD_FIXTURE_SUMMARY)
@@ -1,5 +1,6 @@
1
1
  # coding=utf-8
2
2
  """Tests that sync python plugin repositories."""
3
+ import pytest
3
4
  import unittest
4
5
 
5
6
  from pulp_smash import config
@@ -640,6 +641,52 @@ class PlatformExcludeTestCase(unittest.TestCase):
640
641
  )
641
642
 
642
643
 
644
+ @pytest.mark.parallel
645
+ def test_proxy_sync(
646
+ python_repo,
647
+ python_repo_api_client,
648
+ python_remote_factory,
649
+ python_content_api_client,
650
+ http_proxy,
651
+ ):
652
+ """Test syncing with a proxy."""
653
+ body = gen_python_remote(proxy_url=http_proxy.proxy_url)
654
+ remote = python_remote_factory(**body)
655
+ sync_resp = python_repo_api_client.sync(python_repo.pulp_href, {"remote": remote.pulp_href})
656
+ monitor_task(sync_resp.task)
657
+
658
+ repo = python_repo_api_client.read(python_repo.pulp_href)
659
+ assert repo.latest_version_href[-2] == "1"
660
+
661
+ content_resp = python_content_api_client.list(repository_version=repo.latest_version_href)
662
+ assert content_resp.count == 2
663
+
664
+
665
+ @pytest.mark.parallel
666
+ def test_proxy_auth_sync(
667
+ python_repo,
668
+ python_repo_api_client,
669
+ python_remote_factory,
670
+ python_content_api_client,
671
+ http_proxy_with_auth,
672
+ ):
673
+ """Test syncing with a proxy with auth."""
674
+ body = gen_python_remote(
675
+ proxy_url=http_proxy_with_auth.proxy_url,
676
+ proxy_username=http_proxy_with_auth.username,
677
+ proxy_password=http_proxy_with_auth.password,
678
+ )
679
+ remote = python_remote_factory(**body)
680
+ sync_resp = python_repo_api_client.sync(python_repo.pulp_href, {"remote": remote.pulp_href})
681
+ monitor_task(sync_resp.task)
682
+
683
+ repo = python_repo_api_client.read(python_repo.pulp_href)
684
+ assert repo.latest_version_href[-2] == "1"
685
+
686
+ content_resp = python_content_api_client.list(repository_version=repo.latest_version_href)
687
+ assert content_resp.count == 2
688
+
689
+
643
690
  def sync_to_remote(self, body, create=False, mirror=False):
644
691
  """Takes a body and creates/updates a remote object, then it performs a sync"""
645
692
  if create:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pulp-python
3
- Version: 3.7.3
3
+ Version: 3.9.0
4
4
  Summary: pulp-python plugin for the Pulp Project
5
5
  Home-page: https://www.pulpproject.org
6
6
  Author: Pulp Project Developers
@@ -0,0 +1,4 @@
1
+ pulpcore<3.25,>=3.22.0
2
+ pkginfo<1.9.7,>=1.8.2
3
+ bandersnatch<6.2,>=6.1
4
+ pypi-simple<1.0.0,>=0.9.0
@@ -16,11 +16,12 @@ ignore = [
16
16
  "dev_requirements.txt",
17
17
  "doc_requirements.txt",
18
18
  "docs/**",
19
- "flake8.cfg",
20
19
  "template_config.yml",
21
20
  ".travis/**",
22
21
  ".travis.yml",
23
22
  "shelf_reader-0.1-py2-none-any.whl",
24
23
  ".github/**",
25
24
  ".ci/**",
25
+ "lint_requirements.txt",
26
+ ".flake8",
26
27
  ]
@@ -0,0 +1,4 @@
1
+ pulpcore>=3.22.0,<3.25
2
+ pkginfo>=1.8.2,<1.9.7
3
+ bandersnatch>=6.1,<6.2
4
+ pypi-simple>=0.9.0,<1.0.0
@@ -10,7 +10,7 @@ with open("README.md") as f:
10
10
 
11
11
  setup(
12
12
  name="pulp-python",
13
- version="3.7.3",
13
+ version="3.9.0",
14
14
  description="pulp-python plugin for the Pulp Project",
15
15
  long_description=long_description,
16
16
  long_description_content_type="text/markdown",
@@ -1,5 +0,0 @@
1
- pulpcore<3.25,>=3.17.0
2
- pkginfo~=1.8.2
3
- packaging~=21.3
4
- bandersnatch<5.4,>=5.0.0
5
- pypi-simple~=0.9.0
@@ -1,5 +0,0 @@
1
- pulpcore>=3.17.0,<3.25
2
- pkginfo~=1.8.2
3
- packaging~=21.3
4
- bandersnatch>=5.0.0,<5.4
5
- pypi-simple~=0.9.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes