devpi-server 6.15.0__tar.gz → 6.16.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 (136) hide show
  1. {devpi-server-6.15.0 → devpi_server-6.16.0}/.flake8 +2 -2
  2. {devpi-server-6.15.0 → devpi_server-6.16.0}/CHANGELOG +26 -0
  3. devpi_server-6.16.0/CHANGELOG.short.rst +116 -0
  4. {devpi-server-6.15.0 → devpi_server-6.16.0}/PKG-INFO +58 -35
  5. devpi_server-6.16.0/devpi_server/__init__.py +1 -0
  6. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/auth.py +10 -2
  7. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/config.py +4 -8
  8. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/filestore.py +43 -14
  9. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/importexport.py +15 -9
  10. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/interfaces.py +83 -14
  11. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/keyfs.py +188 -87
  12. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/keyfs_types.py +45 -4
  13. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/log.py +10 -5
  14. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/main.py +41 -17
  15. devpi_server-6.16.0/devpi_server/markers.py +34 -0
  16. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/middleware.py +4 -3
  17. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/mirror.py +28 -18
  18. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/model.py +158 -69
  19. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/mythread.py +21 -17
  20. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/readonly.py +25 -16
  21. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/replica.py +21 -22
  22. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/views.py +35 -68
  23. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server.egg-info/PKG-INFO +58 -35
  24. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server.egg-info/SOURCES.txt +2 -5
  25. devpi_server-6.16.0/mypy.ini +105 -0
  26. {devpi-server-6.15.0 → devpi_server-6.16.0}/pyproject.toml +119 -9
  27. devpi_server-6.16.0/setup.cfg +4 -0
  28. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/plugin.py +15 -19
  29. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/reqmock.py +36 -26
  30. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_filestore.py +22 -17
  31. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_importexport.py +46 -37
  32. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_keyfs.py +29 -15
  33. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_main.py +15 -24
  34. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_mirror.py +35 -16
  35. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_mirror_no_project_list.py +17 -17
  36. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_model.py +45 -53
  37. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_mythread.py +8 -0
  38. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_replica.py +11 -6
  39. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_views.py +28 -3
  40. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_views_push_external.py +77 -17
  41. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_views_status.py +5 -7
  42. {devpi-server-6.15.0 → devpi_server-6.16.0}/tox.ini +16 -7
  43. devpi-server-6.15.0/AUTHORS +0 -13
  44. devpi-server-6.15.0/devpi_server/__init__.py +0 -1
  45. devpi-server-6.15.0/devpi_server/markers.py +0 -14
  46. devpi-server-6.15.0/devpi_server.egg-info/not-zip-safe +0 -1
  47. devpi-server-6.15.0/setup.cfg +0 -7
  48. devpi-server-6.15.0/setup.py +0 -116
  49. devpi-server-6.15.0/test_devpi_server/test_reqmock.py +0 -48
  50. {devpi-server-6.15.0 → devpi_server-6.16.0}/LICENSE +0 -0
  51. {devpi-server-6.15.0 → devpi_server-6.16.0}/MANIFEST.in +0 -0
  52. {devpi-server-6.15.0 → devpi_server-6.16.0}/README.rst +0 -0
  53. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/__main__.py +0 -0
  54. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/auth_basic.py +0 -0
  55. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/auth_devpi.py +0 -0
  56. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/__init__.py +0 -0
  57. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/crontab.template +0 -0
  58. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/devpi.service.template +0 -0
  59. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/launchd-macos.txt.template +0 -0
  60. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-http.conf.template +0 -0
  61. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-location.conf.template +0 -0
  62. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-proxy.conf.template +0 -0
  63. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-server.conf.template +0 -0
  64. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi.conf.template +0 -0
  65. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/supervisor-devpi.conf.template +0 -0
  66. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/supervisord.conf.template +0 -0
  67. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/cfg/windows-service.txt.template +0 -0
  68. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/exceptions.py +0 -0
  69. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/filestore_fs.py +0 -0
  70. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/fileutil.py +0 -0
  71. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/fsck.py +0 -0
  72. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/genconfig.py +0 -0
  73. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/hookspecs.py +0 -0
  74. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/init.py +0 -0
  75. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/keyfs_sqlite.py +0 -0
  76. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/keyfs_sqlite_fs.py +0 -0
  77. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/passwd.py +0 -0
  78. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/py.typed +0 -0
  79. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/sizeof.py +0 -0
  80. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/vendor/__init__.py +0 -0
  81. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/vendor/_pip.py +0 -0
  82. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server/view_auth.py +0 -0
  83. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server.egg-info/dependency_links.txt +0 -0
  84. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server.egg-info/entry_points.txt +0 -0
  85. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server.egg-info/requires.txt +7 -7
  86. {devpi-server-6.15.0 → devpi_server-6.16.0}/devpi_server.egg-info/top_level.txt +0 -0
  87. {devpi-server-6.15.0 → devpi_server-6.16.0}/pytest_devpi_server/__init__.py +0 -0
  88. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/__init__.py +0 -0
  89. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/conftest.py +0 -0
  90. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/example.py +0 -0
  91. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/functional.py +0 -0
  92. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/badindexname/dataindex.json +0 -0
  93. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/badusername/dataindex.json +0 -0
  94. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/basescycle/dataindex.json +0 -0
  95. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/createdmodified/dataindex.json +0 -0
  96. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/deletedbase/dataindex.json +0 -0
  97. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/mirrordata/dataindex.json +0 -0
  98. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/mirrordata/root/pypi/dddttt/0.1.dev1/dddttt-0.1.dev1.tar.gz +0 -0
  99. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/modifiedpypi/dataindex.json +0 -0
  100. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/nocreatedmodified/dataindex.json +0 -0
  101. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization/dataindex.json +0 -0
  102. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  103. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization_merge/dataindex.json +0 -0
  104. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello-pkg/hello.pkg-1.1.tar.gz +0 -0
  105. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  106. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/norootpypi/dataindex.json +0 -0
  107. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/nouser/dataindex.json +0 -0
  108. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/removedindexplugin/dataindex.json +0 -0
  109. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/removedindexplugin/user/dev/pkg/pkg-1.0.tar.gz +0 -0
  110. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/dataindex.json +0 -0
  111. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/0.9/hello-0.9.tar.gz +0 -0
  112. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/sha256=ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73/hello-0.9.tar.gz.toxresult0 +0 -0
  113. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/toxresult_upload_default/dataindex.json +0 -0
  114. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/py.typed +0 -0
  115. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/simpypi.py +0 -0
  116. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_auth.py +0 -0
  117. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_authcheck.py +0 -0
  118. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_config.py +0 -0
  119. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_conftest.py +0 -0
  120. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_fileutil.py +0 -0
  121. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_fsck.py +0 -0
  122. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_genconfig.py +0 -0
  123. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_keyfs_sqlite_fs.py +0 -0
  124. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_log.py +0 -0
  125. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_nginx.py +0 -0
  126. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_nginx_replica.py +0 -0
  127. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_permissions.py +0 -0
  128. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_readonly.py +0 -0
  129. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_replica_functional.py +0 -0
  130. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_stage_customizer.py +0 -0
  131. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming.py +0 -0
  132. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming_nginx.py +0 -0
  133. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming_replica.py +0 -0
  134. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming_replica_nginx.py +0 -0
  135. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_view_auth.py +0 -0
  136. {devpi-server-6.15.0 → devpi_server-6.16.0}/test_devpi_server/test_views_patch.py +0 -0
@@ -1,9 +1,9 @@
1
1
  [flake8]
2
- ignore = E501,E741,W503
2
+ # E203 is ignored because we enforce ruff format which contradicts it
3
+ ignore = E203,E501,E704,E741,W503
3
4
  per-file-ignores =
4
5
  plugin.py:E127,E128,E225,E231
5
6
  importexport.py:E127
6
7
  model.py:E128,E225,E231
7
- setup.py:E121,E126
8
8
  test_importexport.py:E121
9
9
  test*.py:E126,E127,E128,E225,E226,E231,E251
@@ -2,6 +2,32 @@
2
2
 
3
3
  .. towncrier release notes start
4
4
 
5
+ 6.16.0 (2025-06-25)
6
+ ===================
7
+
8
+ Deprecations and Removals
9
+ -------------------------
10
+
11
+ - Dropped support for Python 3.7 and 3.8.
12
+
13
+
14
+
15
+ Features
16
+ --------
17
+
18
+ - Update stored package metadata fields to version 2.4 for license expressions (PEP 639).
19
+
20
+
21
+
22
+ Bug Fixes
23
+ ---------
24
+
25
+ - Preserve hash when importing mirror data to prevent unnecessary updates later on.
26
+
27
+ - Keep original metadata_version in database.
28
+
29
+
30
+
5
31
  6.15.0 (2025-05-18)
6
32
  ===================
7
33
 
@@ -0,0 +1,116 @@
1
+
2
+
3
+ =========
4
+ Changelog
5
+ =========
6
+
7
+
8
+
9
+
10
+ .. towncrier release notes start
11
+
12
+ 6.16.0 (2025-06-25)
13
+ ===================
14
+
15
+ Deprecations and Removals
16
+ -------------------------
17
+
18
+ - Dropped support for Python 3.7 and 3.8.
19
+
20
+
21
+
22
+ Features
23
+ --------
24
+
25
+ - Update stored package metadata fields to version 2.4 for license expressions (PEP 639).
26
+
27
+
28
+
29
+ Bug Fixes
30
+ ---------
31
+
32
+ - Preserve hash when importing mirror data to prevent unnecessary updates later on.
33
+
34
+ - Keep original metadata_version in database.
35
+
36
+
37
+
38
+ 6.15.0 (2025-05-18)
39
+ ===================
40
+
41
+ Features
42
+ --------
43
+
44
+ - Add ``--connection-limit`` option to devpi-server passed on to waitress.
45
+
46
+
47
+
48
+ 6.14.0 (2024-10-16)
49
+ ===================
50
+
51
+ Features
52
+ --------
53
+
54
+ - Allow pushing of versions which only have documentation and no releases.
55
+
56
+ - Allow pushing of release files only with no documentation. Requires devpi-client 7.2.0.
57
+
58
+ - Allow pushing of documentation only with no release files. Requires devpi-client 7.2.0.
59
+
60
+
61
+
62
+ Bug Fixes
63
+ ---------
64
+
65
+ - No longer automatically "register" a project when pushing releases to PyPI. The reply changed from HTTP status 410 to 400 breaking the upload. With devpi-client 7.2.0 there is a ``--register-project`` option if it is still required for some other package registry.
66
+
67
+
68
+
69
+ 6.13.0 (2024-09-19)
70
+ ===================
71
+
72
+ Deprecations and Removals
73
+ -------------------------
74
+
75
+ - Remove/Deprecate "master" related terminology in favor of "primary".
76
+ Usage related changes are the switch to ``--primary-url`` instead of ``--master-url`` and ``--role=primary`` instead of ``--role=master``.
77
+ Using the old terms will now output warnings.
78
+ The ``+status`` API has additional fields and the ``role`` field content will change with 7.0.0.
79
+
80
+
81
+
82
+ Features
83
+ --------
84
+
85
+ - Enable logging command line options for all commands.
86
+
87
+ - Added support uv pip as an installer.
88
+
89
+
90
+
91
+ Bug Fixes
92
+ ---------
93
+
94
+ - Don't report on lagging event processing while replicating.
95
+
96
+ - Report primary serial correctly with streaming replication.
97
+
98
+ - Don't store file data in memory when fetching a release while pushing from a mirror.
99
+
100
+ - Only warn about replica not being in sync instead of fatal status while still replicating.
101
+
102
+
103
+
104
+ 6.12.1 (2024-07-24)
105
+ ===================
106
+
107
+ Bug Fixes
108
+ ---------
109
+
110
+ - Support Python 3.13 by depending on legacy-cgi.
111
+
112
+ - Preserve query string when proxying requests from replica to primary. This fixes force removal on non-volatile indexes and probably other bugs.
113
+
114
+ - Fix #1044: Correctly update cache expiry time when mirrored server returns 304 Not Modified.
115
+
116
+
@@ -1,37 +1,51 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: devpi-server
3
- Version: 6.15.0
4
- Summary: devpi-server: reliable private and pypi.org caching server
5
- Home-page: https://devpi.net
6
- Maintainer: Florian Schulze
7
- Maintainer-email: mail@pyfidelity.com
8
- License: MIT
3
+ Version: 6.16.0
4
+ Summary: devpi-server: reliable, private, and pypi.org caching server
5
+ Maintainer-email: Florian Schulze <mail@pyfidelity.com>
6
+ License-Expression: MIT
9
7
  Project-URL: Bug Tracker, https://github.com/devpi/devpi/issues
10
8
  Project-URL: Changelog, https://github.com/devpi/devpi/blob/main/server/CHANGELOG
11
9
  Project-URL: Documentation, https://doc.devpi.net
12
10
  Project-URL: Funding, https://github.com/sponsors/devpi
11
+ Project-URL: Homepage, https://devpi.net
13
12
  Project-URL: Source Code, https://github.com/devpi/devpi
14
- Keywords: pypi realtime cache server
13
+ Keywords: pypi,realtime,cache,server
15
14
  Classifier: Development Status :: 5 - Production/Stable
16
15
  Classifier: Environment :: Web Environment
17
16
  Classifier: Intended Audience :: Developers
18
17
  Classifier: Intended Audience :: System Administrators
19
- Classifier: License :: OSI Approved :: MIT License
20
18
  Classifier: Programming Language :: Python
21
19
  Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Topic :: Internet :: WWW/HTTP
23
- Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
24
- Classifier: Programming Language :: Python :: Implementation :: PyPy
25
- Classifier: Programming Language :: Python :: 3.7
26
- Classifier: Programming Language :: Python :: 3.8
27
20
  Classifier: Programming Language :: Python :: 3.9
28
21
  Classifier: Programming Language :: Python :: 3.10
29
22
  Classifier: Programming Language :: Python :: 3.11
30
23
  Classifier: Programming Language :: Python :: 3.12
31
24
  Classifier: Programming Language :: Python :: 3.13
32
- Requires-Python: >=3.7
25
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
26
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
27
+ Classifier: Topic :: Internet :: WWW/HTTP
28
+ Requires-Python: >=3.9
29
+ Description-Content-Type: text/x-rst
33
30
  License-File: LICENSE
34
- License-File: AUTHORS
31
+ Requires-Dist: argon2-cffi
32
+ Requires-Dist: attrs>=22.2.0
33
+ Requires-Dist: defusedxml
34
+ Requires-Dist: devpi_common<5,>3.6.0
35
+ Requires-Dist: httpx
36
+ Requires-Dist: itsdangerous>=0.24
37
+ Requires-Dist: lazy
38
+ Requires-Dist: legacy-cgi; python_version >= "3.13"
39
+ Requires-Dist: passlib[argon2]
40
+ Requires-Dist: platformdirs
41
+ Requires-Dist: pluggy<2.0,>=0.6.0
42
+ Requires-Dist: py>=1.4.23
43
+ Requires-Dist: pyramid>=2
44
+ Requires-Dist: repoze.lru>=0.6
45
+ Requires-Dist: strictyaml
46
+ Requires-Dist: waitress>=1.0.1
47
+ Requires-Dist: ruamel.yaml
48
+ Dynamic: license-file
35
49
 
36
50
  =============================================================================
37
51
  devpi-server: server for private package indexes and PyPI caching
@@ -97,14 +111,42 @@ For support contracts and paid help contact ``mail at pyfidelity.com``.
97
111
  .. _GitHub Discussions: https://github.com/devpi/devpi/discussions
98
112
 
99
113
 
114
+
100
115
  =========
101
116
  Changelog
102
117
  =========
103
118
 
104
119
 
105
120
 
121
+
106
122
  .. towncrier release notes start
107
123
 
124
+ 6.16.0 (2025-06-25)
125
+ ===================
126
+
127
+ Deprecations and Removals
128
+ -------------------------
129
+
130
+ - Dropped support for Python 3.7 and 3.8.
131
+
132
+
133
+
134
+ Features
135
+ --------
136
+
137
+ - Update stored package metadata fields to version 2.4 for license expressions (PEP 639).
138
+
139
+
140
+
141
+ Bug Fixes
142
+ ---------
143
+
144
+ - Preserve hash when importing mirror data to prevent unnecessary updates later on.
145
+
146
+ - Keep original metadata_version in database.
147
+
148
+
149
+
108
150
  6.15.0 (2025-05-18)
109
151
  ===================
110
152
 
@@ -184,22 +226,3 @@ Bug Fixes
184
226
  - Fix #1044: Correctly update cache expiry time when mirrored server returns 304 Not Modified.
185
227
 
186
228
 
187
-
188
- 6.12.0 (2024-06-25)
189
- ===================
190
-
191
- Features
192
- --------
193
-
194
- - Added ``devpiserver_on_toxresult_store`` hook to allow blocking or skipping a toxresult upload on more specific conditions as ``acl_toxresult_upload`` would allow.
195
-
196
- - Added ``devpiserver_on_toxresult_upload_forbidden`` hook to allow returning a custom message and result (403 or 200).
197
-
198
-
199
-
200
- Bug Fixes
201
- ---------
202
-
203
- - Return json data if toxresult upload is forbidden.
204
-
205
-
@@ -0,0 +1 @@
1
+ __version__ = "6.16.0"
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import base64
2
4
  import hashlib
3
5
  import itertools
@@ -6,6 +8,12 @@ import secrets
6
8
  from .log import threadlog
7
9
  from passlib.context import CryptContext
8
10
  from passlib.utils.handlers import MinimalHandler
11
+ from typing import TYPE_CHECKING
12
+
13
+
14
+ if TYPE_CHECKING:
15
+ from typing import Any
16
+ from typing import Union
9
17
 
10
18
 
11
19
  notset = object()
@@ -133,7 +141,7 @@ class Auth:
133
141
  username,
134
142
  result.get("groups", []),
135
143
  result.get("from_user_object", False)])
136
- assert not isinstance(pseudopass, str) or pseudopass.encode('ascii') # type: ignore[attr-defined]
144
+ assert not isinstance(pseudopass, str) or pseudopass.encode("ascii")
137
145
  return {"password": pseudopass,
138
146
  "expiration": self.LOGIN_EXPIRATION}
139
147
 
@@ -175,7 +183,7 @@ class DevpiHandler(MinimalHandler):
175
183
  return "%s:%s" % (salt, hash)
176
184
 
177
185
  @classmethod
178
- def verify(cls, secret, hash):
186
+ def verify(cls, secret: Union[str, bytes], hash: Union[str, bytes], **context_kwds: Any) -> Any: # noqa: A002, ARG003
179
187
  (salt, hash) = cls._get_salt_and_hash(hash)
180
188
  return salt and hash and (getpwhash(secret, salt) == hash)
181
189
 
@@ -355,10 +355,6 @@ def add_secretfile_option(parser, pluginmanager):
355
355
  "validation. If not specified, a random secret is "
356
356
  "generated on each start up.")
357
357
 
358
-
359
- def add_deploy_options(parser, pluginmanager):
360
- add_secretfile_option(parser, pluginmanager)
361
-
362
358
  parser.addoption(
363
359
  "--argon2-memory-cost", type=int, default=DEFAULT_ARGON2_MEMORY_COST,
364
360
  help=argparse.SUPPRESS)
@@ -371,6 +367,10 @@ def add_deploy_options(parser, pluginmanager):
371
367
  "--argon2-time-cost", type=int, default=DEFAULT_ARGON2_TIME_COST,
372
368
  help=argparse.SUPPRESS)
373
369
 
370
+
371
+ def add_deploy_options(parser, pluginmanager):
372
+ add_secretfile_option(parser, pluginmanager)
373
+
374
374
  parser.addoption(
375
375
  "--requests-only", action="store_true",
376
376
  help="only start as a worker which handles read/write web requests "
@@ -1125,10 +1125,6 @@ class Config(object):
1125
1125
  return self.get_derived_key(b'devpi-server-replica')
1126
1126
 
1127
1127
 
1128
- def getpath(path):
1129
- return py.path.local(os.path.expanduser(str(path)))
1130
-
1131
-
1132
1128
  def gensecret():
1133
1129
  from .log import threadlog as log
1134
1130
  from .main import CommandRunner
@@ -5,6 +5,7 @@ for all indexes.
5
5
  """
6
6
  import hashlib
7
7
  import mimetypes
8
+ from .readonly import get_mutable_deepcopy
8
9
  from wsgiref.handlers import format_date_time
9
10
  import re
10
11
  from devpi_common.metadata import splitbasename
@@ -333,7 +334,7 @@ class FileStore:
333
334
 
334
335
  def maplink(self, link, user, index, project):
335
336
  key = key_from_link(self.keyfs, link, user, index)
336
- entry = FileEntry(key, readonly=False)
337
+ entry = MutableFileEntry(key)
337
338
  entry.url = link.geturl_nofragment().url
338
339
  entry.hash_spec = unicode_if_bytes(link.hash_spec)
339
340
  entry.project = project
@@ -357,14 +358,14 @@ class FileStore:
357
358
  return None
358
359
  return key
359
360
 
360
- def get_file_entry(self, relpath, readonly=True):
361
+ def get_file_entry(self, relpath):
361
362
  key = self.get_key_from_relpath(relpath)
362
363
  if key is None:
363
364
  return None
364
- return self.get_file_entry_from_key(key, readonly=readonly)
365
+ return FileEntry(key)
365
366
 
366
- def get_file_entry_from_key(self, key, meta=_nodefault, readonly=True):
367
- return FileEntry(key, meta=meta, readonly=readonly)
367
+ def get_file_entry_from_key(self, key, meta=_nodefault):
368
+ return MutableFileEntry(key, meta=meta)
368
369
 
369
370
  def store(self, user, index, basename, content_or_file, *, dir_hash_spec=None, hashes=None):
370
371
  # dir_hash_spec is set for toxresult files
@@ -376,7 +377,7 @@ class FileStore:
376
377
  key = self.keyfs.STAGEFILE(
377
378
  user=user, index=index,
378
379
  hashdir_a=hashdir_a, hashdir_b=hashdir_b, filename=basename)
379
- entry = FileEntry(key, readonly=False)
380
+ entry = MutableFileEntry(key)
380
381
  entry.file_set_content(content_or_file, hashes=hashes)
381
382
  return entry
382
383
 
@@ -401,8 +402,9 @@ class BadGateway(Exception):
401
402
  self.url = url
402
403
 
403
404
 
404
- class FileEntry(object):
405
- __slots__ = ('_meta', '_storepath', 'basename', 'key', 'readonly', 'relpath')
405
+ class BaseFileEntry:
406
+ __slots__ = ("_meta", "_storepath", "basename", "key", "relpath")
407
+
406
408
  BadGateway = BadGateway
407
409
  _hash_spec = metaprop("hash_spec") # e.g. "md5=120938012"
408
410
  last_modified = metaprop("last_modified")
@@ -410,11 +412,10 @@ class FileEntry(object):
410
412
  project = metaprop("project")
411
413
  version = metaprop("version")
412
414
 
413
- def __init__(self, key, meta=_nodefault, readonly=True):
415
+ def __init__(self, key, meta=_nodefault):
414
416
  self.key = key
415
417
  self.relpath = key.relpath
416
418
  self.basename = self.relpath.split("/")[-1]
417
- self.readonly = readonly
418
419
  self._storepath = "/".join(("+files", str(self.relpath)))
419
420
  self._meta = _nodefault
420
421
  if meta is not _nodefault:
@@ -517,9 +518,7 @@ class FileEntry(object):
517
518
 
518
519
  @property
519
520
  def meta(self):
520
- if self._meta is _nodefault:
521
- self._meta = self.key.get(readonly=self.readonly)
522
- return self._meta
521
+ raise NotImplementedError
523
522
 
524
523
  def file_exists(self):
525
524
  return self.tx.conn.io_file_exists(self._storepath)
@@ -531,7 +530,10 @@ class FileEntry(object):
531
530
  return self.tx.conn.io_file_size(self._storepath)
532
531
 
533
532
  def __repr__(self):
534
- return f"<FileEntry {self.key!r}>"
533
+ return f"<{self.__class__.__name__} {self.key!r}>"
534
+
535
+ def file_new_open(self):
536
+ return self.tx.conn.io_file_new_open(self._storepath)
535
537
 
536
538
  def file_open_read(self):
537
539
  return self.tx.conn.io_file_open(self._storepath)
@@ -563,6 +565,9 @@ class FileEntry(object):
563
565
  # changed which will not replay correctly at a replica.
564
566
  self.key.set(self.meta)
565
567
 
568
+ def file_set_content_no_meta(self, content_or_file, *, hashes=None): # noqa: ARG002
569
+ self.tx.conn.io_file_set(self._storepath, content_or_file)
570
+
566
571
  def gethttpheaders(self):
567
572
  assert self.file_exists()
568
573
  headers = {}
@@ -605,6 +610,30 @@ class FileEntry(object):
605
610
  return None
606
611
 
607
612
 
613
+ class FileEntry(BaseFileEntry):
614
+ @property
615
+ def meta(self):
616
+ if self._meta is _nodefault:
617
+ self._meta = self.key.get()
618
+ return self._meta
619
+
620
+ @property
621
+ def readonly(self):
622
+ return True
623
+
624
+
625
+ class MutableFileEntry(BaseFileEntry):
626
+ @property
627
+ def meta(self):
628
+ if self._meta is _nodefault:
629
+ self._meta = get_mutable_deepcopy(self.key.get())
630
+ return self._meta
631
+
632
+ @property
633
+ def readonly(self):
634
+ return False
635
+
636
+
608
637
  def get_checksum_error(content_or_hash, relpath, hash_spec):
609
638
  warnings.warn(
610
639
  "The get_checksum_error function is deprecated, "
@@ -257,7 +257,7 @@ class IndexDump:
257
257
 
258
258
  def dump_releasefiles(self, linkstore):
259
259
  for link in linkstore.get_links(rel="releasefile"):
260
- entry = self.exporter.filestore.get_file_entry(link.entrypath)
260
+ entry = self.exporter.filestore.get_file_entry(link.relpath)
261
261
  if not entry.last_modified:
262
262
  continue
263
263
  if not entry.file_exists():
@@ -279,13 +279,15 @@ class IndexDump:
279
279
  self.basedir.join(linkstore.project, reflink._hash_spec,
280
280
  tox_link.basename)
281
281
  )
282
- self.add_filedesc(type="toxresult",
283
- project=linkstore.project,
284
- relpath=relpath,
285
- version=linkstore.version,
286
- entrymapping=tox_link.entry.meta,
287
- for_entrypath=reflink.entrypath,
288
- log=tox_link.get_logs())
282
+ self.add_filedesc(
283
+ type="toxresult",
284
+ project=linkstore.project,
285
+ relpath=relpath,
286
+ version=linkstore.version,
287
+ entrymapping=tox_link.entry.meta,
288
+ for_entrypath=reflink.relpath,
289
+ log=tox_link.get_logs(),
290
+ )
289
291
 
290
292
  def add_filedesc(self, type, project, relpath, **kw):
291
293
  if not self.exporter.basepath.join(relpath).check():
@@ -580,7 +582,10 @@ class Importer:
580
582
  (_, links_with_data, serial) = stage._load_cache_links(project)
581
583
  if links_with_data is None:
582
584
  links_with_data = []
583
- links = [(url.basename, entry.relpath)]
585
+ entrypath = entry.relpath
586
+ if hash_spec := entry.best_available_hash_spec:
587
+ entrypath = f"{entrypath}#{hash_spec}"
588
+ links = [(url.basename, entrypath)]
584
589
  requires_python = [versions[version].get('requires_python')]
585
590
  yanked = [versions[version].get('yanked')]
586
591
  for key, href, require_python, is_yanked in links_with_data:
@@ -618,6 +623,7 @@ class Importer:
618
623
  hashes=hashes, last_modified=last_modified)
619
624
  else:
620
625
  msg = f"unknown file type: {type}"
626
+ f.close()
621
627
  raise Fatal(msg)
622
628
  if link is not None:
623
629
  history_log = filedesc.get('log')