Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Introduce an experimental uv toolchain #1989

Merged
merged 74 commits into from
Jul 12, 2024
Merged

feat: Introduce an experimental uv toolchain #1989

merged 74 commits into from
Jul 12, 2024

Conversation

groodt
Copy link
Collaborator

@groodt groodt commented Jun 18, 2024

Context

This PR introduces a toolchain for uv and a module extension that can install it. It will be followed by some rules that make use of the toolchain for things like dependency locking.

Relates to #1975

Future enhancements (in follow-up PRs):

  • Introduce a mechanism to use a uv toolchain for locking dependencies
  • Introduce a mechanism to use a uv toolchain for exporting a venv for downstream tools and IDEs
  • Factor out the uv download url
  • Decide on a final location in the repo structure for the toolchain (and any rules)

Notes

Try it out:

cd examples/bzlmod
bazel run @rules_python//python/uv:current_toolchain

I was able to produce a windows lockfile from my osx_x86_64:

Click me
# This file was autogenerated by uv via the following command:
#    uv pip compile --python external/rules_python~~python~python_3_9_x86_64-apple-darwin/bin/python3 --python-platform windows --python-version 3.9 --no-strip-extras --generate-hashes --output-file bazel-out/darwin_x86_64-fastbuild/bin/spike_uv_pip_compile.requirements.out requirements.in
alabaster==0.7.16 \
    --hash=sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65 \
    --hash=sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92
    # via sphinx
astroid==2.13.5 \
    --hash=sha256:6891f444625b6edb2ac798829b689e95297e100ddf89dbed5a8c610e34901501 \
    --hash=sha256:df164d5ac811b9f44105a72b8f9d5edfb7b5b2d7e979b04ea377a77b3229114a
    # via pylint
babel==2.15.0 \
    --hash=sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb \
    --hash=sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413
    # via sphinx
certifi==2024.6.2 \
    --hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \
    --hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56
    # via requests
chardet==4.0.0 \
    --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
    --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
    # via requests
colorama==0.4.6 \
    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
    # via
    #   -r requirements.in
    #   pylint
    #   sphinx
dill==0.3.8 \
    --hash=sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca \
    --hash=sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7
    # via pylint
docutils==0.21.2 \
    --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \
    --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2
    # via sphinx
idna==2.10 \
    --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
    --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
    # via requests
imagesize==1.4.1 \
    --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \
    --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a
    # via sphinx
importlib-metadata==7.2.1 \
    --hash=sha256:509ecb2ab77071db5137c655e24ceb3eee66e7bbc6574165d0d114d9fc4bbe68 \
    --hash=sha256:ffef94b0b66046dd8ea2d619b701fe978d9264d38f3998bc4c27ec3b146a87c8
    # via sphinx
isort==5.13.2 \
    --hash=sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109 \
    --hash=sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6
    # via pylint
jinja2==3.1.4 \
    --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \
    --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d
    # via sphinx
lazy-object-proxy==1.10.0 \
    --hash=sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56 \
    --hash=sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4 \
    --hash=sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8 \
    --hash=sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282 \
    --hash=sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757 \
    --hash=sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424 \
    --hash=sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b \
    --hash=sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255 \
    --hash=sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70 \
    --hash=sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94 \
    --hash=sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074 \
    --hash=sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c \
    --hash=sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee \
    --hash=sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9 \
    --hash=sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9 \
    --hash=sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69 \
    --hash=sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f \
    --hash=sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3 \
    --hash=sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9 \
    --hash=sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d \
    --hash=sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977 \
    --hash=sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b \
    --hash=sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43 \
    --hash=sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658 \
    --hash=sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a \
    --hash=sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd \
    --hash=sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83 \
    --hash=sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4 \
    --hash=sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696 \
    --hash=sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05 \
    --hash=sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3 \
    --hash=sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6 \
    --hash=sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895 \
    --hash=sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4 \
    --hash=sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba \
    --hash=sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03 \
    --hash=sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c
    # via astroid
markupsafe==2.1.5 \
    --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \
    --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \
    --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \
    --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \
    --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \
    --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \
    --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \
    --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \
    --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \
    --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \
    --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \
    --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \
    --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \
    --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \
    --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \
    --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \
    --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \
    --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \
    --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \
    --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \
    --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \
    --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \
    --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \
    --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \
    --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \
    --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \
    --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \
    --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \
    --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \
    --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \
    --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \
    --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \
    --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \
    --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \
    --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \
    --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \
    --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \
    --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \
    --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \
    --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \
    --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \
    --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \
    --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \
    --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \
    --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \
    --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \
    --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \
    --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \
    --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \
    --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \
    --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \
    --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \
    --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \
    --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \
    --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \
    --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \
    --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \
    --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \
    --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \
    --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68
    # via jinja2
mccabe==0.7.0 \
    --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \
    --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e
    # via pylint
packaging==24.1 \
    --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \
    --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124
    # via sphinx
pathspec==0.12.1 \
    --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \
    --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712
    # via yamllint
platformdirs==4.2.2 \
    --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
    --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
    # via pylint
pygments==2.18.0 \
    --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \
    --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a
    # via sphinx
pylint==2.15.10 \
    --hash=sha256:9df0d07e8948a1c3ffa3b6e2d7e6e63d9fb457c5da5b961ed63106594780cc7e \
    --hash=sha256:b3dc5ef7d33858f297ac0d06cc73862f01e4f2e74025ec3eff347ce0bc60baf5
    # via
    #   -r requirements.in
    #   pylint-print
pylint-print==1.0.1 \
    --hash=sha256:30aa207e9718ebf4ceb47fb87012092e6d8743aab932aa07aa14a73e750ad3d0 \
    --hash=sha256:a2b2599e7887b93e551db2624c523c1e6e9e58c3be8416cd98d41e4427e2669b
    # via -r requirements.in
python-dateutil==2.9.0.post0 \
    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
    # via
    #   -r requirements.in
    #   s3cmd
python-magic==0.4.27 \
    --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \
    --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3
    # via s3cmd
pyyaml==6.0.1 \
    --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
    --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
    --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
    --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
    --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
    --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
    --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
    --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
    --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
    --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
    --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
    --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
    --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
    --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
    --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
    --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
    --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
    --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
    --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
    --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
    --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
    --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
    --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
    --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
    --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
    --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
    --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
    --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
    --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
    --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
    --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
    --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
    --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
    --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
    --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
    --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
    --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
    --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
    --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
    --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
    --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
    --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
    --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
    --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
    --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
    --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
    --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
    --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
    --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
    --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
    --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
    # via yamllint
requests==2.25.1 \
    --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
    --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
    # via
    #   -r requirements.in
    #   sphinx
s3cmd==2.1.0 \
    --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa \
    --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03
    # via -r requirements.in
six==1.16.0 \
    --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
    --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
    # via python-dateutil
snowballstemmer==2.2.0 \
    --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
    --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
    # via sphinx
sphinx==7.3.7 \
    --hash=sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3 \
    --hash=sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc
    # via -r requirements.in
sphinxcontrib-applehelp==1.0.8 \
    --hash=sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619 \
    --hash=sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4
    # via sphinx
sphinxcontrib-devhelp==1.0.6 \
    --hash=sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f \
    --hash=sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3
    # via sphinx
sphinxcontrib-htmlhelp==2.0.5 \
    --hash=sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015 \
    --hash=sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04
    # via sphinx
sphinxcontrib-jsmath==1.0.1 \
    --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \
    --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8
    # via sphinx
sphinxcontrib-qthelp==1.0.7 \
    --hash=sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6 \
    --hash=sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182
    # via sphinx
sphinxcontrib-serializinghtml==1.1.10 \
    --hash=sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7 \
    --hash=sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f
    # via
    #   -r requirements.in
    #   sphinx
tabulate==0.9.0 \
    --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \
    --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f
    # via -r requirements.in
tomli==2.0.1 \
    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
    # via
    #   pylint
    #   sphinx
tomlkit==0.12.5 \
    --hash=sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f \
    --hash=sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c
    # via pylint
typing-extensions==4.12.2 \
    --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
    --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
    # via
    #   astroid
    #   pylint
urllib3==1.26.19 \
    --hash=sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3 \
    --hash=sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429
    # via requests
websockets==12.0 \
    --hash=sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b \
    --hash=sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6 \
    --hash=sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df \
    --hash=sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b \
    --hash=sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205 \
    --hash=sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892 \
    --hash=sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53 \
    --hash=sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2 \
    --hash=sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed \
    --hash=sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c \
    --hash=sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd \
    --hash=sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b \
    --hash=sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931 \
    --hash=sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30 \
    --hash=sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370 \
    --hash=sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be \
    --hash=sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec \
    --hash=sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf \
    --hash=sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62 \
    --hash=sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b \
    --hash=sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402 \
    --hash=sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f \
    --hash=sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123 \
    --hash=sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9 \
    --hash=sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603 \
    --hash=sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45 \
    --hash=sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558 \
    --hash=sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4 \
    --hash=sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438 \
    --hash=sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137 \
    --hash=sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480 \
    --hash=sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447 \
    --hash=sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8 \
    --hash=sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04 \
    --hash=sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c \
    --hash=sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb \
    --hash=sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967 \
    --hash=sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b \
    --hash=sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d \
    --hash=sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def \
    --hash=sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c \
    --hash=sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92 \
    --hash=sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2 \
    --hash=sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113 \
    --hash=sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b \
    --hash=sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28 \
    --hash=sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7 \
    --hash=sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d \
    --hash=sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f \
    --hash=sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468 \
    --hash=sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8 \
    --hash=sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae \
    --hash=sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611 \
    --hash=sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d \
    --hash=sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9 \
    --hash=sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca \
    --hash=sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f \
    --hash=sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2 \
    --hash=sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077 \
    --hash=sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2 \
    --hash=sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6 \
    --hash=sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374 \
    --hash=sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc \
    --hash=sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e \
    --hash=sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53 \
    --hash=sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399 \
    --hash=sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547 \
    --hash=sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3 \
    --hash=sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870 \
    --hash=sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5 \
    --hash=sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8 \
    --hash=sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7
    # via -r requirements.in
wheel==0.43.0 \
    --hash=sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85 \
    --hash=sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81
    # via -r requirements.in
wrapt==1.16.0 \
    --hash=sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc \
    --hash=sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81 \
    --hash=sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09 \
    --hash=sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e \
    --hash=sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca \
    --hash=sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0 \
    --hash=sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb \
    --hash=sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487 \
    --hash=sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40 \
    --hash=sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c \
    --hash=sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060 \
    --hash=sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202 \
    --hash=sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41 \
    --hash=sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9 \
    --hash=sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b \
    --hash=sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664 \
    --hash=sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d \
    --hash=sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362 \
    --hash=sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00 \
    --hash=sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc \
    --hash=sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1 \
    --hash=sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267 \
    --hash=sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956 \
    --hash=sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966 \
    --hash=sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1 \
    --hash=sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228 \
    --hash=sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72 \
    --hash=sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d \
    --hash=sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292 \
    --hash=sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0 \
    --hash=sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0 \
    --hash=sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36 \
    --hash=sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c \
    --hash=sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5 \
    --hash=sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f \
    --hash=sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73 \
    --hash=sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b \
    --hash=sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2 \
    --hash=sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593 \
    --hash=sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39 \
    --hash=sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389 \
    --hash=sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf \
    --hash=sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf \
    --hash=sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89 \
    --hash=sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c \
    --hash=sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c \
    --hash=sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f \
    --hash=sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440 \
    --hash=sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465 \
    --hash=sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136 \
    --hash=sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b \
    --hash=sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8 \
    --hash=sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3 \
    --hash=sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8 \
    --hash=sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6 \
    --hash=sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e \
    --hash=sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f \
    --hash=sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c \
    --hash=sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e \
    --hash=sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8 \
    --hash=sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2 \
    --hash=sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020 \
    --hash=sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35 \
    --hash=sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d \
    --hash=sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3 \
    --hash=sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537 \
    --hash=sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809 \
    --hash=sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d \
    --hash=sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a \
    --hash=sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4
    # via astroid
yamllint==1.35.1 \
    --hash=sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3 \
    --hash=sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd
    # via -r requirements.in
zipp==3.19.2 \
    --hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \
    --hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c
    # via importlib-metadata

@groodt groodt requested a review from aignas June 18, 2024 13:34
Copy link
Collaborator

@aignas aignas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided that I needed to refresh my memory on bzlmod now that it seems more stable and functional. I definitely have a lot to learn. Anyway, so this only works with bzlmod. Maybe it's not much work to do similar in WORKSPACE. Does compile_pip_requirements even work in bzlmod?

Yes it does :)

I decided to fetch the uv binaries from PyPI just in case we want to launch it with Python in future e.g. python -m uv pip compile, but I actually just punted and launched it with the worst Bash script for now.
I decided to manually unpack the wheel as a filegroup and grab the rust binary from the wheel because all of the whl_library stuff looks too complicated and overkill given that Im not running it via Python

As I mentioned, I think we should probably use toolchains and use the binaries directly from GH releases page of uv. It is a more direct way of having the platform binary available to you.

I've always liked the idea of having "pinning" be a concern of the "pip" extension / repository. They are intrinsically linked in that to retrieve third-party dependencies, you need a "pinned" lockfile for the relevant platforms. So why not extend "pip" to do it? Anyway, that's why I started here, but it obviously isn't vendoring the lockfiles into the right locations right now. It's just a simple way to launch "uv"

I like the idea of a pin target. I am wondering about what we can do here. I think in the ideal case we should have:

  • A rule (not a macro) that does uv pip compile and uses a uv toolchain and the python toolchain to get the tools. This allows us to pass the right interpreter to uv all the time. We should have exec configuration on the rule for the toolchains.
  • The output file should be created by the rule and that output file can be synched using the same technique we have in examples/pip_parse_vendored. That way we can reuse the existing techniques to write back to the system.
  • I like the idea of keeping the wrapping of uv as minimal as possible.

Bikeshedding:

  • Is pin the best name? Should we instead use lock?
  • Should we accept requirements.in or pyproject.toml as input to the pip.parse so that the user does not need to specify it using in the pin target? I would probably say yes to this.
  • Should we automatically generate targets for pinning for all of the platform targets that the user wants to support? I would also probably say yes to that.

Having an interface like:

pip.parse(
    hub_name = "foo",
    python_versions = [], # means all
    platforms = ["windows_x86_64", "linux_x86_64", "mac_aarch64"], # could be default
    src = "pyproject.toml", # could be default
    lockfile_dir = "", # default, means the root of the workspace
)

maybe could be feasible?

Other things I would love to fix the following issues as part of this redesign:

  • Users needing to create a requirements_lock.txt file to store the output of the pinning. The error is unintuitive and I think we could do some stuff there - aspects write_source_file does not fail if the destination file is not present.

I might have forgotten something, so if I remember I'll update this lengthy prose.

python/private/bzlmod/pip.bzl Outdated Show resolved Hide resolved
@groodt
Copy link
Collaborator Author

groodt commented Jun 19, 2024

Thanks for the initial review @aignas

Having an interface like:

Yes. I like the look of it. We will keep iterating on it. The interface you have there is almost an exact match of what I have in mind.

Should we accept requirements.in or pyproject.toml as input to the pip.parse so that the user does not need to specify it using in the pin target? I would probably say yes to this.

Yes. That's aligned with my thinking and helps with the migration path and onboarding. In addition, we should also support a simple bazel list of requirements specifiers `requirement_specifiers = ["requests > 2.0.0", "click > 7.0.0", ]

Should we automatically generate targets for pinning for all of the platform targets that the user wants to support? I would also probably say yes to that.

Yes. That's aligned with my thinking too.

Users needing to create a requirements_lock.txt file to store the output of the pinning. The error is unintuitive and I think we could do some stuff there - aspects write_source_file does not fail if the destination file is not present.

Yes. I'd like to fix it too. I think in your interface sketch, as long as we use a directory with deterministic named files, then it should be reasonable to do. I think that would let the target run on initial bootstrap without failing 🤞

@groodt
Copy link
Collaborator Author

groodt commented Jun 19, 2024

  • A rule (not a macro) that does uv pip compile and uses a uv toolchain and the python toolchain to get the tools. This allows us to pass the right interpreter to uv all the time. We should have exec configuration on the rule for the toolchains.

Yes, agreed. I think a toolchain makes sense. I avoided in this quick hack because every time I get involved with toolchains, I confuse myself 😂 I think cfg = exec here makes sense and uv works well because it can "cross-pin" for the target platform, but please double check it all during reviews.

@rickeylev
Copy link
Contributor

re: toolchains: Yeah, toolchains are exactly for this sort of thing. uv is basically a "compiler" for the requirements.in "source code".

Here's the basic for a toolchain-based implementation. T

# BUILD

toolchain_type(name = "uv_toolchain_type")
toolchain(
  name = "uv_toolchain",
  toolchain_type = ":uv_toolchain_type",
  toolchain = ":uv_toolchain_impl"
)

uv_toolchain(
  name = "uv_toolchain_impl",
  uv = "@uv//:bin",
)

# uv_toolchain.bzl

def _uv_toolchain_impl(ctx):
  return [platform_common.ToolchainInfo(
    uv = ctx.attr.uv
  )]

uv_toolchain = rule(
  implementation = _uv_toolchain_impl,
  attrs = {
    "uv": attr.label(executable=True, cfg="exec")
  }
)

# compile_requirements.bzl

def _compile_requirements_impl(ctx):
  requirements_out = ctx.actions.declare_file(ctx.label.name + ".requirements.out")
  uv = ctx.toolchains[":uv_toolchain_type"].uv
  pv = ctx.toolchains["//python:toolchain_type"].interpreter_version_info
  python = ctx.toolchains["//python:exec_tools_toolchain_type"].interpreter
  args = ctx.actions.args()
  # IDK the args, but you get the gist. Additional args like platform, cpu, etc
  # can be added by depending on the flags and passing them along.
  args.add("--python", python)
  args.add("--python-version", "{}.{}".format(pv.major, pv.minor))
  args.add("--requirements-in", ctx.file.requirements_in)
  args.add("--requirements-out", requirements_out)
  ctx.actions.run(
    executable = uv,
    args = args,
    inputs = depset([requirements_in]),
    outputs = [requirements_out],
    tools = [python]
  )
  return [DefaultInfo(
    files = [requirements_out],
  )]
compile_requirements = rule(
  implementation = _compile_requirements_impl,
  attrs = {
    "requirements_in": attr.label(allow_single_file = True)
  },
  toolchains = [
      "//python:toolchain_type",
      "//python:exec_tools_toolchain_type",
      ":uv_toolchain_type",
  ]
)

That should be the gist of it. The above allows bazel build //:compile_with_uv, where the output would be the resolved requirements file.

An alternative might be to add it to the exec_tools toolchain type, too, directly or indirectly.

In order to have something like bazel run //:compile_using_uv, where uv is directly run, then the above has to change a bit (the uv binary has to be in the target config for that, so requires some slightly different wiring).

@groodt
Copy link
Collaborator Author

groodt commented Jun 19, 2024

  • Is pin the best name? Should we instead use lock?

You're probably right. I use them interchangeably a lot of the time, but it's probably better to be precise here. I'm not sure if there is a good write-up anywhere on the topic that clearly describes the difference between pinning vs locking?

To me, here's the difference:

  • Pinning - Updating requirement specifiers to pin them to some range of versions. Possibly SemVer style or ~= is a good example of pinning within a range. Renovate and / or dependabot would bump pins in { requirements.in | pyproject.toml } and then lock outputs to concrete requirements.txt. See locking definition.
  • Locking - Recording the precise version of dependency in a file.

I think if we align on those, then I think :lock is probably the more precise term to be using here?

@aignas
Copy link
Collaborator

aignas commented Jun 19, 2024

I had some code laying around with using the uv binaries directly, so I took the liberty to push that here.

@groodt
Copy link
Collaborator Author

groodt commented Jun 20, 2024

Thanks both! I'll hopefully have some time today and I think my goal will be to add a simple toolchain. It's likely that it could be merged independently even if unused while we work on the rest of the interface and functionality.

Some additional thoughts I've had:

  • Should the toolchain be optional or mandatory (I mean in end-state. Not while we introduce the functionality. While we introduce it, it could be optional, but I'm tempted to say end-state its not-optional if you use the pypi_install / pap.parse) functionality? Also fine to late-bind on this and we can figure it out while the ideas develop.
  • Maybe controversial, but Im going to try frame this not as "uv" specifically, but more as a "locking toolchain"? Of course, we can share that it has some "uv" functionality, and we can probably expose a raw "uv" mode behind something like "unsafe_uv_args" or let people execute the toolchain directly, but I would really rather support the standards (in this case PEP 621 for pyproject.toml and PEP 508 dependency specifiers) than specific tools for now. We can discuss face to face, but PEP 621 is supported by pip, uv, PDM and Poetry will support it in v2. The 508 specifiers are supported by everything of course. That would mean that the fact that we produce / vendor a "requirements.txt" as a lockfile is actually our implementation choice and we can change it later if necessary. Either to a "uv lockfile", or our own format, or some future Python lockfile standard if one emerges. The 621 files are supported by tools like dependabot and renovate, and it's easy to document how to trigger a bazel build after a dependabot PR that bumps the pyproject.toml
  • On IDE support, I think in addition to the :lock target, I would also like to add a :ide-venv target that spits out a .venv for the IDEs

@wingsofovnia
Copy link
Contributor

wingsofovnia commented Jun 21, 2024

First of all, thank you for looking into that! I've been personally struggling with maintaining pip-compile'd lock files across multiple platforms a lot. I wish I knew uv existed 🥲, however, I eventually migrated to rules_pycross for better QoL and happy with the choice.

One thought for consideration: how does it align with #1360 ? If rules_pycross is pulled, pip-compile will be replaced with PDM/Poetry altogether, both of which produce platform-independent lock files. In my opinion, having platform-agnostic lock files should be the ultimate goal as it is easier to maintain, and it seems uv authors are of the same opinion (see uv#Limitations). That said, perhaps expediting rules_pycross integration should be prioritized? At the same time, I can see that using uv to bring great value at less effort.

@groodt
Copy link
Collaborator Author

groodt commented Jun 21, 2024

They're unrelated for the most part. This PR is to assist with a better implementation of compile_pip_requirements. That issue was more how we could align to bring some code across for another initiative that we likely won't pursue at the moment. We did vendor some of the code at the time https://github.com/bazelbuild/rules_python/tree/main/third_party/rules_pycross We can probably close that issue #1360 tbh.

If pycross is working for you, that's great! There's no real reason to favor rules_python. Use what works best for you.

We keep close eyes on the standards, and I'm sure one day a lockfile standard will emerge. However, I must say that it's not that important that a lockfile is standardized as long as the input dependency specifiers are standardized. Which fortunately they are and have been for a long time. So largely anyone using a PEP621 pyproject.toml can relock dependencies and I would largely expect the lockfiles to be similar. The important thing is whether a resolver can find a valid solution, not that all resolvers produce identical solutions.

@wingsofovnia
Copy link
Contributor

wingsofovnia commented Jun 21, 2024

We keep close eyes on the standards, and I'm sure one day a lockfile standard will emerge. However, I must say that it's not that important that a lockfile is standardized as long as the input dependency specifiers are standardized. Which fortunately they are. So largely anyone using a PEP621 pyproject.toml can relock dependencies and I would largely expect to the lockfiles to be similar. The important thing is whether a resolver can find a valid solution, not that all resolvers produce identical solutions.

I am fully on the same page. I might have been unclear in that bit as I was primarily looking at this PR as a way to make multi-platform like easier, but I wasn't concerned with that. I am not even convinced a lockfile standard is needed at all. My sentiment was about using something like pmd or poetry that generates platform-agnostic lockfiles given that there was an intention to integrate rules_pycross that already uses just that (or at least that was my understanding of what was supposed to happen), rather than (what feels like but technically is not) a wrapper tool around pip-compile to fix its incompetence in multi-platform lock files :)

Either way, if uv is integrated into compile_pip_requirements and can generate all the lock files for specified platforms on any host in one go, it will be a win. No more bringing Docker --platform linux/amd64 to create a lock file for linux on a Mac laptop :)

@ewianda
Copy link
Contributor

ewianda commented Jun 21, 2024

Creating cross-platform uv.lock file is in the works, is that going to be considered as well

@groodt
Copy link
Collaborator Author

groodt commented Jun 22, 2024

Creating astral-sh/uv#3347 file is in the works, is that going to be considered as well

We are aware of that. The PR is still in development and discussions with the maintainers, so it's too early to commit to anything. The lockfile format itself is really an implementation detail in this design though.

I'm curious though: what difference does it make to the functionality what the lockfile format is? As I mentioned in other comments, the lockfile format itself is not the most important thing.

So largely anyone using a PEP621 pyproject.toml can relock dependencies and I would largely expect the lockfiles to be similar. The important thing is whether a resolver can find a valid solution, not that all resolvers produce identical solutions.

@ewianda
Copy link
Contributor

ewianda commented Jun 23, 2024

I'm curious though: what difference does it make to the functionality what the lockfile format is? As I mentioned in other comments, the lockfile format itself is not the most important thing.

I will say that a lock file that includes the URLs of the wheels/sdist, has the advantage of utilizing the bazel downloader directly without an extra step of fetching the metadata. Also having a single lock file for all platforms is a plus.

@aignas
Copy link
Collaborator

aignas commented Jun 23, 2024

FYI, @groodt, I have created #2006 to move the remaining of the PyPI related
code to python/pypi where we are re-exporting the publicly consumable
symbols. I think we should clean them up before releasing 1.0.0, but for now
all the changes where backwards compatible. I think this spike could structure
the files like:

  • python/private/pypi/uv_toolchain.bzl - A uv toolchain definition.
  • python/private/pypi/uv_pip_compile.bzl - A uv pip compile implementation
    that is imported from the pip.parse hub repo.

The gist is that we can keep implementation specific names in private/pypi
whilst the public API can be whatever we choose. I see we have
compile_pip_requirements right now in //python:pip.bzl, but since there
will be two implementation of that in rules_python (one legacy and one with
uv), I thought having a name pip_compile and uv_pip_compile for the
internal bazel functions might make more sense for us to quickly see what is
going on when browsing the code.

@ewianda

I will say that a lock file that includes the URLs of the wheels/sdist, has
the advantage of utilizing the bazel downloader directly without an extra
step of fetching the metadata. Also having a single lock file for all
platforms is a plus.

There are many projects that have very different requirements, so its hard to
have a one-thing-fits-all solution. And no-one is arguing with you that having
a single lock file is nice and having URLs in the lock file is also good - this
is where the Python community as a whole is moving. On the other hand, some
rules_python users mentioned that they have different requirements.txt files
for different python versions, OS, arch combinations and supporting these use
cases is where we meet our users today.

As for needing to call PyPI before fetching - it is done only needed when
rules_python or the lock file is updated. However, the packages won't be
re-fetched if they do not change, because of how the repository cache and
bzlmod works. Maybe in the future we can optimize that even more. If there
are bugs where refetching is done more eagerly, please raise issues. In the
case of bazel mod vendor, that feature is going to land only in 7.3.0 and I
am sure there will be more things ironed before it is fully usable - bazel
team is using rules_python so we are likely to collaborate with them on that
at some point.

If you would like to consider rules_python to support uv, pdm or poetry
or another universal lock file format, please create a GitHub issue so that we
have a paper trail to clearly see who are interested in these features and who
could help test them once (if) somebody starts implementing it. Let's continue
discussions about alternative lock formats in such issues and keep this PR
focused on this spike of having uv pip compile integrated.

Copy link
Contributor

@rickeylev rickeylev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: how toolchain registration is working

I don't think you can really make things work the way you want. The basic conflict is between well defined names and well defined ordering.

In a comment, you said you want to allow defining a toolchain, but not have it automaticaly registered, so the end user can manually register it themselves. Doing this requires having a well-defined name they can reference.

However, well-defined ordering requires names to have a numeric prefix, because an :all or pkg/... pattern will sort lexicographical. Having a numeric prefix means the user can't really predict what the name will be. Without the prefix, it can only work reliably if all the toolchains in the pattern are mutually exclusive.

Additionally, remember that it's best to tell users to register foo:all, since it allows us to add additional toolchain types without users having to do anything. This then means that, whatever the well defined name is, the meaningful part has to be part of the package name, not target.

The only way I can see to satisfy both requirements is to duplicate the toolchain definitions. e.g. @uv_toolchains//:all contains all the toolchains with their numeric prefixes. @uv_toolchains/{name}:all contains the particular toolchains for a given name for someone to register them separately

python/uv/private/BUILD.bazel Show resolved Hide resolved
python/uv/extensions.bzl Outdated Show resolved Hide resolved
python/uv/private/resolved_toolchain.bzl Outdated Show resolved Hide resolved
python/uv/BUILD.bazel Outdated Show resolved Hide resolved
python/uv/BUILD.bazel Outdated Show resolved Hide resolved
python/uv/toolchain.bzl Outdated Show resolved Hide resolved
python/uv/toolchain.bzl Outdated Show resolved Hide resolved
python/uv/toolchain.bzl Show resolved Hide resolved
python/private/pypi/uv_toolchain.bzl Outdated Show resolved Hide resolved
python/uv/repositories.bzl Outdated Show resolved Hide resolved
@groodt groodt changed the title WIP (feat): Introduce an experimental uv toolchain (feat): Introduce an experimental uv toolchain Jul 9, 2024
@groodt
Copy link
Collaborator Author

groodt commented Jul 10, 2024

re: how toolchain registration is working

Ok. You've convinced me. Also some issues I encountered with rules_jsonnet and some passing conversations with people at work also led me to discover this: bazelbuild/bazel#22024 (reply in thread)

I tend to agree with you. The naming doesn't matter and it's cleaner to use :all. I don't like the idea of having other extensions transitively clobber toolchain registration, so I've required it to be registered in the root module. If / when the uv toolchain is registered as part of rules_python, we can implement the recommendation described in the discussion linked above. This would mean the root module has full control, but for convenience or engineer laziness, rules_python can provide sane defaults that would work in most cases, but do have a risk of transitive clobbering.

I know I implemented the numeric prefixes in earlier iterations, but I've removed it for now. I found it to be a bit too arcane and unnecessary right now. It can be added back later once we're further along.

@groodt groodt requested a review from rickeylev July 10, 2024 14:23
@groodt
Copy link
Collaborator Author

groodt commented Jul 10, 2024

Ok @aignas @rickeylev This is probably ready for another pass. I think I've incorporated the majority of the requested changes.

I've also updated the original PR description with some items to follow-up with #1989 (comment) so please have a read of that as well and let me know if there is more to add.

@groodt groodt changed the title (feat): Introduce an experimental uv toolchain feat: Introduce an experimental uv toolchain Jul 11, 2024
Copy link
Collaborator

@aignas aignas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. Left a few comments that would be good address (other than the bikeshedding one) and feel free to merge this one and create small PRs or create issues to not forget to create PRs later. You could also just address the comments here if you wish.

@@ -41,6 +42,7 @@ filegroup(
"//python/pip_install:distribution",
"//python/private:distribution",
"//python/runfiles:distribution",
"//python/uv:distribution",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if having python/uv/private is better than python/private/uv but I don't we can discuss/bikeshed it later, we can merge it as is right now.

python/uv/private/toolchains_repo.bzl Show resolved Hide resolved
Comment on lines +58 to +60
url = url,
sha256 = UV_TOOL_VERSIONS[repository_ctx.attr.uv_version][repository_ctx.attr.platform].sha256,
stripPrefix = strip_prefix,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, in order to ensure that this works correctly with internal indexes and the bazel downloader, it would be good to pass the auth headers, similar to how it is done in whl_library.bzl.

python/uv/repositories.bzl Show resolved Hide resolved
# Marked manual so that `bazel test //...` passes
# even if no toolchain is registered.
tags = ["manual"],
# EXPERIMENTAL: Visibility is restricted to allow for changes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to factor visibility into a variable? I just mention it in case you plan to patch-out the visibility restrictions to experiment with. It'd be easier to patch the variable than all the various visibility lines

@rickeylev rickeylev added this pull request to the merge queue Jul 12, 2024
Merged via the queue into main with commit eeb7494 Jul 12, 2024
7 checks passed
github-merge-queue bot pushed a commit that referenced this pull request Jul 13, 2024
Follows: #1989

Addresses the following:
* Removes usage of `maybe`
* Pretty-print renders some generated *.bazel
* Shorter default_repo_names
}

# From: https://github.com/astral-sh/uv/releases
UV_TOOL_VERSIONS = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI @groodt you may be interested in https://github.com/aspect-build/rules_lint/blob/main/lint/mirror_ruff.sh which auto-updates our mirror of Ruff, another tool distributed in the same way by astral-sh. We have GitHub Actions send the update PRs so no humans need to do anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants