devpi-server 6.19.0__tar.gz → 6.19.1__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 (137) hide show
  1. {devpi_server-6.19.0 → devpi_server-6.19.1}/CHANGELOG +10 -0
  2. {devpi_server-6.19.0 → devpi_server-6.19.1}/CHANGELOG.short.rst +10 -9
  3. {devpi_server-6.19.0 → devpi_server-6.19.1}/PKG-INFO +13 -10
  4. devpi_server-6.19.1/devpi_server/__init__.py +1 -0
  5. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/mirror.py +0 -1
  6. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/model.py +0 -3
  7. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/view_auth.py +5 -1
  8. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server.egg-info/PKG-INFO +13 -10
  9. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server.egg-info/requires.txt +1 -0
  10. {devpi_server-6.19.0 → devpi_server-6.19.1}/pyproject.toml +2 -0
  11. devpi_server-6.19.1/test_devpi_server/__init__.py +0 -0
  12. devpi_server-6.19.1/test_devpi_server/py.typed +0 -0
  13. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_replica.py +119 -0
  14. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_stage_customizer.py +0 -11
  15. {devpi_server-6.19.0 → devpi_server-6.19.1}/tox.ini +1 -1
  16. devpi_server-6.19.0/devpi_server/__init__.py +0 -1
  17. devpi_server-6.19.0/devpi_server/py.typed +0 -1
  18. devpi_server-6.19.0/test_devpi_server/py.typed +0 -1
  19. {devpi_server-6.19.0 → devpi_server-6.19.1}/.flake8 +0 -0
  20. {devpi_server-6.19.0 → devpi_server-6.19.1}/LICENSE +0 -0
  21. {devpi_server-6.19.0 → devpi_server-6.19.1}/MANIFEST.in +0 -0
  22. {devpi_server-6.19.0 → devpi_server-6.19.1}/README.rst +0 -0
  23. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/__main__.py +0 -0
  24. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/auth.py +0 -0
  25. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/auth_basic.py +0 -0
  26. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/auth_devpi.py +0 -0
  27. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/__init__.py +0 -0
  28. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/crontab.template +0 -0
  29. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/devpi.service.template +0 -0
  30. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/launchd-macos.txt.template +0 -0
  31. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/nginx-devpi-caching-http.conf.template +0 -0
  32. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/nginx-devpi-caching-location.conf.template +0 -0
  33. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/nginx-devpi-caching-proxy.conf.template +0 -0
  34. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/nginx-devpi-caching-server.conf.template +0 -0
  35. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/nginx-devpi.conf.template +0 -0
  36. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/supervisor-devpi.conf.template +0 -0
  37. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/supervisord.conf.template +0 -0
  38. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/cfg/windows-service.txt.template +0 -0
  39. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/compat.py +0 -0
  40. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/config.py +0 -0
  41. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/exceptions.py +0 -0
  42. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/filestore.py +0 -0
  43. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/filestore_db.py +0 -0
  44. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/filestore_fs.py +0 -0
  45. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/filestore_fs_base.py +0 -0
  46. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/filestore_hash_hl.py +0 -0
  47. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/fileutil.py +0 -0
  48. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/fsck.py +0 -0
  49. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/genconfig.py +0 -0
  50. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/hookspecs.py +0 -0
  51. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/htmlpage.py +0 -0
  52. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/httpclient.py +0 -0
  53. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/importexport.py +0 -0
  54. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/init.py +0 -0
  55. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/interfaces.py +0 -0
  56. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/keyfs.py +0 -0
  57. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/keyfs_sqlite.py +0 -0
  58. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/keyfs_sqlite_fs.py +0 -0
  59. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/keyfs_types.py +0 -0
  60. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/log.py +0 -0
  61. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/main.py +0 -0
  62. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/markers.py +0 -0
  63. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/middleware.py +0 -0
  64. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/mythread.py +0 -0
  65. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/normalized.py +0 -0
  66. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/passwd.py +0 -0
  67. /devpi_server-6.19.0/test_devpi_server/__init__.py → /devpi_server-6.19.1/devpi_server/py.typed +0 -0
  68. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/readonly.py +0 -0
  69. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/replica.py +0 -0
  70. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/sizeof.py +0 -0
  71. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server/views.py +0 -0
  72. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server.egg-info/SOURCES.txt +0 -0
  73. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server.egg-info/dependency_links.txt +0 -0
  74. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server.egg-info/entry_points.txt +0 -0
  75. {devpi_server-6.19.0 → devpi_server-6.19.1}/devpi_server.egg-info/top_level.txt +0 -0
  76. {devpi_server-6.19.0 → devpi_server-6.19.1}/mypy.ini +0 -0
  77. {devpi_server-6.19.0 → devpi_server-6.19.1}/pytest_devpi_server/__init__.py +0 -0
  78. {devpi_server-6.19.0 → devpi_server-6.19.1}/setup.cfg +0 -0
  79. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/conftest.py +0 -0
  80. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/example.py +0 -0
  81. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/functional.py +0 -0
  82. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/badindexname/dataindex.json +0 -0
  83. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/badusername/dataindex.json +0 -0
  84. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/basescycle/dataindex.json +0 -0
  85. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/createdmodified/dataindex.json +0 -0
  86. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/deletedbase/dataindex.json +0 -0
  87. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/mirrordata/dataindex.json +0 -0
  88. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/mirrordata/root/pypi/dddttt/0.1.dev1/dddttt-0.1.dev1.tar.gz +0 -0
  89. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/modifiedpypi/dataindex.json +0 -0
  90. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/nocreatedmodified/dataindex.json +0 -0
  91. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/normalization/dataindex.json +0 -0
  92. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/normalization/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  93. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/normalization_merge/dataindex.json +0 -0
  94. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello-pkg/hello.pkg-1.1.tar.gz +0 -0
  95. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  96. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/norootpypi/dataindex.json +0 -0
  97. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/nouser/dataindex.json +0 -0
  98. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/removedindexplugin/dataindex.json +0 -0
  99. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/removedindexplugin/user/dev/pkg/pkg-1.0.tar.gz +0 -0
  100. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/toxresult_naming_scheme/dataindex.json +0 -0
  101. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/0.9/hello-0.9.tar.gz +0 -0
  102. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/sha256=ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73/hello-0.9.tar.gz.toxresult0 +0 -0
  103. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/importexportdata/toxresult_upload_default/dataindex.json +0 -0
  104. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/plugin.py +0 -0
  105. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/reqmock.py +0 -0
  106. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/simpypi.py +0 -0
  107. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_auth.py +0 -0
  108. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_authcheck.py +0 -0
  109. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_config.py +0 -0
  110. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_conftest.py +0 -0
  111. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_filestore.py +0 -0
  112. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_filestore_fs.py +0 -0
  113. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_fileutil.py +0 -0
  114. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_fsck.py +0 -0
  115. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_genconfig.py +0 -0
  116. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_importexport.py +0 -0
  117. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_keyfs.py +0 -0
  118. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_log.py +0 -0
  119. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_main.py +0 -0
  120. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_mirror.py +0 -0
  121. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_mirror_no_project_list.py +0 -0
  122. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_model.py +0 -0
  123. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_mythread.py +0 -0
  124. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_nginx.py +0 -0
  125. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_nginx_replica.py +0 -0
  126. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_permissions.py +0 -0
  127. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_readonly.py +0 -0
  128. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_replica_functional.py +0 -0
  129. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_streaming.py +0 -0
  130. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_streaming_nginx.py +0 -0
  131. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_streaming_replica.py +0 -0
  132. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_streaming_replica_nginx.py +0 -0
  133. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_view_auth.py +0 -0
  134. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_views.py +0 -0
  135. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_views_patch.py +0 -0
  136. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_views_push_external.py +0 -0
  137. {devpi_server-6.19.0 → devpi_server-6.19.1}/test_devpi_server/test_views_status.py +0 -0
@@ -2,6 +2,16 @@
2
2
 
3
3
  .. towncrier release notes start
4
4
 
5
+ 6.19.1 (2026-02-09)
6
+ ===================
7
+
8
+ Bug Fixes
9
+ ---------
10
+
11
+ - Pin setuptools as pyramid still requires pkg_resources.
12
+
13
+ - Always allow replicas to access deleted releases to get the proper ``410 Gone`` instead of ``403 Forbidden`` when ``devpi-lockdown`` is in use.
14
+
5
15
  6.19.0 (2026-02-06)
6
16
  ===================
7
17
 
@@ -9,6 +9,16 @@ Changelog
9
9
 
10
10
  .. towncrier release notes start
11
11
 
12
+ 6.19.1 (2026-02-09)
13
+ ===================
14
+
15
+ Bug Fixes
16
+ ---------
17
+
18
+ - Pin setuptools as pyramid still requires pkg_resources.
19
+
20
+ - Always allow replicas to access deleted releases to get the proper ``410 Gone`` instead of ``403 Forbidden`` when ``devpi-lockdown`` is in use.
21
+
12
22
  6.19.0 (2026-02-06)
13
23
  ===================
14
24
 
@@ -137,12 +147,3 @@ Bug Fixes
137
147
 
138
148
  - Keep original metadata_version in database.
139
149
 
140
-
141
- 6.15.0 (2025-05-18)
142
- ===================
143
-
144
- Features
145
- --------
146
-
147
- - Add ``--connection-limit`` option to devpi-server passed on to waitress.
148
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devpi-server
3
- Version: 6.19.0
3
+ Version: 6.19.1
4
4
  Summary: devpi-server: reliable, private, and pypi.org caching server
5
5
  Maintainer-email: Florian Schulze <mail@pyfidelity.com>
6
6
  License-Expression: MIT
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
25
26
  Classifier: Programming Language :: Python :: Implementation :: PyPy
26
27
  Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
27
28
  Classifier: Topic :: Internet :: WWW/HTTP
@@ -42,6 +43,7 @@ Requires-Dist: pluggy<2.0,>=0.6.0
42
43
  Requires-Dist: py>=1.4.23
43
44
  Requires-Dist: pyramid>=2
44
45
  Requires-Dist: repoze.lru>=0.6
46
+ Requires-Dist: setuptools<=81
45
47
  Requires-Dist: strenum; python_version < "3.11"
46
48
  Requires-Dist: strictyaml
47
49
  Requires-Dist: waitress>=1.0.1
@@ -122,6 +124,16 @@ Changelog
122
124
 
123
125
  .. towncrier release notes start
124
126
 
127
+ 6.19.1 (2026-02-09)
128
+ ===================
129
+
130
+ Bug Fixes
131
+ ---------
132
+
133
+ - Pin setuptools as pyramid still requires pkg_resources.
134
+
135
+ - Always allow replicas to access deleted releases to get the proper ``410 Gone`` instead of ``403 Forbidden`` when ``devpi-lockdown`` is in use.
136
+
125
137
  6.19.0 (2026-02-06)
126
138
  ===================
127
139
 
@@ -250,12 +262,3 @@ Bug Fixes
250
262
 
251
263
  - Keep original metadata_version in database.
252
264
 
253
-
254
- 6.15.0 (2025-05-18)
255
- ===================
256
-
257
- Features
258
- --------
259
-
260
- - Add ``--connection-limit`` option to devpi-server passed on to waitress.
261
-
@@ -0,0 +1 @@
1
+ __version__ = "6.19.1"
@@ -740,7 +740,6 @@ class MirrorStage(BaseStage):
740
740
  serial: int,
741
741
  etag: str | None,
742
742
  ) -> None:
743
- assert links != () # we don't store the old "Not Found" marker anymore
744
743
  assert isinstance(serial, int)
745
744
  assert project == normalize_name(project), project
746
745
  data: CacheLinks = {
@@ -546,9 +546,6 @@ class BaseStageCustomizer:
546
546
  principals = {':ANONYMOUS:'}
547
547
  else:
548
548
  principals = set(principals)
549
- if self.stage.xom.is_primary():
550
- # replicas always need to be able to download packages
551
- principals.add("+replica")
552
549
  # admins should always be able to read the packages
553
550
  if restrict_modify is None:
554
551
  principals.add("root")
@@ -6,6 +6,7 @@ from devpi_server.config import hookimpl
6
6
  from devpi_server.config import traced_pluggy_call
7
7
  from devpi_server.model import BaseStage
8
8
  from devpi_server.model import UpstreamError
9
+ from devpi_server.replica import REPLICA_USER_NAME
9
10
  from devpi_server.views import abort
10
11
  from pyramid.authorization import ACLHelper
11
12
  from pyramid.authorization import Allow
@@ -62,7 +63,10 @@ class RootFactory:
62
63
 
63
64
  @cached_property
64
65
  def __acl__(self):
65
- acl = [(Allow, Everyone, 'user_login')]
66
+ acl = [
67
+ (Allow, Everyone, "user_login"),
68
+ (Allow, REPLICA_USER_NAME, "pkg_read"),
69
+ ]
66
70
  if self.restrict_modify is None:
67
71
  acl.extend(
68
72
  [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devpi-server
3
- Version: 6.19.0
3
+ Version: 6.19.1
4
4
  Summary: devpi-server: reliable, private, and pypi.org caching server
5
5
  Maintainer-email: Florian Schulze <mail@pyfidelity.com>
6
6
  License-Expression: MIT
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
25
26
  Classifier: Programming Language :: Python :: Implementation :: PyPy
26
27
  Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
27
28
  Classifier: Topic :: Internet :: WWW/HTTP
@@ -42,6 +43,7 @@ Requires-Dist: pluggy<2.0,>=0.6.0
42
43
  Requires-Dist: py>=1.4.23
43
44
  Requires-Dist: pyramid>=2
44
45
  Requires-Dist: repoze.lru>=0.6
46
+ Requires-Dist: setuptools<=81
45
47
  Requires-Dist: strenum; python_version < "3.11"
46
48
  Requires-Dist: strictyaml
47
49
  Requires-Dist: waitress>=1.0.1
@@ -122,6 +124,16 @@ Changelog
122
124
 
123
125
  .. towncrier release notes start
124
126
 
127
+ 6.19.1 (2026-02-09)
128
+ ===================
129
+
130
+ Bug Fixes
131
+ ---------
132
+
133
+ - Pin setuptools as pyramid still requires pkg_resources.
134
+
135
+ - Always allow replicas to access deleted releases to get the proper ``410 Gone`` instead of ``403 Forbidden`` when ``devpi-lockdown`` is in use.
136
+
125
137
  6.19.0 (2026-02-06)
126
138
  ===================
127
139
 
@@ -250,12 +262,3 @@ Bug Fixes
250
262
 
251
263
  - Keep original metadata_version in database.
252
264
 
253
-
254
- 6.15.0 (2025-05-18)
255
- ===================
256
-
257
- Features
258
- --------
259
-
260
- - Add ``--connection-limit`` option to devpi-server passed on to waitress.
261
-
@@ -11,6 +11,7 @@ pluggy<2.0,>=0.6.0
11
11
  py>=1.4.23
12
12
  pyramid>=2
13
13
  repoze.lru>=0.6
14
+ setuptools<=81
14
15
  strictyaml
15
16
  waitress>=1.0.1
16
17
  ruamel.yaml
@@ -22,6 +22,7 @@ dependencies = [
22
22
  "py>=1.4.23",
23
23
  "pyramid>=2",
24
24
  "repoze.lru>=0.6",
25
+ "setuptools<=81", # removed pkg_resources required by pyramid
25
26
  "strenum;python_version<'3.11'",
26
27
  "strictyaml",
27
28
  "waitress>=1.0.1",
@@ -46,6 +47,7 @@ classifiers = [
46
47
  "Programming Language :: Python :: 3.11",
47
48
  "Programming Language :: Python :: 3.12",
48
49
  "Programming Language :: Python :: 3.13",
50
+ "Programming Language :: Python :: 3.14",
49
51
  "Programming Language :: Python :: Implementation :: PyPy",
50
52
  "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
51
53
  "Topic :: Internet :: WWW/HTTP",
File without changes
File without changes
@@ -1305,6 +1305,125 @@ def test_replica_user_auth_before_other_plugins(makexom):
1305
1305
  auth._get_auth_status(replica.REPLICA_USER_NAME, '')
1306
1306
 
1307
1307
 
1308
+ def test_pkg_read_permission(makemapp, maketestapp, makexom):
1309
+ from devpi_common.types import ensure_unicode
1310
+ from devpi_server.config import hookimpl
1311
+ from devpi_server.model import ACLList
1312
+ from webob.headers import ResponseHeaders
1313
+ import itsdangerous
1314
+
1315
+ class Plugin:
1316
+ @hookimpl
1317
+ def devpiserver_indexconfig_defaults(self, index_type): # noqa: ARG002
1318
+ return {"acl_pkg_read": ACLList(["root"])}
1319
+
1320
+ @hookimpl
1321
+ def devpiserver_stage_get_principals_for_pkg_read(self, ixconfig):
1322
+ return ixconfig.get("acl_pkg_read", None)
1323
+
1324
+ plugin = Plugin()
1325
+ xom = makexom(plugins=[plugin])
1326
+ xom.config.nodeinfo["role"] = "primary"
1327
+ testapp = maketestapp(xom)
1328
+ mapp = makemapp(testapp)
1329
+ auth_serializer = itsdangerous.TimedSerializer(mapp.xom.config.get_replica_secret())
1330
+ uuid = "111"
1331
+ api = mapp.create_and_use("someuser/dev")
1332
+ mapp.upload_file_pypi("hello-1.0.tar.gz", b"content", "hello", "1.0")
1333
+ (path,) = mapp.get_release_paths("hello")
1334
+ # replica should be able to access the release
1335
+ testapp.xget(
1336
+ 200,
1337
+ "/+authcheck",
1338
+ headers=ResponseHeaders(
1339
+ {
1340
+ H_REPLICA_UUID: uuid,
1341
+ "X-Original-URI": path,
1342
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1343
+ }
1344
+ ),
1345
+ )
1346
+ testapp.xget(
1347
+ 200,
1348
+ path,
1349
+ headers=ResponseHeaders(
1350
+ {
1351
+ H_REPLICA_UUID: uuid,
1352
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1353
+ }
1354
+ ),
1355
+ )
1356
+ # after project deletion the file is marked as gone
1357
+ mapp.delete_project("hello")
1358
+ testapp.xget(
1359
+ 200,
1360
+ "/+authcheck",
1361
+ headers=ResponseHeaders(
1362
+ {
1363
+ H_REPLICA_UUID: uuid,
1364
+ "X-Original-URI": path,
1365
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1366
+ }
1367
+ ),
1368
+ )
1369
+ testapp.xget(
1370
+ 410,
1371
+ path,
1372
+ headers=ResponseHeaders(
1373
+ {
1374
+ H_REPLICA_UUID: uuid,
1375
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1376
+ }
1377
+ ),
1378
+ )
1379
+ # after index deletion the file is still marked as gone
1380
+ mapp.delete_index(api.stagename)
1381
+ testapp.xget(
1382
+ 200,
1383
+ "/+authcheck",
1384
+ headers=ResponseHeaders(
1385
+ {
1386
+ H_REPLICA_UUID: uuid,
1387
+ "X-Original-URI": path,
1388
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1389
+ }
1390
+ ),
1391
+ )
1392
+ testapp.xget(
1393
+ 410,
1394
+ path,
1395
+ headers=ResponseHeaders(
1396
+ {
1397
+ H_REPLICA_UUID: uuid,
1398
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1399
+ }
1400
+ ),
1401
+ )
1402
+ # also after user deletion
1403
+ mapp.delete_user(api.user)
1404
+ testapp.xget(
1405
+ 200,
1406
+ "/+authcheck",
1407
+ headers=ResponseHeaders(
1408
+ {
1409
+ H_REPLICA_UUID: uuid,
1410
+ "X-Original-URI": path,
1411
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1412
+ }
1413
+ ),
1414
+ )
1415
+ testapp.xget(
1416
+ 410,
1417
+ path,
1418
+ headers=ResponseHeaders(
1419
+ {
1420
+ H_REPLICA_UUID: uuid,
1421
+ "Authorization": f"Bearer {ensure_unicode(auth_serializer.dumps(uuid))}",
1422
+ }
1423
+ ),
1424
+ )
1425
+
1426
+
1308
1427
  class TestFileReplicationSharedData:
1309
1428
  @pytest.fixture
1310
1429
  def shared_data(self, replica_xom):
@@ -269,7 +269,6 @@ def test_package_filters(makemapp, maketestapp, makexom):
269
269
 
270
270
  def test_pkg_read_permission(makemapp, maketestapp, makexom):
271
271
  from devpi_server.model import ACLList
272
- from devpi_server.replica import REPLICA_USER_NAME
273
272
  from webob.headers import ResponseHeaders
274
273
  import json
275
274
 
@@ -341,16 +340,6 @@ def test_pkg_read_permission(makemapp, maketestapp, makexom):
341
340
  req = dict(name="hello", version="1.0", targetindex="otheruser/dev")
342
341
  r = testapp.push("/someuser/dev", json.dumps(req))
343
342
  assert r.status_code == 403
344
- with xom.keyfs.read_transaction():
345
- stage = xom.model.getstage(api1.stagename)
346
- # by default get_principals_for_pkg_read returns just the set principals
347
- assert stage.customizer.get_principals_for_pkg_read() == {
348
- 'root', 'someuser'}
349
- # but if the server acts as a primary for replicas,
350
- # then the special replica user is included
351
- xom.config.nodeinfo['role'] = 'primary'
352
- assert set(stage.customizer.get_principals_for_pkg_read()) == {
353
- 'root', 'someuser', REPLICA_USER_NAME}
354
343
 
355
344
 
356
345
  def test_sro_skip_plugin(makemapp, maketestapp, makexom, pypistage):
@@ -1,5 +1,5 @@
1
1
  [tox]
2
- envlist=py39{,-keyfs_sqlite,-hash_hl},py313,pypy3,flake8
2
+ envlist=py39{,-keyfs_sqlite,-hash_hl},py314,pypy3,flake8
3
3
 
4
4
 
5
5
  [devpisettings]
@@ -1 +0,0 @@
1
- __version__ = "6.19.0"
@@ -1 +0,0 @@
1
- partial
@@ -1 +0,0 @@
1
- partial
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes