devpi-server 6.14.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.
- {devpi-server-6.14.0 → devpi_server-6.16.0}/.flake8 +2 -2
- {devpi-server-6.14.0 → devpi_server-6.16.0}/CHANGELOG +36 -0
- devpi_server-6.16.0/CHANGELOG.short.rst +116 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/MANIFEST.in +3 -1
- {devpi-server-6.14.0 → devpi_server-6.16.0}/PKG-INFO +68 -56
- devpi_server-6.16.0/devpi_server/__init__.py +1 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/auth.py +10 -2
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/config.py +32 -10
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/filestore.py +43 -14
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/hookspecs.py +6 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/importexport.py +15 -9
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/interfaces.py +83 -14
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/keyfs.py +188 -87
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/keyfs_types.py +45 -4
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/log.py +10 -5
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/main.py +41 -17
- devpi_server-6.16.0/devpi_server/markers.py +34 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/middleware.py +4 -3
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/mirror.py +33 -22
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/model.py +160 -70
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/mythread.py +21 -17
- devpi_server-6.16.0/devpi_server/py.typed +1 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/readonly.py +25 -16
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/replica.py +21 -22
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/view_auth.py +15 -3
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/views.py +35 -68
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server.egg-info/PKG-INFO +68 -56
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server.egg-info/SOURCES.txt +4 -5
- devpi_server-6.16.0/mypy.ini +105 -0
- devpi_server-6.16.0/pyproject.toml +288 -0
- devpi_server-6.16.0/setup.cfg +4 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/plugin.py +15 -19
- devpi_server-6.16.0/test_devpi_server/py.typed +1 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/reqmock.py +36 -26
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_filestore.py +22 -17
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_importexport.py +46 -37
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_keyfs.py +29 -15
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_main.py +25 -26
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_mirror.py +35 -16
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_mirror_no_project_list.py +17 -17
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_model.py +47 -55
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_mythread.py +8 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_replica.py +11 -6
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_view_auth.py +18 -8
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_views.py +34 -9
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_views_push_external.py +77 -17
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_views_status.py +5 -7
- {devpi-server-6.14.0 → devpi_server-6.16.0}/tox.ini +17 -8
- devpi-server-6.14.0/AUTHORS +0 -13
- devpi-server-6.14.0/devpi_server/__init__.py +0 -1
- devpi-server-6.14.0/devpi_server/markers.py +0 -14
- devpi-server-6.14.0/devpi_server.egg-info/not-zip-safe +0 -1
- devpi-server-6.14.0/pyproject.toml +0 -99
- devpi-server-6.14.0/setup.cfg +0 -7
- devpi-server-6.14.0/setup.py +0 -116
- devpi-server-6.14.0/test_devpi_server/test_reqmock.py +0 -48
- {devpi-server-6.14.0 → devpi_server-6.16.0}/LICENSE +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/README.rst +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/__main__.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/auth_basic.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/auth_devpi.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/__init__.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/crontab.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/devpi.service.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/launchd-macos.txt.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-http.conf.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-location.conf.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-proxy.conf.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi-caching-server.conf.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/nginx-devpi.conf.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/supervisor-devpi.conf.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/supervisord.conf.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/cfg/windows-service.txt.template +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/exceptions.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/filestore_fs.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/fileutil.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/fsck.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/genconfig.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/init.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/keyfs_sqlite.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/keyfs_sqlite_fs.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/passwd.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/sizeof.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/vendor/__init__.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server/vendor/_pip.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server.egg-info/dependency_links.txt +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server.egg-info/entry_points.txt +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server.egg-info/requires.txt +7 -7
- {devpi-server-6.14.0 → devpi_server-6.16.0}/devpi_server.egg-info/top_level.txt +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/pytest_devpi_server/__init__.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/__init__.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/conftest.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/example.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/functional.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/badindexname/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/badusername/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/basescycle/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/createdmodified/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/deletedbase/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/mirrordata/dataindex.json +0 -0
- {devpi-server-6.14.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
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/modifiedpypi/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/nocreatedmodified/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization_merge/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello-pkg/hello.pkg-1.1.tar.gz +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/norootpypi/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/nouser/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/removedindexplugin/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/removedindexplugin/user/dev/pkg/pkg-1.0.tar.gz +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/dataindex.json +0 -0
- {devpi-server-6.14.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
- {devpi-server-6.14.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
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/importexportdata/toxresult_upload_default/dataindex.json +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/simpypi.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_auth.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_authcheck.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_config.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_conftest.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_fileutil.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_fsck.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_genconfig.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_keyfs_sqlite_fs.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_log.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_nginx.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_nginx_replica.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_permissions.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_readonly.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_replica_functional.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_stage_customizer.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming_nginx.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming_replica.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_streaming_replica_nginx.py +0 -0
- {devpi-server-6.14.0 → devpi_server-6.16.0}/test_devpi_server/test_views_patch.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[flake8]
|
|
2
|
-
|
|
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,42 @@
|
|
|
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
|
+
|
|
31
|
+
6.15.0 (2025-05-18)
|
|
32
|
+
===================
|
|
33
|
+
|
|
34
|
+
Features
|
|
35
|
+
--------
|
|
36
|
+
|
|
37
|
+
- Add ``--connection-limit`` option to devpi-server passed on to waitress.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
5
41
|
6.14.0 (2024-10-16)
|
|
6
42
|
===================
|
|
7
43
|
|
|
@@ -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,4 +1,6 @@
|
|
|
1
1
|
include .flake8 CHANGELOG *.ini *.rst LICENSE
|
|
2
|
-
recursive-include devpi_server
|
|
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,37 +1,51 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: devpi-server
|
|
3
|
-
Version: 6.
|
|
4
|
-
Summary: devpi-server: reliable private and pypi.org caching server
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,52 @@ 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
|
+
|
|
150
|
+
6.15.0 (2025-05-18)
|
|
151
|
+
===================
|
|
152
|
+
|
|
153
|
+
Features
|
|
154
|
+
--------
|
|
155
|
+
|
|
156
|
+
- Add ``--connection-limit`` option to devpi-server passed on to waitress.
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
108
160
|
6.14.0 (2024-10-16)
|
|
109
161
|
===================
|
|
110
162
|
|
|
@@ -174,43 +226,3 @@ Bug Fixes
|
|
|
174
226
|
- Fix #1044: Correctly update cache expiry time when mirrored server returns 304 Not Modified.
|
|
175
227
|
|
|
176
228
|
|
|
177
|
-
|
|
178
|
-
6.12.0 (2024-06-25)
|
|
179
|
-
===================
|
|
180
|
-
|
|
181
|
-
Features
|
|
182
|
-
--------
|
|
183
|
-
|
|
184
|
-
- Added ``devpiserver_on_toxresult_store`` hook to allow blocking or skipping a toxresult upload on more specific conditions as ``acl_toxresult_upload`` would allow.
|
|
185
|
-
|
|
186
|
-
- Added ``devpiserver_on_toxresult_upload_forbidden`` hook to allow returning a custom message and result (403 or 200).
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
Bug Fixes
|
|
191
|
-
---------
|
|
192
|
-
|
|
193
|
-
- Return json data if toxresult upload is forbidden.
|
|
194
|
-
|
|
195
|
-
|
|
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.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(
|
|
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
|
|
|
@@ -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.")
|
|
@@ -332,10 +355,6 @@ def add_secretfile_option(parser, pluginmanager):
|
|
|
332
355
|
"validation. If not specified, a random secret is "
|
|
333
356
|
"generated on each start up.")
|
|
334
357
|
|
|
335
|
-
|
|
336
|
-
def add_deploy_options(parser, pluginmanager):
|
|
337
|
-
add_secretfile_option(parser, pluginmanager)
|
|
338
|
-
|
|
339
358
|
parser.addoption(
|
|
340
359
|
"--argon2-memory-cost", type=int, default=DEFAULT_ARGON2_MEMORY_COST,
|
|
341
360
|
help=argparse.SUPPRESS)
|
|
@@ -348,6 +367,10 @@ def add_deploy_options(parser, pluginmanager):
|
|
|
348
367
|
"--argon2-time-cost", type=int, default=DEFAULT_ARGON2_TIME_COST,
|
|
349
368
|
help=argparse.SUPPRESS)
|
|
350
369
|
|
|
370
|
+
|
|
371
|
+
def add_deploy_options(parser, pluginmanager):
|
|
372
|
+
add_secretfile_option(parser, pluginmanager)
|
|
373
|
+
|
|
351
374
|
parser.addoption(
|
|
352
375
|
"--requests-only", action="store_true",
|
|
353
376
|
help="only start as a worker which handles read/write web requests "
|
|
@@ -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
|
|
450
|
-
return
|
|
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
|
|
@@ -1099,10 +1125,6 @@ class Config(object):
|
|
|
1099
1125
|
return self.get_derived_key(b'devpi-server-replica')
|
|
1100
1126
|
|
|
1101
1127
|
|
|
1102
|
-
def getpath(path):
|
|
1103
|
-
return py.path.local(os.path.expanduser(str(path)))
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
1128
|
def gensecret():
|
|
1107
1129
|
from .log import threadlog as log
|
|
1108
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 =
|
|
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
|
|
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
|
|
365
|
+
return FileEntry(key)
|
|
365
366
|
|
|
366
|
-
def get_file_entry_from_key(self, key, meta=_nodefault
|
|
367
|
-
return
|
|
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 =
|
|
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
|
|
405
|
-
__slots__ = (
|
|
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
|
|
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
|
-
|
|
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"<
|
|
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, "
|
|
@@ -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.
|
|
@@ -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.
|
|
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(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
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')
|