From 607aa9baf863e01122ee029e29da7d4fb54c3b95 Mon Sep 17 00:00:00 2001 From: JensDiemer Date: Sun, 15 Sep 2024 12:19:42 +0200 Subject: [PATCH 1/2] Update requirements --- .pre-commit-config.yaml | 2 +- README.md | 8 +- requirements.dev.txt | 416 ++++++++++++++++++++-------------------- requirements.txt | 48 ++--- 4 files changed, 242 insertions(+), 232 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e210176..61dd4fd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,6 @@ default_install_hook_types: repos: - repo: https://github.com/jedie/cli-base-utilities - rev: v0.10.3 + rev: v0.11.0 hooks: - id: update-readme-history diff --git a/README.md b/README.md index 5075fb9..84c585f 100644 --- a/README.md +++ b/README.md @@ -346,6 +346,8 @@ See also git tags: https://github.com/jedie/manageprojects/tags [comment]: <> (✂✂✂ auto generated history start ✂✂✂) +* [**dev**](https://github.com/jedie/manageprojects/compare/v0.18.0...main) + * 2024-09-15 - Update requirements * [v0.18.0](https://github.com/jedie/manageprojects/compare/v0.17.1...v0.18.0) * 2024-08-29 - Fix wrong "module" in publish call :( * 2024-08-29 - Fix wrong "distribution_name" in publish command @@ -371,15 +373,15 @@ See also git tags: https://github.com/jedie/manageprojects/tags * 2023-12-21 - Unify BASE_PATH / PACKAGE_ROOT etc. * 2023-12-21 - Apply manageprojects updates: Skip Python 3.9 support * 2023-12-21 - Update requirements + +
Expand older history entries ... + * [v0.16.2](https://github.com/jedie/manageprojects/compare/v0.16.1...v0.16.2) * 2023-12-16 - Update pre-commit-config * 2023-12-16 - Skip test_readme_history() on CI * 2023-12-16 - Add git hook "update-readme-history" * 2023-12-16 - Apply cookiecutter updates * 2023-12-16 - Update requirements - -
Expand older history entries ... - * [v0.16.1](https://github.com/jedie/manageprojects/compare/v0.16.0...v0.16.1) * 2023-12-05 - Fix "format file" and very verbose error output * [v0.16.0](https://github.com/jedie/manageprojects/compare/v0.15.4...v0.16.0) diff --git a/requirements.dev.txt b/requirements.dev.txt index aa80cef..53770c0 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -64,13 +64,13 @@ boolean-py==4.0 \ --hash=sha256:17b9a181630e43dde1851d42bef546d616d5d9b4480357514597e78b203d06e4 \ --hash=sha256:2876f2051d7d6394a531d82dc6eb407faa0b01a0a0b3083817ccd7323b8d96bd # via license-expression -build==1.2.1 \ - --hash=sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d \ - --hash=sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4 +build==1.2.2 \ + --hash=sha256:119b2fb462adef986483438377a13b2f42064a2a3a4161f24a0cca698a07ac8c \ + --hash=sha256:277ccc71619d98afdd841a0e96ac9fe1593b823af481d3b0cea748e8894e0613 # via pip-tools -bx-py-utils==98 \ - --hash=sha256:30fb49b0f0b21c9fbd544895480a1aa9da63f410572e4b46afbca792e64cec98 \ - --hash=sha256:ac8b193ff117420b77b43c043e50b195d6aa9ee7e72e22265ca699730b61f71f +bx-py-utils==101 \ + --hash=sha256:2aa295cde55da99b77f5f2f8b5bf8c0bec7e0046511832989ecbb1a43183cf75 \ + --hash=sha256:eece1f0b1e3c091d38f3013984056b05f43c6a0fd716489cf337d89df802ab59 # via # cli-base-utilities # manageprojects (pyproject.toml) @@ -84,78 +84,78 @@ cachetools==5.5.0 \ --hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \ --hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a # via tox -certifi==2024.7.4 \ - --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ - --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 +certifi==2024.8.30 \ + --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \ + --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9 # via requests -cffi==1.17.0 \ - --hash=sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f \ - --hash=sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab \ - --hash=sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499 \ - --hash=sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058 \ - --hash=sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693 \ - --hash=sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb \ - --hash=sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377 \ - --hash=sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885 \ - --hash=sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2 \ - --hash=sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401 \ - --hash=sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4 \ - --hash=sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b \ - --hash=sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59 \ - --hash=sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f \ - --hash=sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c \ - --hash=sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555 \ - --hash=sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa \ - --hash=sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424 \ - --hash=sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb \ - --hash=sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2 \ - --hash=sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8 \ - --hash=sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e \ - --hash=sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9 \ - --hash=sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82 \ - --hash=sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828 \ - --hash=sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759 \ - --hash=sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc \ - --hash=sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118 \ - --hash=sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf \ - --hash=sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932 \ - --hash=sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a \ - --hash=sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29 \ - --hash=sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206 \ - --hash=sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2 \ - --hash=sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c \ - --hash=sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c \ - --hash=sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0 \ - --hash=sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a \ - --hash=sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195 \ - --hash=sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6 \ - --hash=sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9 \ - --hash=sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc \ - --hash=sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb \ - --hash=sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0 \ - --hash=sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7 \ - --hash=sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb \ - --hash=sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a \ - --hash=sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492 \ - --hash=sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720 \ - --hash=sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42 \ - --hash=sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7 \ - --hash=sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d \ - --hash=sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d \ - --hash=sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb \ - --hash=sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4 \ - --hash=sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2 \ - --hash=sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b \ - --hash=sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8 \ - --hash=sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e \ - --hash=sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204 \ - --hash=sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3 \ - --hash=sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150 \ - --hash=sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4 \ - --hash=sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76 \ - --hash=sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e \ - --hash=sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb \ - --hash=sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91 +cffi==1.17.1 \ + --hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \ + --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \ + --hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \ + --hash=sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15 \ + --hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \ + --hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \ + --hash=sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8 \ + --hash=sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36 \ + --hash=sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17 \ + --hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \ + --hash=sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc \ + --hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \ + --hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \ + --hash=sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702 \ + --hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \ + --hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \ + --hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \ + --hash=sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6 \ + --hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \ + --hash=sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b \ + --hash=sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e \ + --hash=sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be \ + --hash=sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c \ + --hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \ + --hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \ + --hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \ + --hash=sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8 \ + --hash=sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1 \ + --hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \ + --hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \ + --hash=sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67 \ + --hash=sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595 \ + --hash=sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0 \ + --hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \ + --hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \ + --hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \ + --hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \ + --hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \ + --hash=sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3 \ + --hash=sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16 \ + --hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \ + --hash=sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e \ + --hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \ + --hash=sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964 \ + --hash=sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c \ + --hash=sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576 \ + --hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \ + --hash=sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3 \ + --hash=sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662 \ + --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \ + --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \ + --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \ + --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ + --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \ + --hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \ + --hash=sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14 \ + --hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \ + --hash=sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9 \ + --hash=sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7 \ + --hash=sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382 \ + --hash=sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a \ + --hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \ + --hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \ + --hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \ + --hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \ + --hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \ + --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b # via cryptography cfgv==3.4.0 \ --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \ @@ -259,9 +259,9 @@ charset-normalizer==3.3.2 \ --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 # via requests -cli-base-utilities==0.10.3 \ - --hash=sha256:5c54cdd5e5122abf6a86aa6926b4b2dbc6632c113885692c8d95f531bb2b181b \ - --hash=sha256:95b1119d9f4bb335974faf655e853f58bdd04af0bac99d22005b6fb79b9e4b14 +cli-base-utilities==0.11.0 \ + --hash=sha256:2c674f3af4898f97d101f6687fb45e85a4b996b93fb9a155a29b4daaf302dc43 \ + --hash=sha256:fc503f8df7e19653167ebd12551718aa0c114114b959f177be2092eab1ce1b11 # via manageprojects (pyproject.toml) click==8.1.7 \ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ @@ -359,34 +359,34 @@ coverage==7.6.1 \ --hash=sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234 \ --hash=sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc # via manageprojects (pyproject.toml) -cryptography==43.0.0 \ - --hash=sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709 \ - --hash=sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069 \ - --hash=sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2 \ - --hash=sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b \ - --hash=sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e \ - --hash=sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70 \ - --hash=sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778 \ - --hash=sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22 \ - --hash=sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895 \ - --hash=sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf \ - --hash=sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431 \ - --hash=sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f \ - --hash=sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947 \ - --hash=sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74 \ - --hash=sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc \ - --hash=sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66 \ - --hash=sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66 \ - --hash=sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf \ - --hash=sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f \ - --hash=sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5 \ - --hash=sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e \ - --hash=sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f \ - --hash=sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55 \ - --hash=sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1 \ - --hash=sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47 \ - --hash=sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5 \ - --hash=sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0 +cryptography==43.0.1 \ + --hash=sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494 \ + --hash=sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806 \ + --hash=sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d \ + --hash=sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062 \ + --hash=sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2 \ + --hash=sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4 \ + --hash=sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1 \ + --hash=sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85 \ + --hash=sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84 \ + --hash=sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042 \ + --hash=sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d \ + --hash=sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962 \ + --hash=sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2 \ + --hash=sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa \ + --hash=sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d \ + --hash=sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365 \ + --hash=sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96 \ + --hash=sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47 \ + --hash=sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d \ + --hash=sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d \ + --hash=sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c \ + --hash=sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb \ + --hash=sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277 \ + --hash=sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172 \ + --hash=sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034 \ + --hash=sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a \ + --hash=sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289 # via secretstorage cyclonedx-python-lib==7.6.0 \ --hash=sha256:30655e89e5f987dc8d57835919748d71589fafeb33ff1dec45048eb72eda3cf9 \ @@ -417,9 +417,9 @@ docutils==0.21.2 \ editorconfig==0.12.4 \ --hash=sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80 # via manageprojects (pyproject.toml) -filelock==3.15.4 \ - --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \ - --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7 +filelock==3.16.0 \ + --hash=sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec \ + --hash=sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609 # via # cachecontrol # tox @@ -446,17 +446,17 @@ html5lib==1.1 \ --hash=sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d \ --hash=sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f # via pip-audit -identify==2.6.0 \ - --hash=sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf \ - --hash=sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0 +identify==2.6.1 \ + --hash=sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0 \ + --hash=sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98 # via pre-commit -idna==3.8 \ - --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \ - --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603 +idna==3.9 \ + --hash=sha256:69297d5da0cc9281c77efffb4e730254dd45943f45bbfb461de5991713989b1e \ + --hash=sha256:e5c5dafde284f26e9e0f28f6ea2d6400abd5ca099864a67f576f3981c6476124 # via requests -importlib-metadata==8.4.0 \ - --hash=sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1 \ - --hash=sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5 +importlib-metadata==8.5.0 \ + --hash=sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b \ + --hash=sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7 # via twine isort==5.13.2 \ --hash=sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109 \ @@ -566,69 +566,77 @@ mdurl==0.1.2 \ --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba # via markdown-it-py -more-itertools==10.4.0 \ - --hash=sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27 \ - --hash=sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923 +more-itertools==10.5.0 \ + --hash=sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef \ + --hash=sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6 # via # jaraco-classes # jaraco-functools -msgpack==1.0.8 \ - --hash=sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982 \ - --hash=sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3 \ - --hash=sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40 \ - --hash=sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee \ - --hash=sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693 \ - --hash=sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950 \ - --hash=sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151 \ - --hash=sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24 \ - --hash=sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305 \ - --hash=sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b \ - --hash=sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c \ - --hash=sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659 \ - --hash=sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d \ - --hash=sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18 \ - --hash=sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746 \ - --hash=sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868 \ - --hash=sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2 \ - --hash=sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba \ - --hash=sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228 \ - --hash=sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2 \ - --hash=sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273 \ - --hash=sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c \ - --hash=sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653 \ - --hash=sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a \ - --hash=sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596 \ - --hash=sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd \ - --hash=sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8 \ - --hash=sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa \ - --hash=sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85 \ - --hash=sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc \ - --hash=sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836 \ - --hash=sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3 \ - --hash=sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58 \ - --hash=sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128 \ - --hash=sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db \ - --hash=sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f \ - --hash=sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77 \ - --hash=sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad \ - --hash=sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13 \ - --hash=sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8 \ - --hash=sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b \ - --hash=sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a \ - --hash=sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543 \ - --hash=sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b \ - --hash=sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce \ - --hash=sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d \ - --hash=sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a \ - --hash=sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c \ - --hash=sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f \ - --hash=sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e \ - --hash=sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011 \ - --hash=sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04 \ - --hash=sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480 \ - --hash=sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a \ - --hash=sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d \ - --hash=sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d +msgpack==1.1.0 \ + --hash=sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b \ + --hash=sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf \ + --hash=sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca \ + --hash=sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330 \ + --hash=sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f \ + --hash=sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f \ + --hash=sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39 \ + --hash=sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247 \ + --hash=sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b \ + --hash=sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c \ + --hash=sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7 \ + --hash=sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044 \ + --hash=sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6 \ + --hash=sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b \ + --hash=sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0 \ + --hash=sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2 \ + --hash=sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468 \ + --hash=sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7 \ + --hash=sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734 \ + --hash=sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434 \ + --hash=sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325 \ + --hash=sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1 \ + --hash=sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846 \ + --hash=sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88 \ + --hash=sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420 \ + --hash=sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e \ + --hash=sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2 \ + --hash=sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59 \ + --hash=sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb \ + --hash=sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68 \ + --hash=sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915 \ + --hash=sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f \ + --hash=sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701 \ + --hash=sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b \ + --hash=sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d \ + --hash=sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa \ + --hash=sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d \ + --hash=sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd \ + --hash=sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc \ + --hash=sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48 \ + --hash=sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb \ + --hash=sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74 \ + --hash=sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b \ + --hash=sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346 \ + --hash=sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e \ + --hash=sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6 \ + --hash=sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5 \ + --hash=sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f \ + --hash=sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5 \ + --hash=sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b \ + --hash=sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c \ + --hash=sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f \ + --hash=sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec \ + --hash=sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8 \ + --hash=sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5 \ + --hash=sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d \ + --hash=sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e \ + --hash=sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e \ + --hash=sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870 \ + --hash=sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f \ + --hash=sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96 \ + --hash=sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c \ + --hash=sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd \ + --hash=sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788 # via cachecontrol mypy==1.11.2 \ --hash=sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36 \ @@ -728,9 +736,9 @@ pkginfo==1.10.0 \ --hash=sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297 \ --hash=sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097 # via twine -platformdirs==4.2.2 \ - --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ - --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 +platformdirs==4.3.3 \ + --hash=sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5 \ + --hash=sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0 # via # black # tox @@ -885,9 +893,9 @@ rfc3986==2.0.0 \ --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c # via twine -rich==13.8.0 \ - --hash=sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc \ - --hash=sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4 +rich==13.8.1 \ + --hash=sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06 \ + --hash=sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a # via # cli-base-utilities # cookiecutter @@ -940,9 +948,9 @@ tomlkit==0.13.2 \ # via # cli-base-utilities # manageprojects (pyproject.toml) -tox==4.18.0 \ - --hash=sha256:0a457400cf70615dc0627eb70d293e80cd95d8ce174bb40ac011011f0c03a249 \ - --hash=sha256:5dfa1cab9f146becd6e351333a82f9e0ade374451630ba65ee54584624c27b58 +tox==4.18.1 \ + --hash=sha256:35d472032ee1f73fe20c3e0e73d7073a4e85075c86ff02c576f9fc7c6a15a578 \ + --hash=sha256:3c0c96bc3a568a5c7e66387a4cfcf8c875b52e09f4d47c9f7a277ec82f1a0b11 # via manageprojects (pyproject.toml) twine==5.1.1 \ --hash=sha256:215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997 \ @@ -952,9 +960,9 @@ typeguard==4.3.0 \ --hash=sha256:4d24c5b39a117f8a895b9da7a9b3114f04eb63bade45a4492de49b175b6f7dfa \ --hash=sha256:92ee6a0aec9135181eae6067ebd617fd9de8d75d714fb548728a4933b1dea651 # via manageprojects (pyproject.toml) -types-python-dateutil==2.9.0.20240821 \ - --hash=sha256:9649d1dcb6fef1046fb18bebe9ea2aa0028b160918518c34589a46045f6ebd98 \ - --hash=sha256:f5889fcb4e63ed4aaa379b44f93c32593d50b9a94c9a60a0c854d8cc3511cd57 +types-python-dateutil==2.9.0.20240906 \ + --hash=sha256:27c8cc2d058ccb14946eebcaaa503088f4f6dbc4fb6093d3d456a49aef2753f6 \ + --hash=sha256:9706c3b68284c25adffc47319ecc7947e5bb86b3773f843c73906fd598bc176e # via arrow typing-extensions==4.12.2 \ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ @@ -963,16 +971,16 @@ typing-extensions==4.12.2 \ # mypy # rich-click # typeguard -urllib3==2.2.2 \ - --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ - --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 +urllib3==2.2.3 \ + --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \ + --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9 # via # manageprojects (pyproject.toml) # requests # twine -virtualenv==20.26.3 \ - --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \ - --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589 +virtualenv==20.26.4 \ + --hash=sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55 \ + --hash=sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c # via # pre-commit # tox @@ -984,9 +992,9 @@ wheel==0.44.0 \ --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ --hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49 # via pip-tools -zipp==3.20.1 \ - --hash=sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064 \ - --hash=sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b +zipp==3.20.2 \ + --hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \ + --hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: @@ -996,7 +1004,7 @@ pip==24.2 \ # via # pip-api # pip-tools -setuptools==74.0.0 \ - --hash=sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f \ - --hash=sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e +setuptools==74.1.2 \ + --hash=sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308 \ + --hash=sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6 # via pip-tools diff --git a/requirements.txt b/requirements.txt index 3807a00..ae9bef2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -56,13 +56,13 @@ black==24.8.0 \ --hash=sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920 \ --hash=sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1 # via darker -bx-py-utils==98 \ - --hash=sha256:30fb49b0f0b21c9fbd544895480a1aa9da63f410572e4b46afbca792e64cec98 \ - --hash=sha256:ac8b193ff117420b77b43c043e50b195d6aa9ee7e72e22265ca699730b61f71f +bx-py-utils==101 \ + --hash=sha256:2aa295cde55da99b77f5f2f8b5bf8c0bec7e0046511832989ecbb1a43183cf75 \ + --hash=sha256:eece1f0b1e3c091d38f3013984056b05f43c6a0fd716489cf337d89df802ab59 # via cli-base-utilities -certifi==2024.7.4 \ - --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ - --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 +certifi==2024.8.30 \ + --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \ + --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9 # via requests chardet==5.2.0 \ --hash=sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7 \ @@ -160,9 +160,9 @@ charset-normalizer==3.3.2 \ --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 # via requests -cli-base-utilities==0.10.3 \ - --hash=sha256:5c54cdd5e5122abf6a86aa6926b4b2dbc6632c113885692c8d95f531bb2b181b \ - --hash=sha256:95b1119d9f4bb335974faf655e853f58bdd04af0bac99d22005b6fb79b9e4b14 +cli-base-utilities==0.11.0 \ + --hash=sha256:2c674f3af4898f97d101f6687fb45e85a4b996b93fb9a155a29b4daaf302dc43 \ + --hash=sha256:fc503f8df7e19653167ebd12551718aa0c114114b959f177be2092eab1ce1b11 # via manageprojects (pyproject.toml) click==8.1.7 \ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ @@ -212,9 +212,9 @@ graylint==1.1.1 \ --hash=sha256:0fd8e02972ca03d0ef2bf0adea76b5343efcd492d7afb5f658f3e3a724f55a36 \ --hash=sha256:b7e0eab6c159684dbf5ef84e942c3340f6a6549b02a3d11b1a1763cc4f8f0593 # via darker -idna==3.8 \ - --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \ - --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603 +idna==3.9 \ + --hash=sha256:69297d5da0cc9281c77efffb4e730254dd45943f45bbfb461de5991713989b1e \ + --hash=sha256:e5c5dafde284f26e9e0f28f6ea2d6400abd5ca099864a67f576f3981c6476124 # via requests isort==5.13.2 \ --hash=sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109 \ @@ -345,9 +345,9 @@ pathspec==0.12.1 \ --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 # via black -platformdirs==4.2.2 \ - --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ - --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 +platformdirs==4.3.3 \ + --hash=sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5 \ + --hash=sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0 # via black pycodestyle==2.12.1 \ --hash=sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3 \ @@ -445,9 +445,9 @@ requests==2.32.3 \ --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 # via cookiecutter -rich==13.8.0 \ - --hash=sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc \ - --hash=sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4 +rich==13.8.1 \ + --hash=sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06 \ + --hash=sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a # via # cli-base-utilities # cookiecutter @@ -487,9 +487,9 @@ tomlkit==0.13.2 \ # via # cli-base-utilities # manageprojects (pyproject.toml) -types-python-dateutil==2.9.0.20240821 \ - --hash=sha256:9649d1dcb6fef1046fb18bebe9ea2aa0028b160918518c34589a46045f6ebd98 \ - --hash=sha256:f5889fcb4e63ed4aaa379b44f93c32593d50b9a94c9a60a0c854d8cc3511cd57 +types-python-dateutil==2.9.0.20240906 \ + --hash=sha256:27c8cc2d058ccb14946eebcaaa503088f4f6dbc4fb6093d3d456a49aef2753f6 \ + --hash=sha256:9706c3b68284c25adffc47319ecc7947e5bb86b3773f843c73906fd598bc176e # via arrow typing-extensions==4.12.2 \ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ @@ -497,7 +497,7 @@ typing-extensions==4.12.2 \ # via # mypy # rich-click -urllib3==2.2.2 \ - --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ - --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 +urllib3==2.2.3 \ + --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \ + --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9 # via requests From 44addbf4eb6b7b7da58236543167e1dccd22465f Mon Sep 17 00:00:00 2001 From: JensDiemer Date: Sun, 15 Sep 2024 01:17:23 +0200 Subject: [PATCH 2/2] NEW: setup_python.py Download and setup redistributable Python Interpreter from https://github.com/indygreg/python-build-standalone/ if needed ;) --- .github/workflows/setup_python.yml | 38 ++ README.md | 6 +- docs/install_python.md | 2 +- docs/setup_python.md | 135 +++++ manageprojects/__init__.py | 2 +- manageprojects/install_python.py | 14 +- manageprojects/setup_python.py | 520 ++++++++++++++++++ manageprojects/setup_python_example.sh | 10 + ...s.py => docwrite_macros_install_python.py} | 0 .../tests/docwrite_macros_setup_python.py | 55 ++ manageprojects/tests/test_install_python.py | 2 +- manageprojects/tests/test_setup_python.py | 54 ++ .../utilities/include_install_python.py | 2 +- .../utilities/include_setup_python.py | 58 ++ pyproject.toml | 2 +- 15 files changed, 886 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/setup_python.yml create mode 100644 docs/setup_python.md create mode 100644 manageprojects/setup_python.py create mode 100644 manageprojects/setup_python_example.sh rename manageprojects/tests/{docwrite_macros.py => docwrite_macros_install_python.py} (100%) create mode 100644 manageprojects/tests/docwrite_macros_setup_python.py create mode 100644 manageprojects/tests/test_setup_python.py create mode 100644 manageprojects/utilities/include_setup_python.py diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml new file mode 100644 index 0000000..910ea46 --- /dev/null +++ b/.github/workflows/setup_python.yml @@ -0,0 +1,38 @@ +name: setup-python + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.12", "3.11", "3.10", "3.9"] + steps: + - uses: actions/checkout@v4 + + - name: 'Set up Python ${{ matrix.python-version }}' + uses: actions/setup-python@v5 + # https://github.com/marketplace/actions/setup-python + with: + python-version: '${{ matrix.python-version }}' + + - name: 'Just call --help with Python v${{ matrix.python-version }}' + run: | + python3 manageprojects/setup_python.py --help + + - name: 'Call setup python script with Python v${{ matrix.python-version }}' + env: + PYTHONUNBUFFERED: 1 + PYTHONWARNINGS: always + run: | + sudo python3 manageprojects/setup_python.py -vv + + - name: 'Test the installed interpreter' + run: | + $(python3 manageprojects/setup_python.py) -VV diff --git a/README.md b/README.md index 84c585f..360540a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ Besides this, `manageprojects` also includes other generic helper for Python pac * `publish_package()` - Build and upload a new release to PyPi, but with many pre-checks. * `format-file` - Format/Check a Python source file with Darker & Co., useful as IDE action. - * `install_python.py` - Install Python interpreter, if needed, from official Python FTP server, verified. + * `install_python.py` - [Install Python interpreter, if needed, from official Python FTP server, verified.](https://github.com/jedie/manageprojects/blob/main/docs/install_python.md) + * `setup_python.py` - [Download and setup redistributable Python Interpreter, if needed.](https://github.com/jedie/manageprojects/blob/main/docs/setup_python.md) Read below the `Helper` section. @@ -346,7 +347,8 @@ See also git tags: https://github.com/jedie/manageprojects/tags [comment]: <> (✂✂✂ auto generated history start ✂✂✂) -* [**dev**](https://github.com/jedie/manageprojects/compare/v0.18.0...main) +* [v0.19.0](https://github.com/jedie/manageprojects/compare/v0.18.0...v0.19.0) + * 2024-09-15 - NEW: setup_python.py * 2024-09-15 - Update requirements * [v0.18.0](https://github.com/jedie/manageprojects/compare/v0.17.1...v0.18.0) * 2024-08-29 - Fix wrong "module" in publish call :( diff --git a/docs/install_python.md b/docs/install_python.md index 0bab30e..e08cedc 100644 --- a/docs/install_python.md +++ b/docs/install_python.md @@ -54,7 +54,7 @@ class IncludeInstallPythonTestCase(IncludeInstallPythonBaseTestCase): # Set the path where the `install_python.py` should be copied to: DESTINATION_PATH = Path(your_package.__file__).parent) / 'install_python.py' - # Just call the method in a test, it will pass, if the file is up2date: + # The test will pass, if the file is up2date, if it's update the script! def test_install_python_is_up2date(self): self.auto_update_install_python() ``` diff --git a/docs/setup_python.md b/docs/setup_python.md new file mode 100644 index 0000000..5576e1e --- /dev/null +++ b/docs/setup_python.md @@ -0,0 +1,135 @@ +# Boot Redistributable Python + +This is a standalone script (no dependencies) to download and setup +https://github.com/indygreg/python-build-standalone/ redistributable Python interpreter. +But only if it's needed! + +Minimal version to used this script is Python v3.9. + +The downloaded archive will be verified with the hash checksum. + +The download will be only done, if the system Python is not the same major version as requested +and if the local Python is not up-to-date. + +## CLI + +The CLI interface looks like e.g.: + +```shell +$ python3 setup_python.py --help + +usage: setup_python.py [-h] [-v] [--skip-temp-deletion] [--force-update] [major_version] + +Download and setup redistributable Python Interpreter from https://github.com/indygreg/python-build-standalone/ if +needed ;) + +positional arguments: + major_version Specify the Python version like: 3.10, 3.11, 3.12, ... (default: 3.12) + +options: + -h, --help show this help message and exit + -v, --verbose Increase verbosity level (can be used multiple times, e.g.: -vv) (default: 0) + --skip-temp-deletion Skip deletion of temporary files (default: False) + --force-update Update local Python interpreter, even if it is up-to-date (default: False) + +``` + +## Include in own projects + +There is a unittest base class to include `setup_python.py` script in your project. +If will check if the file is up2date and if not, it will update it. + +Just include `manageprojects` as a dev dependency in your project. +And add a test like this: + +```python +class IncludeSetupPythonTestCase(IncludeSetupPythonBaseTestCase): + + # Set the path where the `setup_python.py` should be copied to: + DESTINATION_PATH = Path(your_package.__file__).parent) / 'setup_python.py' + + # The test will pass, if the file is up2date, if it's update the script! + def test_setup_python_is_up2date(self): + self.auto_update_setup_python() +``` + +Feel free to do it in a completely different way, this is just a suggestion ;) + +## Workflow - 1. Check system Python + +If the system Python is the same major version as the required Python, we skip the download. + +The script just returns the path to the system Python interpreter. + +A local installed interpreter (e.g. in "~/.local") will be auto updated. + +## Workflow - 2. Collect latest release data + +We fetch the latest release data from the GitHub API: +https://raw.githubusercontent.com/indygreg/python-build-standalone/latest-release/latest-release.json + +## Workflow - 3. Obtaining optimized Python distribution + +See: https://gregoryszorc.com/docs/python-build-standalone/main/running.html + +We choose the optimized variant based on the priority list: + +1. `pgo+lto` +2. `pgo` +3. `lto` + +For `x86-64` Linux we check the CPU flags from `/proc/cpuinfo` to determine the best variant. + +The "debug" build are ignored. + +## Workflow - 4. Check existing Python + +If the latest Python version is already installed, we skip the download. + +## Workflow - 4. Download and verify Archive + +All downloads will be done with a secure connection (SSL) and server authentication. + +If the latest Python version is already installed, we skip the download. + +Download will be done in a temporary directory. + +We download the archive file and the hash file for verification: + +* Archive extension: `.tar.zst` +* Hash extension: `.tar.zst.sha256` + +We check the file hash after downloading the archive. + +## Workflow - 5. Add info JSON + +We add the file `info.json` with all relevant information. + +## Workflow - 6. Setup Python + +We add a shell script to `~/.local/bin/pythonX.XX` to start the Python interpreter. + +We display version information from Python and pip on `stderr`. + +The extracted Python will be moved to the final destination in `~/.local/pythonX.XX/`. + +The script set's the correct `PYTHONHOME` environment variable. + +## Workflow - 7. print the path + +If no errors occurred, the path to the Python interpreter will be printed to `stdout`. +So it's usable in shell scripts, like: + +```shell +#!/usr/bin/env sh + +set -e + +PY_313_BIN=$(python3 setup_python.py -v 3.13) +echo "Python 3.13 used from: '${PY_313_BIN}'" + +set -x + +${PY_313_BIN} -VV + +``` \ No newline at end of file diff --git a/manageprojects/__init__.py b/manageprojects/__init__.py index 313bea9..e70d607 100644 --- a/manageprojects/__init__.py +++ b/manageprojects/__init__.py @@ -3,5 +3,5 @@ Manage Python / Django projects """ -__version__ = '0.18.0' +__version__ = '0.19.0' __author__ = 'Jens Diemer ' diff --git a/manageprojects/install_python.py b/manageprojects/install_python.py index 8367a6d..71945be 100644 --- a/manageprojects/install_python.py +++ b/manageprojects/install_python.py @@ -38,12 +38,12 @@ """DocWrite: install_python.md # Install Python Interpreter Download Python source code from official Python FTP server: -DocWriteMacro: manageprojects.tests.docwrite_macros.ftp_url""" +DocWriteMacro: manageprojects.tests.docwrite_macros_install_python.ftp_url""" PY_FTP_INDEX_URL = 'https://www.python.org/ftp/python/' """DocWrite: install_python.md ## Supported Python Versions The following major Python versions are supported and verified with GPG keys: -DocWriteMacro: manageprojects.tests.docwrite_macros.supported_python_versions +DocWriteMacro: manageprojects.tests.docwrite_macros_install_python.supported_python_versions The GPG keys taken from the official Python download page: https://www.python.org/downloads/""" GPG_KEY_IDS = { # Thomas Wouters (3.12.x and 3.13.x source files and tags): @@ -58,7 +58,7 @@ """DocWrite: install_python.md ## Workflow - 3. Check local installed Python We assume that the `make altinstall` will install local Python interpreter into: -DocWriteMacro: manageprojects.tests.docwrite_macros.default_install_prefix +DocWriteMacro: manageprojects.tests.docwrite_macros_install_python.default_install_prefix See: https://docs.python.org/3/using/configure.html#cmdoption-prefix""" DEFAULT_INSTALL_PREFIX = '/usr/local' @@ -198,7 +198,7 @@ def install_python( """DocWrite: install_python.md ## Workflow - 2. Get latest Python release We fetch the latest Python release from the Python FTP server, from: - DocWriteMacro: manageprojects.tests.docwrite_macros.ftp_url""" + DocWriteMacro: manageprojects.tests.docwrite_macros_install_python.ftp_url""" # Get latest full version number of Python from Python FTP: py_required_version = get_latest_versions( html=get_html_page(PY_FTP_INDEX_URL), @@ -225,7 +225,7 @@ def install_python( """DocWrite: install_python.md ## Workflow - 4. Download Python sources The download will be done in a temporary directory. The directory will be deleted after the installation. This can be skipped via CLI argument. The directory will be prefixed with: - DocWriteMacro: manageprojects.tests.docwrite_macros.temp_prefix""" + DocWriteMacro: manageprojects.tests.docwrite_macros_install_python.temp_prefix""" with TemporaryDirectory(prefix=TEMP_PREFIX, delete=delete_temp) as temp_path: base_url = f'{PY_FTP_INDEX_URL}{py_required_version}' @@ -291,7 +291,7 @@ def get_parser() -> argparse.ArgumentParser: ```shell $ python3 install_python.py --help - DocWriteMacro: manageprojects.tests.docwrite_macros.help + DocWriteMacro: manageprojects.tests.docwrite_macros_install_python.help ``` """ parser = argparse.ArgumentParser( @@ -349,6 +349,6 @@ def main() -> Path: """DocWrite: install_python.md ## Workflow - 7. print the path If no errors occurred, the path to the Python interpreter will be printed to `stdout`. So it's usable in shell scripts, like: - DocWriteMacro: manageprojects.tests.docwrite_macros.example_shell_script + DocWriteMacro: manageprojects.tests.docwrite_macros_install_python.example_shell_script """ print(python_path) diff --git a/manageprojects/setup_python.py b/manageprojects/setup_python.py new file mode 100644 index 0000000..87a2e55 --- /dev/null +++ b/manageprojects/setup_python.py @@ -0,0 +1,520 @@ +#!/usr/bin/env python3 + +""" + DocWrite: setup_python.md # Boot Redistributable Python + + This is a standalone script (no dependencies) to download and setup + https://github.com/indygreg/python-build-standalone/ redistributable Python interpreter. + But only if it's needed! +""" +from __future__ import annotations + +import argparse +import dataclasses +import datetime +import hashlib +import json +import logging +import platform +import re +import shlex +import shutil +import ssl +import subprocess +import sys +import tempfile +import time +from pathlib import Path +from urllib import request + + +"""DocWrite: setup_python.md # Boot Redistributable Python +Minimal version to used this script is Python v3.9.""" +assert sys.version_info >= (3, 9), f'Python version {sys.version_info} is too old!' + + +DEFAULT_MAJOR_VERSION = '3.12' +GUTHUB_PROJECT = 'indygreg/python-build-standalone' +LASTEST_RELEASE_URL = f'https://raw.githubusercontent.com/{GUTHUB_PROJECT}/latest-release/latest-release.json' +HASH_NAME = 'sha256' +ARCHIVE_EXTENSION = '.tar.zst' +ARCHIVE_HASH_EXTENSION = f'.tar.zst.{HASH_NAME}' + +OPTIMIZATION_PRIORITY = ['pgo+lto', 'pgo', 'lto'] +TEMP_PREFIX = 'redist_python_' +DOWNLOAD_CHUNK_SIZE = 512 * 1024 # 512 KiB + +logger = logging.getLogger(__name__) + + +def assert_is_dir(path): + if not isinstance(path, Path): + path = Path(path) + + if not path.is_dir(): + raise NotADirectoryError(f'Directory does not exists: "{path}"') + + +def assert_is_file(path): + if not isinstance(path, Path): + path = Path(path) + + assert_is_dir(path.parent) + + if not path.is_file(): + raise FileNotFoundError(f'File does not exists: "{path}"') + + +class TemporaryDirectory: + """tempfile.TemporaryDirectory in Python 3.9 has no "delete", yet.""" + + def __init__(self, prefix, delete: bool): + self.prefix = prefix + self.delete = delete + + def __enter__(self) -> Path: + self.temp_path = Path(tempfile.mkdtemp(prefix=self.prefix)) + return self.temp_path + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.delete: + shutil.rmtree(self.temp_path, ignore_errors=True) + if exc_type: + return False + + +def urlopen(url: str): + print(f'Fetching {url}', file=sys.stderr) + """DocWrite: setup_python.md ## Workflow - 4. Download and verify Archive + All downloads will be done with a secure connection (SSL) and server authentication.""" + context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH) + return request.urlopen(url=url, context=context) + + +def fetch(url: str) -> bytes: + return urlopen(url).read() + + +def fetch_json(url: str) -> dict: + return json.loads(fetch(url)) + + +def download(*, url: str, dst_path: Path, total_size: int, hash_name: str, hash_value: str) -> Path: + """DocWrite: setup_python.md # Boot Redistributable Python + The downloaded archive will be verified with the hash checksum. + """ + filename = Path(url).name + file_path = dst_path / filename + logger.debug('Download %s into %s...', url, file_path) + + file_hash = hashlib.new(hash_name) + response = urlopen(url) + next_update = time.monotonic() + 1 + with file_path.open('wb') as f: + while True: + chunk = response.read(DOWNLOAD_CHUNK_SIZE) + if not chunk: + break + f.write(chunk) + file_hash.update(chunk) + if time.monotonic() >= next_update: + f.flush() + percent = (file_path.stat().st_size / total_size) * 100 + print( + f'\rDownloaded {file_path.stat().st_size} Bytes ({percent:.1f}%)...', + file=sys.stderr, + end='', + flush=True, + ) + next_update += 1 + + file_size = file_path.stat().st_size + print(f'\rDownloaded {file_size} Bytes (100%)', file=sys.stderr, flush=True) + assert file_size == total_size, f'Downloaded {file_size=} Bytes is not expected {total_size=} Bytes!' + + file_hash = file_hash.hexdigest() + logger.debug('Check %s hash...', file_hash) + assert file_hash == hash_value, f'{file_hash=} != {hash_value=}' + print(f'{hash_name} checksum verified: {file_hash!r}, ok.', file=sys.stderr) + + return file_path + + +def removesuffix(text: str, suffix: str) -> str: + assert text.endswith(suffix), f'{text=} does not end with {suffix=}' + return text[: -len(suffix)] + + +def run(args, **kwargs): + logger.debug('Running: %s (%s)', shlex.join(str(arg) for arg in args), kwargs) + return subprocess.run(args, **kwargs) + + +def verbose_check_output(args) -> str: + completed_process = run(args, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + return completed_process.stdout.strip() + + +def get_platform_parts(): + """DocWrite: setup_python.md ## Workflow - 3. Obtaining optimized Python distribution + See: https://gregoryszorc.com/docs/python-build-standalone/main/running.html + """ + parts = [sys.platform] + abi = 'gnu' if any(platform.libc_ver()) else 'musl' + logger.debug('Use %r ABI', abi) + parts.append(abi) + + arch = platform.machine().lower() + if sys.platform == 'linux' and arch == 'x86_64': + """DocWrite: setup_python.md ## Workflow - 3. Obtaining optimized Python distribution + For `x86-64` Linux we check the CPU flags from `/proc/cpuinfo` to determine the best variant.""" + try: + contents = Path('/proc/cpuinfo').read_text() + except OSError: + pass + else: + # Based on https://github.com/pypa/hatch/blob/master/src/hatch/python/resolve.py + # See https://clang.llvm.org/docs/UsersManual.html#x86 for the + # instructions for each architecture variant and + # https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/cpufeatures.h + # for the corresponding Linux flags + v2_flags = {'cx16', 'lahf_lm', 'popcnt', 'pni', 'sse4_1', 'sse4_2', 'ssse3'} + v3_flags = {'avx', 'avx2', 'bmi1', 'bmi2', 'f16c', 'fma', 'movbe', 'xsave'} | v2_flags + v4_flags = {'avx512f', 'avx512bw', 'avx512cd', 'avx512dq', 'avx512vl'} | v3_flags + + cpu_flags = set() + for line in contents.splitlines(): + key, _, value = line.partition(':') + if key.strip() == 'flags': + cpu_flags |= set(value.strip().split()) + + logger.debug('CPU flags: %s', ', '.join(sorted(cpu_flags))) + + missing_v4_flags = v4_flags - cpu_flags + if not missing_v4_flags: + arch = 'x86_64_v4' + else: + logger.debug('Missing v4 flags: %s', ', '.join(sorted(missing_v4_flags))) + missing_v3_flags = v3_flags - cpu_flags + if not missing_v3_flags: + arch = 'x86_64_v3' + else: + logger.debug('Missing v3 flags: %s', ', '.join(sorted(missing_v3_flags))) + missing_v2_flags = v2_flags - cpu_flags + if not missing_v2_flags: + arch = 'x86_64_v2' + else: + logger.debug('Missing v2 flags: %s', ', '.join(sorted(missing_v2_flags))) + + logger.info('Use arch: %r', arch) + parts.append(arch) + return parts + + +def get_best_variant(names): + """DocWrite: setup_python.md ## Workflow - 3. Obtaining optimized Python distribution + We choose the optimized variant based on the priority list: + DocWriteMacro: manageprojects.tests.docwrite_macros_setup_python.optimization_priority + """ + for optimization in OPTIMIZATION_PRIORITY: + for name in names: + if optimization in name: + return name + logger.warning('No optimization found in names: %r', names) + return names[0] + + +def get_python_version(python_bin: str | Path) -> str | None: + logger.debug('Check %s version', python_bin) + if output := run([python_bin, '-V'], capture_output=True, text=True).stdout.split(): + full_version = output[-1] + logger.info('Version of "%s" is: %r', python_bin, full_version) + return full_version + + +@dataclasses.dataclass +class DownloadInfo: + url: str + size: int + + +def setup_python( + *, + major_version: str, + delete_temp: bool = True, + force_update: bool = False, +): + """DocWrite: setup_python.md # Boot Redistributable Python + The download will be only done, if the system Python is not the same major version as requested + and if the local Python is not up-to-date. + """ + + logger.info('Requested major Python version: %s', major_version) + + existing_version = None + existing_python_bin = None + """DocWrite: setup_python.md ## Workflow - 1. Check system Python + If the system Python is the same major version as the required Python, we skip the download.""" + for try_version in (major_version, '3'): + filename = f'python{try_version}' + logger.debug('Checking %s...', filename) + if python3bin := shutil.which(filename): + if (full_version := get_python_version(python3bin)) and full_version.startswith(major_version): + existing_version = full_version + existing_python_bin = python3bin + """DocWrite: setup_python.md ## Workflow - 1. Check system Python + The script just returns the path to the system Python interpreter.""" + + if 'local' in python3bin: + """DocWrite: setup_python.md ## Workflow - 1. Check system Python + A local installed interpreter (e.g. in "~/.local") will be auto updated.""" + continue + + logger.info('System Python v%s already installed: Return path %r of it.', existing_version, python3bin) + return Path(python3bin) + else: + logger.debug('%s not found, ok.', filename) + + logger.debug('Existing Python version: %s', existing_version) + + filters = [ARCHIVE_EXTENSION, *get_platform_parts()] + logger.debug('Use filters: %s', filters) + + """DocWrite: setup_python.md ## Workflow - 2. Collect latest release data + We fetch the latest release data from the GitHub API: + DocWriteMacro: manageprojects.tests.docwrite_macros_setup_python.lastest_release_url""" + data = fetch_json(LASTEST_RELEASE_URL) + logger.debug('Latest release data: %r', data) + tag = data['tag'] + release_url = f'https://api.github.com/repos/{GUTHUB_PROJECT}/releases/tags/{tag}' + release_data = fetch_json(release_url) + assets = release_data['assets'] + + archive_infos = {} + hash_urls = {} + + for asset in assets: + full_name = asset['name'] + if '-debug-' in full_name: + """DocWrite: setup_python.md ## Workflow - 3. Obtaining optimized Python distribution + The "debug" build are ignored.""" + continue + + if not full_name.startswith(f'cpython-{major_version}.'): + # Ignore all other major versions + continue + + if not all(f in full_name for f in filters): + # Ignore incompatible assets + continue + + """DocWrite: setup_python.md ## Workflow - 4. Download and verify Archive + We download the archive file and the hash file for verification: + DocWriteMacro: manageprojects.tests.docwrite_macros_setup_python.extension_info + """ + if full_name.endswith(ARCHIVE_EXTENSION): + name = removesuffix(full_name, ARCHIVE_EXTENSION) + archive_infos[name] = DownloadInfo(url=asset['browser_download_url'], size=asset['size']) + elif full_name.endswith(ARCHIVE_HASH_EXTENSION): + name = removesuffix(full_name, ARCHIVE_HASH_EXTENSION) + hash_urls[name] = asset['browser_download_url'] + + assert archive_infos, f'No "{ARCHIVE_EXTENSION}" found in {assets=}' + assert hash_urls, f'No "{ARCHIVE_HASH_EXTENSION}" found in {assets=}' + + assert archive_infos.keys() == hash_urls.keys(), f'{archive_infos.keys()=} != {hash_urls.keys()=}' + + best_variant = get_best_variant(archive_infos.keys()) + logger.debug('Use best variant: %r', best_variant) + + """DocWrite: setup_python.md ## Workflow - 4. Check existing Python + If the latest Python version is already installed, we skip the download.""" + if existing_python_bin and existing_version: + # full_name e.g.: cpython-3.13.0rc2+20240909-x86_64_v3-unknown-linux-gnu-pgo-full.tar.zst + # get full version: 3.13.0rc2 + if match := re.search(r'cpython-(.+?)\+', best_variant): + full_version = match.group(1) + logger.debug('Available Python version: %s', full_version) + if full_version == existing_version: + logger.info( + 'Local Python v%s is up-to-date: Return path %r of it.', existing_version, existing_python_bin + ) + if force_update: + logger.info('Force update requested: Continue with download ...') + else: + return Path(existing_python_bin) + else: + logger.warning('No version found in %r', best_variant) + + local_path = Path.home() / '.local' + logger.debug('Check "%s" directory', local_path) + local_path.mkdir(parents=False, exist_ok=True) + + """DocWrite: setup_python.md ## Workflow - 4. Download and verify Archive + If the latest Python version is already installed, we skip the download.""" + archive_info: DownloadInfo = archive_infos[best_variant] + logger.debug('Archive info: %s', archive_info) + + hash_url: str = hash_urls[best_variant] + logger.debug('Hash URL: %s', hash_url) + + # Download checksum file: + hash_value = fetch(hash_url).decode().strip() + logger.debug('%s hash value: %s', HASH_NAME, hash_value) + + """DocWrite: setup_python.md ## Workflow - 4. Download and verify Archive + Download will be done in a temporary directory.""" + with TemporaryDirectory(prefix=TEMP_PREFIX, delete=delete_temp) as temp_path: + """DocWrite: setup_python.md ## Workflow - 4. Download and verify Archive + We check the file hash after downloading the archive.""" + archive_temp_path = download( + url=archive_info.url, + dst_path=temp_path, + total_size=archive_info.size, + hash_name=HASH_NAME, + hash_value=hash_value, + ) + + # Extract .tar.zstd archive file into temporary directory: + logger.debug('Extract %s into %s ...', archive_temp_path, temp_path) + run( + ['tar', '--use-compress-program=zstd', '--extract', '--file', archive_temp_path, '--directory', temp_path], + check=True, + ) + + src_path = temp_path / 'python' + assert_is_dir(src_path) + + temp_python_path = src_path / 'install' / 'bin' / 'python3' + assert_is_file(temp_python_path) + + python_version_info = verbose_check_output([str(temp_python_path), '-VV']).strip() + pip_version_into = verbose_check_output([str(temp_python_path), '-m', 'pip', '-VV']).strip() + + """DocWrite: setup_python.md ## Workflow - 5. Add info JSON + We add the file `info.json` with all relevant information.""" + info_file_path = src_path / 'info.json' + info = dict( + download_by=__file__, + download_dt=datetime.datetime.now().isoformat(), + download_filters=filters, + major_version=major_version, + tag=tag, + archive_url=archive_info.url, + hash_url=hash_url, + archive_hash_name=HASH_NAME, + archive_hash_value=hash_value, + python_version_info=python_version_info, + pip_version_info=pip_version_into, + ) + info_file_path.write_text(json.dumps(info, indent=4, ensure_ascii=False)) + + """DocWrite: setup_python.md ## Workflow - 6. Setup Python + The extracted Python will be moved to the final destination in `~/.local/pythonX.XX/`.""" + dest_path = Path.home() / '.local' / f'python{major_version}' + logger.debug('Move %s to %s ...', src_path, dest_path) + if dest_path.exists(): + logger.info('Remove existing %r ...', dest_path) + shutil.rmtree(dest_path) + shutil.move(src_path, dest_path) + + python_home_path = dest_path / 'install' + + """DocWrite: setup_python.md ## Workflow - 6. Setup Python + We add a shell script to `~/.local/bin/pythonX.XX` to start the Python interpreter.""" + bin_path = python_home_path / 'bin' / f'python{major_version}' + assert_is_file(bin_path) + + local_bin_path = Path.home() / '.local' / 'bin' / f'python{major_version}' + logger.debug('Create %s ...', local_bin_path) + local_bin_path.parent.mkdir(parents=True, exist_ok=True) + with local_bin_path.open('w') as f: + """DocWrite: setup_python.md ## Workflow - 6. Setup Python + The script set's the correct `PYTHONHOME` environment variable.""" + f.writelines( + [ + '#!/bin/sh\n', + f'export PYTHONHOME="{python_home_path}"\n', + f'exec "{bin_path}" "$@"\n', + ] + ) + local_bin_path.chmod(0o777) + + """DocWrite: setup_python.md ## Workflow - 6. Setup Python + We display version information from Python and pip on `stderr`.""" + print('Installed Python:', verbose_check_output([str(local_bin_path), '-VV']), file=sys.stderr) + print('Pip info:', verbose_check_output([str(local_bin_path), '-m', 'pip', '-VV']), file=sys.stderr) + + logger.info('Python v%s installed: Return path %r of it.', major_version, local_bin_path) + return local_bin_path + + +def get_parser() -> argparse.ArgumentParser: + """ + DocWrite: setup_python.md ## CLI + The CLI interface looks like e.g.: + + DocWriteMacro: manageprojects.tests.docwrite_macros_setup_python.help + """ + parser = argparse.ArgumentParser( + description=( + 'Download and setup redistributable Python Interpreter' + f' from https://github.com/{GUTHUB_PROJECT}/ if needed ;)' + ), + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + + parser.add_argument( + 'major_version', + nargs=argparse.OPTIONAL, + default=DEFAULT_MAJOR_VERSION, + help='Specify the Python version like: 3.10, 3.11, 3.12, ...', + ) + parser.add_argument( + '-v', + '--verbose', + action='count', + default=0, + help='Increase verbosity level (can be used multiple times, e.g.: -vv)', + ) + parser.add_argument( + '--skip-temp-deletion', + action='store_true', + help='Skip deletion of temporary files', + ) + parser.add_argument( + '--force-update', + action='store_true', + help='Update local Python interpreter, even if it is up-to-date', + ) + return parser + + +def main(args=None): + parser = get_parser() + args = parser.parse_args(args=args) + verbose2level = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG} + logging.basicConfig( + level=verbose2level.get(args.verbose, logging.DEBUG), + format='%(levelname)9s %(message)s', + stream=sys.stderr, + ) + logger.debug('Arguments: %s', args) + + return setup_python( + major_version=args.major_version, + delete_temp=not args.skip_temp_deletion, + force_update=args.force_update, + ) + + +if __name__ == '__main__': + python_path = main() + + """DocWrite: setup_python.md ## Workflow - 7. print the path + If no errors occurred, the path to the Python interpreter will be printed to `stdout`. + So it's usable in shell scripts, like: + + DocWriteMacro: manageprojects.tests.docwrite_macros_setup_python.example_shell_script + """ + print(python_path) diff --git a/manageprojects/setup_python_example.sh b/manageprojects/setup_python_example.sh new file mode 100644 index 0000000..5633e86 --- /dev/null +++ b/manageprojects/setup_python_example.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh + +set -e + +PY_313_BIN=$(python3 setup_python.py -v 3.13) +echo "Python 3.13 used from: '${PY_313_BIN}'" + +set -x + +${PY_313_BIN} -VV diff --git a/manageprojects/tests/docwrite_macros.py b/manageprojects/tests/docwrite_macros_install_python.py similarity index 100% rename from manageprojects/tests/docwrite_macros.py rename to manageprojects/tests/docwrite_macros_install_python.py diff --git a/manageprojects/tests/docwrite_macros_setup_python.py b/manageprojects/tests/docwrite_macros_setup_python.py new file mode 100644 index 0000000..3b2a1eb --- /dev/null +++ b/manageprojects/tests/docwrite_macros_setup_python.py @@ -0,0 +1,55 @@ +import argparse +from argparse import ArgumentParser +from pathlib import Path +from unittest.mock import patch + +from bx_py_utils.doc_write.data_structures import MacroContext +from bx_py_utils.path import assert_is_file + +from manageprojects import setup_python +from manageprojects.setup_python import ARCHIVE_EXTENSION, ARCHIVE_HASH_EXTENSION, LASTEST_RELEASE_URL + + +PROG = Path(setup_python.__file__).name +EXAMPLE_SCRIPT_PATH = Path(setup_python.__file__).parent / 'setup_python_example.sh' + + +class HelpFormatterMock(argparse.ArgumentDefaultsHelpFormatter): + + def __init__(self, *args, **kwargs): + kwargs['width'] = 120 + kwargs['prog'] = PROG # Force: 'setup_python.py' + super().__init__(*args, **kwargs) + + +def help(macro_context: MacroContext): + yield '```shell' + yield f'$ python3 {PROG} --help\n' + with patch.object(argparse, 'ArgumentDefaultsHelpFormatter', HelpFormatterMock): + parser: ArgumentParser = setup_python.get_parser() + yield parser.format_help() + yield '```' + + +def example_shell_script(macro_context: MacroContext): + assert_is_file(EXAMPLE_SCRIPT_PATH) + + yield '```shell' + yield EXAMPLE_SCRIPT_PATH.read_text() + yield '```' + + +def lastest_release_url(macro_context: MacroContext): + yield LASTEST_RELEASE_URL + + +def optimization_priority(macro_context: MacroContext): + yield '' + for number, optimization in enumerate(setup_python.OPTIMIZATION_PRIORITY, 1): + yield f'{number}. `{optimization}`' + + +def extension_info(macro_context: MacroContext): + yield '' + yield f'* Archive extension: `{ARCHIVE_EXTENSION}`' + yield f'* Hash extension: `{ARCHIVE_HASH_EXTENSION}`' diff --git a/manageprojects/tests/test_install_python.py b/manageprojects/tests/test_install_python.py index e674c97..ab762e3 100644 --- a/manageprojects/tests/test_install_python.py +++ b/manageprojects/tests/test_install_python.py @@ -11,7 +11,7 @@ from rich.rule import Rule from manageprojects.install_python import extract_versions, get_latest_versions -from manageprojects.tests.docwrite_macros import EXAMPLE_SCRIPT_PATH +from manageprojects.tests.docwrite_macros_install_python import EXAMPLE_SCRIPT_PATH from manageprojects.utilities.include_install_python import SOURCE_PATH, IncludeInstallPythonBaseTestCase diff --git a/manageprojects/tests/test_setup_python.py b/manageprojects/tests/test_setup_python.py new file mode 100644 index 0000000..9f8a440 --- /dev/null +++ b/manageprojects/tests/test_setup_python.py @@ -0,0 +1,54 @@ +import filecmp +import subprocess +import sys +import tempfile +from pathlib import Path +from unittest import TestCase + +from bx_py_utils.path import assert_is_file + +from manageprojects.setup_python import main +from manageprojects.utilities.include_setup_python import SOURCE_PATH, IncludeSetupPythonBaseTestCase + + +class SetupPythonTestCase(TestCase): + def test_use_system_python(self): + major_version = f'{sys.version_info.major}.{sys.version_info.minor}' + + python_path = main(args=(major_version,)) + self.assertIsInstance(python_path, Path) + assert_is_file(python_path) + + process = subprocess.run([str(python_path), '-V'], capture_output=True, text=True) + self.assertEqual(process.returncode, 0) + output = process.stdout.strip() + assert output.startswith(f'Python {major_version}.'), f'{output=}' + + +class IncludeSetupPythonTestCase(IncludeSetupPythonBaseTestCase): + maxDiff = None + + def test_auto_update_setup_python(self): + self.assertIsNone(self.DESTINATION_PATH) + self.assertEqual(SOURCE_PATH.name, 'setup_python.py') + + with tempfile.TemporaryDirectory() as temp_dir: + self.DESTINATION_PATH = Path(temp_dir) / 'test.py' + + with self.assertRaises(AssertionError) as cm: + self.auto_update_setup_python() + + self.assertIn('File does not exists', str(cm.exception)) + + self.DESTINATION_PATH.write_text('OLD') + self.assertFalse(filecmp.cmp(SOURCE_PATH, self.DESTINATION_PATH)) + + with self.assertRaises(AssertionError) as cm: + self.auto_update_setup_python() + self.assertIn(' updated, please commit ', str(cm.exception)) + + self.assertTrue(filecmp.cmp(SOURCE_PATH, self.DESTINATION_PATH)) + + result = self.auto_update_setup_python() + self.assertIsNone(result) + self.assertTrue(filecmp.cmp(SOURCE_PATH, self.DESTINATION_PATH)) diff --git a/manageprojects/utilities/include_install_python.py b/manageprojects/utilities/include_install_python.py index 98166ad..9d1925c 100644 --- a/manageprojects/utilities/include_install_python.py +++ b/manageprojects/utilities/include_install_python.py @@ -24,7 +24,7 @@ class IncludeInstallPythonTestCase(IncludeInstallPythonBaseTestCase): # Set the path where the `install_python.py` should be copied to: DESTINATION_PATH = Path(your_package.__file__).parent) / 'install_python.py' - # Just call the method in a test, it will pass, if the file is up2date: + # The test will pass, if the file is up2date, if it's update the script! def test_install_python_is_up2date(self): self.auto_update_install_python() ``` diff --git a/manageprojects/utilities/include_setup_python.py b/manageprojects/utilities/include_setup_python.py new file mode 100644 index 0000000..7a7c6b1 --- /dev/null +++ b/manageprojects/utilities/include_setup_python.py @@ -0,0 +1,58 @@ +import filecmp +from pathlib import Path +from unittest import TestCase + +from bx_py_utils.path import assert_is_dir, assert_is_file + +import manageprojects + + +SOURCE_PATH = Path(manageprojects.__file__).parent / 'setup_python.py' + + +class IncludeSetupPythonBaseTestCase(TestCase): + """DocWrite: setup_python.md ## Include in own projects + There is a unittest base class to include `setup_python.py` script in your project. + If will check if the file is up2date and if not, it will update it. + + Just include `manageprojects` as a dev dependency in your project. + And add a test like this: + + ```python + class IncludeSetupPythonTestCase(IncludeSetupPythonBaseTestCase): + + # Set the path where the `setup_python.py` should be copied to: + DESTINATION_PATH = Path(your_package.__file__).parent) / 'setup_python.py' + + # The test will pass, if the file is up2date, if it's update the script! + def test_setup_python_is_up2date(self): + self.auto_update_setup_python() + ``` + + Feel free to do it in a completely different way, this is just a suggestion ;) + """ + + DESTINATION_PATH: Path = None # must be set in the subclass + + def auto_update_setup_python(self): + assert_is_file(SOURCE_PATH) + + self.assertIsInstance( + self.DESTINATION_PATH, + Path, + 'Please set the "DESTINATION_PATH" in the subclass', + ) + self.assertEqual(self.DESTINATION_PATH.suffix, '.py') + assert_is_dir(self.DESTINATION_PATH.parent) + + self.assertTrue( + self.DESTINATION_PATH.is_file(), + f'File does not exists: "{self.DESTINATION_PATH}"\n(Please add at least a empty file there!)', + ) + + if filecmp.cmp(SOURCE_PATH, self.DESTINATION_PATH, shallow=False): + # Files are equal -> nothing to do + return + + self.DESTINATION_PATH.write_text(SOURCE_PATH.read_text()) + self.fail(f'File "{self.DESTINATION_PATH}" was updated, please commit the changes') diff --git a/pyproject.toml b/pyproject.toml index e308d80..298802e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -156,7 +156,7 @@ exclude_lines = [ legacy_tox_ini = """ [tox] isolated_build = True -envlist = py{312,311} +envlist = py{313,312,311} skip_missing_interpreters = True [testenv]