devpi-server 6.9.2__tar.gz → 6.11.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.11.0/.flake8 +9 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/CHANGELOG +42 -0
- devpi_server-6.11.0/CHANGELOG.short.rst +95 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/MANIFEST.in +1 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/PKG-INFO +56 -35
- devpi_server-6.11.0/devpi_server/__init__.py +1 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/auth.py +4 -2
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/supervisor-devpi.conf.template +0 -2
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/config.py +75 -41
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/filestore.py +43 -29
- devpi_server-6.11.0/devpi_server/filestore_fs.py +136 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/fileutil.py +2 -2
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/fsck.py +16 -18
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/hookspecs.py +13 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/importexport.py +82 -84
- devpi_server-6.11.0/devpi_server/init.py +38 -0
- devpi_server-6.11.0/devpi_server/interfaces.py +220 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/keyfs.py +122 -104
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/keyfs_sqlite.py +30 -31
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/keyfs_sqlite_fs.py +7 -102
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/log.py +2 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/main.py +94 -43
- devpi_server-6.11.0/devpi_server/markers.py +14 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/middleware.py +0 -2
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/mirror.py +78 -27
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/model.py +45 -27
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/passwd.py +9 -20
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/readonly.py +5 -4
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/replica.py +94 -111
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/sizeof.py +1 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/views.py +214 -204
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server.egg-info/PKG-INFO +56 -35
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server.egg-info/SOURCES.txt +7 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server.egg-info/requires.txt +1 -1
- devpi_server-6.11.0/pytest_devpi_server/__init__.py +4 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/setup.py +3 -3
- devpi_server-6.11.0/test_devpi_server/__init__.py +0 -0
- devpi_server-6.11.0/test_devpi_server/conftest.py +17 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/example.py +1 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/functional.py +10 -10
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/mirrordata/dataindex.json +1 -1
- devpi_server-6.11.0/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/0.9/hello-0.9.tar.gz +1 -0
- devpi-server-6.9.2/test_devpi_server/conftest.py → devpi_server-6.11.0/test_devpi_server/plugin.py +92 -43
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/reqmock.py +19 -19
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/simpypi.py +9 -16
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_auth.py +5 -4
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_config.py +15 -13
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_filestore.py +59 -47
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_fileutil.py +5 -5
- devpi_server-6.11.0/test_devpi_server/test_fsck.py +41 -0
- devpi_server-6.11.0/test_devpi_server/test_genconfig.py +50 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_importexport.py +135 -115
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_keyfs.py +105 -89
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_keyfs_sqlite_fs.py +4 -4
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_log.py +3 -2
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_main.py +6 -6
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_mirror.py +109 -55
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_model.py +100 -116
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_nginx.py +5 -2
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_nginx_replica.py +5 -5
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_permissions.py +8 -6
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_readonly.py +2 -2
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_replica.py +88 -75
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_replica_functional.py +6 -6
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_stage_customizer.py +15 -15
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_streaming.py +2 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_streaming_nginx.py +1 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_streaming_replica.py +1 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_streaming_replica_nginx.py +1 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_view_auth.py +10 -8
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_views.py +74 -79
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_views_push_external.py +1 -1
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_views_status.py +1 -1
- devpi_server-6.11.0/tox.ini +40 -0
- devpi-server-6.9.2/devpi_server/__init__.py +0 -1
- devpi-server-6.9.2/devpi_server/init.py +0 -53
- devpi-server-6.9.2/devpi_server/interfaces.py +0 -147
- devpi-server-6.9.2/pytest_devpi_server/__init__.py +0 -25
- devpi-server-6.9.2/test_devpi_server/importexportdata/mirrordata/root/pypi/dddttt/0.1.dev1/dddttt-0.1.dev1.tar.gz +0 -0
- devpi-server-6.9.2/test_devpi_server/test_genconfig.py +0 -19
- devpi-server-6.9.2/tox.ini +0 -49
- {devpi-server-6.9.2 → devpi_server-6.11.0}/AUTHORS +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/LICENSE +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/README.rst +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/__main__.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/auth_basic.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/auth_devpi.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/__init__.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/crontab.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/devpi.service.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/launchd-macos.txt.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/nginx-devpi-caching-http.conf.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/nginx-devpi-caching-location.conf.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/nginx-devpi-caching-proxy.conf.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/nginx-devpi-caching-server.conf.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/nginx-devpi.conf.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/supervisord.conf.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/cfg/windows-service.txt.template +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/exceptions.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/genconfig.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/keyfs_types.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/mythread.py +0 -0
- {devpi-server-6.9.2/test_devpi_server → devpi_server-6.11.0/devpi_server/vendor}/__init__.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/vendor/_pip.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server/view_auth.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server.egg-info/dependency_links.txt +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server.egg-info/entry_points.txt +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server.egg-info/not-zip-safe +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/devpi_server.egg-info/top_level.txt +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/pyproject.toml +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/setup.cfg +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/badindexname/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/badusername/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/basescycle/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/createdmodified/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/deletedbase/dataindex.json +0 -0
- /devpi-server-6.9.2/test_devpi_server/importexportdata/normalization/root/dev/hello.pkg/hello.pkg-1.0.tar.gz → /devpi_server-6.11.0/test_devpi_server/importexportdata/mirrordata/root/pypi/dddttt/0.1.dev1/dddttt-0.1.dev1.tar.gz +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/modifiedpypi/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/nocreatedmodified/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/normalization/dataindex.json +0 -0
- /devpi-server-6.9.2/test_devpi_server/importexportdata/removedindexplugin/user/dev/pkg/pkg-1.0.tar.gz → /devpi_server-6.11.0/test_devpi_server/importexportdata/normalization/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/normalization_merge/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello-pkg/hello.pkg-1.1.tar.gz +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/norootpypi/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/nouser/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/removedindexplugin/dataindex.json +0 -0
- /devpi-server-6.9.2/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/0.9/hello-0.9.tar.gz → /devpi_server-6.11.0/test_devpi_server/importexportdata/removedindexplugin/user/dev/pkg/pkg-1.0.tar.gz +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/sha256=ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73/hello-0.9.tar.gz.toxresult0 +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/importexportdata/toxresult_upload_default/dataindex.json +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_authcheck.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_conftest.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_mirror_no_project_list.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_mythread.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_reqmock.py +0 -0
- {devpi-server-6.9.2 → devpi_server-6.11.0}/test_devpi_server/test_views_patch.py +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
[flake8]
|
|
2
|
+
ignore = E501,E741,W503
|
|
3
|
+
per-file-ignores =
|
|
4
|
+
plugin.py:E127,E128,E225,E231,E265,E402,E712
|
|
5
|
+
importexport.py:E127
|
|
6
|
+
model.py:E128,E225,E231
|
|
7
|
+
setup.py:E121,E126
|
|
8
|
+
test_importexport.py:E121
|
|
9
|
+
test*.py:E117,E126,E127,E128,E225,E226,E231,E251,E265,E712,E721
|
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
.. towncrier release notes start
|
|
4
4
|
|
|
5
|
+
6.11.0 (2024-04-20)
|
|
6
|
+
===================
|
|
7
|
+
|
|
8
|
+
Features
|
|
9
|
+
--------
|
|
10
|
+
|
|
11
|
+
- The ``devpi-fsck`` script now returns an error code when there have been missing files or checksum errors.
|
|
12
|
+
|
|
13
|
+
- Fix #983: Add plugin hook for mirror authentication header.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Bug Fixes
|
|
18
|
+
---------
|
|
19
|
+
|
|
20
|
+
- Preserve last modified of docs and toxresults during export/import.
|
|
21
|
+
|
|
22
|
+
- Fix #1033: Use ``int`` for ``--mirror-cache-expiry`` to fix value of ``proxy_cache_valid`` in nginx caching config.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
6.10.0 (2023-12-19)
|
|
27
|
+
===================
|
|
28
|
+
|
|
29
|
+
Features
|
|
30
|
+
--------
|
|
31
|
+
|
|
32
|
+
- Use ``Authorization`` header instead of adding username/password to URL when fetching from mirror.
|
|
33
|
+
|
|
34
|
+
- Fix #998: Use the pure Python httpx library instead of aiohttp to prevent delays in supporting newest Python releases.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Bug Fixes
|
|
39
|
+
---------
|
|
40
|
+
|
|
41
|
+
- Fix #996: support hashes other than sha256 in application/vnd.pypi.simple.v1+json responses.
|
|
42
|
+
|
|
43
|
+
- Only compare hostname instead of full URL prefix when parsing mirror packages to fix mirrors with basic authentication and absolute URLs. See #1006
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
5
47
|
6.9.2 (2023-08-06)
|
|
6
48
|
==================
|
|
7
49
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
=========
|
|
4
|
+
Changelog
|
|
5
|
+
=========
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
.. towncrier release notes start
|
|
11
|
+
|
|
12
|
+
6.10.0 (2023-12-19)
|
|
13
|
+
===================
|
|
14
|
+
|
|
15
|
+
Features
|
|
16
|
+
--------
|
|
17
|
+
|
|
18
|
+
- Use ``Authorization`` header instead of adding username/password to URL when fetching from mirror.
|
|
19
|
+
|
|
20
|
+
- Fix #998: Use the pure Python httpx library instead of aiohttp to prevent delays in supporting newest Python releases.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Bug Fixes
|
|
25
|
+
---------
|
|
26
|
+
|
|
27
|
+
- Fix #996: support hashes other than sha256 in application/vnd.pypi.simple.v1+json responses.
|
|
28
|
+
|
|
29
|
+
- Only compare hostname instead of full URL prefix when parsing mirror packages to fix mirrors with basic authentication and absolute URLs. See #1006
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
6.9.2 (2023-08-06)
|
|
34
|
+
==================
|
|
35
|
+
|
|
36
|
+
Bug Fixes
|
|
37
|
+
---------
|
|
38
|
+
|
|
39
|
+
- Prevent duplicates when adding values to lists in index configuration with ``+=`` operator.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
6.9.1 (2023-07-02)
|
|
43
|
+
==================
|
|
44
|
+
|
|
45
|
+
Bug Fixes
|
|
46
|
+
---------
|
|
47
|
+
|
|
48
|
+
- Prevent error in find_pre_existing_file in case of incomplete metadata.
|
|
49
|
+
|
|
50
|
+
- Fix #980: Remove long deprecated backward compatibility for old pluggy versions to fix error with pluggy 1.1.0.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
6.9.0 (2023-05-23)
|
|
54
|
+
==================
|
|
55
|
+
|
|
56
|
+
Features
|
|
57
|
+
--------
|
|
58
|
+
|
|
59
|
+
- Support export directory layout for ``--replica-file-search-path`` option.
|
|
60
|
+
|
|
61
|
+
- Fix #931: Add ``mirror_no_project_list`` setting for mirror indexes that have no full project list like google cloud artifacts or if you want to prevent downloading the full list for huge indexes like PyPI.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
Bug Fixes
|
|
65
|
+
---------
|
|
66
|
+
|
|
67
|
+
- Keep a reference to async tasks to avoid their removal mid execution.
|
|
68
|
+
|
|
69
|
+
- Support changed default of ``enforce_content_length`` in urllib3 >= 2.
|
|
70
|
+
|
|
71
|
+
- Fix #934: Properly set PATH_INFO when outside URL is used with sub-path.
|
|
72
|
+
|
|
73
|
+
- Fix #945: Adapt FatalError to be usable as an async HTTP response when updating a project on a mirror.
|
|
74
|
+
|
|
75
|
+
- Fix wrong hash metadata introduced in 6.5.0 for toxresults which prevents replication. The metadata can be fixed by an export/import cycle.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
6.8.0 (2022-12-05)
|
|
79
|
+
==================
|
|
80
|
+
|
|
81
|
+
Features
|
|
82
|
+
--------
|
|
83
|
+
|
|
84
|
+
- Fix #929: Cache normalized project names per transaction on mirror index instances.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
Bug Fixes
|
|
88
|
+
---------
|
|
89
|
+
|
|
90
|
+
- Fix #914: add locking to list_projects_perstage of mirror indexes to prevent multiple slow concurrent updates of the full project name list.
|
|
91
|
+
|
|
92
|
+
- Catch exceptions in async_httpget analog to httpget.
|
|
93
|
+
|
|
94
|
+
- Add locking to mirror name cache to prevent race condition on updates.
|
|
95
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: devpi-server
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.11.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
|
|
@@ -26,9 +26,26 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
26
26
|
Classifier: Programming Language :: Python :: 3.9
|
|
27
27
|
Classifier: Programming Language :: Python :: 3.10
|
|
28
28
|
Classifier: Programming Language :: Python :: 3.11
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
29
30
|
Requires-Python: >=3.7
|
|
30
31
|
License-File: LICENSE
|
|
31
32
|
License-File: AUTHORS
|
|
33
|
+
Requires-Dist: py>=1.4.23
|
|
34
|
+
Requires-Dist: httpx
|
|
35
|
+
Requires-Dist: argon2-cffi
|
|
36
|
+
Requires-Dist: attrs>=21.3.0
|
|
37
|
+
Requires-Dist: defusedxml
|
|
38
|
+
Requires-Dist: devpi_common<5,>3.6.0
|
|
39
|
+
Requires-Dist: itsdangerous>=0.24
|
|
40
|
+
Requires-Dist: platformdirs
|
|
41
|
+
Requires-Dist: pyramid>=2
|
|
42
|
+
Requires-Dist: waitress>=1.0.1
|
|
43
|
+
Requires-Dist: repoze.lru>=0.6
|
|
44
|
+
Requires-Dist: passlib[argon2]
|
|
45
|
+
Requires-Dist: pluggy<2.0,>=0.6.0
|
|
46
|
+
Requires-Dist: ruamel.yaml
|
|
47
|
+
Requires-Dist: strictyaml
|
|
48
|
+
Requires-Dist: lazy
|
|
32
49
|
|
|
33
50
|
=============================================================================
|
|
34
51
|
devpi-server: server for private package indexes and PyPI caching
|
|
@@ -102,85 +119,89 @@ Changelog
|
|
|
102
119
|
|
|
103
120
|
.. towncrier release notes start
|
|
104
121
|
|
|
105
|
-
6.
|
|
106
|
-
|
|
122
|
+
6.11.0 (2024-04-20)
|
|
123
|
+
===================
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
|
|
125
|
+
Features
|
|
126
|
+
--------
|
|
110
127
|
|
|
111
|
-
-
|
|
128
|
+
- The ``devpi-fsck`` script now returns an error code when there have been missing files or checksum errors.
|
|
129
|
+
|
|
130
|
+
- Fix #983: Add plugin hook for mirror authentication header.
|
|
112
131
|
|
|
113
132
|
|
|
114
|
-
6.9.1 (2023-07-02)
|
|
115
|
-
==================
|
|
116
133
|
|
|
117
134
|
Bug Fixes
|
|
118
135
|
---------
|
|
119
136
|
|
|
120
|
-
-
|
|
137
|
+
- Preserve last modified of docs and toxresults during export/import.
|
|
121
138
|
|
|
122
|
-
- Fix #
|
|
139
|
+
- Fix #1033: Use ``int`` for ``--mirror-cache-expiry`` to fix value of ``proxy_cache_valid`` in nginx caching config.
|
|
123
140
|
|
|
124
141
|
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
|
|
143
|
+
6.10.0 (2023-12-19)
|
|
144
|
+
===================
|
|
127
145
|
|
|
128
146
|
Features
|
|
129
147
|
--------
|
|
130
148
|
|
|
131
|
-
-
|
|
149
|
+
- Use ``Authorization`` header instead of adding username/password to URL when fetching from mirror.
|
|
150
|
+
|
|
151
|
+
- Fix #998: Use the pure Python httpx library instead of aiohttp to prevent delays in supporting newest Python releases.
|
|
132
152
|
|
|
133
|
-
- Fix #931: Add ``mirror_no_project_list`` setting for mirror indexes that have no full project list like google cloud artifacts or if you want to prevent downloading the full list for huge indexes like PyPI.
|
|
134
153
|
|
|
135
154
|
|
|
136
155
|
Bug Fixes
|
|
137
156
|
---------
|
|
138
157
|
|
|
139
|
-
-
|
|
140
|
-
|
|
141
|
-
- Support changed default of ``enforce_content_length`` in urllib3 >= 2.
|
|
142
|
-
|
|
143
|
-
- Fix #934: Properly set PATH_INFO when outside URL is used with sub-path.
|
|
158
|
+
- Fix #996: support hashes other than sha256 in application/vnd.pypi.simple.v1+json responses.
|
|
144
159
|
|
|
145
|
-
-
|
|
160
|
+
- Only compare hostname instead of full URL prefix when parsing mirror packages to fix mirrors with basic authentication and absolute URLs. See #1006
|
|
146
161
|
|
|
147
|
-
- Fix wrong hash metadata introduced in 6.5.0 for toxresults which prevents replication. The metadata can be fixed by an export/import cycle.
|
|
148
162
|
|
|
149
163
|
|
|
150
|
-
6.
|
|
164
|
+
6.9.2 (2023-08-06)
|
|
151
165
|
==================
|
|
152
166
|
|
|
153
|
-
|
|
154
|
-
|
|
167
|
+
Bug Fixes
|
|
168
|
+
---------
|
|
155
169
|
|
|
156
|
-
-
|
|
170
|
+
- Prevent duplicates when adding values to lists in index configuration with ``+=`` operator.
|
|
157
171
|
|
|
158
172
|
|
|
173
|
+
6.9.1 (2023-07-02)
|
|
174
|
+
==================
|
|
175
|
+
|
|
159
176
|
Bug Fixes
|
|
160
177
|
---------
|
|
161
178
|
|
|
162
|
-
-
|
|
163
|
-
|
|
164
|
-
- Catch exceptions in async_httpget analog to httpget.
|
|
179
|
+
- Prevent error in find_pre_existing_file in case of incomplete metadata.
|
|
165
180
|
|
|
166
|
-
-
|
|
181
|
+
- Fix #980: Remove long deprecated backward compatibility for old pluggy versions to fix error with pluggy 1.1.0.
|
|
167
182
|
|
|
168
183
|
|
|
169
|
-
6.
|
|
184
|
+
6.9.0 (2023-05-23)
|
|
170
185
|
==================
|
|
171
186
|
|
|
172
187
|
Features
|
|
173
188
|
--------
|
|
174
189
|
|
|
175
|
-
-
|
|
176
|
-
|
|
177
|
-
- Automatically check for ``+files`` when using ``--replica-file-search-path``.
|
|
190
|
+
- Support export directory layout for ``--replica-file-search-path`` option.
|
|
178
191
|
|
|
179
|
-
-
|
|
192
|
+
- Fix #931: Add ``mirror_no_project_list`` setting for mirror indexes that have no full project list like google cloud artifacts or if you want to prevent downloading the full list for huge indexes like PyPI.
|
|
180
193
|
|
|
181
194
|
|
|
182
195
|
Bug Fixes
|
|
183
196
|
---------
|
|
184
197
|
|
|
185
|
-
-
|
|
198
|
+
- Keep a reference to async tasks to avoid their removal mid execution.
|
|
199
|
+
|
|
200
|
+
- Support changed default of ``enforce_content_length`` in urllib3 >= 2.
|
|
201
|
+
|
|
202
|
+
- Fix #934: Properly set PATH_INFO when outside URL is used with sub-path.
|
|
203
|
+
|
|
204
|
+
- Fix #945: Adapt FatalError to be usable as an async HTTP response when updating a project on a mirror.
|
|
205
|
+
|
|
206
|
+
- Fix wrong hash metadata introduced in 6.5.0 for toxresults which prevents replication. The metadata can be fixed by an export/import cycle.
|
|
186
207
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '6.11.0'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import hashlib
|
|
3
|
+
import itertools
|
|
3
4
|
import itsdangerous
|
|
4
5
|
import secrets
|
|
5
6
|
from .log import threadlog
|
|
@@ -53,7 +54,8 @@ class Auth:
|
|
|
53
54
|
# one of the plugins returned valid userinfo
|
|
54
55
|
# return union of all groups which may be contained in that info
|
|
55
56
|
groups = (ui.get('groups', []) for ui in userinfo_list)
|
|
56
|
-
return dict(status="ok", groups=sorted(
|
|
57
|
+
return dict(status="ok", groups=sorted(
|
|
58
|
+
set(itertools.chain.from_iterable(groups))))
|
|
57
59
|
|
|
58
60
|
def _validate(self, authuser, authpassword, request=None):
|
|
59
61
|
""" Validates user credentials.
|
|
@@ -131,7 +133,7 @@ class Auth:
|
|
|
131
133
|
username,
|
|
132
134
|
result.get("groups", []),
|
|
133
135
|
result.get("from_user_object", False)])
|
|
134
|
-
assert not isinstance(pseudopass, str) or pseudopass.encode('ascii')
|
|
136
|
+
assert not isinstance(pseudopass, str) or pseudopass.encode('ascii') # type: ignore[attr-defined]
|
|
135
137
|
return {"password": pseudopass,
|
|
136
138
|
"expiration": self.LOGIN_EXPIRATION}
|
|
137
139
|
|
|
@@ -179,7 +179,7 @@ def add_web_options(parser, pluginmanager):
|
|
|
179
179
|
|
|
180
180
|
def add_mirror_options(parser, pluginmanager):
|
|
181
181
|
parser.addoption(
|
|
182
|
-
"--mirror-cache-expiry", type=
|
|
182
|
+
"--mirror-cache-expiry", type=int, metavar="SECS",
|
|
183
183
|
default=DEFAULT_MIRROR_CACHE_EXPIRY,
|
|
184
184
|
help="(experimental) time after which projects in mirror indexes "
|
|
185
185
|
"are checked for new releases.")
|
|
@@ -420,7 +420,8 @@ def get_parser(pluginmanager):
|
|
|
420
420
|
"mirror of pypi.org and is created by default. "
|
|
421
421
|
"All indices are suitable for pip or easy_install usage "
|
|
422
422
|
"and setup.py upload ... invocations.",
|
|
423
|
-
add_help=False
|
|
423
|
+
add_help=False,
|
|
424
|
+
pluginmanager=pluginmanager)
|
|
424
425
|
addoptions(parser, pluginmanager)
|
|
425
426
|
pluginmanager.hook.devpiserver_add_parser_options(parser=parser)
|
|
426
427
|
return parser
|
|
@@ -439,7 +440,7 @@ def find_config_file():
|
|
|
439
440
|
if os.path.exists(config_file):
|
|
440
441
|
config_files.append(config_file)
|
|
441
442
|
if len(config_files) > 1:
|
|
442
|
-
log.
|
|
443
|
+
log.warning("Multiple configuration files found:\n%s", "\n".join(config_files))
|
|
443
444
|
if len(config_files):
|
|
444
445
|
return config_files[-1]
|
|
445
446
|
|
|
@@ -476,7 +477,7 @@ def default_getter(name, config_options, environ):
|
|
|
476
477
|
return
|
|
477
478
|
if name == "serverdir":
|
|
478
479
|
if "DEVPI_SERVERDIR" in environ:
|
|
479
|
-
log.
|
|
480
|
+
log.warning(
|
|
480
481
|
"Using deprecated DEVPI_SERVERDIR environment variable. "
|
|
481
482
|
"You should switch to use DEVPISERVER_SERVERDIR.")
|
|
482
483
|
return environ["DEVPI_SERVERDIR"]
|
|
@@ -540,8 +541,9 @@ def get_action_long_name(action):
|
|
|
540
541
|
|
|
541
542
|
|
|
542
543
|
class MyArgumentParser(argparse.ArgumentParser):
|
|
543
|
-
def __init__(self, *args, **kwargs):
|
|
544
|
+
def __init__(self, *args, pluginmanager=None, **kwargs):
|
|
544
545
|
self.addoption = self.add_argument
|
|
546
|
+
self.pluginmanager = pluginmanager
|
|
545
547
|
super(MyArgumentParser, self).__init__(*args, **kwargs)
|
|
546
548
|
|
|
547
549
|
def post_process_actions(self, defaultget=None):
|
|
@@ -573,6 +575,39 @@ class MyArgumentParser(argparse.ArgumentParser):
|
|
|
573
575
|
grp.addoption = grp.add_argument
|
|
574
576
|
return grp
|
|
575
577
|
|
|
578
|
+
def add_all_options(self):
|
|
579
|
+
addoptions(self, self.pluginmanager)
|
|
580
|
+
|
|
581
|
+
def add_configfile_option(self):
|
|
582
|
+
add_configfile_option(self, self.pluginmanager)
|
|
583
|
+
|
|
584
|
+
def add_export_options(self):
|
|
585
|
+
add_export_options(self, self.pluginmanager)
|
|
586
|
+
|
|
587
|
+
def add_hard_links_option(self):
|
|
588
|
+
add_hard_links_option(self, self.pluginmanager)
|
|
589
|
+
|
|
590
|
+
def add_help_option(self):
|
|
591
|
+
add_help_option(self, self.pluginmanager)
|
|
592
|
+
|
|
593
|
+
def add_import_options(self):
|
|
594
|
+
add_import_options(self, self.pluginmanager)
|
|
595
|
+
|
|
596
|
+
def add_init_options(self):
|
|
597
|
+
add_init_options(self, self.pluginmanager)
|
|
598
|
+
|
|
599
|
+
def add_master_url_option(self):
|
|
600
|
+
add_master_url_option(self, self.pluginmanager)
|
|
601
|
+
|
|
602
|
+
def add_role_option(self):
|
|
603
|
+
add_role_option(self, self.pluginmanager)
|
|
604
|
+
|
|
605
|
+
def add_secretfile_option(self):
|
|
606
|
+
add_secretfile_option(self, self.pluginmanager)
|
|
607
|
+
|
|
608
|
+
def add_storage_options(self):
|
|
609
|
+
add_storage_options(self, self.pluginmanager)
|
|
610
|
+
|
|
576
611
|
|
|
577
612
|
def new_secret():
|
|
578
613
|
return base64.b64encode(secrets.token_bytes(32))
|
|
@@ -591,7 +626,7 @@ class Config(object):
|
|
|
591
626
|
|
|
592
627
|
@cached_property
|
|
593
628
|
def waitress_info(self):
|
|
594
|
-
from .main import
|
|
629
|
+
from .main import Fatal
|
|
595
630
|
host = self.args.host
|
|
596
631
|
port = self.args.port
|
|
597
632
|
default_host_port = (host == 'localhost') and (port == 3141)
|
|
@@ -609,7 +644,7 @@ class Config(object):
|
|
|
609
644
|
port = None
|
|
610
645
|
if self.args.listen:
|
|
611
646
|
if not default_host_port:
|
|
612
|
-
|
|
647
|
+
raise Fatal("You can use either --listen or --host/--port, not both together.")
|
|
613
648
|
host = None
|
|
614
649
|
port = None
|
|
615
650
|
for listen in self.args.listen:
|
|
@@ -796,13 +831,15 @@ class Config(object):
|
|
|
796
831
|
self.nodeinfo["role"] = "standalone"
|
|
797
832
|
|
|
798
833
|
def _automatic_role(self, role):
|
|
799
|
-
from .main import
|
|
834
|
+
from .main import Fatal
|
|
800
835
|
if role == "replica" and not self.master_url:
|
|
801
|
-
|
|
802
|
-
|
|
836
|
+
raise Fatal(
|
|
837
|
+
"configuration error, masterurl isn't set in nodeinfo, but "
|
|
838
|
+
"role is set to replica")
|
|
803
839
|
if role != "replica" and self.master_url:
|
|
804
|
-
|
|
805
|
-
|
|
840
|
+
raise Fatal(
|
|
841
|
+
"configuration error, masterurl set in nodeinfo, but role "
|
|
842
|
+
"isn't set to replica")
|
|
806
843
|
if role != "replica":
|
|
807
844
|
self.master_url = None
|
|
808
845
|
if role == "master":
|
|
@@ -810,13 +847,13 @@ class Config(object):
|
|
|
810
847
|
self.nodeinfo["role"] = "standalone"
|
|
811
848
|
|
|
812
849
|
def _change_role(self, old_role, new_role):
|
|
813
|
-
from .main import
|
|
850
|
+
from .main import Fatal
|
|
814
851
|
if new_role == "replica":
|
|
815
852
|
if old_role and old_role != "replica":
|
|
816
|
-
|
|
817
|
-
|
|
853
|
+
msg = f"cannot run as replica, was previously run as {old_role}"
|
|
854
|
+
raise Fatal(msg)
|
|
818
855
|
if not self.master_url:
|
|
819
|
-
|
|
856
|
+
raise Fatal("need to specify --master-url to run as replica")
|
|
820
857
|
else:
|
|
821
858
|
self.master_url = None
|
|
822
859
|
self.nodeinfo["role"] = new_role
|
|
@@ -839,12 +876,13 @@ class Config(object):
|
|
|
839
876
|
self.nodeinfo.pop("masterurl", None)
|
|
840
877
|
|
|
841
878
|
def _storage_info_from_name(self, name, settings):
|
|
842
|
-
from .main import
|
|
879
|
+
from .main import Fatal
|
|
843
880
|
storages = self.pluginmanager.hook.devpiserver_storage_backend(settings=settings)
|
|
844
881
|
for storage in storages:
|
|
845
882
|
if storage['name'] == name:
|
|
846
883
|
return storage
|
|
847
|
-
|
|
884
|
+
msg = f"The backend {name!r} can't be found, is the plugin not installed?"
|
|
885
|
+
raise Fatal(msg)
|
|
848
886
|
|
|
849
887
|
def _storage_info(self):
|
|
850
888
|
name = self.storage_info["name"]
|
|
@@ -884,7 +922,7 @@ class Config(object):
|
|
|
884
922
|
secretfile = self.serverdir.join('.secret')
|
|
885
923
|
if not secretfile.check(file=True):
|
|
886
924
|
return None
|
|
887
|
-
log.
|
|
925
|
+
log.warning(
|
|
888
926
|
"Using deprecated existing secret file at '%s', use "
|
|
889
927
|
"--secretfile to explicitly provide the location." % secretfile)
|
|
890
928
|
return secretfile
|
|
@@ -892,25 +930,25 @@ class Config(object):
|
|
|
892
930
|
os.path.expanduser(self.args.secretfile))
|
|
893
931
|
|
|
894
932
|
def get_validated_secret(self):
|
|
895
|
-
from .main import
|
|
933
|
+
from .main import Fatal
|
|
896
934
|
import stat
|
|
897
935
|
if not self.secretfile.check(file=True):
|
|
898
|
-
|
|
936
|
+
raise Fatal("The given secret file doesn't exist.")
|
|
899
937
|
if self.secretfile.stat().mode & stat.S_IRWXO and sys.platform != "win32":
|
|
900
|
-
|
|
938
|
+
raise Fatal("The given secret file is world accessible, the access mode must be user accessible only (0600).")
|
|
901
939
|
if self.secretfile.stat().mode & stat.S_IRWXG and sys.platform != "win32":
|
|
902
|
-
|
|
940
|
+
raise Fatal("The given secret file is group accessible, the access mode must be user accessible only (0600).")
|
|
903
941
|
if self.secretfile.dirpath().stat().mode & stat.S_IWGRP and sys.platform != "win32":
|
|
904
|
-
|
|
942
|
+
raise Fatal("The folder of the given secret file is group writable, it must only be writable by the user.")
|
|
905
943
|
if self.secretfile.dirpath().stat().mode & stat.S_IWOTH and sys.platform != "win32":
|
|
906
|
-
|
|
944
|
+
raise Fatal("The folder of the given secret file is world writable, it must only be writable by the user.")
|
|
907
945
|
secret = self.secretfile.read_binary()
|
|
908
946
|
if len(secret) < 32:
|
|
909
|
-
|
|
947
|
+
raise Fatal(
|
|
910
948
|
"The secret in the given secret file is too short, "
|
|
911
949
|
"it should be at least 32 characters long.")
|
|
912
950
|
if len(set(secret)) < 6:
|
|
913
|
-
|
|
951
|
+
raise Fatal(
|
|
914
952
|
"The secret in the given secret file is too weak, "
|
|
915
953
|
"it should use less repetition.")
|
|
916
954
|
return secret
|
|
@@ -918,7 +956,7 @@ class Config(object):
|
|
|
918
956
|
@cached_property
|
|
919
957
|
def basesecret(self):
|
|
920
958
|
if self.secretfile is None:
|
|
921
|
-
log.
|
|
959
|
+
log.warning(
|
|
922
960
|
"No secret file provided, creating a new random secret. "
|
|
923
961
|
"Login tokens issued before are invalid. "
|
|
924
962
|
"Use --secretfile option to provide a persistent secret. "
|
|
@@ -970,21 +1008,20 @@ def getpath(path):
|
|
|
970
1008
|
def gensecret():
|
|
971
1009
|
from .log import configure_cli_logging
|
|
972
1010
|
from .log import threadlog as log
|
|
1011
|
+
from .main import CommandRunner
|
|
973
1012
|
from .main import Fatal
|
|
974
|
-
from .main import fatal
|
|
975
1013
|
import stat
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
parser = MyArgumentParser(
|
|
1014
|
+
with CommandRunner() as runner:
|
|
1015
|
+
parser = runner.create_parser(
|
|
979
1016
|
description="Create a random secret.",
|
|
980
1017
|
add_help=False)
|
|
981
|
-
add_help_option(
|
|
982
|
-
add_configfile_option(
|
|
983
|
-
add_secretfile_option(
|
|
984
|
-
config =
|
|
1018
|
+
parser.add_help_option()
|
|
1019
|
+
parser.add_configfile_option()
|
|
1020
|
+
parser.add_secretfile_option()
|
|
1021
|
+
config = runner.get_config(sys.argv, parser=parser)
|
|
985
1022
|
configure_cli_logging(config.args)
|
|
986
1023
|
if config.args.secretfile is None:
|
|
987
|
-
|
|
1024
|
+
raise Fatal("You need to provide a location for the secret file.")
|
|
988
1025
|
if not config.secretfile.exists():
|
|
989
1026
|
with config.secretfile.open("wb") as f:
|
|
990
1027
|
f.write(new_secret())
|
|
@@ -999,7 +1036,4 @@ def gensecret():
|
|
|
999
1036
|
# run checks
|
|
1000
1037
|
config.get_validated_secret()
|
|
1001
1038
|
log.info("Permissions of secret file look good.")
|
|
1002
|
-
|
|
1003
|
-
tw = py.io.TerminalWriter(sys.stderr)
|
|
1004
|
-
tw.line("fatal: %s" % e.args[0], red=True)
|
|
1005
|
-
return 1
|
|
1039
|
+
return runner.return_code
|