devpi-server 6.14.0__tar.gz → 6.15.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 (132) hide show
  1. {devpi-server-6.14.0 → devpi-server-6.15.0}/CHANGELOG +10 -0
  2. {devpi-server-6.14.0 → devpi-server-6.15.0}/MANIFEST.in +3 -1
  3. {devpi-server-6.14.0 → devpi-server-6.15.0}/PKG-INFO +11 -22
  4. devpi-server-6.15.0/devpi_server/__init__.py +1 -0
  5. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/config.py +28 -2
  6. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/hookspecs.py +6 -0
  7. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/mirror.py +5 -4
  8. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/model.py +2 -1
  9. devpi-server-6.15.0/devpi_server/py.typed +1 -0
  10. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/view_auth.py +15 -3
  11. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server.egg-info/PKG-INFO +11 -22
  12. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server.egg-info/SOURCES.txt +2 -0
  13. {devpi-server-6.14.0 → devpi-server-6.15.0}/pyproject.toml +79 -0
  14. {devpi-server-6.14.0 → devpi-server-6.15.0}/setup.py +1 -1
  15. devpi-server-6.15.0/test_devpi_server/py.typed +1 -0
  16. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_main.py +10 -2
  17. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_model.py +2 -2
  18. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_view_auth.py +18 -8
  19. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_views.py +6 -6
  20. {devpi-server-6.14.0 → devpi-server-6.15.0}/tox.ini +1 -1
  21. devpi-server-6.14.0/devpi_server/__init__.py +0 -1
  22. {devpi-server-6.14.0 → devpi-server-6.15.0}/.flake8 +0 -0
  23. {devpi-server-6.14.0 → devpi-server-6.15.0}/AUTHORS +0 -0
  24. {devpi-server-6.14.0 → devpi-server-6.15.0}/LICENSE +0 -0
  25. {devpi-server-6.14.0 → devpi-server-6.15.0}/README.rst +0 -0
  26. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/__main__.py +0 -0
  27. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/auth.py +0 -0
  28. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/auth_basic.py +0 -0
  29. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/auth_devpi.py +0 -0
  30. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/__init__.py +0 -0
  31. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/crontab.template +0 -0
  32. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/devpi.service.template +0 -0
  33. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/launchd-macos.txt.template +0 -0
  34. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/nginx-devpi-caching-http.conf.template +0 -0
  35. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/nginx-devpi-caching-location.conf.template +0 -0
  36. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/nginx-devpi-caching-proxy.conf.template +0 -0
  37. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/nginx-devpi-caching-server.conf.template +0 -0
  38. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/nginx-devpi.conf.template +0 -0
  39. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/supervisor-devpi.conf.template +0 -0
  40. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/supervisord.conf.template +0 -0
  41. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/cfg/windows-service.txt.template +0 -0
  42. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/exceptions.py +0 -0
  43. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/filestore.py +0 -0
  44. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/filestore_fs.py +0 -0
  45. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/fileutil.py +0 -0
  46. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/fsck.py +0 -0
  47. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/genconfig.py +0 -0
  48. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/importexport.py +0 -0
  49. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/init.py +0 -0
  50. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/interfaces.py +0 -0
  51. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/keyfs.py +0 -0
  52. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/keyfs_sqlite.py +0 -0
  53. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/keyfs_sqlite_fs.py +0 -0
  54. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/keyfs_types.py +0 -0
  55. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/log.py +0 -0
  56. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/main.py +0 -0
  57. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/markers.py +0 -0
  58. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/middleware.py +0 -0
  59. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/mythread.py +0 -0
  60. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/passwd.py +0 -0
  61. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/readonly.py +0 -0
  62. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/replica.py +0 -0
  63. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/sizeof.py +0 -0
  64. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/vendor/__init__.py +0 -0
  65. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/vendor/_pip.py +0 -0
  66. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server/views.py +0 -0
  67. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server.egg-info/dependency_links.txt +0 -0
  68. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server.egg-info/entry_points.txt +0 -0
  69. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server.egg-info/not-zip-safe +0 -0
  70. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server.egg-info/requires.txt +0 -0
  71. {devpi-server-6.14.0 → devpi-server-6.15.0}/devpi_server.egg-info/top_level.txt +0 -0
  72. {devpi-server-6.14.0 → devpi-server-6.15.0}/pytest_devpi_server/__init__.py +0 -0
  73. {devpi-server-6.14.0 → devpi-server-6.15.0}/setup.cfg +0 -0
  74. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/__init__.py +0 -0
  75. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/conftest.py +0 -0
  76. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/example.py +0 -0
  77. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/functional.py +0 -0
  78. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/badindexname/dataindex.json +0 -0
  79. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/badusername/dataindex.json +0 -0
  80. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/basescycle/dataindex.json +0 -0
  81. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/createdmodified/dataindex.json +0 -0
  82. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/deletedbase/dataindex.json +0 -0
  83. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/mirrordata/dataindex.json +0 -0
  84. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/mirrordata/root/pypi/dddttt/0.1.dev1/dddttt-0.1.dev1.tar.gz +0 -0
  85. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/modifiedpypi/dataindex.json +0 -0
  86. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/nocreatedmodified/dataindex.json +0 -0
  87. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/normalization/dataindex.json +0 -0
  88. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/normalization/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  89. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/normalization_merge/dataindex.json +0 -0
  90. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello-pkg/hello.pkg-1.1.tar.gz +0 -0
  91. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  92. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/norootpypi/dataindex.json +0 -0
  93. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/nouser/dataindex.json +0 -0
  94. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/removedindexplugin/dataindex.json +0 -0
  95. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/removedindexplugin/user/dev/pkg/pkg-1.0.tar.gz +0 -0
  96. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/dataindex.json +0 -0
  97. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/0.9/hello-0.9.tar.gz +0 -0
  98. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/sha256=ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73/hello-0.9.tar.gz.toxresult0 +0 -0
  99. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/importexportdata/toxresult_upload_default/dataindex.json +0 -0
  100. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/plugin.py +0 -0
  101. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/reqmock.py +0 -0
  102. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/simpypi.py +0 -0
  103. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_auth.py +0 -0
  104. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_authcheck.py +0 -0
  105. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_config.py +0 -0
  106. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_conftest.py +0 -0
  107. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_filestore.py +0 -0
  108. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_fileutil.py +0 -0
  109. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_fsck.py +0 -0
  110. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_genconfig.py +0 -0
  111. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_importexport.py +0 -0
  112. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_keyfs.py +0 -0
  113. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_keyfs_sqlite_fs.py +0 -0
  114. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_log.py +0 -0
  115. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_mirror.py +0 -0
  116. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_mirror_no_project_list.py +0 -0
  117. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_mythread.py +0 -0
  118. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_nginx.py +0 -0
  119. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_nginx_replica.py +0 -0
  120. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_permissions.py +0 -0
  121. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_readonly.py +0 -0
  122. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_replica.py +0 -0
  123. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_replica_functional.py +0 -0
  124. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_reqmock.py +0 -0
  125. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_stage_customizer.py +0 -0
  126. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_streaming.py +0 -0
  127. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_streaming_nginx.py +0 -0
  128. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_streaming_replica.py +0 -0
  129. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_streaming_replica_nginx.py +0 -0
  130. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_views_patch.py +0 -0
  131. {devpi-server-6.14.0 → devpi-server-6.15.0}/test_devpi_server/test_views_push_external.py +0 -0
  132. {devpi-server-6.14.0 → devpi-server-6.15.0}/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.15.0 (2025-05-18)
6
+ ===================
7
+
8
+ Features
9
+ --------
10
+
11
+ - Add ``--connection-limit`` option to devpi-server passed on to waitress.
12
+
13
+
14
+
5
15
  6.14.0 (2024-10-16)
6
16
  ===================
7
17
 
@@ -1,4 +1,6 @@
1
1
  include .flake8 CHANGELOG *.ini *.rst LICENSE
2
- recursive-include devpi_server *.template
2
+ recursive-include devpi_server py.typed
3
+ recursive-include devpi_server *.template
4
+ recursive-include test_devpi_server py.typed
3
5
  recursive-include test_devpi_server *.py
4
6
  recursive-include test_devpi_server/importexportdata *.gz* *.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devpi-server
3
- Version: 6.14.0
3
+ Version: 6.15.0
4
4
  Summary: devpi-server: reliable private and pypi.org caching server
5
5
  Home-page: https://devpi.net
6
6
  Maintainer: Florian Schulze
@@ -105,6 +105,16 @@ Changelog
105
105
 
106
106
  .. towncrier release notes start
107
107
 
108
+ 6.15.0 (2025-05-18)
109
+ ===================
110
+
111
+ Features
112
+ --------
113
+
114
+ - Add ``--connection-limit`` option to devpi-server passed on to waitress.
115
+
116
+
117
+
108
118
  6.14.0 (2024-10-16)
109
119
  ===================
110
120
 
@@ -193,24 +203,3 @@ Bug Fixes
193
203
  - Return json data if toxresult upload is forbidden.
194
204
 
195
205
 
196
-
197
- 6.11.0 (2024-04-20)
198
- ===================
199
-
200
- Features
201
- --------
202
-
203
- - The ``devpi-fsck`` script now returns an error code when there have been missing files or checksum errors.
204
-
205
- - Fix #983: Add plugin hook for mirror authentication header.
206
-
207
-
208
-
209
- Bug Fixes
210
- ---------
211
-
212
- - Preserve last modified of docs and toxresults during export/import.
213
-
214
- - Fix #1033: Use ``int`` for ``--mirror-cache-expiry`` to fix value of ``proxy_cache_valid`` in nginx caching config.
215
-
216
-
@@ -0,0 +1 @@
1
+ __version__ = '6.15.0'
@@ -56,6 +56,24 @@ def get_pluginmanager(load_entrypoints=True):
56
56
  return pm
57
57
 
58
58
 
59
+ def traced_pluggy_call(hook, **caller_kwargs):
60
+ firstresult = hook.spec.opts.get("firstresult", False) if hook.spec else False
61
+ results = []
62
+ plugin_names = []
63
+ hookimpls = hook._hookimpls if hasattr(hook, '_hookimpls') else hook.get_hookimpls()
64
+ for hook_impl in reversed(hookimpls):
65
+ args = [caller_kwargs[argname] for argname in hook_impl.argnames]
66
+ res = hook_impl.function(*args)
67
+ if res is not None:
68
+ results.append(res)
69
+ plugin_names.append(hook_impl.plugin_name)
70
+ if firstresult:
71
+ break
72
+ if firstresult:
73
+ results = results[0] if results else None
74
+ return (results, plugin_names)
75
+
76
+
59
77
  def add_help_option(parser, pluginmanager):
60
78
  parser.addoption(
61
79
  "-h", "--help",
@@ -155,6 +173,11 @@ def add_web_options(parser, pluginmanager):
155
173
  default=50,
156
174
  help="number of threads to start for serving clients.")
157
175
 
176
+ parser.addoption(
177
+ "--connection-limit", type=int,
178
+ default=100,
179
+ help="maximum number of simultaneous client connections.")
180
+
158
181
  parser.addoption(
159
182
  "--trusted-proxy", type=str,
160
183
  help="IP address of proxy we trust. See waitress documentation.")
@@ -446,8 +469,9 @@ def find_config_file():
446
469
  config_files.append(config_file)
447
470
  if len(config_files) > 1:
448
471
  log.warning("Multiple configuration files found:\n%s", "\n".join(config_files))
449
- if len(config_files):
450
- return config_files[-1]
472
+ if not config_files:
473
+ return None
474
+ return config_files[-1]
451
475
 
452
476
 
453
477
  class InvalidConfigError(ValueError):
@@ -680,6 +704,8 @@ class Config(object):
680
704
  kwargs["trusted_proxy_count"] = self.args.trusted_proxy_count
681
705
  if self.args.trusted_proxy_headers is not None:
682
706
  kwargs["trusted_proxy_headers"] = self.args.trusted_proxy_headers
707
+ if self.args.connection_limit is not None:
708
+ kwargs["connection_limit"] = self.args.connection_limit
683
709
  return dict(kwargs=kwargs, addresses=addresses)
684
710
 
685
711
  @cached_property
@@ -158,6 +158,12 @@ def devpiserver_get_identity(request, credentials):
158
158
  """
159
159
 
160
160
 
161
+ @hookspec
162
+ def devpiserver_identity_loaded(request, credential_plugin_name, identity_plugin_name, identity):
163
+ """ called when identity is loaded for a request.
164
+ """
165
+
166
+
161
167
  @hookspec(warn_on_impl=DeprecationWarning("Use new devpiserver_auth_request hook instead"))
162
168
  def devpiserver_auth_user(userdict, username, password):
163
169
  """return dict containing authentication validation results.
@@ -120,9 +120,9 @@ class IndexParser:
120
120
  appear later are ignored.
121
121
  """
122
122
  entry = self.basename2link.get(newlink.basename)
123
- if entry is None or not entry.hash_spec and (newlink.hash_spec or (
123
+ if entry is None or (not entry.hash_spec and (newlink.hash_spec or (
124
124
  not entry.requires_python and newlink.requires_python
125
- )):
125
+ ))):
126
126
  self.basename2link[newlink.basename] = newlink
127
127
  threadlog.debug("indexparser: adding link %s", newlink)
128
128
  else:
@@ -324,9 +324,9 @@ class MirrorStage(BaseStage):
324
324
  if key == "mirror_cache_expiry":
325
325
  try:
326
326
  value = int(value)
327
- except (TypeError, ValueError):
327
+ except (TypeError, ValueError) as e:
328
328
  raise self.InvalidIndexconfig([
329
- "'mirror_cache_expiry' option must be an integer"])
329
+ "'mirror_cache_expiry' option must be an integer"]) from e
330
330
  return value
331
331
  if key == "mirror_no_project_list":
332
332
  return ensure_boolean(value)
@@ -334,6 +334,7 @@ class MirrorStage(BaseStage):
334
334
  return ensure_boolean(value)
335
335
  if key in ("custom_data", "description", "mirror_web_url_fmt", "title"):
336
336
  return value
337
+ return None
337
338
 
338
339
  def delete(self):
339
340
  # delete all projects on this index
@@ -1770,7 +1770,8 @@ class SimplelinkMeta:
1770
1770
  __slots__ = (
1771
1771
  '__basename', '__cmpval', '__ext', '__hash_spec',
1772
1772
  '__name', '__path', '__url', '__version',
1773
- 'key', 'href', 'require_python', 'yanked')
1773
+ 'href', 'key', 'require_python', 'yanked',
1774
+ )
1774
1775
 
1775
1776
  def __init__(self, link_info):
1776
1777
  self.__basename = notset
@@ -0,0 +1 @@
1
+ partial
@@ -4,6 +4,7 @@ from devpi_common.types import ensure_unicode
4
4
  from devpi_common.validation import normalize_name
5
5
  from devpi_server.auth import Auth
6
6
  from devpi_server.config import hookimpl
7
+ from devpi_server.config import traced_pluggy_call
7
8
  from devpi_server.views import abort
8
9
  from devpi_server.model import BaseStage
9
10
  from devpi_server.model import UpstreamError
@@ -218,12 +219,23 @@ class DevpiSecurityPolicy:
218
219
  return [('WWW-Authenticate', 'Basic realm="%s"' % self.realm)]
219
220
 
220
221
  def _get_credentials(self, request):
221
- return self.hook.devpiserver_get_credentials(request=request)
222
+ return traced_pluggy_call(
223
+ self.hook.devpiserver_get_credentials,
224
+ request=request)
222
225
 
223
226
  def load_identity(self, request):
224
- credentials = self._get_credentials(request)
225
- return self.hook.devpiserver_get_identity(
227
+ (credentials, plugin_names) = self._get_credentials(request)
228
+ credential_plugin_name = plugin_names[0] if plugin_names else None
229
+ (result, plugin_names) = traced_pluggy_call(
230
+ self.hook.devpiserver_get_identity,
226
231
  request=request, credentials=credentials)
232
+ identity_plugin_name = plugin_names[0] if plugin_names else None
233
+ self.hook.devpiserver_identity_loaded(
234
+ request=request,
235
+ credential_plugin_name=credential_plugin_name,
236
+ identity_plugin_name=identity_plugin_name,
237
+ identity=result)
238
+ return result
227
239
 
228
240
  def identity(self, request):
229
241
  return self.identity_cache.get_or_create(request)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devpi-server
3
- Version: 6.14.0
3
+ Version: 6.15.0
4
4
  Summary: devpi-server: reliable private and pypi.org caching server
5
5
  Home-page: https://devpi.net
6
6
  Maintainer: Florian Schulze
@@ -105,6 +105,16 @@ Changelog
105
105
 
106
106
  .. towncrier release notes start
107
107
 
108
+ 6.15.0 (2025-05-18)
109
+ ===================
110
+
111
+ Features
112
+ --------
113
+
114
+ - Add ``--connection-limit`` option to devpi-server passed on to waitress.
115
+
116
+
117
+
108
118
  6.14.0 (2024-10-16)
109
119
  ===================
110
120
 
@@ -193,24 +203,3 @@ Bug Fixes
193
203
  - Return json data if toxresult upload is forbidden.
194
204
 
195
205
 
196
-
197
- 6.11.0 (2024-04-20)
198
- ===================
199
-
200
- Features
201
- --------
202
-
203
- - The ``devpi-fsck`` script now returns an error code when there have been missing files or checksum errors.
204
-
205
- - Fix #983: Add plugin hook for mirror authentication header.
206
-
207
-
208
-
209
- Bug Fixes
210
- ---------
211
-
212
- - Preserve last modified of docs and toxresults during export/import.
213
-
214
- - Fix #1033: Use ``int`` for ``--mirror-cache-expiry`` to fix value of ``proxy_cache_valid`` in nginx caching config.
215
-
216
-
@@ -36,6 +36,7 @@ devpi_server/mirror.py
36
36
  devpi_server/model.py
37
37
  devpi_server/mythread.py
38
38
  devpi_server/passwd.py
39
+ devpi_server/py.typed
39
40
  devpi_server/readonly.py
40
41
  devpi_server/replica.py
41
42
  devpi_server/sizeof.py
@@ -68,6 +69,7 @@ test_devpi_server/conftest.py
68
69
  test_devpi_server/example.py
69
70
  test_devpi_server/functional.py
70
71
  test_devpi_server/plugin.py
72
+ test_devpi_server/py.typed
71
73
  test_devpi_server/reqmock.py
72
74
  test_devpi_server/simpypi.py
73
75
  test_devpi_server/test_auth.py
@@ -2,6 +2,85 @@
2
2
  extend = "../pyproject.toml"
3
3
 
4
4
 
5
+ [tool.ruff.format]
6
+ exclude = [
7
+ "devpi_server/__init__.py",
8
+ "devpi_server/auth.py",
9
+ "devpi_server/auth_basic.py",
10
+ "devpi_server/auth_devpi.py",
11
+ "devpi_server/config.py",
12
+ "devpi_server/exceptions.py",
13
+ "devpi_server/filestore.py",
14
+ "devpi_server/filestore_fs.py",
15
+ "devpi_server/fileutil.py",
16
+ "devpi_server/fsck.py",
17
+ "devpi_server/genconfig.py",
18
+ "devpi_server/hookspecs.py",
19
+ "devpi_server/importexport.py",
20
+ "devpi_server/init.py",
21
+ "devpi_server/interfaces.py",
22
+ "devpi_server/keyfs.py",
23
+ "devpi_server/keyfs_sqlite.py",
24
+ "devpi_server/keyfs_sqlite_fs.py",
25
+ "devpi_server/keyfs_types.py",
26
+ "devpi_server/log.py",
27
+ "devpi_server/main.py",
28
+ "devpi_server/markers.py",
29
+ "devpi_server/middleware.py",
30
+ "devpi_server/mirror.py",
31
+ "devpi_server/model.py",
32
+ "devpi_server/mythread.py",
33
+ "devpi_server/passwd.py",
34
+ "devpi_server/readonly.py",
35
+ "devpi_server/replica.py",
36
+ "devpi_server/sizeof.py",
37
+ "devpi_server/vendor/_pip.py",
38
+ "devpi_server/view_auth.py",
39
+ "devpi_server/views.py",
40
+ "pytest_devpi_server/__init__.py",
41
+ "setup.py",
42
+ "test_devpi_server/conftest.py",
43
+ "test_devpi_server/example.py",
44
+ "test_devpi_server/functional.py",
45
+ "test_devpi_server/plugin.py",
46
+ "test_devpi_server/reqmock.py",
47
+ "test_devpi_server/simpypi.py",
48
+ "test_devpi_server/test_auth.py",
49
+ "test_devpi_server/test_authcheck.py",
50
+ "test_devpi_server/test_config.py",
51
+ "test_devpi_server/test_conftest.py",
52
+ "test_devpi_server/test_filestore.py",
53
+ "test_devpi_server/test_fileutil.py",
54
+ "test_devpi_server/test_fsck.py",
55
+ "test_devpi_server/test_genconfig.py",
56
+ "test_devpi_server/test_importexport.py",
57
+ "test_devpi_server/test_keyfs.py",
58
+ "test_devpi_server/test_keyfs_sqlite_fs.py",
59
+ "test_devpi_server/test_log.py",
60
+ "test_devpi_server/test_main.py",
61
+ "test_devpi_server/test_mirror.py",
62
+ "test_devpi_server/test_mirror_no_project_list.py",
63
+ "test_devpi_server/test_model.py",
64
+ "test_devpi_server/test_mythread.py",
65
+ "test_devpi_server/test_nginx.py",
66
+ "test_devpi_server/test_nginx_replica.py",
67
+ "test_devpi_server/test_permissions.py",
68
+ "test_devpi_server/test_replica.py",
69
+ "test_devpi_server/test_replica_functional.py",
70
+ "test_devpi_server/test_reqmock.py",
71
+ "test_devpi_server/test_stage_customizer.py",
72
+ "test_devpi_server/test_streaming.py",
73
+ "test_devpi_server/test_streaming_nginx.py",
74
+ "test_devpi_server/test_streaming_replica.py",
75
+ "test_devpi_server/test_streaming_replica_nginx.py",
76
+ "test_devpi_server/test_view_auth.py",
77
+ "test_devpi_server/test_views.py",
78
+ "test_devpi_server/test_views_patch.py",
79
+ "test_devpi_server/test_views_push_external.py",
80
+ "test_devpi_server/test_views_status.py",
81
+ ]
82
+
83
+
5
84
  [tool.ruff.lint]
6
85
  extend-ignore = [
7
86
  "A001", # cleanup later - variable name shadows builtin
@@ -59,7 +59,7 @@ if __name__ == "__main__":
59
59
  'Funding': 'https://github.com/sponsors/devpi',
60
60
  'Source Code': 'https://github.com/devpi/devpi'
61
61
  },
62
- version='6.14.0',
62
+ version='6.15.0',
63
63
  maintainer="Florian Schulze",
64
64
  maintainer_email="mail@pyfidelity.com",
65
65
  packages=[
@@ -0,0 +1 @@
1
+ partial
@@ -337,15 +337,23 @@ def test_init_server_directory(call_devpi_in_dir, tmpdir):
337
337
 
338
338
 
339
339
  def test_serve_threads(monkeypatch, tmpdir):
340
- def check_threads(app, host, port, threads, max_request_body_size):
340
+ def check_threads(app, host, port, threads, connection_limit, max_request_body_size):
341
341
  assert threads == 100
342
342
  monkeypatch.setattr("waitress.serve", check_threads)
343
343
  from devpi_server.main import main
344
344
  main(["devpi-server", "--threads", "100"])
345
345
 
346
346
 
347
+ def test_serve_connection_limit(monkeypatch, tmpdir):
348
+ def check_connection_limit(app, host, port, threads, connection_limit, max_request_body_size):
349
+ assert connection_limit == 200
350
+ monkeypatch.setattr("waitress.serve", check_connection_limit)
351
+ from devpi_server.main import main
352
+ main(["devpi-server", "--connection-limit", "200"])
353
+
354
+
347
355
  def test_serve_max_body(monkeypatch, tmpdir):
348
- def check_max_body(app, host, port, threads, max_request_body_size):
356
+ def check_max_body(app, host, port, threads, connection_limit, max_request_body_size):
349
357
  assert max_request_body_size == 42
350
358
  monkeypatch.setattr("waitress.serve", check_max_body)
351
359
  from devpi_server.main import main
@@ -1513,10 +1513,10 @@ class TestUsers:
1513
1513
  model.create_user("user2", password="password")
1514
1514
  model.create_user("user3", password="password")
1515
1515
  newusers = model.get_usernames().difference(baselist)
1516
- assert newusers == set("user1 user2 user3".split())
1516
+ assert newusers == {"user1", "user2", "user3"}
1517
1517
  model.get_user("user3").delete()
1518
1518
  newusers = model.get_usernames().difference(baselist)
1519
- assert newusers == set("user1 user2".split())
1519
+ assert newusers == {"user1", "user2"}
1520
1520
 
1521
1521
  def test_server_passwd(self, model, monkeypatch):
1522
1522
  monkeypatch.setattr(getpass, "getpass", lambda x: "123")
@@ -12,6 +12,8 @@ class TestCredentialPlugin:
12
12
  @pytest.fixture
13
13
  def plugin(self):
14
14
  class Plugin:
15
+ __name__ = 'TestCredentialPlugin'
16
+
15
17
  @hookimpl
16
18
  def devpiserver_get_credentials(self, request): # noqa: ARG002
17
19
  return self.results.pop()
@@ -25,18 +27,20 @@ class TestCredentialPlugin:
25
27
  def test_credential_plugin_no_credentials(self, blank_request, dsp, plugin):
26
28
  plugin.results = [None]
27
29
  request = blank_request()
28
- assert dsp._get_credentials(request) is None
30
+ assert dsp._get_credentials(request) == (None, [])
29
31
 
30
32
  def test_credential_plugin_got_credentials(self, blank_request, dsp, plugin):
31
33
  plugin.results = [('foo', 'bar')]
32
34
  request = blank_request()
33
- assert dsp._get_credentials(request) == ('foo', 'bar')
35
+ assert dsp._get_credentials(request) == (('foo', 'bar'), ['TestCredentialPlugin'])
34
36
 
35
37
 
36
38
  class TestCredentialPlugins:
37
39
  @pytest.fixture
38
40
  def plugin1(self):
39
41
  class Plugin:
42
+ __name__ = 'TestCredentialPlugin1'
43
+
40
44
  @hookimpl
41
45
  def devpiserver_get_credentials(self, request): # noqa: ARG002
42
46
  return self.results.pop()
@@ -45,6 +49,8 @@ class TestCredentialPlugins:
45
49
  @pytest.fixture
46
50
  def plugin2(self):
47
51
  class Plugin:
52
+ __name__ = 'TestCredentialPlugin2'
53
+
48
54
  @hookimpl
49
55
  def devpiserver_get_credentials(self, request): # noqa: ARG002
50
56
  return self.results.pop()
@@ -62,32 +68,36 @@ class TestCredentialPlugins:
62
68
  plugin1.results = [None]
63
69
  plugin2.results = [None]
64
70
  request = blank_request()
65
- assert dsp._get_credentials(request) is None
71
+ assert dsp._get_credentials(request) == (None, [])
66
72
 
67
73
  def test_credential_plugins_got_one_credential(self, blank_request, dsp, plugin1, plugin2):
68
74
  plugin1.results = [('foo', 'bar')]
69
75
  plugin2.results = [None]
70
76
  request = blank_request()
71
- assert dsp._get_credentials(request) == ('foo', 'bar')
77
+ assert dsp._get_credentials(request) == (('foo', 'bar'), ['TestCredentialPlugin1'])
72
78
 
73
79
  def test_credential_plugins_got_another_credential(self, blank_request, dsp, plugin1, plugin2):
74
80
  plugin1.results = [None]
75
81
  plugin2.results = [('foo', 'bar')]
76
82
  request = blank_request()
77
- assert dsp._get_credentials(request) == ('foo', 'bar')
83
+ assert dsp._get_credentials(request) == (('foo', 'bar'), ['TestCredentialPlugin2'])
78
84
 
79
85
  def test_credential_plugins_got_two_credential(self, blank_request, dsp, plugin1, plugin2):
80
86
  plugin1.results = [('ham', 'egg')]
81
87
  plugin2.results = [('foo', 'bar')]
82
88
  request = blank_request()
83
89
  # one of them wins, depends on entry point order and is undefined
84
- assert dsp._get_credentials(request) in [('ham', 'egg'), ('foo', 'bar')]
90
+ assert dsp._get_credentials(request) in [
91
+ (('ham', 'egg'), ['TestCredentialPlugin1']),
92
+ (('foo', 'bar'), ['TestCredentialPlugin2'])]
85
93
 
86
94
 
87
95
  class TestHeaderCredentialPlugin:
88
96
  @pytest.fixture
89
97
  def plugin(self):
90
98
  class Plugin:
99
+ __name__ = 'TestHeaderCredentialPlugin'
100
+
91
101
  @hookimpl
92
102
  def devpiserver_get_credentials(self, request):
93
103
  if 'X-Devpi-User' in request.headers:
@@ -101,13 +111,13 @@ class TestHeaderCredentialPlugin:
101
111
  @pytest.mark.usefixtures("plugin")
102
112
  def test_credential_plugin_no_credentials(self, blank_request, dsp):
103
113
  request = blank_request()
104
- assert dsp._get_credentials(request) is None
114
+ assert dsp._get_credentials(request) == (None, [])
105
115
 
106
116
  @pytest.mark.usefixtures("plugin")
107
117
  def test_credential_plugin_got_credentials(self, blank_request, dsp):
108
118
  request = blank_request()
109
119
  request.headers['X-Devpi-User'] = 'foo'
110
- assert dsp._get_credentials(request) == ('foo', '')
120
+ assert dsp._get_credentials(request) == (('foo', ''), ['TestHeaderCredentialPlugin'])
111
121
 
112
122
 
113
123
  class TestDevpiSecurityPolicy:
@@ -1980,7 +1980,7 @@ def test_delete_volatile_force(mapp, testapp):
1980
1980
 
1981
1981
 
1982
1982
  @pytest.mark.nomocking
1983
- def test_mirror_use_external_urls(mapp, simpypi, testapp, xom):
1983
+ def test_mirror_use_external_urls(mapp, simpypi, testapp):
1984
1984
  indexconfig = dict(
1985
1985
  type="mirror",
1986
1986
  mirror_url=simpypi.simpleurl,
@@ -1989,18 +1989,18 @@ def test_mirror_use_external_urls(mapp, simpypi, testapp, xom):
1989
1989
  volatile=True)
1990
1990
  api = mapp.create_and_use(indexconfig=indexconfig)
1991
1991
  name = "pytest"
1992
- pkgver = "%s-2.6.zip" % name
1992
+ pkgver = f"{name}-2.6.zip"
1993
1993
  simpypi.add_release(name, pkgver=pkgver)
1994
- simpypi.add_file('/%s/%s' % (name, pkgver), b'123')
1995
- r = testapp.get(api.index + '/+simple/%s' % name)
1994
+ simpypi.add_file(f'/{name}/{pkgver}', b'123')
1995
+ r = testapp.get(f'{api.index}/+simple/{name}')
1996
1996
  (link,) = sorted(
1997
- x.get('href').replace('../../', '/%s/' % api.stagename)
1997
+ x.get('href').replace('../../', f'/{api.stagename}/')
1998
1998
  for x in getlinks(r.text))
1999
1999
  path = link[1:]
2000
2000
  assert '2.6' in path
2001
2001
  # we should get a redirect to the original URL
2002
2002
  r = testapp.xget(302, link, follow=False)
2003
- assert r.location == "%s/pytest/pytest-2.6.zip" % simpypi.baseurl
2003
+ assert r.location == f"{simpypi.baseurl}/pytest/pytest-2.6.zip"
2004
2004
  with testapp.xom.keyfs.read_transaction():
2005
2005
  assert not getentry(testapp, path).file_exists()
2006
2006
  # now turn external urls off
@@ -18,7 +18,7 @@ deps=
18
18
  webtest
19
19
  pytest
20
20
  pytest-asyncio
21
- pytest-github-actions-annotate-failures
21
+ pytest-github-actions-annotate-failures!=0.3.0
22
22
  pytest-instafail
23
23
  pytest-timeout
24
24
  beautifulsoup4 != 4.12.1
@@ -1 +0,0 @@
1
- __version__ = '6.14.0'
File without changes
File without changes
File without changes
File without changes
File without changes