diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index e25e44e86e..43ce104a69 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,13 +1,17 @@ name: Clippy -on: [push, pull_request] +on: + workflow_dispatch: + push: + branches: [main] + pull_request: jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable diff --git a/.github/workflows/python-format.yml b/.github/workflows/python-format.yml new file mode 100644 index 0000000000..48a8378c51 --- /dev/null +++ b/.github/workflows/python-format.yml @@ -0,0 +1,18 @@ +name: Python format + +on: + workflow_dispatch: + push: + branches: [main] + pull_request: + +jobs: + format: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: eifinger/setup-rye@v2 + - name: Rye fmt + run: rye fmt --check \ No newline at end of file diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml new file mode 100644 index 0000000000..01ea11fce2 --- /dev/null +++ b/.github/workflows/python-lint.yml @@ -0,0 +1,18 @@ +name: Python lint + +on: + workflow_dispatch: + push: + branches: [main] + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: eifinger/setup-rye@v2 + - name: Rye lint + run: rye lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 38993719dc..f9260772f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -97,12 +97,12 @@ jobs: target: aarch64-unknown-linux-musl name: aarch64-linux - build: macos - os: macos-12 + os: macos-14 rust: stable target: x86_64-apple-darwin name: x86_64-macos - build: macos-arm - os: macos-12 + os: macos-14 rust: stable target: aarch64-apple-darwin name: aarch64-macos diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 7541e24dfe..a511902c92 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -1,13 +1,17 @@ name: Rustfmt -on: [push, pull_request] +on: + workflow_dispatch: + push: + branches: [main] + pull_request: jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 234c1ab719..79ed4cfbf0 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -1,13 +1,17 @@ name: Spelling -on: [push, pull_request] +on: + workflow_dispatch: + push: + branches: [main] + pull_request: jobs: typos: name: "Spell check" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: crate-ci/typos@master with: - files: . \ No newline at end of file + files: . diff --git a/.github/workflows/sync-python-releases.yml b/.github/workflows/sync-python-releases.yml index ad9df0efd7..84c618cdde 100644 --- a/.github/workflows/sync-python-releases.yml +++ b/.github/workflows/sync-python-releases.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Rye - uses: eifinger/setup-rye@v1 + uses: eifinger/setup-rye@v2 with: enable-cache: true - name: Sync Python Releases diff --git a/.github/workflows/sync-uv-releases.yml b/.github/workflows/sync-uv-releases.yml index 4000bbd4d2..9c7a4fd653 100644 --- a/.github/workflows/sync-uv-releases.yml +++ b/.github/workflows/sync-uv-releases.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Rye - uses: eifinger/setup-rye@v1 + uses: eifinger/setup-rye@v2 with: enable-cache: true - name: Sync UV Releases diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5b9a9dbea9..0158e3e431 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,10 @@ name: Tests -on: [push, pull_request] +on: + workflow_dispatch: + push: + branches: [main] + pull_request: jobs: test-latest-linux: @@ -8,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -27,7 +31,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -46,7 +50,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable diff --git a/.gitignore b/.gitignore index f9af4fa305..b2d5f03e4c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ site __pycache__ .idea token.txt +dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ff91dc110..2c53edbeda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,231 @@ This file contains tracks the changes landing in Rye. It includes changes that were not yet released. + + +## 0.43.0 + +Released on 2024-12-09. + +* Fix compatible toolchain to allow Python 3.13 by @PushUpek in https://github.com/astral-sh/rye/pull/1429 +* Ignore free-threaded CPython builds by @Alexis-D in https://github.com/astral-sh/rye/pull/1437 +* Bump bundled Ruff version to v0.8.2 by @charliermarsh in https://github.com/astral-sh/rye/pull/1440 +* Add support for Python 3.9.21, 3.10.16, 3.11.11, 3.12.8, and 3.13.1 by @github-actions in https://github.com/astral-sh/rye/pull/1439 +* Upgrade bundled uv version to v0.5.7 by @github-actions in https://github.com/astral-sh/rye/pull/1424 + +## 0.42.0 + +Released on 2024-10-21. + +* Sync Python Releases by @github-actions in https://github.com/astral-sh/rye/pull/1406 +* Bump bundled Ruff version to v0.7.0 by @charliermarsh in https://github.com/astral-sh/rye/pull/1419 +* Bump bundled uv version to v0.4.25 by @github-actions in https://github.com/astral-sh/rye/pull/1405 + +## 0.41.0 + +Released on 2024-09-29. + +* Restore hash annotations in lockfile by @charliermarsh in https://github.com/astral-sh/rye/pull/1403 +* Upgrade bundled uv version to v0.4.17 by @github-actions in https://github.com/astral-sh/rye/pull/1393 + +## 0.40.0 + +Released on 2024-09-22. + +* Remove pip-tools support; use uv for all operations by @nazq in https://github.com/astral-sh/rye/pull/1226 +* Respect exclusions when generating hashes by @charliermarsh in https://github.com/astral-sh/rye/pull/1357 +* List all existing tool references on `rye toolchain remove` by @soof-golan in https://github.com/astral-sh/rye/pull/1346 +* Fix a few regex inefficiencies by @InSyncWithFoo in https://github.com/astral-sh/rye/pull/1326 +* Never modify or delete the virtualenv in `rye list` by @bluss in https://github.com/astral-sh/rye/pull/1351 +* Use `inspect_err` over `map_err` by @charliermarsh in https://github.com/astral-sh/rye/pull/1385 +* Upgrade bundled Python releases by @github-actions in https://github.com/astral-sh/rye/pull/1379 +* Upgrade bundled uv version to v0.4.15 by @github-actions in https://github.com/astral-sh/rye/pull/1388 +* Update bundled Ruff version to v0.6.7 by @charliermarsh in https://github.com/astral-sh/rye/pull/1389 + +## 0.39.0 + +Released on 2024-08-20. + +* Update Cargo dependencies by @charliermarsh in https://github.com/astral-sh/rye/pull/1310 +* Fix typo in `add.md` by @mikkelam in https://github.com/astral-sh/rye/pull/1322 +* Fix typo in FAQ by @toadjaune in https://github.com/astral-sh/rye/pull/1330 +* Update Python releases to include Python 3.12.4 by @github-actions in https://github.com/astral-sh/rye/pull/1336 +* Update bundled uv version to `v0.3.0` by @github-actions in https://github.com/astral-sh/rye/pull/1332 + +## 0.38.0 + +Released on 2024-08-01. + +* Fix `rye add` and `rye remove` to create proper `tool.rye` section for `--dev` and `--exclude` flags by @flyaroundme in https://github.com/astral-sh/rye/pull/1256 +* Fix `uninstall_cmd` on Windows by @250h in https://github.com/astral-sh/rye/pull/1153 +* Make rye run print script list to stdout by @mitsuhiko in https://github.com/astral-sh/rye/pull/1268 +* Always write `--index-url` before `--extra-index-url` by @charliermarsh in https://github.com/astral-sh/rye/pull/1278 +* Use case-insensitive comparison for detecting `rye.exe` by @charliermarsh in https://github.com/astral-sh/rye/pull/1286 +* Update Python releases to include stripped variants by @github-actions in https://github.com/astral-sh/rye/pull/1280 +* Fix documentation for readline license by @3w36zj6 in https://github.com/astral-sh/rye/pull/1297 +* Update bundled uv version to `v0.2.33` by @github-actions in https://github.com/astral-sh/rye/pull/1303 +* Update bundled pip to `v24.2.0` by @charliermarsh in https://github.com/astral-sh/rye/pull/1304 + +## 0.37.0 + +Released on 2024-07-20. + +* Fix verify-ssl option parsing by @taki-d in https://github.com/astral-sh/rye/pull/1208 +* Emit warning when asked to build a virtual project by @nazq in https://github.com/astral-sh/rye/pull/1227 +* Add short version `-d` for `rye remove --dev` by @nikhilweee in https://github.com/astral-sh/rye/pull/1239 +* Remove redundant alias information from CLI by @piotrgredowski in https://github.com/astral-sh/rye/pull/1237 +* Update bundled uv version to `v0.2.27` by @github-actions in https://github.com/astral-sh/rye/pull/1232 +* Update Python releases to include `3.12.4` by @github-actions in https://github.com/astral-sh/rye/pull/1230 +* Respect `.python-version` in Rye builds by @charliermarsh in https://github.com/astral-sh/rye/pull/1250 +* Update bundled Ruff version to `v0.5.4` by @charliermarsh in https://github.com/astral-sh/rye/pull/1251 + +## 0.36.0 + +Released on 2024-07-07. + +* Upgrade pyyaml to v6.0.1 by @charliermarsh in https://github.com/astral-sh/rye/pull/1170 +* Pass keyring-provider to uv sync and install by @emarsden-iso in https://github.com/astral-sh/rye/pull/1183 +* Add docs hint for packages on multiple indexes by @dennisrall in https://github.com/astral-sh/rye/pull/1188 +* Pass universal flag down to uv by @lbowenwest in https://github.com/astral-sh/rye/pull/1186 +* Use musl build for ARM Linux by @charliermarsh in https://github.com/astral-sh/rye/pull/1192 +* Bump uv to 0.2.22 by @charliermarsh in https://github.com/astral-sh/rye/pull/1192 +* Upgrade twine to v5.1.1 by @charliermarsh in https://github.com/astral-sh/rye/pull/1199 + +## 0.35.0 + +Released on 2024-06-24. + +* Enforce `--pre` when auto-syncing by @charliermarsh in https://github.com/astral-sh/rye/pull/1107 +* Move from `rye-up.com` to `rye.astral.sh` by @charliermarsh in https://github.com/astral-sh/rye/pull/1113 +* Fix install instructions on README.md for mac/linux by @timothycrosley in https://github.com/astral-sh/rye/pull/1114 +* Fix CLI deserialization of PowerShell (`powershell`) by @charliermarsh in https://github.com/astral-sh/rye/pull/1125 +* Add ability to specify option to generate hashes within pyproject.toml by @asmith26 in https://github.com/astral-sh/rye/pull/1129 +* Bump uv to 0.2.13 by @charliermarsh in https://github.com/astral-sh/rye/pull/1123 +* Improve `config.toml` error messages by @zys864 in https://github.com/astral-sh/rye/pull/1155 + +## 0.34.0 + +Released on 2024-05-20. + +* Add nushell completion support by @MilesCranmer in https://github.com/astral-sh/rye/pull/1030 +* Use uv in rye build when enabled by @bluss in https://github.com/astral-sh/rye/pull/978 +* Add short version add -d for rye add --dev by @bluss in https://github.com/astral-sh/rye/pull/1044 +* Flip uv to the default Rye backend by @charliermarsh in https://github.com/astral-sh/rye/pull/1053 +* Fix Rye not using user-chosen toolchain as default during installation by @pjdon in https://github.com/astral-sh/rye/pull/1054 +* Add keyring support for uv by @emarsden-iso in https://github.com/astral-sh/rye/pull/1016 +* Allow to generate lockfiles with hashes when using uv by @mvaled in https://github.com/astral-sh/rye/pull/1070 +* Bump ruff to 0.4.4 by @davfsa in https://github.com/astral-sh/rye/pull/1075 +* Fix TOML array formatting by @my1e5 in https://github.com/astral-sh/rye/pull/1084 +* Bump uv to 0.1.44 by @charliermarsh in https://github.com/astral-sh/rye/pull/1085 +* Discover cosmo-ified (`.com`) binaries on Windows by @mataha in https://github.com/astral-sh/rye/pull/1091 +* Write `use-uv = true` in no-prompt mode by @charliermarsh in https://github.com/astral-sh/rye/pull/1098 + +## 0.33.0 + +Released on 2024-04-24. + +- Ensure files created by `rye init`, such as `pyproject.toml` and initial python files end with a newline. #979 + +- Add `--refresh` argument on `-f`. #994 + +- Preserve trailing newline in templates. #979 + +- Update uv to 0.1.37. #980 + +- Allow comments in `.python-version`. #1038 + +- Update Python releases to include 3.12.3 et al. #1022 + +## 0.32.0 + +Released on 2024-03-29 + +- Update uv to 0.1.26. #924 + +- Always create `.gitignore` file in `rye init`. #919 + +- Prevent `rye fetch --force` from removing a target directory that is not a Python installation. #921 + +- `rye list` always prints the currently installed packages even this project is not managed by Rye. #940 + +- Fix error on using -v or -q with `rye fmt` or `rye lint`. #959 + +- Fix rye fetch detection of registered toolchain. #931 + +- Ignore build-system configuration for virtual projects. #929 + +## 0.31.0 + +Released on 2024-03-22 + +- Update uv to 0.1.23. #916 + +- Allow `rye publish` working outside of project. #910 + +- `rye test --quiet` no longer implies `--no-capture`. #915 + +- Rye now can be used to fetch Python installations even when not using Rye + and build infos are no longer included by default. This means that rather + than having interpreters at `~/.rye/py/cpython@3.11.1/install/bin/python3` + it will now reside at `~/.rye/py/cpython@3.11.1/bin/python3`. #917 + +- Installer now recommends `uv` over `pip-tools`. #918 + +## 0.30.0 + +Released on 2024-03-19 + +- Update uv to 0.1.21. #884, #890, #904 + +- Fix incorrect flag passing of `rye test` `-q` and `-v`. #880 + +- Rye now loads `.env` files. This applies both for Rye's own + use of environment variables but also to scripts launched via + `run`. #894 + +- Fix `rye add m --path ./m` causing a panic on windows. #897 + +## 0.29.0 + +Released on 2024-03-11 + +- Updated to `uv` 0.1.17. #850, #867 + +- Trap panics and silence bad pipe errors. #862 + +- Updating `rye` will now also ensure that the self-venv is updated. Previously + this was deferred until the next `sync`. #863 + +- The `self update` command now accepts `--branch`. #864 + +- Fixed an issue that caused pip-tools to not update. #865 + +- Updates `build` and `certifi`. #866 + ## 0.28.0 -_Unreleased_ +Released on 2024-03-07 - +- `--skip-existing` is now available with Rye's `publish` command. #831 + +- Bumped `uv` to 0.1.15. #760, #820, #837 + +- Bumped `ruff` to 0.3.0. #821 + +- The `init` command now generates a script with the name of the + project rather than `hello`. #801 + +- Retain markers when adding dependencies with features when uv is used. #807 + +- Fixed a bug that caused repeated syncs not to recall all previous options. #830 + +- Report `self-python` version in `--version`. #843 + +- Fixes a bug where `rye config` would not create the `RYE_HOME` folder if needed. #844 + +- `rye add` now retains version and URL for the requirements when `uv` is used. #846 + +- Added a `rye test` command which invokes `pytest`. #847 ## 0.27.0 diff --git a/Cargo.lock b/Cargo.lock index 0bec4dca29..377272c5e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -29,24 +29,22 @@ dependencies = [ [[package]] name = "age" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d55a4d912c80a92762ffd1c884065f3f9646467d22c95390e824a0ff7def472" +checksum = "edeef7d7b199195a2d7d7a8155d2d04aee736e60c5c7bdd7097d115369a8817d" dependencies = [ "age-core", - "base64 0.13.1", + "base64 0.21.7", "bech32", "chacha20poly1305", "cookie-factory", - "hkdf", "hmac", "i18n-embed", "i18n-embed-fl", "lazy_static", "nom", "pin-project", - "rand 0.7.3", - "rand 0.8.5", + "rand", "rust-embed", "scrypt", "sha2", @@ -57,26 +55,26 @@ dependencies = [ [[package]] name = "age-core" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d2e815ac879dc23c1139e720d21c6cd4d1276345c772587285d965a69b8f32" +checksum = "a5f11899bc2bbddd135edbc30c36b1924fa59d0746bb45beb5933fafe3fe509b" dependencies = [ - "base64 0.13.1", + "base64 0.21.7", "chacha20poly1305", "cookie-factory", "hkdf", "io_tee", "nom", - "rand 0.8.5", + "rand", "secrecy", "sha2", ] [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -86,51 +84,60 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" dependencies = [ "backtrace", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -143,15 +150,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.13.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bech32" @@ -167,9 +174,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -182,9 +189,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "serde", @@ -192,9 +199,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -225,31 +232,26 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", ] -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chacha20" version = "0.9.1" @@ -276,11 +278,11 @@ dependencies = [ [[package]] name = "charset" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e9079d1a12a2cc2bffb5db039c43661836ead4082120d5844f02555aca2d46" +checksum = "f1f927b07c74ba84c7e5fe4db2baeb3e996ab2688992e39ac68ce3220a677c7e" dependencies = [ - "base64 0.13.1", + "base64 0.22.1", "encoding_rs", ] @@ -307,9 +309,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", "clap_derive", @@ -317,9 +319,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstyle", "clap_lex", @@ -328,36 +330,46 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.10" +version = "4.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8670053e87c316345e384ca1f3eba3006fc6355ed8b8a1140d104e109e3df34" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_complete_nushell" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb745187d7f4d76267b37485a65e0149edd0e91a4cfcdd3f27524ad86cee9f3" +checksum = "5fe32110e006bccf720f8c9af3fee1ba7db290c724eab61544e1d3295be3a40e" dependencies = [ "clap", + "clap_complete", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "configparser" -version = "3.0.4" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec6d3da8e550377a85339063af6e3735f4b1d9392108da4e083a1b3b9820288" +checksum = "e57e3272f0190c3f1584272d613719ba5fc7df7f4942fe542e63d949cf3a649b" [[package]] name = "console" @@ -374,15 +386,12 @@ dependencies = [ [[package]] name = "cookie-factory" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] [[package]] name = "cpufeatures" @@ -395,18 +404,18 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -420,9 +429,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ "nix", "windows-sys 0.52.0", @@ -430,9 +439,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" dependencies = [ "curl-sys", "libc", @@ -440,14 +449,14 @@ dependencies = [ "openssl-sys", "schannel", "socket2", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "curl-sys" -version = "0.4.71+curl-8.6.0" +version = "0.4.74+curl-8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b12a7ab780395666cb576203dc3ed6e01513754939a600b85196ccf5356bc5" +checksum = "8af10b986114528fcdc4b63b6f5f021b7057618411046a4de2ba0f0149a097bf" dependencies = [ "cc", "libc", @@ -455,55 +464,33 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "curve25519-dalek" -version = "3.2.0" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", "subtle", "zeroize", ] [[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.4" +name = "curve25519-dalek-derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "fnv", - "ident_case", "proc-macro2", "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -521,25 +508,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" - -[[package]] -name = "decompress" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67605f4868a37e8a9c50003a866a6896104602d5d7ddf7e8a7f398ec97e44ab3" -dependencies = [ - "derive_builder", - "flate2", - "infer", - "lazy_static", - "regex", - "tar", - "thiserror", - "zstd 0.12.4", -] +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "deranged" @@ -551,41 +522,21 @@ dependencies = [ ] [[package]] -name = "derive_builder" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.12.0" +name = "derive_arbitrary" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "darling", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder_macro" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" -dependencies = [ - "derive_builder_core", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] name = "deunicode" -version = "1.4.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" +checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" [[package]] name = "dialoguer" @@ -599,15 +550,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.7" @@ -621,20 +563,26 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" @@ -644,9 +592,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -659,9 +607,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -678,9 +626,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" @@ -690,7 +644,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -705,9 +659,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", @@ -715,9 +669,9 @@ dependencies = [ [[package]] name = "fluent" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" dependencies = [ "fluent-bundle", "unic-langid", @@ -725,9 +679,9 @@ dependencies = [ [[package]] name = "fluent-bundle" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" dependencies = [ "fluent-langneg", "fluent-syntax", @@ -750,19 +704,13 @@ dependencies = [ [[package]] name = "fluent-syntax" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ "thiserror", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -802,42 +750,120 @@ dependencies = [ ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "futures" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ - "typenum", - "version_check", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "getrandom" -version = "0.1.16" +name = "futures-channel" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", ] [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "git-testament" @@ -857,7 +883,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", "time", ] @@ -876,9 +902,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -886,9 +912,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hex" @@ -911,7 +937,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -933,15 +959,15 @@ dependencies = [ "serde", "serde_derive", "thiserror", - "toml 0.8.9", + "toml 0.8.19", "unic-langid", ] [[package]] name = "i18n-embed" -version = "0.13.9" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a86226a7a16632de6723449ee5fe70bac5af718bc642ee9ca2f0f6e14fa1fa" +checksum = "94205d95764f5bb9db9ea98fa77f89653365ca748e27161f5bbea2ffd50e459c" dependencies = [ "arc-swap", "fluent", @@ -960,9 +986,9 @@ dependencies = [ [[package]] name = "i18n-embed-fl" -version = "0.6.7" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26a3d3569737dfaac7fc1c4078e6af07471c3060b8e570bcd83cdd5f4685395" +checksum = "9fc1f8715195dffc4caddcf1cf3128da15fe5d8a137606ea8856c9300047d5a2" dependencies = [ "dashmap", "find-crate", @@ -975,7 +1001,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.48", + "syn 2.0.72", "unic-langid", ] @@ -989,15 +1015,9 @@ dependencies = [ "i18n-config", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.5.0" @@ -1010,9 +1030,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -1020,9 +1040,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -1031,15 +1051,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "infer" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3" -dependencies = [ - "cfb", -] - [[package]] name = "inout" version = "0.1.3" @@ -1051,9 +1062,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.35.1" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c985c1bef99cf13c58fade470483d81a2bfe846ebde60ed28cc2dddec2df9e2" +checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" dependencies = [ "console", "lazy_static", @@ -1061,7 +1072,6 @@ dependencies = [ "regex", "serde", "similar", - "yaml-rust", ] [[package]] @@ -1077,18 +1087,18 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] [[package]] name = "intl-memoizer" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" dependencies = [ "type-map", "unic-langid", @@ -1111,46 +1121,46 @@ checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "junction" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca39ef0d69b18e6a2fd14c2f0a1d593200f4a4ed949b240b5917ab51fac754cb" +checksum = "1c9c415a9b7b1e86cd5738f39d34c9e78c765da7fb1756dbd7d31b3b0d2e7afa" dependencies = [ "scopeguard", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libz-sys" -version = "1.1.15" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -1160,9 +1170,9 @@ dependencies = [ [[package]] name = "license" -version = "3.2.0" +version = "3.4.0+3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778718185117620a06e95d2b1e57d50166b1d6bfad93c8abfc1b3344c863ad8c" +checksum = "a7da1e0d845faf299a9fe5f201a918a0dc0d5fc22c7b9580a6a23fed3a912b37" dependencies = [ "reword", "serde", @@ -1177,15 +1187,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1193,32 +1203,32 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "mailparse" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d096594926cab442e054e047eb8c1402f7d5b2272573b97ba68aa40629f9757" +checksum = "3da03d5980411a724e8aaf7b61a7b5e386ec55a7fb49ee3d0ff79efc7e5e7c7e" dependencies = [ "charset", "data-encoding", - "quoted_printable 0.5.0", + "quoted_printable", ] [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minijinja" -version = "1.0.12" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe0ff215195a22884d867b547c70a0c4815cbbcc70991f281dca604b20d10ce" +checksum = "f4bf71af278c578cbcc91d0b1ff092910208bc86f7b3750364642bd424e3dcd3" dependencies = [ "serde", "serde_json", @@ -1232,9 +1242,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -1256,21 +1266,22 @@ dependencies = [ "target-lexicon", "tempfile", "thiserror", - "toml 0.8.9", + "toml 0.8.19", "tracing", "unscanny", "ureq", - "zstd 0.13.0", + "zstd", ] [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -1285,19 +1296,31 @@ dependencies = [ ] [[package]] -name = "ntapi" -version = "0.4.1" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ - "winapi", + "num_enum_derive", ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "num_enum_derive" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.72", +] [[package]] name = "number_prefix" @@ -1307,9 +1330,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.2" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] @@ -1322,9 +1345,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" @@ -1334,18 +1357,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.2.2+3.2.1" +version = "300.3.1+3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bbfad0063610ac26ee79f7484739e2b07555a75c42453b89263830b5c8103bc" +checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.99" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -1356,9 +1379,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1366,15 +1389,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -1385,11 +1408,12 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.10.7", + "digest", + "hmac", ] [[package]] @@ -1428,35 +1452,41 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "poly1305" @@ -1471,9 +1501,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "powerfmt" @@ -1483,9 +1513,21 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] [[package]] name = "proc-macro-error" @@ -1513,9 +1555,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1531,9 +1573,9 @@ dependencies = [ [[package]] name = "python-pkginfo" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037469c164f08c891bf6d69ca02f1d56210011451e229618669777df82124cfa" +checksum = "4320ca452fe003f8a07afb8e30c315bbd813ae8105f454ddefebf15a24021e1f" dependencies = [ "flate2", "fs-err", @@ -1542,42 +1584,23 @@ dependencies = [ "serde", "tar", "thiserror", - "zip", + "zip 1.1.4", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "quoted_printable" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49" - -[[package]] -name = "quoted_printable" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ec282e887b434b68c18fe5c121d38e72a5cf35119b59e54ec5b992ea9c8eb0" - -[[package]] -name = "rand" -version = "0.7.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] +checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73" [[package]] name = "rand" @@ -1586,18 +1609,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1607,16 +1620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -1625,32 +1629,32 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.12", + "getrandom", ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "redox_syscall" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "rand_core 0.5.1", + "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1660,9 +1664,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1671,9 +1675,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reword" @@ -1686,37 +1690,38 @@ dependencies = [ [[package]] name = "rfc2047-decoder" -version = "0.2.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fc4b4e52897c3e30b12b7e9b04461215b647fbe66f6def60dd8edbce14ec2e" +checksum = "e90a668c463c412c3118ae1883e18b53d812c349f5af7a06de3ba4bb0c17cc73" dependencies = [ "base64 0.21.7", "charset", "chumsky", "memchr", - "quoted_printable 0.4.8", + "quoted_printable", "thiserror", ] [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.12", + "cfg-if", + "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "rust-embed" -version = "6.8.1" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -1725,22 +1730,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.8.1" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.48", + "syn 2.0.72", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "7.8.1" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" dependencies = [ "sha2", "walkdir", @@ -1748,9 +1753,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -1758,13 +1763,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1773,41 +1787,52 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "log", + "once_cell", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] [[package]] name = "rye" -version = "0.28.0" +version = "0.43.0" dependencies = [ "age", "anyhow", "bzip2", "clap", "clap_complete", + "clap_complete_nushell", "configparser", "console", "ctrlc", "curl", - "decompress", "dialoguer", + "dotenvy", "flate2", "fslock", "git-testament", @@ -1819,10 +1844,8 @@ dependencies = [ "insta-cmd", "junction", "license", - "memchr", "minijinja", "monotrail-utils", - "nix", "once_cell", "pathdiff", "pep440_rs", @@ -1837,25 +1860,24 @@ dependencies = [ "shlex", "slug", "static_vcruntime", - "sysinfo", "tar", "tempfile", - "toml_edit", + "toml_edit 0.22.20", "url", "walkdir", "which", "winapi", "winreg", "xattr", - "zip", - "zstd 0.13.0", + "zip 0.6.6", + "zstd", ] [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "salsa20" @@ -1892,26 +1914,15 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scrypt" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ - "hmac", "pbkdf2", "salsa20", "sha2", ] -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "secrecy" version = "0.8.0" @@ -1923,9 +1934,9 @@ dependencies = [ [[package]] name = "self-replace" -version = "1.3.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525db198616b2bcd0f245daf7bfd8130222f7ee6af9ff9984c19a61bf1160c55" +checksum = "f7828a58998685d8bf5a3c5e7a3379a5867289c20828c3ee436280b44b598515" dependencies = [ "fastrand 1.9.0", "tempfile", @@ -1938,51 +1949,58 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" dependencies = [ - "self_cell 1.0.3", + "self_cell 1.0.4", ] [[package]] name = "self_cell" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -1995,7 +2013,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -2012,9 +2030,18 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "similar" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "slug" @@ -2028,18 +2055,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.10" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2075,9 +2102,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -2086,40 +2113,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.48" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sysinfo" -version = "0.29.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "winapi", -] - [[package]] name = "tar" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", @@ -2128,19 +2140,19 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "redox_syscall", + "fastrand 2.1.0", + "once_cell", "rustix", "windows-sys 0.52.0", ] @@ -2157,29 +2169,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -2198,9 +2210,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -2208,18 +2220,18 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -2241,21 +2253,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.9" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -2265,12 +2277,23 @@ name = "toml_edit" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -2293,7 +2316,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -2307,9 +2330,9 @@ dependencies = [ [[package]] name = "type-map" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ "rustc-hash", ] @@ -2322,18 +2345,18 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unic-langid" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238722e6d794ed130f91f4ea33e01fcff4f188d92337a21297892521c72df516" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" dependencies = [ "unic-langid-impl", ] [[package]] name = "unic-langid-impl" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd55a2063fdea4ef1f8633243a7b0524cbeef1905ae04c31a1c9b9775c55bc6" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" dependencies = [ "serde", "tinystr", @@ -2353,24 +2376,24 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "universal-hash" @@ -2396,16 +2419,16 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "flate2", "log", "once_cell", "rustls", - "rustls-webpki", + "rustls-pki-types", "serde", "serde_json", "url", @@ -2414,9 +2437,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2424,12 +2447,6 @@ dependencies = [ "serde", ] -[[package]] -name = "uuid" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" - [[package]] name = "vcpkg" version = "0.2.15" @@ -2438,26 +2455,20 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2466,9 +2477,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2476,24 +2487,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2501,40 +2512,42 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "which" -version = "6.0.0" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" +checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075" dependencies = [ "either", "home", - "once_cell", "rustix", - "windows-sys 0.52.0", + "winsafe", ] [[package]] @@ -2555,11 +2568,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -2583,7 +2596,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -2603,17 +2625,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2624,9 +2647,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2636,9 +2659,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2648,9 +2671,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2660,9 +2689,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2672,9 +2701,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2684,9 +2713,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2696,15 +2725,24 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.37" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -2719,14 +2757,21 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "x25519-dalek" -version = "1.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core 0.5.1", + "rand_core", + "serde", "zeroize", ] @@ -2741,40 +2786,32 @@ dependencies = [ "rustix", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -2787,7 +2824,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -2797,55 +2834,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", - "bzip2", "crc32fast", "crossbeam-utils", "flate2", - "time", ] [[package]] -name = "zstd" -version = "0.12.4" +name = "zip" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" dependencies = [ - "zstd-safe 6.0.6", + "arbitrary", + "bzip2", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "num_enum", + "thiserror", + "time", ] [[package]] name = "zstd" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ - "zstd-safe 7.0.0", + "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.0.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/README.md b/README.md index 0b3929bfc4..8142af3473 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,21 @@ ----
-[![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye-up.com) +[![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye.astral.sh) [![](https://dcbadge.vercel.app/api/server/drbkcdtSbg?style=flat)](https://discord.gg/drbkcdtSbg)
+> [!NOTE] +> +> If you're getting started with Rye, consider [uv](https://github.com/astral-sh/uv), the +> [successor project](https://lucumr.pocoo.org/2024/2/15/rye-grows-with-uv/) from the same maintainers. +> +> While Rye is actively maintained, uv offers a more stable and feature-complete experience, and is the recommended +> choice for new projects. +> +> Having trouble migrating? [Let us know what's missing.](https://github.com/astral-sh/rye/discussions/1342) + Rye is a comprehensive project and package management solution for Python. Born from [its creator's](https://github.com/mitsuhiko) desire to establish a one-stop-shop for all Python users, Rye provides a unified experience to install and manage Python @@ -47,21 +57,21 @@ The installation takes just a minute: * **Linux and macOS:** ``` - curl -sSf https://rye-up.com/get | bash + curl -sSf https://rye.astral.sh/get | bash ``` * **Windows:** - Download and run the installer ([64bit Intel](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-windows.exe) or [32bit Intel](https://github.com/astral-sh/rye/releases/latest/download/rye-x86-windows.exe)). + Download and run the installer ([64-bit (x86-64)](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-windows.exe) or [32-bit (x86)](https://github.com/astral-sh/rye/releases/latest/download/rye-x86-windows.exe)). -For more details and other options, refer to the [installation instructions](https://rye-up.com/guide/installation/). +For more details and other options, refer to the [installation instructions](https://rye.astral.sh/guide/installation/). ## Learn More Did I spark your interest? -* [Visit the Website](https://rye-up.com/) -* [Read the Documentation](https://rye-up.com/guide/) +* [Visit the Website](https://rye.astral.sh/) +* [Read the Documentation](https://rye.astral.sh/guide/) * [Report Problems in the Issue Tracker](https://github.com/astral-sh/rye/issues) ## More @@ -70,5 +80,5 @@ Did I spark your interest? on GitHub * [Discord](https://discord.gg/drbkcdtSbg), for conversations with other developers in text form * [Issue Tracker](https://github.com/astral-sh/rye/issues), if you run into bugs or have suggestions -* [Badges](https://rye-up.com/community/#badges), if you want to show that you use Rye +* [Badges](https://rye.astral.sh/community/#badges), if you want to show that you use Rye * License: MIT diff --git a/docs/.includes/curl-to-bash-options.md b/docs/.includes/curl-to-bash-options.md index 9fec62be05..9c67a60039 100644 --- a/docs/.includes/curl-to-bash-options.md +++ b/docs/.includes/curl-to-bash-options.md @@ -14,5 +14,5 @@ variables: This for instance installs a specific version of Rye without asking questions: ```bash -curl -sSf https://rye-up.com/get | RYE_VERSION="0.4.0" RYE_INSTALL_OPTION="--yes" bash +curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.4.0" RYE_INSTALL_OPTION="--yes" bash ``` \ No newline at end of file diff --git a/docs/.includes/quick-install.md b/docs/.includes/quick-install.md index 1ae5b317b0..383c9116b6 100644 --- a/docs/.includes/quick-install.md +++ b/docs/.includes/quick-install.md @@ -4,14 +4,14 @@ operating system and CPU architecture and install it: ```bash - curl -sSf https://rye-up.com/get | bash + curl -sSf https://rye.astral.sh/get | bash ``` Alternatively if you don't trust this approach, you can download the latest release binary. On first run it will install itself. - * [rye-x86_64-linux.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-linux.gz) for 64bit Intel computers - * [rye-aarch64-linux.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-aarch64-linux.gz) for 64bit ARM computers + * [rye-x86_64-linux.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-linux.gz) Intel/AMD (x86-64). + * [rye-aarch64-linux.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-aarch64-linux.gz) for ARM64. ```bash gunzip rye-x86_64-linux.gz @@ -25,14 +25,14 @@ operating system and CPU architecture and install it: ```bash - curl -sSf https://rye-up.com/get | bash + curl -sSf https://rye.astral.sh/get | bash ``` Alternatively if you don't trust this approach, you can download the latest release binary. On first run it will install itself. - * [rye-aarch64-macos.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-aarch64-macos.gz) for M1/M2 Macs - * [rye-x86_64-macos.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-macos.gz) for Intel Macs + * [rye-aarch64-macos.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-aarch64-macos.gz) for Apple Silicon (M1/M2/M3) (ARM64). + * [rye-x86_64-macos.gz](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-macos.gz) for Intel processors (x86-64). ```bash gunzip rye-aarch64-macos.gz @@ -47,8 +47,8 @@ to have "Developer Mode" activated when using Rye and before starting the installation. [Learn more](../guide/faq.md). - * [rye-x86_64-windows.exe](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-windows.exe) for 64bit Intel Windows - * [rye-x86-windows.exe](https://github.com/astral-sh/rye/releases/latest/download/rye-x86-windows.exe) for 32bit Intel Windows + * [rye-x86_64-windows.exe](https://github.com/astral-sh/rye/releases/latest/download/rye-x86_64-windows.exe) for 64-bit (x86-64). + * [rye-x86-windows.exe](https://github.com/astral-sh/rye/releases/latest/download/rye-x86-windows.exe) for 32-bit (x86). !!!Note diff --git a/docs/CNAME b/docs/CNAME index ced6063925..0c6d7c3fb4 100644 --- a/docs/CNAME +++ b/docs/CNAME @@ -1 +1 @@ -rye-up.com +rye.astral.sh diff --git a/docs/community.md b/docs/community.md index b82ef2bc8a..ec394f8ce0 100644 --- a/docs/community.md +++ b/docs/community.md @@ -22,21 +22,21 @@ You can also reach out [via Twitter](https://twitter.com/mitsuhiko) or Want to show that you are using Rye? Why not throw a badge into your project's `README.md`: ```md -[![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye-up.com) +[![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye.astral.sh) ``` ... or `README.rst`: ```rst .. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json - :target: https://rye-up.com + :target: https://rye.astral.sh :alt: Rye ``` ... or, as HTML: ```html -Rye +Rye ``` -[![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye-up.com) +[![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye.astral.sh) diff --git a/docs/guide/basics.md b/docs/guide/basics.md index b807af57d9..bd8eacc4b8 100644 --- a/docs/guide/basics.md +++ b/docs/guide/basics.md @@ -74,8 +74,8 @@ Use the `add` command to add dependencies to your project. rye add "flask>=2.0" ``` -Note that after `add` you need to run `sync` again to actually install it. If you -want to add packages from custom indexes, you have to [configure the source](sources.md) +Followed by `rye sync` to install the dependency into the virtual environment. +If you want to add packages from custom indexes, you have to [configure the source](sources.md) first. ## Listing Dependencies @@ -97,13 +97,14 @@ rye remove flask ## Working with the Project -To run executables in the context of the virtualenv you can use the `run` command. For -instance if you want to use `black` you can add and run it like this: +To run executables in the context of the virtualenv you can use the `run` command. For +instance, to use `black`, add it to the project, sync the virtual environment, and run it +over the current directory like this: ``` rye add black rye sync -rye run black +rye run black . ``` If you want to have the commands available directly you will need to activate the @@ -163,11 +164,11 @@ The following structure will be created: ``` The [`pyproject.toml`](pyproject.md) will be generated with a -[`[project.scripts]`](pyproject.md#projectscripts) section named `hello` -that points to the `main()` function of `__init__.py`. After you -synchronized your changes, you can run the script with `rye run hello`. +[`[project.scripts]`](pyproject.md#projectscripts) section containing a +`my-project` script that points to the `main()` function of `__init__.py`. After +you synchronized your changes, you can run the script with `rye run my-project`. ```shell rye sync -rye run hello +rye run my-project ``` diff --git a/docs/guide/commands/add.md b/docs/guide/commands/add.md index 816cd154a3..6875900c8b 100644 --- a/docs/guide/commands/add.md +++ b/docs/guide/commands/add.md @@ -2,7 +2,7 @@ Adds a Python package to this project. The command takes a PEP 508 requirement string but provides additional helper arguments to make this process more user friendly. For -instance instead of passing git references within the requiement string, the `--git` +instance instead of passing git references within the requirement string, the `--git` parameter can be used. If auto sync is disabled, after a dependency is added it's not automatically @@ -73,7 +73,7 @@ Added packagename @ file:///path/to/packagename as regular dependency * `--pre`: Include pre-releases when finding a package version -* `--pin `: Overrides the pin operator [possible values: `equal`, `tilde-equal``, `greater-than-equal``] +* `--pin `: Overrides the pin operator [possible values: `equal`, `tilde-equal`, `greater-than-equal`] * `--sync`: Runs `sync` automatically even if auto-sync is disabled. @@ -83,4 +83,4 @@ Added packagename @ file:///path/to/packagename as regular dependency * `-q, --quiet`: Turns off all output -* `-h, --help`: Print help (see a summary with '-h') \ No newline at end of file +* `-h, --help`: Print help (see a summary with `-h`) diff --git a/docs/guide/commands/build.md b/docs/guide/commands/build.md index a8dff8a1d9..46d7498031 100644 --- a/docs/guide/commands/build.md +++ b/docs/guide/commands/build.md @@ -46,7 +46,7 @@ By default you will find the artifacts in the `dist` folder. * `-o, --out `: An output directory (defaults to `workspace/dist`) -* `--pyproject `: Use this `pyproject.toml`` file +* `--pyproject `: Use this `pyproject.toml` file * `-c, --clean`: Clean the output directory first @@ -54,4 +54,4 @@ By default you will find the artifacts in the `dist` folder. * `-q, --quiet`: Turns off all output -* `-h, --help`: Print help \ No newline at end of file +* `-h, --help`: Print help diff --git a/docs/guide/commands/fetch.md b/docs/guide/commands/fetch.md index d85be0980d..66ee918f8a 100644 --- a/docs/guide/commands/fetch.md +++ b/docs/guide/commands/fetch.md @@ -3,6 +3,10 @@ Fetches a Python interpreter for the local machine. This command is available under the aliases `rye fetch` and `rye toolchain fetch`. +As of Rye 0.31.0 toolchains are always fetched without build info. This +means that in the folder where toolchains are stored only the interpreter +is found. For more information see [Fetching Toolchains](../toolchains/index.md#build-info). + ## Example Fetch a specific version of Python: @@ -25,6 +29,13 @@ Unpacking Downloaded cpython@3.8.17 ``` +To fetch a version of Python into a specific location rather than rye's +interpreter cache: + +``` +$ rye fetch cpython@3.9.1 --target-path=my-interpreter +``` + ## Arguments * `[VERSION]`: The version of Python to fetch. @@ -35,6 +46,12 @@ Downloaded cpython@3.8.17 * `-f, --force`: Fetch the Python toolchain even if it is already installed. +* `--target-path` ``: Fetches the Python toolchain into an explicit location rather + +* `--build-info`: Fetches with build info + +* `--no-build-info`: Fetches without build info + * `-v, --verbose`: Enables verbose diagnostics * `-q, --quiet`: Turns off all output diff --git a/docs/guide/commands/index.md b/docs/guide/commands/index.md index 3fedf90ee8..07c99679df 100644 --- a/docs/guide/commands/index.md +++ b/docs/guide/commands/index.md @@ -18,8 +18,17 @@ This is a list of all the commands that rye provides: * [run](run.md): Runs a command installed into this package * [show](show.md): Prints the current state of the project * [sync](sync.md): Updates the virtualenv based on the pyproject.toml +* [test](test.md): Runs the project's tests * [toolchain](toolchain/index.md): Helper utility to manage Python toolchains * [tools](tools/index.md): Helper utility to manage global tools. * [self](self/index.md): Rye self management * [uninstall](uninstall.md): Uninstalls a global tool (alias) -* [version](version.md): Get or set project version \ No newline at end of file +* [version](version.md): Get or set project version + +## Options + +The toplevel `rye` command accepts the following options: + +* `--env-file` ``: This can be supplied multiple times to make rye load + a given `.env` file. Note that this file is not referenced to handle the + `RYE_HOME` variable which must be supplied as environment variable always. \ No newline at end of file diff --git a/docs/guide/commands/lock.md b/docs/guide/commands/lock.md index 374e0744b6..079f69363d 100644 --- a/docs/guide/commands/lock.md +++ b/docs/guide/commands/lock.md @@ -3,7 +3,7 @@ Updates the lockfiles without installing dependencies. Usually one would use the [`sync`](sync.md) command instead which both locks and installs dependencies. -For more information see [Synching and Locking](../sync.md). +For more information see [Syncing and Locking](../sync.md). ## Example @@ -30,6 +30,8 @@ Done! * `--all-features`: Enables all features +* `--generate-hashes`: Set to true to lock with hashes in the lockfile + * `--with-sources`: Set to true to lock with sources in the lockfile * `--pyproject `: Use this pyproject.toml file diff --git a/docs/guide/commands/publish.md b/docs/guide/commands/publish.md index 0baab43003..7baeb6bf11 100644 --- a/docs/guide/commands/publish.md +++ b/docs/guide/commands/publish.md @@ -40,6 +40,8 @@ $ rye publish dist/example-0.1.0.tar.gz * `--cert `: Path to alternate CA bundle +* `--skip-existing`: Skip files already published (repository must support this feature) + * `-y, --yes`: Skip prompts * `-v, --verbose`: Enables verbose diagnostics diff --git a/docs/guide/commands/self/completion.md b/docs/guide/commands/self/completion.md index 5631ae0e5e..2b3c56cf42 100644 --- a/docs/guide/commands/self/completion.md +++ b/docs/guide/commands/self/completion.md @@ -7,7 +7,7 @@ Generates a completion script for a shell Generate a completion script for zsh and load it: ``` -$ eval (rye self completion -s zsh) +$ eval "$(rye self completion -s zsh)" ``` ## Arguments @@ -18,6 +18,6 @@ _no arguments_ * `-s, --shell `: The shell to generate a completion script for (defaults to 'bash') - [possible values: `bash`, `elvish`, `fish`, `powershell`, `zsh`] + [possible values: `bash`, `elvish`, `fish`, `powershell`, `zsh`, `nushell`] * `-h, --help`: Print help (see a summary with '-h') diff --git a/docs/guide/commands/self/update.md b/docs/guide/commands/self/update.md index 629c4ca352..9eb16bfd80 100644 --- a/docs/guide/commands/self/update.md +++ b/docs/guide/commands/self/update.md @@ -25,6 +25,12 @@ Compile a specific revision: $ rye self update --rev 08910bc9b3b7c72a3d3ac694c4f3412259161477 ``` +Compile latest development version: + +``` +$ rye self update --branch main +``` + ## Arguments _no arguments_ @@ -37,6 +43,8 @@ _no arguments_ * `--rev `: Update to a specific git rev +* `--branch `: Update to a specific git branch + * `--force`: Force reinstallation * `-h, --help`: Print help (see a summary with '-h') diff --git a/docs/guide/commands/sync.md b/docs/guide/commands/sync.md index f1306264cb..df85869325 100644 --- a/docs/guide/commands/sync.md +++ b/docs/guide/commands/sync.md @@ -2,7 +2,7 @@ Updates the lockfiles and installs the dependencies into the virtualenv. -For more information see [Synching and Locking](../sync.md). +For more information see [Syncing and Locking](../sync.md). ## Example @@ -53,6 +53,8 @@ To exit the sub shell run `exit`. * `--all-features`: Enables all features +* `--generate-hashes`: Set to true to lock with hashes in the lockfile + * `--with-sources`: Set to true to lock with sources in the lockfile * `--pyproject `: Use this pyproject.toml file diff --git a/docs/guide/commands/test.md b/docs/guide/commands/test.md new file mode 100644 index 0000000000..8a4d6671f2 --- /dev/null +++ b/docs/guide/commands/test.md @@ -0,0 +1,58 @@ +# `test` + ++++ 0.28.0 + +Run the test suites of the project. At the moment this always runs `pytest`. +Note that `pytest` must be installed into the virtual env unlike `ruff` +which is used behind the scenes automatically for linting and formatting. +Thus in order to use this, you need to declare `pytest` as dev dependency. + +``` +$ rye add --dev pytest +``` + +It's recommended to place tests in a folder called `tests` adjacent to the +`src` folder of your project. + +For more information about how to use pytest, have a look at the +[Pytest Documentation](https://docs.pytest.org/en/8.0.x/). + +## Example + +Run the test suite: + +``` +$ rye test +platform win32 -- Python 3.11.1, pytest-8.0.2, pluggy-1.4.0 +rootdir: /Users/john/Development/stuff +plugins: anyio-4.3.0 +collected 1 item + +stuff/tests/test_batch.py . [100%] +``` + +## Arguments + +* `[EXTRA_ARGS]...` Extra arguments to the test runner. + + These arguments are forwarded directly to the underlying test runner (currently + always `pytest`). Note that extra arguments must be separated from other arguments + with the `--` marker. + +## Options + +* `-a, --all`: Test all packages in the workspace + +* `-p, --package `: Run the test suite of a specific package + +* `--pyproject `: Use this `pyproject.toml` file + +* `-v, --verbose`: Enables verbose diagnostics + +* `-q, --quiet`: Turns off all output + +* `-i, --ignore`: Ignore the specified directory + +* `-s`, `--no-capture`: Disable stdout/stderr capture for the test runner + +* `-h, --help`: Print help (see a summary with '-h') diff --git a/docs/guide/config.md b/docs/guide/config.md index 93ad8aae0b..f21d8578a4 100644 --- a/docs/guide/config.md +++ b/docs/guide/config.md @@ -5,16 +5,16 @@ also a bit of global configuration to influence how it works. ## Changing Home Folder -By default Rye places all it's configuration in `~/.rye` on Unix and `%USERPROFILE%\.rye` on +By default Rye places all its configuration in `~/.rye` on Unix and `%USERPROFILE%\.rye` on Windows. This behavior can be changed via the `RYE_HOME` environment variable. This is useful -if you do not like the default location of where Rye places it's configuration or if you need +if you do not like the default location where Rye places its configuration or if you need to isolate it. ## Home Folder Structure -The `.rye` home folder contains both user configuration as well as Rye managed state such +The `.rye` home folder contains both user configuration as well as Rye-managed state such as [installed toolchains](toolchains/index.md). The following files and folders are placed within the -`.rye` folder. Note that not all are there always. +`.rye` folder. Note that not all are always there. ### `config.toml` @@ -39,7 +39,7 @@ which automatically proxies to the current virtualenv or globally installed [too ## Config File -The config file `config.toml` in the `.rye` folder today only is used to manage defaults. This +The config file `config.toml` in the `.rye` folder today is only used to manage defaults. This is a fully annotated config file: ```toml @@ -73,32 +73,33 @@ http = "http://127.0.0.1:4000" https = "http://127.0.0.1:4000" [behavior] -# When set to true the `managed` flag is always assumed to be true. +# When set to `true` the `managed` flag is always assumed to be `true`. force-rye-managed = false # Enables global shims when set to `true`. This means that the installed -# `python` shim will resolve to a Rye managed toolchain even outside of +# `python` shim will resolve to a Rye-managed toolchain even outside of # virtual environments. global-python = false -# When set to `true` enables experimental support of uv as a replacement -# for pip-tools. Learn more about uv here: https://github.com/astral-sh/uv -use-uv = false - # Enable or disable automatic `sync` after `add` and `remove`. This defaults # to `true` when uv is enabled and `false` otherwise. autosync = true -# Marks the managed .venv in a way that cloud based synchronization systems -# like Dropbox and iCloud Files will not upload it. This defaults to true +# Marks the managed .venv in a way that cloud-based synchronization systems +# like Dropbox and iCloud Files will not upload it. This defaults to `true` # as a .venv in cloud storage typically does not make sense. Set this to # `false` to disable this behavior. venv-mark-sync-ignore = true -# a array of tables with optional sources. Same format as in pyproject.toml +# When set to `true` Rye will fetch certain interpreters with build information. +# This will increase the space requirements, will put the interpreter into an +# extra folder called `./install/` and place build artifacts adjacent in `./build`. +fetch-with-build-info = false + +# An array of tables with optional sources. Same format as in pyproject.toml [[sources]] name = "default" -url = "http://pypi.org/simple/" +url = "https://pypi.org/simple/" ``` ## Manipulating Config @@ -107,7 +108,7 @@ url = "http://pypi.org/simple/" The configuration can be read and modified with `rye config`. The keys are in dotted notation. `--get` reads a key, `--set`, `--set-int`, -`--set-bool`, or `--unset` modify one. +`--set-bool`, and `--unset` modify one. ```bash rye config --set proxy.http=http://127.0.0.1:4000 @@ -119,4 +120,4 @@ For more information see [`config`](commands/config.md). ## Per Project Config -For the project specific `pyproject.toml` config see [pyproject.toml](pyproject.md). +For the project-specific `pyproject.toml` config see [pyproject.toml](pyproject.md). diff --git a/docs/guide/docker.md b/docs/guide/docker.md new file mode 100644 index 0000000000..3f1a8447b8 --- /dev/null +++ b/docs/guide/docker.md @@ -0,0 +1,105 @@ +# Building a Container with Docker + +If you want to put your Python code into a container, you probably have some server code that you don't submit to PyPI or another registry. +If that's the case, read on. Else, skip to [the next section](#container-from-a-python-package). + +This guide requires some familiarity with Docker and Dockerfiles. + +## Container from Source + +1. Make sure that your project is set up as a [virtual project](./virtual.md). + This means that you can't install it, and it won't mark itself as a dependency. + If you need your project to be installable, go to [the next section](#container-from-a-python-package). + + - Your `pyproject.toml` should contain `virtual = true` under the `[tool.rye]` section. If it's not there, add it and run `rye sync`. + - If you're just setting up a project, run `rye init --virtual` instead of `rye init`. + +2. Create a `Dockerfile` in your project root with the following content, using [`uv`](https://github.com/astral-sh/uv): + + ```Dockerfile + FROM python:slim + + RUN pip install uv + + WORKDIR /app + COPY requirements.lock ./ + RUN uv pip install --no-cache --system -r requirements.lock + + COPY src . + CMD python main.py + ``` + + Or, using `pip`: + + ```Dockerfile + FROM python:slim + + WORKDIR /app + COPY requirements.lock ./ + RUN PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -r requirements.lock + + COPY src . + CMD python main.py + ``` + +3. You can now build your image like this: + + ```bash + docker build . + ``` + +### Dockerfile Adjustments + +The `Dockerfile`s in this guide are examples. Some adjustments you might want to make: + +- The command (`CMD python src/main.py`) should point to your script. +- Adjust the base image (`FROM python:slim`): + - Prefer a tagged version that matches the one from your `.python-version` file, e.g. `FROM python:3.12.0-slim`. + - The `-slim` variants are generally a good tradeoff between image size and compatibility and should work fine for most workloads. + But you can also use `-alpine` for smaller images (but potential compatibility issues) or no suffix for ones that contain more system tools. +- If you need additional system packages, install them before copying your source code, i.e. before the line `COPY src .`. + When using Debian-based images (i.e. `-slim` or no-suffix variants), that could look like this: + + ```Dockerfile + RUN apt-get update \ + && apt-get install -y --no-install-recommends some-dependency another-dependency \ + && rm -rf /var/lib/apt/lists/* + ``` + +## Container from a Python Package + +If your code is an installable package, it's recommended that you first build it, then install it inside your Docker image. +This way you can be sure that the image is exactly the same as what a user installation would be. + +An example `Dockerfile` might look like this with [`uv`](https://github.com/astral-sh/uv): + +```Dockerfile +FROM python:slim +RUN pip install uv +RUN --mount=source=dist,target=/dist uv pip install --no-cache /dist/*.whl +CMD python -m my_package +``` + +To build your docker image, you'll have to first build your wheel, like this: + +```bash +rye build --wheel --clean +docker build . --tag your-image-name +``` + +Note that this approach bundles your dependencies and code in a single layer. +This might be nice for performance, but it also means that all dependencies are re-installed during every image build, and different versions won't share the disk space for the dependencies. + +The [Dockerfile adjustments from the previous section](#dockerfile-adjustments) apply. + +## Explanations + +Rye's lockfile standard is the `requirements.txt` format from `pip` (and used by [`uv`](https://github.com/astral-sh/uv)), so you don't actually need `rye` in your container to be able to install dependencies. +This makes the Dockerfile much simpler and avoids the necessity for multi-stage builds if small images are desired. + +The `--no-cache-dir` and `--no-cache` parameters, passed to `pip` and `uv` respectively, make the image smaller by not +writing any temporary files. While caching can speed up subsequent builds, it's not necessary in a container where the +image is built once and then used many times. + +Similarly, the `PYTHONDONTWRITEBYTECODE=1` environment variable is set to avoid writing `.pyc` files, which are not +needed in a container. (`uv` skips writing `.pyc` files by default.) diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 9e72463b2d..c2c2d00ee5 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -92,7 +92,7 @@ the default one higher priority in the `PATH: ``` export PATH="/usr/bin:$PATH" -curl -sSf https://rye-up.com/get | bash +curl -sSf https://rye.astral.sh/get | bash ``` ## References to Build-Time Paths @@ -102,7 +102,7 @@ accommodating to portable builds there are various limitations still with this approach. One of them is that built Python distributions capture some absolute paths and other build-time configuration. These file paths are then often used by build tools to invoke C compilers. For instance you might run into a compiler -error like ``error: stdio.h: No such file or directory`` when building C +error like `error: stdio.h: No such file or directory` when building C extensions. There is no known solution to this problem today other than [registering a non portable toolchain](toolchains/index.md#registering-toolchains). @@ -133,12 +133,12 @@ due to limitations in `libedit`. In some cases though you might also discover t the backspace key does not work or arrow keys don't work as expected. This can be because the _terminfo_ database cannot be found. -For solutions to this issue, read the [behavior quirks guide](https://python-build-standalone.readthedocs.io/en/latest/quirks.html) in the +For solutions to this issue, read the [behavior quirks guide](https://gregoryszorc.com/docs/python-build-standalone/main/quirks.html) in the Standalone Python Builds documentation for solutions. ## Can I use Rye Alongside Other Python Installations? -Rye given it's experimental nature does not want to disrupt already existing Python +Rye given its experimental nature does not want to disrupt already existing Python workflows. As such using it alongside other Python installations is intentionally supported. Even if the Rye shims come first on the `PATH`, Rye will automatically resolve to a different Python installation on the search path when invoked in a diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 25b62b8d51..1f883ee211 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -137,7 +137,7 @@ to learn more. ## Shell Completion -Rye supports generating completion scripts for Bash, Zsh, Fish or Powershell. Here are some common locations for each shell: +Rye supports generating completion scripts for Bash, Zsh, Fish, Powershell and Nushell. Here are some common locations for each shell: === "Bash" @@ -183,6 +183,12 @@ Rye supports generating completion scripts for Bash, Zsh, Fish or Powershell. He rye self completion -s powershell | Out-File -Encoding utf8 $PROFILE\..\Completions\rye_completion.ps1 ``` +=== "NuShell" + + ```nushell + rye self completion -s nushell | save --append $nu.env-path + ``` + ## Updating Rye To update rye to the latest version you can use `rye` itself: diff --git a/docs/guide/publish.md b/docs/guide/publish.md index eb5b9c276a..b08ff7e8a7 100644 --- a/docs/guide/publish.md +++ b/docs/guide/publish.md @@ -61,3 +61,7 @@ rye publish --token --yes ``` Rye will store your repository info in `$HOME/.rye/credentials` for future use. + +### --skip-existing + +You can use `--skip-existing` to skip any distribution files that have already been published to the repository. Note that some repositories may not support this feature. diff --git a/docs/guide/pyproject.md b/docs/guide/pyproject.md index 832b6b0309..7c5a6bcc72 100644 --- a/docs/guide/pyproject.md +++ b/docs/guide/pyproject.md @@ -60,6 +60,33 @@ pulled in as indirect dependencies. These are added here automatically with `ry excluded-dependencies = ["cffi"] ``` +## `tool.rye.universal` + ++++ 0.36.0 + +When this flag is enabled all `lock` and `sync` operations in the project or workspace +operate as if `--universal` is passed. This means that the dependency resolver will +attempt to generate a resolution that's valid on all platforms, operating systems, and +architectures, rather than a resolution that's specific to the current platform. + +```toml +[tool.rye] +universal = true +``` + +## `tool.rye.generate-hashes` + ++++ 0.35.0 + +When this flag is enabled all `lock` and `sync` operations in the project or workspace +operate as if `--generate-hashes` is passed. This means that all dependencies in all +lock files will include a hash. + +```toml +[tool.rye] +generate-hashes = true +``` + ## `tool.rye.lock-with-sources` +++ 0.18.0 @@ -158,6 +185,18 @@ This key can be used to provide environment variables with a script: devserver = { cmd = "flask run --debug", env = { FLASK_APP = "./hello.py" } } ``` +### `env-file` + ++++ 0.30.0 + +This is similar to `env` but rather than setting environment variables directly, it instead +points to a file that should be loaded (relative to the `pyproject.toml`): + +```toml +[tool.rye.scripts] +devserver = { cmd = "flask run --debug", env-file = ".dev.env" } +``` + ### `chain` This is a special key that can be set instead of `cmd` to make a command invoke multiple diff --git a/docs/guide/rust.md b/docs/guide/rust.md index b0b13a5846..55b1ebfdd1 100644 --- a/docs/guide/rust.md +++ b/docs/guide/rust.md @@ -41,14 +41,6 @@ it as a global tool: rye install maturin ``` -Note that `maturin develop` requires `pip` to be installed into the virtualenv. Before -you can use it you need to add it: - -``` -rye add --dev pip -rye sync -``` - Rye recommends mixed python/rust modules. In that case you can save some valuable iteration time by running `maturin develop --skip-install`: diff --git a/docs/guide/sources.md b/docs/guide/sources.md index cae24beb75..4371da8942 100644 --- a/docs/guide/sources.md +++ b/docs/guide/sources.md @@ -85,6 +85,9 @@ This is a [PEP 503](https://www.python.org/dev/peps/pep-0503/) type index as pro by tools such as PyPI or [devpi](https://github.com/devpi/devpi). It corresponds to the arguments `--index-url` or `--extra-index-url` in pip. +Note: see the [`uv` documentation](https://github.com/astral-sh/uv/blob/main/PIP_COMPATIBILITY.md#packages-that-exist-on-multiple-indexes) +for more on the use of multiple indexes. + ### `find-links` This is a source that can be of a variety of types and has to point to a file path diff --git a/docs/guide/sync.md b/docs/guide/sync.md index 6d100253ce..4e2746e8cc 100644 --- a/docs/guide/sync.md +++ b/docs/guide/sync.md @@ -1,13 +1,9 @@ # Syncing and Locking -Rye supports two systems to manage dependencies: -[uv](https://github.com/astral-sh/uv) and -[pip-tools](https://github.com/jazzband/pip-tools). It currently defaults to -`pip-tools` but will offer you the option to use `uv` instead. `uv` will become -the default choice once it stabilzes as it offers significantly better performance. +Rye uses [`uv`](https://github.com/astral-sh/uv) to manage dependencies. In order to download dependencies rye creates two "lockfiles" (called -`requirements.lock` and `requirements-dev.lock`). These are not real lockfiles +`requirements.lock` and `requirements-dev.lock`). These are not real lockfiles, but they fulfill a similar purpose until a better solution has been implemented. Whenever `rye sync` is called, it will update lockfiles as well as the @@ -66,12 +62,12 @@ rye lock Flask --pre +++ 0.18.0 By default (unless the `tool.rye.lock-with-sources` config key is set to `true` in the -`pyproject.toml`) lock files are not generated with source references. This means that -if custom sources are used the lock file cannot be installed via `pip` unless also +`pyproject.toml`) lockfiles are not generated with source references. This means that +if custom sources are used the lockfile cannot be installed via `uv` or `pip`, unless `--find-links` and other parameters are manually passed. This can be particularly useful -when the lock file is used for docker image builds. +when the lockfile is used for Docker image builds. -When this flag is passed then the lock file is generated with references to `--index-url`, +When this flag is passed then the lockfile is generated with references to `--index-url`, `--extra-index-url` or `--find-links`. ``` @@ -100,11 +96,18 @@ lockfile (`requirements-dev.lock`). rye sync --no-dev ``` -## Limitations +## Platform Compatibility -Lockfiles depend on the platform they were generated on. This is a known limitation -in pip-tools. +By default, lockfiles depend on the platform they were generated on. For example, if your project relies on platform-specific packages and you generate lockfiles on Windows, these lockfiles will include Windows-specific projects. Consequently, they won't be compatible with other platforms like Linux or macOS. + +To generate a cross-platform lockfile, you can enable uv's `universal` setting +by adding the following to your `pyproject.toml`: + +```toml +[tool.rye] +universal = true +``` diff --git a/docs/guide/toolchains/cpython.md b/docs/guide/toolchains/cpython.md index b676b6d609..d42dcaca1f 100644 --- a/docs/guide/toolchains/cpython.md +++ b/docs/guide/toolchains/cpython.md @@ -28,7 +28,7 @@ different from a regular Python build. The following changes to a regular Python versions you should be aware of: -* `libedit` instead of `readline`: unfortunately `readline` is GPL2 licensed +* `libedit` instead of `readline`: unfortunately `readline` is GPLv3 licensed and this is a hazard for redistributions. As such, the portable Python builds link against the more freely licensed `libedit` instead. @@ -37,8 +37,8 @@ The following changes to a regular Python versions you should be aware of: Additionally due to how these builds are created, there are some other quirks you might run into related to terminal support or TKinter. Some of these -issues are collected in the [FAQ](../faq.md). Additionally the Python -Standalone Builds have a [Behavior Quirks](https://python-build-standalone.readthedocs.io/en/latest/quirks.html) +issues are collected in the [FAQ](../faq.md). Additionally, the Python +Standalone Builds have a [Behavior Quirks](https://gregoryszorc.com/docs/python-build-standalone/main/quirks.html) page. ## Sources diff --git a/docs/guide/toolchains/index.md b/docs/guide/toolchains/index.md index 9281cd0cf9..955c11d882 100644 --- a/docs/guide/toolchains/index.md +++ b/docs/guide/toolchains/index.md @@ -95,6 +95,19 @@ Toolchains are fetched from two sources: * [Indygreg's Portable Python Builds](https://github.com/indygreg/python-build-standalone) for CPython * [PyPy.org](https://www.pypy.org/) for PyPy +You can also fetch toolchains into a specific location. In this case the interpreter is not +stored where Rye normally consults it, but in a specific location. Rye will then not be able +to use it unless it's manually registered. This however can be useful for debugging or advanced +setups: + +``` +rye toolchain fetch cpython@3.8.5 --target-path=my-interpreter +``` + +If you want to use rye interpreter fetching without installing rye, you might want to export the +`RYE_NO_AUTO_INSTALL` environment variable and set it to `1` as otherwise the installer will kick +in. + ## Registering Toolchains Additionally, it's possible to register an external toolchain with the `rye toolchain register` @@ -126,3 +139,19 @@ rye toolchain remove cpython@3.8.5 !!! Warning Removing an actively used toolchain will render the virtualenvs that refer to use broken. + +## Build Info + ++++ 0.31.0 + +Prior to Rye 0.31.0 the Python installations were fetched with build infos. You can see +this because the folder structure in `~/.rye/py/INTERPRETER` is a bit different. Rather than +finding `cpython@3.8.5/bin/python3` there you will instead have an extra `install` folder +(`cpython@3.8.5/install/bin/python3`) alongside a `build` folder containing the intermediate +build outputs. Starting with 0.31.0 the build info is removed by default. If +you want to get it back, you can explicitly fetch with `--build-info` or you can +set the `behavior.fetch-with-build-info` config flag to true: + +``` +rye config --set-bool behavior.fetch-with-build-info=true +``` diff --git a/docs/guide/virtual.md b/docs/guide/virtual.md index ee0533db13..133ae885c9 100644 --- a/docs/guide/virtual.md +++ b/docs/guide/virtual.md @@ -18,11 +18,11 @@ rye run mkdocs ``` This will create a `pyproject.toml` but does not actually declare any python code itself. -Yet when synching you will end up with mkdocs in your project. +Yet when syncing you will end up with mkdocs in your project. ## Behavior Changes -When synching the project itself is never installed into the virtualenv as it's not +When syncing the project itself is never installed into the virtualenv as it's not considered to be a valid package. Likewise you cannot publish virtual packages to PyPI or another index. diff --git a/docs/guide/workspaces.md b/docs/guide/workspaces.md index 67a3fd0b2b..fbd33c4a1f 100644 --- a/docs/guide/workspaces.md +++ b/docs/guide/workspaces.md @@ -1,15 +1,15 @@ # Workspaces Workspaces are a feature that allows you to work with multiple packages that -have dependencies to each other. A workspace is declared by setting the -`tool.rye.workspace` key a `pyproject.toml`. Afterwards all projects within +have dependencies on each other. A workspace is declared by setting the +`tool.rye.workspace` key in `pyproject.toml`. Afterwards, all projects within that workspace share a singular virtualenv. ## Declaring Workspaces -A workspace is declared by the "toplevel" `pyproject.toml`. At the very least -the key `tool.rye.workspace` needs to be added. It's recommended that a glob -pattern is also set in the `members` key to prevent accidentally including +A workspace is declared in the "toplevel" `pyproject.toml`. At the very least +the key `tool.rye.workspace` needs to be added. It's also recommended to +set a glob pattern in the `members` key to prevent accidentally including unintended folders as projects. ```toml @@ -18,7 +18,7 @@ members = ["myname-*"] ``` This declares a workspace where all folders starting with the name `myname-` -are considered. If the toplevel workspace in itself should not be a project, +are considered. If the toplevel workspace itself should not be a project, then it should be declared as a virtual package: ```toml @@ -29,12 +29,12 @@ virtual = true members = ["myname-*"] ``` -For more information on that see [Virtual Packages](../virtual/). +For more information on that, see [Virtual Packages](../virtual/). ## Syncing -In a workspace it does not matter which project you are working with, the entire -workspace is synchronized at all times. This has some untypical consequences but +In a workspace, it does not matter which project you are working with, the entire +workspace is synchronized at all times. This has some atypical consequences but simplifies the general development workflow. When a package depends on another package it's first located in the workspace locally diff --git a/docs/index.md b/docs/index.md index 866283c269..ccfdcbcf34 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,6 +8,16 @@ hide:

Rye: a Hassle-Free Python Experience

+!!! note + + If you're getting started with Rye, consider [uv](https://github.com/astral-sh/uv), the + [successor project](https://lucumr.pocoo.org/2024/2/15/rye-grows-with-uv/) from the same maintainers. + + While Rye is actively maintained, uv offers a more stable and feature-complete experience, and is the recommended + choice for new projects. + + Having trouble migrating? [Let us know what's missing.](https://github.com/astral-sh/rye/discussions/1342) + Rye is a comprehensive project and package management solution for Python. Born from [its creator's](https://github.com/mitsuhiko) desire to establish a one-stop-shop for all Python users, Rye provides a unified experience to install and manages Python diff --git a/docs/philosophy.md b/docs/philosophy.md index 9850d45cbd..6022030bd7 100644 --- a/docs/philosophy.md +++ b/docs/philosophy.md @@ -11,14 +11,13 @@ on my mind when I built it: of dependencies. Not even `pip` or `setuptools` are installed into it. Rye manages the virtualenv from outside the virtualenv. -- **No Core Non Standard Stuff:** Rye (with the exception of it's own `tool` section +- **No Core Non-Standard Stuff:** Rye (with the exception of its own `tool` section in the `pyproject.toml`) uses standardized keys. That means it uses regular requirements as you would expect. It also does not use a custom lock file - format and uses [`uv`](https://github.com/astral-sh/uv) and - [`pip-tools`](https://github.com/jazzband/pip-tools) behind the scenes. + format and uses [`uv`](https://github.com/astral-sh/uv). -- **No Pip:** Rye uses pip, but it does not expose it. It manage dependencies in - `pyproject.toml` only. +- **No Pip:** Rye uses [`uv`](https://github.com/astral-sh/uv) to manage dependencies, + through `pyproject.toml` only. - **No System Python:** I can't deal with any more linux distribution weird Python installations or whatever mess there is on macOS. I used to build my own Pythons @@ -53,10 +52,8 @@ lack of standardization. Here is what this project ran into over the years: which allows both remote and local references to co-exist and it rewrites them on publish. -- **No Exposed Pip:** pip is intentionally not exposed. If you were to install something - into the virtualenv, it disappears next time you sync. If you symlink `rye` to - `~/.rye/shims/pip` you can get access to pip without installing it into the - virtualenv. There be dragons. +- **No Exposed Pip:** pip is intentionally not exposed. If you install something + into the virtualenv with pip, it disappears next time you sync. - **No Workspace Spec:** for monorepos and things of that nature, the Python ecosystem would need a definition of workspaces. Today that does not exist which forces every diff --git a/mkdocs.yml b/mkdocs.yml index a4b97d19d0..3f9e29a608 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,7 @@ nav: - Dependencies: guide/deps.md - Workspaces: guide/workspaces.md - Virtual Projects: guide/virtual.md + - Docker: guide/docker.md - Commands: - Overview: guide/commands/index.md - add: guide/commands/add.md @@ -39,6 +40,7 @@ nav: - run: guide/commands/run.md - show: guide/commands/show.md - sync: guide/commands/sync.md + - test: guide/commands/test.md - toolchain: - Overview: guide/commands/toolchain/index.md - fetch: guide/commands/toolchain/fetch.md diff --git a/notes/pep508.md b/notes/pep508.md index f2cf2b1ede..0238c8efb8 100644 --- a/notes/pep508.md +++ b/notes/pep508.md @@ -96,7 +96,7 @@ dependencies = [ ] [project.dependencies_meta.0] -exta_information = 42 +extra_information = 42 ``` Unfortunately that would still cause issues for tools that locally interpret `pyproject.toml` diff --git a/pyproject.toml b/pyproject.toml index 43067e34af..c136f82907 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,18 +15,15 @@ dependencies = [ readme = "README.md" requires-python = ">= 3.8" -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["dummy"] - [tool.rye] managed = true +virtual = true [tool.rye.scripts] serve-docs = "mkdocs serve" [tool.rye.workspace] members = ["rye-devtools"] + +[tool.pytest.ini_options] +addopts = "--ignore target" diff --git a/requirements-dev.lock b/requirements-dev.lock index 9fd084536d..70c818ffe0 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -6,18 +6,18 @@ # features: [] # all-features: false # with-sources: false +# generate-hashes: false --e file:. -e file:rye-devtools -anyio==4.2.0 +anyio==4.4.0 # via httpx -certifi==2023.5.7 +certifi==2024.6.2 # via httpcore # via httpx # via requests -charset-normalizer==3.1.0 +charset-normalizer==3.3.2 # via requests -click==8.1.3 +click==8.1.7 # via mkdocs colorama==0.4.6 # via mkdocs-material @@ -25,16 +25,17 @@ ghp-import==2.1.0 # via mkdocs h11==0.14.0 # via httpcore -httpcore==1.0.2 +httpcore==1.0.5 # via httpx -httpx==0.26.0 +httpx==0.27.0 # via rye-devtools -idna==3.4 +idna==3.7 # via anyio # via httpx # via requests -isort==5.13.2 -jinja2==3.1.2 +iniconfig==2.0.0 + # via pytest +jinja2==3.1.4 # via mkdocs # via mkdocs-material markdown==3.3.7 @@ -42,53 +43,50 @@ markdown==3.3.7 # via mkdocs # via mkdocs-material # via pymdown-extensions -markupsafe==2.1.2 +markupsafe==2.1.5 # via jinja2 -mdx-gh-links==0.3 - # via rye-dev +mdx-gh-links==0.4 mergedeep==1.3.4 # via mkdocs mkdocs==1.4.3 # via mkdocs-material # via mkdocs-simple-hooks - # via rye-dev mkdocs-include-markdown-plugin==4.0.4 - # via rye-dev -mkdocs-material==9.1.12 - # via rye-dev -mkdocs-material-extensions==1.1.1 +mkdocs-material==9.1.20 +mkdocs-material-extensions==1.3.1 # via mkdocs-material mkdocs-simple-hooks==0.1.5 - # via rye-dev mkdocs-version-annotations==1.0.0 - # via rye-dev -packaging==23.1 +packaging==24.1 # via mkdocs -pygments==2.15.1 + # via pytest +pluggy==1.5.0 + # via pytest +pygments==2.18.0 # via mkdocs-material pymdown-extensions==9.11 # via mkdocs-material - # via rye-dev -python-dateutil==2.8.2 +pytest==8.0.2 +python-dateutil==2.9.0.post0 # via ghp-import -pyyaml==6.0 +pyyaml==6.0.1 # via mkdocs # via pymdown-extensions # via pyyaml-env-tag pyyaml-env-tag==0.1 # via mkdocs -regex==2023.5.5 +regex==2024.5.15 # via mkdocs-material -requests==2.31.0 +requests==2.32.3 # via mkdocs-material six==1.16.0 # via python-dateutil -sniffio==1.3.0 +sniffio==1.3.1 # via anyio # via httpx socksio==1.0.0 # via httpx -urllib3==2.0.2 +urllib3==2.2.2 # via requests -watchdog==3.0.0 +watchdog==4.0.1 # via mkdocs diff --git a/requirements.lock b/requirements.lock index 4bf6f467c2..c2cd892a2d 100644 --- a/requirements.lock +++ b/requirements.lock @@ -6,18 +6,18 @@ # features: [] # all-features: false # with-sources: false +# generate-hashes: false --e file:. -e file:rye-devtools -anyio==4.2.0 +anyio==4.4.0 # via httpx -certifi==2023.5.7 +certifi==2024.6.2 # via httpcore # via httpx # via requests -charset-normalizer==3.1.0 +charset-normalizer==3.3.2 # via requests -click==8.1.3 +click==8.1.7 # via mkdocs colorama==0.4.6 # via mkdocs-material @@ -25,15 +25,15 @@ ghp-import==2.1.0 # via mkdocs h11==0.14.0 # via httpcore -httpcore==1.0.2 +httpcore==1.0.5 # via httpx -httpx==0.26.0 +httpx==0.27.0 # via rye-devtools -idna==3.4 +idna==3.7 # via anyio # via httpx # via requests -jinja2==3.1.2 +jinja2==3.1.4 # via mkdocs # via mkdocs-material markdown==3.3.7 @@ -41,53 +41,46 @@ markdown==3.3.7 # via mkdocs # via mkdocs-material # via pymdown-extensions -markupsafe==2.1.2 +markupsafe==2.1.5 # via jinja2 -mdx-gh-links==0.3 - # via rye-dev +mdx-gh-links==0.4 mergedeep==1.3.4 # via mkdocs mkdocs==1.4.3 # via mkdocs-material # via mkdocs-simple-hooks - # via rye-dev mkdocs-include-markdown-plugin==4.0.4 - # via rye-dev -mkdocs-material==9.1.12 - # via rye-dev -mkdocs-material-extensions==1.1.1 +mkdocs-material==9.1.20 +mkdocs-material-extensions==1.3.1 # via mkdocs-material mkdocs-simple-hooks==0.1.5 - # via rye-dev mkdocs-version-annotations==1.0.0 - # via rye-dev -packaging==23.1 +packaging==24.1 # via mkdocs -pygments==2.15.1 +pygments==2.18.0 # via mkdocs-material pymdown-extensions==9.11 # via mkdocs-material - # via rye-dev -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via ghp-import -pyyaml==6.0 +pyyaml==6.0.1 # via mkdocs # via pymdown-extensions # via pyyaml-env-tag pyyaml-env-tag==0.1 # via mkdocs -regex==2023.5.5 +regex==2024.5.15 # via mkdocs-material -requests==2.31.0 +requests==2.32.3 # via mkdocs-material six==1.16.0 # via python-dateutil -sniffio==1.3.0 +sniffio==1.3.1 # via anyio # via httpx socksio==1.0.0 # via httpx -urllib3==2.0.2 +urllib3==2.2.2 # via requests -watchdog==3.0.0 +watchdog==4.0.1 # via mkdocs diff --git a/rye-devtools/pyproject.toml b/rye-devtools/pyproject.toml index 9d7b27a75d..81e65bff86 100644 --- a/rye-devtools/pyproject.toml +++ b/rye-devtools/pyproject.toml @@ -19,7 +19,7 @@ build-backend = "hatchling.build" [tool.rye] managed = true dev-dependencies = [ - "isort>=5.13.2", + "pytest==8.0.2", ] [tool.hatch.metadata] diff --git a/rye-devtools/src/rye_devtools/__init__.py b/rye-devtools/src/rye_devtools/__init__.py index 0f1844592b..e69de29bb2 100644 --- a/rye-devtools/src/rye_devtools/__init__.py +++ b/rye-devtools/src/rye_devtools/__init__.py @@ -1,2 +0,0 @@ -from . import find_downloads -from . import find_uv_downloads diff --git a/rye-devtools/src/rye_devtools/common.py b/rye-devtools/src/rye_devtools/common.py index 254f7cb004..1433a54408 100644 --- a/rye-devtools/src/rye_devtools/common.py +++ b/rye-devtools/src/rye_devtools/common.py @@ -1,9 +1,8 @@ -from typing import Self import itertools import sys import time from datetime import datetime, timezone -from typing import NamedTuple +from typing import NamedTuple, Self import httpx diff --git a/rye-devtools/src/rye_devtools/find_downloads.py b/rye-devtools/src/rye_devtools/find_downloads.py index 2d4db55a35..13e92f6ddb 100644 --- a/rye-devtools/src/rye_devtools/find_downloads.py +++ b/rye-devtools/src/rye_devtools/find_downloads.py @@ -1,16 +1,15 @@ -"""This script is used to generate rye/src/downloads.inc. +"""This script is used to generate rye/src/sources/generated/python_downloads.inc. It finds the latest Python releases, sorts them by various factors (arch, platform, flavor) and generates download links to be included into rye at build time. """ + import abc import asyncio -import itertools import os import re import sys -import unittest from dataclasses import dataclass from enum import StrEnum from urllib.parse import unquote @@ -18,21 +17,7 @@ import httpx from httpx import HTTPStatusError -from .common import Version, fetch, PlatformTriple - - -def log(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -def batched(iterable, n): - "Batch data into tuples of length n. The last batch may be shorter." - # batched('ABCDEFG', 3) --> ABC DEF G - if n < 1: - raise ValueError("n must be at least one") - it = iter(iterable) - while batch := tuple(itertools.islice(it, n)): - yield batch +from .common import PlatformTriple, Version, batched, fetch, log class PythonImplementation(StrEnum): @@ -209,6 +194,11 @@ def match_mapping( # Map, old, special triplets to proper triples for parsing, or # return the triple if it's not a special one triple = cls.SPECIAL_TRIPLES.get(triple, triple) + + # freethreaded builds are experimental, ignore them for now + if "freethreaded" in triple: + return + pieces = triple.split("-") flavor = match_flavor(triple) env, pieces = match_mapping(pieces, cls.ENV_MAPPING) @@ -434,31 +424,3 @@ def main(): if __name__ == "__main__": main() - - -class Tests(unittest.TestCase): - def test_parse_triplets(self): - expected = { - "aarch64-apple-darwin-lto": PlatformTriple("aarch64", "macos", None, "lto"), - "aarch64-unknown-linux-gnu-pgo+lto": PlatformTriple( - "aarch64", "linux", "gnu", "pgo+lto" - ), - # "x86_64-unknown-linux-musl-debug": PlatformTriple( - # "x86_64", "linux", "musl", "debug" - # ), - "aarch64-unknown-linux-gnu-debug-full": PlatformTriple( - "aarch64", "linux", "gnu", "debug" - ), - "x86_64-unknown-linux-gnu-debug": PlatformTriple( - "x86_64", "linux", "gnu", "debug" - ), - "linux64": PlatformTriple("x86_64", "linux", "gnu", None), - "ppc64le-unknown-linux-gnu-noopt-full": None, - "x86_64_v3-unknown-linux-gnu-lto": None, - "x86_64-pc-windows-msvc-shared-pgo": PlatformTriple( - "x86_64", "windows", None, "shared-pgo" - ), - } - - for input, expected in expected.items(): - self.assertEqual(CPythonFinder.parse_triple(input), expected, input) diff --git a/rye-devtools/src/rye_devtools/find_uv_downloads.py b/rye-devtools/src/rye_devtools/find_uv_downloads.py index b677ecef0d..f27d0e5fcb 100644 --- a/rye-devtools/src/rye_devtools/find_uv_downloads.py +++ b/rye-devtools/src/rye_devtools/find_uv_downloads.py @@ -1,17 +1,19 @@ -"""This script is used to generate rye/src/generated/uv_downloads.inc. +"""This script is used to generate rye/src/sources/generated/uv_downloads.inc. It finds the latest UV releases and generates rust code that can be included into rye at build time. """ -from .common import Version, PlatformTriple, fetch, log -import re -from typing import AsyncIterator import asyncio import os -import httpx +import re import sys from dataclasses import dataclass +from typing import AsyncIterator + +import httpx + +from .common import PlatformTriple, Version, fetch, log @dataclass @@ -33,14 +35,20 @@ class UvDownloads: "aarch64": "aarch64", } + GLIBC = { + "x86_64": "gnu", + "i686": "gnu", + "aarch64": "musl", + } + PLATFORM_ENV = { "unknown-linux-gnu": ("linux", "gnu"), - # "unknown-linux-musl": ("linux", "musl"), + "unknown-linux-musl": ("linux", "musl"), "apple-darwin": ("macos", None), "pc-windows-msvc": ("windows", None), } - RE = re.compile(r"uv-(?P[^\-]+)-(?P.+)(\.tar\.gz|\.zip)$") + RE = re.compile(r"uv-(?P[^-]+)-(?P.+)(\.tar\.gz|\.zip)$") def __init__(self, client: httpx.Client) -> None: self.client = client @@ -62,7 +70,7 @@ async def most_recent_downloads( url = asset["browser_download_url"] if (triple := self.parse_triple(url)) is not None: sha_resp = await fetch(self.client, url + ".sha256") - sha256 = sha_resp.text.split(' ')[0].strip() + sha256 = sha_resp.text.split(" ")[0].strip() yield UvDownload( triple=triple, version=version, @@ -79,9 +87,10 @@ def parse_triple(cls, url: str) -> PlatformTriple | None: if arch_str in cls.ARCH and plat_env_str in cls.PLATFORM_ENV: arch = cls.ARCH[arch_str] plat, env = cls.PLATFORM_ENV[plat_env_str] - return PlatformTriple( - arch=arch, platform=plat, environment=env, flavor=None - ) + if env is None or env == cls.GLIBC[arch_str]: + return PlatformTriple( + arch=arch, platform=plat, environment=env, flavor=None + ) return None @@ -89,7 +98,7 @@ def parse_triple(cls, url: str) -> PlatformTriple | None: def render(downloads: list[UvDownload]): print("// Generated by rye-devtools. DO NOT EDIT.") print( - "// To regenerate, run `rye run uv-downloads > rye/src/generated/uv_downloads.inc` from the root of the repository." + "// To regenerate, run `rye run uv-downloads > rye/src/sources/generated/uv_downloads.inc` from the root of the repository." ) print("use std::borrow::Cow;") print("pub const UV_DOWNLOADS: &[UvDownload] = &[") @@ -123,8 +132,6 @@ async def async_main(): "Authorization": "Bearer " + token, } - downloads = [] - log("Fetching all uv downloads.") async with httpx.AsyncClient(follow_redirects=True, headers=headers) as client: finder = UvDownloads(client) diff --git a/rye-devtools/tests/test_basic.py b/rye-devtools/tests/test_basic.py new file mode 100644 index 0000000000..9aa20fadcc --- /dev/null +++ b/rye-devtools/tests/test_basic.py @@ -0,0 +1,41 @@ +import pytest +from rye_devtools.common import batched +from rye_devtools.find_downloads import CPythonFinder, PlatformTriple + + +def test_batched(): + assert list(batched("ABCDEFG", 3)) == [tuple("ABC"), tuple("DEF"), tuple("G")] + + +@pytest.mark.parametrize( + "input, expected", + [ + ("aarch64-apple-darwin-lto", PlatformTriple("aarch64", "macos", None, "lto")), + ( + "aarch64-unknown-linux-gnu-pgo+lto", + PlatformTriple("aarch64", "linux", "gnu", "pgo+lto"), + ), + # ( + # "x86_64-unknown-linux-musl-debug", + # PlatformTriple("x86_64", "linux", "musl", "debug"), + # ), + ( + "aarch64-unknown-linux-gnu-debug-full", + PlatformTriple("aarch64", "linux", "gnu", "debug"), + ), + ( + "x86_64-unknown-linux-gnu-debug", + PlatformTriple("x86_64", "linux", "gnu", "debug"), + ), + ("linux64", PlatformTriple("x86_64", "linux", "gnu", None)), + ("ppc64le-unknown-linux-gnu-noopt-full", None), + ("x86_64_v3-unknown-linux-gnu-lto", None), + ( + "x86_64-pc-windows-msvc-shared-pgo", + PlatformTriple("x86_64", "windows", None, "shared-pgo"), + ), + ("aarch64-apple-darwin-freethreaded+pgo-full", None), + ], +) +def test_parse_triplets(input, expected): + assert CPythonFinder.parse_triple(input) == expected diff --git a/rye/Cargo.toml b/rye/Cargo.toml index 538a1eb5f2..2664716695 100644 --- a/rye/Cargo.toml +++ b/rye/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "rye" -version = "0.28.0" +version = "0.43.0" edition = "2021" license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -age = "0.9.1" +age = "0.10.0" anyhow = { version = "1.0.70", features = ["backtrace"] } clap = { version = "4.3.5", default-features = false, features = [ "derive", @@ -16,20 +16,15 @@ clap = { version = "4.3.5", default-features = false, features = [ "std", ] } clap_complete = "4.2.1" +clap_complete_nushell = "4.5.1" console = "0.15.7" curl = { version = "0.4.44", features = ["ssl", "static-curl", "static-ssl"] } -decompress = { version = "0.6.0", default-features = false, features = [ - "tarzst", - "targz", -] } flate2 = "1.0.25" git-testament = "0.2.4" globset = "0.4.10" indicatif = "0.17.3" -memchr = "2.5.0" license = { version = "3.1.1", features = ["offline"] } -minijinja = { version = "1.0.0", features = ["json"] } -nix = { version = "0.27.1", default-features = false, features = ["process"] } +minijinja = { version = "2.0.1", features = ["json"] } once_cell = "1.17.1" pathdiff = "0.2.1" pep440_rs = "0.4.0" @@ -42,7 +37,7 @@ shlex = "1.3.0" slug = "0.1.4" tar = "0.4.38" tempfile = "3.5.0" -toml_edit = "0.21.0" +toml_edit = "0.22.9" url = "2.3.1" walkdir = "2.3.3" which = "6.0.0" @@ -57,15 +52,15 @@ self-replace = "1.3.5" configparser = "3.0.2" monotrail-utils = { git = "https://github.com/konstin/poc-monotrail", rev = "e0251f68c254f834180198b8677fcf85d4b6a844" } python-pkginfo = { version = "0.6.0", features = ["serde"] } -sysinfo = { version = "0.29.4", default-features = false, features = [] } home = "0.5.9" ctrlc = "3.4.2" +dotenvy = "0.15.7" [target."cfg(unix)".dependencies] xattr = "1.3.1" [target."cfg(windows)".dependencies] -winapi = { version = "0.3.9", default-features = false, features = [] } +winapi = { version = "0.3.9", default-features = false, features = ["winuser", "winioctl", "ioapiset"] } winreg = "0.52.0" [target."cfg(windows)".build-dependencies] diff --git a/rye/src/bootstrap.rs b/rye/src/bootstrap.rs index 3a55016cee..e548775a3e 100644 --- a/rye/src/bootstrap.rs +++ b/rye/src/bootstrap.rs @@ -9,16 +9,17 @@ use anyhow::{anyhow, bail, Context, Error}; use console::style; use indicatif::{ProgressBar, ProgressStyle}; use once_cell::sync::Lazy; +use tempfile::tempdir_in; use crate::config::Config; -use crate::piptools::LATEST_PIP; use crate::platform::{ - get_app_dir, get_canonical_py_path, get_toolchain_python_bin, list_known_toolchains, + get_app_dir, get_canonical_py_path, get_python_bin_within, get_toolchain_python_bin, + list_known_toolchains, }; use crate::pyproject::latest_available_python_version; use crate::sources::py::{get_download_url, PythonVersion, PythonVersionRequest}; use crate::utils::{check_checksum, symlink_file, unpack_archive, CommandOutput, IoPathContext}; -use crate::uv::Uv; +use crate::uv::UvBuilder; /// this is the target version that we want to fetch pub const SELF_PYTHON_TARGET_VERSION: PythonVersionRequest = PythonVersionRequest { @@ -31,11 +32,11 @@ pub const SELF_PYTHON_TARGET_VERSION: PythonVersionRequest = PythonVersionReques suffix: None, }; -const SELF_VERSION: u64 = 14; +const SELF_VERSION: u64 = 26; -const SELF_REQUIREMENTS: &str = r#" -build==1.0.3 -certifi==2023.11.17 +pub const SELF_REQUIREMENTS: &str = r#" +build==1.2.1 +certifi==2024.2.2 charset-normalizer==3.3.2 click==8.1.7 distlib==0.3.8 @@ -46,11 +47,11 @@ platformdirs==4.0.0 pyproject_hooks==1.0.0 requests==2.31.0 tomli==2.0.1 -twine==4.0.2 +twine==5.1.1 unearth==0.14.0 urllib3==2.0.7 virtualenv==20.25.0 -ruff==0.2.2 +ruff==0.8.2 "#; static FORCED_TO_UPDATE: AtomicBool = AtomicBool::new(false); @@ -64,6 +65,28 @@ fn is_up_to_date() -> bool { *UP_TO_UPDATE || FORCED_TO_UPDATE.load(atomic::Ordering::Relaxed) } +#[derive(Debug, Clone)] +pub(crate) enum SelfVenvStatus { + NotUpToDate, + DoesNotExist, +} + +/// Get self venv path and check if it exists and is up to date +pub fn get_self_venv_status() -> Result { + let app_dir = get_app_dir(); + let venv_dir = app_dir.join("self"); + + if venv_dir.is_dir() { + if is_up_to_date() { + Ok(venv_dir) + } else { + Err((venv_dir, SelfVenvStatus::NotUpToDate)) + } + } else { + Err((venv_dir, SelfVenvStatus::DoesNotExist)) + } +} + /// Bootstraps the venv for rye itself pub fn ensure_self_venv(output: CommandOutput) -> Result { ensure_self_venv_with_toolchain(output, None) @@ -75,26 +98,31 @@ pub fn ensure_self_venv_with_toolchain( toolchain_version_request: Option, ) -> Result { let app_dir = get_app_dir(); - let venv_dir = app_dir.join("self"); - if venv_dir.is_dir() { - if is_up_to_date() { - return Ok(venv_dir); - } else { - if output != CommandOutput::Quiet { - echo!("Detected outdated rye internals. Refreshing"); - } + let venv_dir = match get_self_venv_status() { + Ok(venv_dir) => return Ok(venv_dir), + Err((venv_dir, SelfVenvStatus::DoesNotExist)) => venv_dir, + Err((venv_dir, SelfVenvStatus::NotUpToDate)) => { + echo!(if output, "Detected outdated rye internals. Refreshing"); fs::remove_dir_all(&venv_dir) .path_context(&venv_dir, "could not remove self-venv for update")?; + + let pip_tools_dir = app_dir.join("pip-tools"); + if pip_tools_dir.is_dir() { + fs::remove_dir_all(&pip_tools_dir) + .context("could not remove pip-tools for update")?; + } + + venv_dir } - } + }; - if output != CommandOutput::Quiet { - echo!("Bootstrapping rye internals"); - } + echo!(if output, "Bootstrapping rye internals"); // Ensure we have uv - let uv = Uv::ensure_exists(CommandOutput::Quiet)?; + let uv = UvBuilder::new() + .with_output(CommandOutput::Quiet) + .ensure_exists()?; let version = match toolchain_version_request { Some(ref version_request) => ensure_specific_self_toolchain(output, version_request) @@ -125,8 +153,8 @@ pub fn ensure_self_venv_with_toolchain( let uv_venv = uv.venv(&venv_dir, &py_bin, &version, None)?; // write our marker uv_venv.write_marker()?; - // update pip and our requirements - uv_venv.update(LATEST_PIP, SELF_REQUIREMENTS)?; + // update our requirements + uv_venv.update_requirements(SELF_REQUIREMENTS)?; // Update the shims let shims = app_dir.join("shims"); @@ -254,9 +282,9 @@ pub fn get_pip_module(venv: &Path) -> Result { Ok(rv) } -/// we only support cpython 3.9 to 3.12 +/// we only support cpython 3.9 to 3.13 pub fn is_self_compatible_toolchain(version: &PythonVersion) -> bool { - version.name == "cpython" && version.major == 3 && version.minor >= 9 && version.minor <= 12 + version.name == "cpython" && version.major == 3 && version.minor >= 9 && version.minor <= 13 } /// Ensure that the toolchain for the self environment is available. @@ -269,15 +297,17 @@ fn ensure_latest_self_toolchain(output: CommandOutput) -> Result, + /// Include build info (overrides configured default). + pub build_info: Option, +} + +impl FetchOptions { + /// Basic fetch options. + pub fn with_output(output: CommandOutput) -> FetchOptions { + FetchOptions { + output, + ..Default::default() + } + } +} + +impl Default for FetchOptions { + fn default() -> Self { + Self { + output: CommandOutput::Normal, + force: false, + target_path: None, + build_info: None, + } + } +} + /// Fetches a version if missing. pub fn fetch( version: &PythonVersionRequest, - output: CommandOutput, - force: bool, + options: FetchOptions, ) -> Result { - if let Ok(version) = PythonVersion::try_from(version.clone()) { - let py_bin = get_toolchain_python_bin(&version)?; - if !force && py_bin.is_file() { - if output == CommandOutput::Verbose { - echo!("Python version already downloaded. Skipping."); + // Check if there is registered toolchain that matches the request + if options.target_path.is_none() { + if let Ok(version) = PythonVersion::try_from(version.clone()) { + let py_bin = get_toolchain_python_bin(&version)?; + if !options.force && py_bin.is_file() { + echo!(if verbose options.output, "Python version already downloaded. Skipping."); + return Ok(version); } - return Ok(version); } } - let (version, url, sha256) = match get_download_url(version) { Some(result) => result, None => bail!("unknown version {}", version), }; - let target_dir = get_canonical_py_path(&version)?; - let target_py_bin = get_toolchain_python_bin(&version)?; - if output == CommandOutput::Verbose { - echo!("target dir: {}", target_dir.display()); - } - if target_dir.is_dir() && target_py_bin.is_file() { - if !force { - if output == CommandOutput::Verbose { - echo!("Python version already downloaded. Skipping."); + let target_dir = match options.target_path { + Some(ref target_dir) => { + if target_dir.is_file() { + bail!("target directory '{}' is a file", target_dir.display()); + } + echo!(if options.output, "Downloading to '{}'", target_dir.display()); + if target_dir.is_dir() { + if options.force { + // Refuse to remove the target directory if it's not empty and not a python installation + if target_dir.read_dir()?.next().is_some() + && !get_python_bin_within(target_dir).exists() + { + bail!( + "target directory '{}' exists and is not a Python installation", + target_dir.display() + ); + } + fs::remove_dir_all(target_dir) + .path_context(target_dir, "could not remove target directory")?; + } else { + bail!("target directory '{}' exists", target_dir.display()); + } } - return Ok(version); + Cow::Borrowed(target_dir.as_path()) } - if output != CommandOutput::Quiet { - echo!("Removing the existing Python version"); + None => { + let target_dir = get_canonical_py_path(&version)?; + let target_py_bin = get_toolchain_python_bin(&version)?; + if target_py_bin.is_file() { + if !options.force { + echo!(if verbose options.output, "Python version already downloaded. Skipping."); + return Ok(version); + } + echo!(if options.output, "Removing the existing Python version"); + fs::remove_dir_all(&target_dir).with_context(|| { + format!("failed to remove target folder {}", target_dir.display()) + })?; + } + echo!(if verbose options.output, "target dir: {}", target_dir.display()); + Cow::Owned(target_dir) } - fs::remove_dir_all(&target_dir) - .with_context(|| format!("failed to remove target folder {}", target_dir.display()))?; - } + }; - fs::create_dir_all(&target_dir).path_context(&target_dir, "failed to create target folder")?; - - if output == CommandOutput::Verbose { - echo!("download url: {}", url); - } - if output != CommandOutput::Quiet { - echo!("{} {}", style("Downloading").cyan(), version); - } - let archive_buffer = download_url(url, output)?; + echo!(if verbose options.output, "download url: {}", url); + echo!(if options.output, "{} {}", style("Downloading").cyan(), version); + let archive_buffer = download_url(url, options.output)?; if let Some(sha256) = sha256 { - if output != CommandOutput::Quiet { - echo!("{} {}", style("Checking").cyan(), "checksum"); - } + echo!(if options.output, "{} {}", style("Checking").cyan(), "checksum"); check_checksum(&archive_buffer, sha256) .with_context(|| format!("Checksum check of {} failed", &url))?; - } else if output != CommandOutput::Quiet { - echo!("Checksum check skipped (no hash available)"); + } else { + echo!(if options.output, "Checksum check skipped (no hash available)"); } - if output != CommandOutput::Quiet { - echo!("{}", style("Unpacking").cyan()); + echo!(if options.output, "{}", style("Unpacking").cyan()); + + let parent = target_dir + .parent() + .ok_or_else(|| anyhow!("cannot unpack to root"))?; + if !parent.exists() { + fs::create_dir_all(parent).path_context(&target_dir, "failed to create target folder")?; } - unpack_archive(&archive_buffer, &target_dir, 1).with_context(|| { + + let with_build_info = options + .build_info + .unwrap_or_else(|| Config::current().fetch_with_build_info()); + let temp_dir = tempdir_in(parent).context("temporary unpack location")?; + + unpack_archive(&archive_buffer, temp_dir.path(), 1).with_context(|| { format!( "unpacking of downloaded tarball {} to '{}' failed", &url, - target_dir.display() + temp_dir.path().display(), ) })?; - if output != CommandOutput::Quiet { - echo!("{} {}", style("Downloaded").green(), version); + // if we want to retain build infos or the installation has no build infos, then move + // the folder into the permanent location + if with_build_info || !installation_has_build_info(temp_dir.path()) { + let temp_dir = temp_dir.into_path(); + fs::rename(&temp_dir, &target_dir).inspect_err(|_| { + fs::remove_dir_all(&temp_dir).ok(); + }) + + // otherwise move the contents of the `install` folder over. + } else { + fs::rename(temp_dir.path().join("install"), &target_dir) } + .path_context(&target_dir, "unable to persist download")?; + + echo!(if options.output, "{} {}", style("Downloaded").green(), version); Ok(version) } +fn installation_has_build_info(p: &Path) -> bool { + let mut has_install = false; + let mut has_build = false; + if let Ok(dir) = p.read_dir() { + for entry in dir.flatten() { + match entry.file_name().to_str() { + Some("install") => has_install = true, + Some("build") => has_build = true, + _ => {} + } + } + } + has_install && has_build +} + pub fn download_url(url: &str, output: CommandOutput) -> Result, Error> { match download_url_ignore_404(url, output)? { Some(result) => Ok(result), @@ -502,6 +616,6 @@ fn validate_shared_libraries(py: &Path) -> Result<(), Error> { } bail!( "Python installation is unable to run on this machine due to missing libraries.\n\ - Visit https://rye-up.com/guide/faq/#missing-shared-libraries-on-linux for next steps." + Visit https://rye.astral.sh/guide/faq/#missing-shared-libraries-on-linux for next steps." ); } diff --git a/rye/src/cli/add.rs b/rye/src/cli/add.rs index a3dd4269db..e181c88342 100644 --- a/rye/src/cli/add.rs +++ b/rye/src/cli/add.rs @@ -1,62 +1,21 @@ use std::env; -use std::io::Write; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Error}; use clap::{Parser, ValueEnum}; -use pep440_rs::{Operator, Version, VersionSpecifier, VersionSpecifiers}; +use pep440_rs::{Operator, VersionSpecifier, VersionSpecifiers}; use pep508_rs::{Requirement, VersionOrUrl}; -use serde::Deserialize; use url::Url; use crate::bootstrap::ensure_self_venv; use crate::config::Config; -use crate::consts::VENV_BIN; +use crate::lock::KeyringProvider; use crate::pyproject::{BuildSystem, DependencyKind, ExpandedSources, PyProject}; use crate::sources::py::PythonVersion; use crate::sync::{autosync, sync, SyncOptions}; -use crate::utils::{format_requirement, set_proxy_variables, CommandOutput}; -use crate::uv::Uv; - -const PACKAGE_FINDER_SCRIPT: &str = r#" -import sys -import json -from unearth.finder import PackageFinder -from unearth.session import PyPISession -from packaging.version import Version - -py_ver = sys.argv[1] -package = sys.argv[2] -sources = json.loads(sys.argv[3]) -pre = len(sys.argv) > 4 and sys.argv[4] == "--pre" - -finder = PackageFinder( - index_urls=[x[0] for x in sources["index_urls"]], - find_links=sources["find_links"], - trusted_hosts=sources["trusted_hosts"], -) -if py_ver: - finder.target_python.py_ver = tuple(map(int, py_ver.split('.'))) -choices = iter(finder.find_matches(package)) -if not pre: - choices = (m for m in choices if not(m.version and Version(m.version).is_prerelease)) - -print(json.dumps([x.as_json() for x in choices])) -"#; - -#[derive(Deserialize, Debug)] -struct Match { - name: String, - version: Option, - link: Option, -} - -#[derive(Deserialize, Debug)] -struct Link { - requires_python: Option, -} +use crate::utils::{format_requirement, get_venv_python_bin, CommandOutput}; +use crate::uv::UvBuilder; #[derive(Parser, Debug)] pub struct ReqExtras { @@ -138,7 +97,7 @@ impl ReqExtras { }; req.version_or_url = match req.version_or_url { Some(_) => bail!("requirement already has a version marker"), - None => Some(pep508_rs::VersionOrUrl::Url( + None => Some(VersionOrUrl::Url( format!("git+{}{}", git, suffix).parse().with_context(|| { format!("unable to interpret '{}{}' as git reference", git, suffix) })?, @@ -147,14 +106,15 @@ impl ReqExtras { } else if let Some(ref url) = self.url { req.version_or_url = match req.version_or_url { Some(_) => bail!("requirement already has a version marker"), - None => Some(pep508_rs::VersionOrUrl::Url( - url.parse() - .with_context(|| format!("unable to parse '{}' as url", url))?, - )), + None => { + Some(VersionOrUrl::Url(url.parse().with_context(|| { + format!("unable to parse '{}' as url", url) + })?)) + } }; } else if let Some(ref path) = self.path { // For hatchling build backend, it use {root:uri} for file relative path, - // but this not supported by pip-tools, + // but this not supported by uv, // and use ${PROJECT_ROOT} will cause error in hatchling, so force absolute path. let is_hatchling = PyProject::discover()?.build_backend() == Some(BuildSystem::Hatchling); @@ -170,11 +130,13 @@ impl ReqExtras { path.display() ) })?; - Url::from_file_path(Path::new("/${PROJECT_ROOT}").join(rv)).unwrap() + let mut url = Url::parse("file://")?; + url.set_path(&Path::new("/${PROJECT_ROOT}").join(rv).to_string_lossy()); + url }; req.version_or_url = match req.version_or_url { Some(_) => bail!("requirement already has a version marker"), - None => Some(pep508_rs::VersionOrUrl::Url(file_url)), + None => Some(VersionOrUrl::Url(file_url)), }; } for feature in self.features.iter().flat_map(|x| x.split(',')) { @@ -197,7 +159,7 @@ pub struct Args { #[command(flatten)] req_extras: ReqExtras, /// Add this as dev dependency. - #[arg(long)] + #[arg(short, long)] dev: bool, /// Add this as an excluded dependency that will not be installed even if it's a sub dependency. #[arg(long, conflicts_with = "dev", conflicts_with = "optional")] @@ -205,9 +167,6 @@ pub struct Args { /// Add this to an optional dependency group. #[arg(long, conflicts_with = "dev", conflicts_with = "excluded")] optional: Option, - /// Include pre-releases when finding a package version. - #[arg(long)] - pre: bool, /// Overrides the pin operator #[arg(long)] pin: Option, @@ -223,12 +182,24 @@ pub struct Args { /// Turns off all output. #[arg(short, long, conflicts_with = "verbose")] quiet: bool, + + /// Include pre-releases when finding a package version and automatically syncing the workspace. + #[arg(long)] + pre: bool, + /// Set to `true` to lock with sources in the lockfile when automatically syncing the workspace. + #[arg(long)] + with_sources: bool, + /// Set to `true` to lock with hashes in the lockfile when automatically syncing the workspace. + #[arg(long)] + generate_hashes: bool, + /// Attempt to use `keyring` for authentication for index URLs. + #[arg(long, value_enum, default_value_t)] + keyring_provider: KeyringProvider, } pub fn execute(cmd: Args) -> Result<(), Error> { let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose); - let self_venv = ensure_self_venv(output).context("error bootstrapping venv")?; - let python_path = self_venv.join(VENV_BIN).join("python"); + ensure_self_venv(output).context("error bootstrapping venv")?; let cfg = Config::current(); let mut pyproject_toml = PyProject::discover()?; @@ -259,30 +230,16 @@ pub fn execute(cmd: Args) -> Result<(), Error> { } if !cmd.excluded { - if cfg.use_uv() { - sync(SyncOptions::python_only().pyproject(None)) - .context("failed to sync ahead of add")?; - resolve_requirements_with_uv( - &pyproject_toml, - &py_ver, - &mut requirements, - cmd.pre, - output, - &default_operator, - )?; - } else { - for requirement in &mut requirements { - resolve_requirements_with_unearth( - &pyproject_toml, - &python_path, - &py_ver, - requirement, - cmd.pre, - output, - &default_operator, - )?; - } - } + sync(SyncOptions::python_only().pyproject(None)).context("failed to sync ahead of add")?; + resolve_requirements_with_uv( + &pyproject_toml, + &py_ver, + &mut requirements, + cmd.pre, + output, + &default_operator, + cmd.keyring_provider, + )?; } for requirement in &requirements { @@ -302,144 +259,19 @@ pub fn execute(cmd: Args) -> Result<(), Error> { } if (cfg.autosync() && !cmd.no_sync) || cmd.sync { - autosync(&pyproject_toml, output)?; + autosync( + &pyproject_toml, + output, + cmd.pre, + cmd.with_sources, + cmd.generate_hashes, + cmd.keyring_provider, + )?; } Ok(()) } -fn resolve_requirements_with_unearth( - pyproject_toml: &PyProject, - python_path: &PathBuf, - py_ver: &PythonVersion, - requirement: &mut Requirement, - pre: bool, - output: CommandOutput, - default_operator: &Operator, -) -> Result<(), Error> { - let matches = find_best_matches_with_unearth( - pyproject_toml, - python_path, - Some(py_ver), - requirement, - pre, - )?; - if matches.is_empty() { - let all_matches = - find_best_matches_with_unearth(pyproject_toml, python_path, None, requirement, pre) - .unwrap_or_default(); - if all_matches.is_empty() { - // if we did not consider pre-releases, maybe we could find it by doing so. In - // that case give the user a helpful warning before erroring. - if !pre { - let all_pre_matches = find_best_matches_with_unearth( - pyproject_toml, - python_path, - None, - requirement, - true, - ) - .unwrap_or_default(); - if let Some(pre) = all_pre_matches.into_iter().next() { - warn!( - "{} ({}) was found considering pre-releases. Pass --pre to allow use.", - pre.name, - pre.version.unwrap_or_default() - ); - } - bail!( - "did not find package '{}' without using pre-releases.", - format_requirement(requirement) - ); - } else { - bail!("did not find package '{}'", format_requirement(requirement)); - } - } else { - if output != CommandOutput::Quiet { - echo!("Available package versions:"); - for pkg in all_matches { - echo!( - " {} ({}) requires Python {}", - pkg.name, - pkg.version.unwrap_or_default(), - pkg.link - .as_ref() - .and_then(|x| x.requires_python.as_ref()) - .map_or("unknown", |x| x as &str) - ); - } - echo!("A possible solution is to raise the version in `requires-python` in `pyproject.toml`."); - } - bail!( - "did not find a version of package '{}' compatible with this version of Python.", - format_requirement(requirement) - ); - } - } - let m = matches.into_iter().next().unwrap(); - if m.version.is_some() && requirement.version_or_url.is_none() { - let version = Version::from_str(m.version.as_ref().unwrap()) - .map_err(|msg| anyhow!("invalid version: {}", msg))?; - requirement.version_or_url = Some(VersionOrUrl::VersionSpecifier( - VersionSpecifiers::from_iter(Some( - VersionSpecifier::new( - // local versions or versions with only one component cannot - // use ~= but need to use ==. - match *default_operator { - _ if version.is_local() => Operator::Equal, - Operator::TildeEqual if version.release.len() < 2 => { - Operator::GreaterThanEqual - } - ref other => other.clone(), - }, - Version::from_str(m.version.as_ref().unwrap()) - .map_err(|msg| anyhow!("invalid version: {}", msg))?, - false, - ) - .map_err(|msg| anyhow!("invalid version specifier: {}", msg))?, - )), - )); - } - requirement.name = m.name; - Ok(()) -} - -fn find_best_matches_with_unearth( - pyproject: &PyProject, - python_path: &PathBuf, - py_ver: Option<&PythonVersion>, - requirement: &Requirement, - pre: bool, -) -> Result, Error> { - let mut unearth = Command::new(python_path); - let sources = ExpandedSources::from_sources(&pyproject.sources()?)?; - - unearth - .arg("-c") - .arg(PACKAGE_FINDER_SCRIPT) - .arg(match py_ver { - Some(ver) => ver.format_simple(), - None => "".into(), - }) - .arg(&format_requirement(requirement).to_string()) - .arg(serde_json::to_string(&sources)?); - if pre { - unearth.arg("--pre"); - } - set_proxy_variables(&mut unearth); - let unearth = unearth.stdout(Stdio::piped()).output()?; - if unearth.status.success() { - Ok(serde_json::from_slice(&unearth.stdout)?) - } else { - let log = String::from_utf8_lossy(&unearth.stderr); - bail!( - "failed to resolve package {}\n{}", - format_requirement(requirement), - log - ); - } -} - fn resolve_requirements_with_uv( pyproject_toml: &PyProject, py_ver: &PythonVersion, @@ -447,50 +279,35 @@ fn resolve_requirements_with_uv( pre: bool, output: CommandOutput, default_operator: &Operator, + keyring_provider: KeyringProvider, ) -> Result<(), Error> { - let mut cmd = Uv::ensure_exists(output)?.cmd(); - cmd.arg("pip") - .arg("compile") - .arg("--python-version") - .arg(py_ver.format_simple()) - .arg("--no-deps") - .arg("--no-header") - .arg("-") - .env("VIRTUAL_ENV", pyproject_toml.venv_path().as_os_str()); - if pre { - cmd.arg("--prerelease=allow"); - } - if output == CommandOutput::Quiet { - cmd.arg("-q"); - } - // this primarily exists for testing - if let Ok(dt) = env::var("__RYE_UV_EXCLUDE_NEWER") { - cmd.arg("--exclude-newer").arg(dt); - } + let venv_path = pyproject_toml.venv_path(); + let py_bin = get_venv_python_bin(&venv_path); let sources = ExpandedSources::from_sources(&pyproject_toml.sources()?)?; - sources.add_as_pip_args(&mut cmd); - let mut child = cmd - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?; - let child_stdin = child.stdin.as_mut().unwrap(); - for requirement in &*requirements { - writeln!(child_stdin, "{}", requirement)?; - } - let rv = child.wait_with_output()?; - if !rv.status.success() { - let log = String::from_utf8_lossy(&rv.stderr); - bail!("failed to resolve packages:\n{}", log); - } + let uv = UvBuilder::new() + .with_output(output.quieter()) + .with_sources(sources) + .ensure_exists()? + .venv(&venv_path, &py_bin, py_ver, None)?; + + for req in requirements { + let mut new_req = uv.resolve( + py_ver, + req, + pre, + env::var("__RYE_UV_EXCLUDE_NEWER").ok(), + keyring_provider, + )?; + + // if a version or URL is already provided we just use the normalized package name but + // retain all old information. + if req.version_or_url.is_some() { + req.name = new_req.name; + continue; + } - for (line, req) in String::from_utf8_lossy(&rv.stdout) - .lines() - .zip(requirements) - { - *req = line.parse()?; - if let Some(ref mut version_or_url) = req.version_or_url { + if let Some(ref mut version_or_url) = new_req.version_or_url { if let VersionOrUrl::VersionSpecifier(ref mut specs) = version_or_url { *version_or_url = VersionOrUrl::VersionSpecifier(VersionSpecifiers::from_iter({ let mut new_specs = Vec::new(); @@ -511,6 +328,10 @@ fn resolve_requirements_with_uv( })); } } + if let Some(old_extras) = &req.extras { + new_req.extras = Some(old_extras.clone()); + } + *req = new_req; } Ok(()) diff --git a/rye/src/cli/build.rs b/rye/src/cli/build.rs index d1db714fd7..3950ca7c7b 100644 --- a/rye/src/cli/build.rs +++ b/rye/src/cli/build.rs @@ -2,18 +2,21 @@ use std::fs; use std::path::PathBuf; use std::process::{Command, Stdio}; -use anyhow::{bail, Error}; +use anyhow::{anyhow, bail, Context, Error}; use clap::Parser; use console::style; -use crate::bootstrap::ensure_self_venv; +use crate::bootstrap::{fetch, FetchOptions}; + +use crate::platform::get_toolchain_python_bin; use crate::pyproject::{locate_projects, PyProject}; -use crate::utils::{get_venv_python_bin, CommandOutput, IoPathContext}; +use crate::utils::{get_venv_python_bin, prepend_path_to_path_env, CommandOutput, IoPathContext}; +use crate::uv::UvBuilder; /// Builds a package for distribution. #[derive(Parser, Debug)] pub struct Args { - /// Build an sdist + /// Build a sdist #[arg(long)] sdist: bool, /// Build a wheel @@ -44,8 +47,8 @@ pub struct Args { pub fn execute(cmd: Args) -> Result<(), Error> { let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose); - let venv = ensure_self_venv(output)?; let project = PyProject::load_or_discover(cmd.pyproject.as_deref())?; + let py_ver = project.venv_python_version()?; let out = match cmd.out { Some(path) => path, @@ -63,17 +66,42 @@ pub fn execute(cmd: Args) -> Result<(), Error> { let projects = locate_projects(project, cmd.all, &cmd.package[..])?; + let all_virtual = projects.iter().all(|p| p.is_virtual()); + if all_virtual { + warn!("skipping build, all projects are virtual"); + return Ok(()); + } + + // Make sure we have a compatible Python version. + let py_ver = fetch(&py_ver.into(), FetchOptions::with_output(output)) + .context("failed fetching toolchain ahead of sync")?; + echo!(if output, "Python version: {}", style(&py_ver).cyan()); + let py_bin = get_toolchain_python_bin(&py_ver)?; + + // Create a virtual environment in which to perform the builds. + let uv = UvBuilder::new() + .with_output(CommandOutput::Quiet) + .ensure_exists()?; + let venv_dir = tempfile::tempdir().context("failed to create temporary directory")?; + let uv_venv = uv + .venv(venv_dir.path(), &py_bin, &py_ver, None) + .context("failed to create build environment")?; + uv_venv.write_marker()?; + uv_venv.bootstrap()?; + for project in projects { // skip over virtual packages on build if project.is_virtual() { continue; } - if output != CommandOutput::Quiet { - echo!("building {}", style(project.normalized_name()?).cyan()); - } + echo!( + if output, + "building {}", + style(project.normalized_name()?).cyan() + ); - let mut build_cmd = Command::new(get_venv_python_bin(&venv)); + let mut build_cmd = Command::new(get_venv_python_bin(venv_dir.path())); build_cmd .arg("-mbuild") .env("NO_COLOR", "1") @@ -81,6 +109,18 @@ pub fn execute(cmd: Args) -> Result<(), Error> { .arg(&out) .arg(&*project.root_path()); + // we need to ensure uv is available to use without installing it into self_venv + let uv = UvBuilder::new() + .with_output(output) + .ensure_exists()? + .with_output(output); + let uv_dir = uv + .uv_bin() + .parent() + .ok_or_else(|| anyhow!("Could not find uv binary in self venv: empty path"))?; + build_cmd.env("PATH", prepend_path_to_path_env(uv_dir)?); + build_cmd.arg("--installer=uv"); + if cmd.wheel { build_cmd.arg("--wheel"); } @@ -88,6 +128,10 @@ pub fn execute(cmd: Args) -> Result<(), Error> { build_cmd.arg("--sdist"); } + if output == CommandOutput::Verbose { + build_cmd.arg("--verbose"); + } + if output == CommandOutput::Quiet { build_cmd.stdout(Stdio::null()); build_cmd.stderr(Stdio::null()); @@ -98,6 +142,5 @@ pub fn execute(cmd: Args) -> Result<(), Error> { bail!("failed to build dist"); } } - Ok(()) } diff --git a/rye/src/cli/fetch.rs b/rye/src/cli/fetch.rs index 376ff774ae..dfbd2284c4 100644 --- a/rye/src/cli/fetch.rs +++ b/rye/src/cli/fetch.rs @@ -1,14 +1,16 @@ +use std::path::PathBuf; + use anyhow::{Context, Error}; use clap::Parser; -use crate::bootstrap::fetch; +use crate::bootstrap::{fetch, FetchOptions}; use crate::config::Config; use crate::platform::get_python_version_request_from_pyenv_pin; use crate::pyproject::PyProject; use crate::sources::py::PythonVersionRequest; use crate::utils::CommandOutput; -/// Fetches a Python interpreter for the local machine. This is an alias of `rye toolchain fetch`. +/// Fetches a Python interpreter for the local machine. #[derive(Parser, Debug)] pub struct Args { /// The version of Python to fetch. @@ -18,6 +20,15 @@ pub struct Args { /// Fetch the Python toolchain even if it is already installed. #[arg(short, long)] force: bool, + /// Fetches the Python toolchain into an explicit location rather. + #[arg(long)] + target_path: Option, + /// Fetches with build info. + #[arg(long)] + build_info: bool, + /// Fetches without build info. + #[arg(long, conflicts_with = "build_info")] + no_build_info: bool, /// Enables verbose diagnostics. #[arg(short, long)] verbose: bool, @@ -43,6 +54,21 @@ pub fn execute(cmd: Args) -> Result<(), Error> { } }; - fetch(&version, output, cmd.force).context("error while fetching Python installation")?; + fetch( + &version, + FetchOptions { + output, + force: cmd.force, + target_path: cmd.target_path, + build_info: if cmd.build_info { + Some(true) + } else if cmd.no_build_info { + Some(false) + } else { + None + }, + }, + ) + .context("error while fetching Python installation")?; Ok(()) } diff --git a/rye/src/cli/init.rs b/rye/src/cli/init.rs index 1bd9d847d2..e88608357a 100644 --- a/rye/src/cli/init.rs +++ b/rye/src/cli/init.rs @@ -152,7 +152,8 @@ if __name__ == "setuptools": pub fn execute(cmd: Args) -> Result<(), Error> { let cfg = Config::current(); - let env = Environment::new(); + let mut env = Environment::new(); + env.set_keep_trailing_newline(true); let dir = env::current_dir()?.join(cmd.path); let toml = dir.join("pyproject.toml"); let readme = dir.join("README.md"); @@ -252,7 +253,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> { } if metadata.author.is_none() { is_metadata_author_none = true; - metadata.author = author.clone(); + metadata.author.clone_from(&author); } if metadata.requires_python.is_none() { metadata.requires_python = Some(requires_python); @@ -335,28 +336,28 @@ pub fn execute(cmd: Args) -> Result<(), Error> { .status() .map(|status| status.success()) .unwrap_or(false) + && is_metadata_author_none { - let gitignore = dir.join(".gitignore"); - - // create a .gitignore if one is missing - if !gitignore.is_file() { - let rv = env.render_named_str( - "gitignore.txt", - GITIGNORE_TEMPLATE, - context! { - is_rust => matches!(build_system, BuildSystem::Maturin) - }, - )?; - fs::write(&gitignore, rv).path_context(&gitignore, "failed to write .gitignore")?; - } - if is_metadata_author_none { - let new_author = get_default_author_with_fallback(&dir); - if author != new_author { - metadata.author = new_author; - } + let new_author = get_default_author_with_fallback(&dir); + if author != new_author { + metadata.author = new_author; } } + let gitignore = dir.join(".gitignore"); + + // create a .gitignore if one is missing + if !gitignore.is_file() { + let rv = env.render_named_str( + "gitignore.txt", + GITIGNORE_TEMPLATE, + context! { + is_rust => matches!(build_system, BuildSystem::Maturin) + }, + )?; + fs::write(&gitignore, rv).path_context(&gitignore, "failed to write .gitignore")?; + } + let rv = env.render_named_str( "pyproject.json", TOML_TEMPLATE, @@ -444,15 +445,14 @@ pub fn execute(cmd: Args) -> Result<(), Error> { } } - if output != CommandOutput::Quiet { - echo!( - "{} Initialized {}project in {}", - style("success:").green(), - if is_virtual { "virtual " } else { "" }, - dir.display() - ); - echo!(" Run `rye sync` to get started"); - } + echo!( + if output, + "{} Initialized {}project in {}", + style("success:").green(), + if is_virtual { "virtual " } else { "" }, + dir.display() + ); + echo!(if output, " Run `rye sync` to get started"); Ok(()) } diff --git a/rye/src/cli/install.rs b/rye/src/cli/install.rs index 64aeb49d8a..aae1632584 100644 --- a/rye/src/cli/install.rs +++ b/rye/src/cli/install.rs @@ -7,10 +7,11 @@ use pep508_rs::Requirement; use crate::cli::add::ReqExtras; use crate::config::Config; use crate::installer::{install, resolve_local_requirement}; +use crate::lock::KeyringProvider; use crate::sources::py::PythonVersionRequest; use crate::utils::CommandOutput; -/// Installs a package as global tool. This is an alias of `rye tools install`. +/// Installs a package as global tool. #[derive(Parser, Debug)] pub struct Args { /// The name of the package to install. @@ -29,6 +30,9 @@ pub struct Args { /// Force install the package even if it's already there. #[arg(short, long)] force: bool, + /// Attempt to use `keyring` for authentication for index URLs. + #[arg(long, value_enum, default_value_t)] + keyring_provider: KeyringProvider, /// Enables verbose diagnostics. #[arg(short, long)] verbose: bool, @@ -74,6 +78,7 @@ pub fn execute(mut cmd: Args) -> Result<(), Error> { &cmd.include_dep, &extra_requirements, output, + cmd.keyring_provider, )?; Ok(()) } diff --git a/rye/src/cli/list.rs b/rye/src/cli/list.rs index c091d37365..92812e25a3 100644 --- a/rye/src/cli/list.rs +++ b/rye/src/cli/list.rs @@ -1,15 +1,11 @@ use std::path::PathBuf; -use std::process::Command; -use anyhow::{bail, Error}; +use anyhow::Error; use clap::Parser; -use crate::bootstrap::ensure_self_venv; -use crate::config::Config; -use crate::consts::VENV_BIN; use crate::pyproject::PyProject; use crate::utils::{get_venv_python_bin, CommandOutput}; -use crate::uv::Uv; +use crate::uv::{UvBuilder, Venv}; /// Prints the currently installed packages. #[derive(Parser, Debug)] @@ -23,30 +19,12 @@ pub fn execute(cmd: Args) -> Result<(), Error> { let project = PyProject::load_or_discover(cmd.pyproject.as_deref())?; let python = get_venv_python_bin(&project.venv_path()); if !python.is_file() { + warn!("Project is not synced, no virtualenv found. Run `rye sync`."); return Ok(()); } - let self_venv = ensure_self_venv(CommandOutput::Normal)?; - - let status = if Config::current().use_uv() { - Uv::ensure_exists(CommandOutput::Normal)? - .cmd() - .arg("pip") - .arg("freeze") - .env("VIRTUAL_ENV", project.venv_path().as_os_str()) - .status()? - } else { - Command::new(self_venv.join(VENV_BIN).join("pip")) - .arg("--python") - .arg(&python) - .arg("freeze") - .env("PYTHONWARNINGS", "ignore") - .env("PIP_DISABLE_PIP_VERSION_CHECK", "1") - .status()? - }; - - if !status.success() { - bail!("failed to print dependencies via pip"); - } - + let uv = UvBuilder::new() + .with_output(CommandOutput::Normal) + .ensure_exists()?; + uv.read_only_venv(&project.venv_path())?.freeze()?; Ok(()) } diff --git a/rye/src/cli/lock.rs b/rye/src/cli/lock.rs index e12e0b6ccb..31b3b16292 100644 --- a/rye/src/cli/lock.rs +++ b/rye/src/cli/lock.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use anyhow::Error; use clap::Parser; -use crate::lock::LockOptions; +use crate::lock::{KeyringProvider, LockOptions}; use crate::sync::{sync, SyncMode, SyncOptions}; use crate::utils::CommandOutput; @@ -34,10 +34,19 @@ pub struct Args { /// Set to true to lock with sources in the lockfile. #[arg(long)] with_sources: bool, + /// Attempt to use `keyring` for authentication for index URLs. + #[arg(long, value_enum, default_value_t)] + keyring_provider: KeyringProvider, + /// Set to true to lock with hashes in the lockfile. + #[arg(long)] + generate_hashes: bool, + /// Use universal lock files. + #[arg(long)] + universal: bool, /// Reset prior lock options. #[arg(long)] reset: bool, - /// Use this pyproject.toml file + /// Use this pyproject.toml file. #[arg(long, value_name = "PYPROJECT_TOML")] pyproject: Option, } @@ -55,8 +64,11 @@ pub fn execute(cmd: Args) -> Result<(), Error> { all_features: cmd.all_features, with_sources: cmd.with_sources, reset: cmd.reset, + generate_hashes: cmd.generate_hashes, + universal: cmd.universal, }, pyproject: cmd.pyproject, + keyring_provider: cmd.keyring_provider, ..SyncOptions::default() })?; Ok(()) diff --git a/rye/src/cli/mod.rs b/rye/src/cli/mod.rs index c34a163de3..ee5ece42b4 100644 --- a/rye/src/cli/mod.rs +++ b/rye/src/cli/mod.rs @@ -1,4 +1,5 @@ use std::env; +use std::path::PathBuf; use anyhow::{bail, Error}; use clap::Parser; @@ -22,6 +23,7 @@ mod rye; mod shim; mod show; mod sync; +mod test; mod toolchain; mod tools; mod uninstall; @@ -29,9 +31,11 @@ mod version; use git_testament::git_testament; -use crate::bootstrap::SELF_PYTHON_TARGET_VERSION; +use crate::bootstrap::{get_self_venv_status, SELF_PYTHON_TARGET_VERSION}; use crate::config::Config; use crate::platform::symlinks_supported; +use crate::pyproject::read_venv_marker; +use crate::utils::IoPathContext; git_testament!(TESTAMENT); @@ -41,6 +45,9 @@ git_testament!(TESTAMENT); struct Args { #[command(subcommand)] command: Option, + /// Load one or more .env files. + #[arg(long)] + env_file: Vec, /// Print the version #[arg(long)] version: bool, @@ -66,6 +73,7 @@ enum Command { Run(run::Args), Show(show::Args), Sync(sync::Args), + Test(test::Args), Toolchain(toolchain::Args), Tools(tools::Args), #[command(name = "self")] @@ -99,6 +107,13 @@ pub fn execute() -> Result<(), Error> { } let args = Args::try_parse()?; + + // handle --env-file. As this happens here this cannot influence `RYE_HOME` or + // the behavior of the shims. + for env_file in &args.env_file { + dotenvy::from_path(env_file).path_context(env_file, "unable to load env file")?; + } + let cmd = if args.version { return print_version(); } else if let Some(cmd) = args.command { @@ -107,6 +122,13 @@ pub fn execute() -> Result<(), Error> { unreachable!() }; + // Add this to warn about the deprecated use of pip-tools + if !Config::current().use_uv() { + warn!( + "The `use-uv` setting is deprecated, as `pip-tools` support was removed in rye 0.40.0" + ); + } + match cmd { Command::Add(cmd) => add::execute(cmd), Command::Build(cmd) => build::execute(cmd), @@ -124,6 +146,7 @@ pub fn execute() -> Result<(), Error> { Command::Run(cmd) => run::execute(cmd), Command::Show(cmd) => show::execute(cmd), Command::Sync(cmd) => sync::execute(cmd), + Command::Test(cmd) => test::execute(cmd), Command::Toolchain(cmd) => toolchain::execute(cmd), Command::Tools(cmd) => tools::execute(cmd), Command::Rye(cmd) => rye::execute(cmd), @@ -151,8 +174,20 @@ fn print_version() -> Result<(), Error> { std::env::consts::OS, std::env::consts::ARCH ); - echo!("self-python: {}", SELF_PYTHON_TARGET_VERSION); + + let self_venv_python = match get_self_venv_status() { + Ok(venv_dir) | Err((venv_dir, _)) => read_venv_marker(&venv_dir).map(|mark| mark.python), + }; + + if let Some(python) = self_venv_python { + echo!("self-python: {}", python); + } else { + echo!( + "self-python: not bootstrapped (target: {})", + SELF_PYTHON_TARGET_VERSION + ); + } echo!("symlink support: {}", symlinks_supported()); - echo!("uv enabled: {}", Config::current().use_uv()); + echo!("uv enabled: {}", true); Ok(()) } diff --git a/rye/src/cli/publish.rs b/rye/src/cli/publish.rs index 45bc4756e2..4cc31b01f1 100644 --- a/rye/src/cli/publish.rs +++ b/rye/src/cli/publish.rs @@ -42,6 +42,9 @@ pub struct Args { /// Path to alternate CA bundle. #[arg(long)] cert: Option, + /// Skip files that have already been published (only applies to repositories supporting this feature) + #[arg(long)] + skip_existing: bool, /// Skip prompts. #[arg(short, long)] yes: bool, @@ -56,16 +59,17 @@ pub struct Args { pub fn execute(cmd: Args) -> Result<(), Error> { let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose); let venv = ensure_self_venv(output)?; - let project = PyProject::discover()?; - - if project.is_virtual() { - bail!("virtual packages cannot be published"); - } // Get the files to publish. let files = match cmd.dist { Some(paths) => paths, - None => vec![project.workspace_path().join("dist").join("*")], + None => { + let project = PyProject::discover()?; + if project.is_virtual() { + bail!("virtual packages cannot be published"); + } + vec![project.workspace_path().join("dist").join("*")] + } }; // a. Get token from arguments and offer encryption, then store in credentials file. @@ -112,7 +116,6 @@ pub fn execute(cmd: Args) -> Result<(), Error> { let maybe_encrypted = maybe_encrypt(&secret, cmd.yes)?; let maybe_encoded = maybe_encode(&secret, &maybe_encrypted); credentials[repository]["token"] = Item::Value(maybe_encoded.expose_secret().into()); - write_credentials(&credentials)?; secret } else if let Some(token) = credentials @@ -167,6 +170,9 @@ pub fn execute(cmd: Args) -> Result<(), Error> { if let Some(cert) = cmd.cert { publish_cmd.arg("--cert").arg(cert); } + if cmd.skip_existing { + publish_cmd.arg("--skip-existing"); + } if output == CommandOutput::Quiet { publish_cmd.stdout(Stdio::null()); diff --git a/rye/src/cli/remove.rs b/rye/src/cli/remove.rs index 73adaa2632..3ded3a76bb 100644 --- a/rye/src/cli/remove.rs +++ b/rye/src/cli/remove.rs @@ -5,6 +5,7 @@ use clap::Parser; use pep508_rs::Requirement; use crate::config::Config; +use crate::lock::KeyringProvider; use crate::pyproject::{DependencyKind, PyProject}; use crate::sync::autosync; use crate::utils::{format_requirement, CommandOutput}; @@ -16,7 +17,7 @@ pub struct Args { #[arg(required = true)] requirements: Vec, /// Remove this from dev dependencies. - #[arg(long)] + #[arg(short, long)] dev: bool, /// Remove this from an optional dependency group. #[arg(long, conflicts_with = "dev")] @@ -33,6 +34,19 @@ pub struct Args { /// Turns off all output. #[arg(short, long, conflicts_with = "verbose")] quiet: bool, + + /// Include pre-releases when automatically syncing the workspace. + #[arg(long)] + pre: bool, + /// Set to `true` to lock with sources in the lockfile when automatically syncing the workspace. + #[arg(long)] + with_sources: bool, + /// Set to `true` to lock with hashes in the lockfile when automatically syncing the workspace. + #[arg(long)] + generate_hashes: bool, + /// Attempt to use `keyring` for authentication for index URLs. + #[arg(long, value_enum, default_value_t)] + keyring_provider: KeyringProvider, } pub fn execute(cmd: Args) -> Result<(), Error> { @@ -65,7 +79,14 @@ pub fn execute(cmd: Args) -> Result<(), Error> { } if (Config::current().autosync() && !cmd.no_sync) || cmd.sync { - autosync(&pyproject_toml, output)?; + autosync( + &pyproject_toml, + output, + cmd.pre, + cmd.with_sources, + cmd.generate_hashes, + cmd.keyring_provider, + )?; } Ok(()) diff --git a/rye/src/cli/run.rs b/rye/src/cli/run.rs index 0daf1980b6..d69433c2f4 100644 --- a/rye/src/cli/run.rs +++ b/rye/src/cli/run.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::env::{self, join_paths, split_paths}; use std::ffi::OsString; use std::path::PathBuf; @@ -10,7 +11,7 @@ use console::style; use crate::pyproject::{PyProject, Script}; use crate::sync::{sync, SyncOptions}; use crate::tui::redirect_to_stderr; -use crate::utils::{exec_spawn, get_venv_python_bin, success_status}; +use crate::utils::{exec_spawn, get_venv_python_bin, success_status, IoPathContext}; /// Runs a command installed into this package. #[derive(Parser, Debug)] @@ -34,7 +35,7 @@ enum Cmd { } pub fn execute(cmd: Args) -> Result<(), Error> { - let _guard = redirect_to_stderr(true); + let guard = redirect_to_stderr(true); let pyproject = PyProject::load_or_discover(cmd.pyproject.as_deref())?; // make sure we have the minimal virtualenv. @@ -42,6 +43,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> { .context("failed to sync ahead of run")?; if cmd.list || cmd.cmd.is_none() { + drop(guard); return list_scripts(&pyproject); } let args = match cmd.cmd { @@ -62,9 +64,9 @@ fn invoke_script( let mut env_overrides = None; match pyproject.get_script_cmd(&args[0].to_string_lossy()) { - Some(Script::Call(entry, env_vars)) => { + Some(Script::Call(entry, env_vars, env_file)) => { let py = OsString::from(get_venv_python_bin(&pyproject.venv_path())); - env_overrides = Some(env_vars); + env_overrides = Some(load_env_vars(pyproject, env_file, env_vars)?); args = if let Some((module, func)) = entry.split_once(':') { if module.is_empty() || func.is_empty() { bail!("Python callable must be in the form : or ") @@ -86,11 +88,11 @@ fn invoke_script( .chain(args.into_iter().skip(1)) .collect(); } - Some(Script::Cmd(script_args, env_vars)) => { + Some(Script::Cmd(script_args, env_vars, env_file)) => { if script_args.is_empty() { bail!("script has no arguments"); } - env_overrides = Some(env_vars); + env_overrides = Some(load_env_vars(pyproject, env_file, env_vars)?); let script_target = venv_bin.join(&script_args[0]); if script_target.is_file() { args = Some(script_target.as_os_str().to_owned()) @@ -157,6 +159,23 @@ fn invoke_script( } } +fn load_env_vars( + pyproject: &PyProject, + env_file: Option, + mut env_vars: HashMap, +) -> Result, Error> { + if let Some(ref env_file) = env_file { + let env_file = pyproject.root_path().join(env_file); + for item in + dotenvy::from_path_iter(&env_file).path_context(&env_file, "could not load env-file")? + { + let (k, v) = item.path_context(&env_file, "invalid value in env-file")?; + env_vars.insert(k, v); + } + } + Ok(env_vars) +} + fn list_scripts(pyproject: &PyProject) -> Result<(), Error> { let mut scripts: Vec<_> = pyproject .list_scripts() diff --git a/rye/src/cli/rye.rs b/rye/src/cli/rye.rs index 85cd9b8aba..87299b89a1 100644 --- a/rye/src/cli/rye.rs +++ b/rye/src/cli/rye.rs @@ -7,8 +7,10 @@ use std::sync::Arc; use std::{env, fs}; use anyhow::{anyhow, bail, Context, Error}; -use clap::{CommandFactory, Parser}; -use clap_complete::Shell; +use clap::builder::PossibleValue; +use clap::{CommandFactory, Parser, ValueEnum}; +use clap_complete::{Generator, Shell}; +use clap_complete_nushell::Nushell; use console::style; use minijinja::render; use self_replace::self_delete_outside_path; @@ -52,12 +54,80 @@ pub struct Args { command: SubCommand, } +#[derive(Clone, Debug)] +enum ShellCompletion { + /// Bourne Again SHell (bash) + Bash, + /// Elvish shell + Elvish, + /// Friendly Interactive SHell (fish) + Fish, + /// PowerShell + PowerShell, + /// Z SHell (zsh) + Zsh, + /// Nushell + Nushell, +} + +impl ValueEnum for ShellCompletion { + /// Returns the variants for the shell completion. + fn value_variants<'a>() -> &'a [Self] { + &[ + ShellCompletion::Bash, + ShellCompletion::Elvish, + ShellCompletion::Fish, + ShellCompletion::PowerShell, + ShellCompletion::Zsh, + ShellCompletion::Nushell, + ] + } + + /// Returns the possible value for the shell completion. + fn to_possible_value<'a>(&self) -> Option { + Some(match self { + ShellCompletion::Bash => PossibleValue::new("bash"), + ShellCompletion::Elvish => PossibleValue::new("elvish"), + ShellCompletion::Fish => PossibleValue::new("fish"), + ShellCompletion::PowerShell => PossibleValue::new("powershell"), + ShellCompletion::Zsh => PossibleValue::new("zsh"), + ShellCompletion::Nushell => PossibleValue::new("nushell"), + }) + } +} + +impl Generator for ShellCompletion { + /// Generate the file name for the completion script. + fn file_name(&self, name: &str) -> String { + match self { + ShellCompletion::Nushell => Nushell.file_name(name), + ShellCompletion::Bash => Shell::Bash.file_name(name), + ShellCompletion::Elvish => Shell::Elvish.file_name(name), + ShellCompletion::Fish => Shell::Fish.file_name(name), + ShellCompletion::PowerShell => Shell::PowerShell.file_name(name), + ShellCompletion::Zsh => Shell::Zsh.file_name(name), + } + } + + /// Generate the completion script for the shell. + fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) { + match self { + ShellCompletion::Nushell => Nushell.generate(cmd, buf), + ShellCompletion::Bash => Shell::Bash.generate(cmd, buf), + ShellCompletion::Elvish => Shell::Elvish.generate(cmd, buf), + ShellCompletion::Fish => Shell::Fish.generate(cmd, buf), + ShellCompletion::PowerShell => Shell::PowerShell.generate(cmd, buf), + ShellCompletion::Zsh => Shell::Zsh.generate(cmd, buf), + } + } +} + /// Generates a completion script for a shell. #[derive(Parser, Debug)] pub struct CompletionCommand { /// The shell to generate a completion script for (defaults to 'bash'). #[arg(short, long)] - shell: Option, + shell: Option, } /// Performs an update of rye. @@ -75,6 +145,9 @@ pub struct UpdateCommand { /// Update to a specific git rev. #[arg(long, conflicts_with = "tag")] rev: Option, + /// Update to a specific git branch. + #[arg(long, conflicts_with = "tag", conflicts_with = "rev")] + branch: Option, /// Force reinstallation #[arg(long)] force: bool, @@ -177,7 +250,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> { fn completion(args: CompletionCommand) -> Result<(), Error> { clap_complete::generate( - args.shell.unwrap_or(Shell::Bash), + args.shell.unwrap_or(ShellCompletion::Bash), &mut super::Args::command(), "rye", &mut std::io::stdout(), @@ -192,7 +265,7 @@ fn update(args: UpdateCommand) -> Result<(), Error> { let current_exe = env::current_exe()?; // git based installation with cargo - if args.rev.is_some() || args.tag.is_some() { + if args.rev.is_some() || args.tag.is_some() || args.branch.is_some() { let mut cmd = Command::new("cargo"); let tmp = tempdir()?; cmd.arg("install") @@ -214,6 +287,9 @@ fn update(args: UpdateCommand) -> Result<(), Error> { } else if let Some(ref tag) = args.tag { cmd.arg("--tag"); cmd.arg(tag); + } else if let Some(ref branch) = args.branch { + cmd.arg("--branch"); + cmd.arg(branch); } if args.force { cmd.arg("--force"); @@ -273,6 +349,10 @@ fn update(args: UpdateCommand) -> Result<(), Error> { Please stop running Python interpreters and retry the update.", )?; + echo!("Validate updated installation"); + validate_updated_exe(¤t_exe) + .context("unable to perform validation of updated installation")?; + echo!("Updated!"); echo!(); Command::new(current_exe).arg("--version").status()?; @@ -280,6 +360,43 @@ fn update(args: UpdateCommand) -> Result<(), Error> { Ok(()) } +fn validate_updated_exe(rye: &Path) -> Result<(), Error> { + let folder = tempfile::tempdir()?; + + // first create a dummy project via the new rye version + if !Command::new(rye) + .arg("init") + .arg("--name=test-project") + .arg("-q") + .arg(".") + .current_dir(folder.path()) + .status()? + .success() + { + bail!("failed to initialize test project"); + } + + // then try to run the python shim in the context of that project. + // this as a by product should update outdated internals and perform + // a python only sync in all versions of rye known currently. + if !Command::new( + get_app_dir() + .join("shims") + .join("python") + .with_extension(EXE_EXTENSION), + ) + .arg("-c") + .arg("") + .current_dir(folder.path()) + .status()? + .success() + { + bail!("failed to run python shim in test project"); + } + + Ok(()) +} + fn update_exe_and_shims(new_exe: &Path) -> Result<(), Error> { let app_dir = get_app_dir().canonicalize()?; let current_exe = env::current_exe()?.canonicalize()?; @@ -337,7 +454,7 @@ fn uninstall(args: UninstallCommand) -> Result<(), Error> { let shim_dir = app_dir.join("shims"); if let Ok(dir) = shim_dir.read_dir() { for entry in dir.flatten() { - fs::remove_file(&entry.path()).ok(); + fs::remove_file(entry.path()).ok(); } } @@ -427,7 +544,7 @@ fn perform_install( // Also pick up the target version from the RYE_TOOLCHAIN_VERSION // environment variable. - let toolchain_version_request = match toolchain_version { + let mut toolchain_version_request = match toolchain_version { Some(version) => Some(version), None => match env::var("RYE_TOOLCHAIN_VERSION") { Ok(val) => Some(val.parse()?), @@ -443,7 +560,7 @@ fn perform_install( echo!("automatically started the installer for you. For more information"); echo!( "read {}", - style("https://rye-up.com/guide/installation/").yellow() + style("https://rye.astral.sh/guide/installation/").yellow() ); } @@ -481,7 +598,7 @@ fn perform_install( echo!("enable symlinks. You need to enable this before continuing the setup."); echo!( "Learn more at {}", - style("https://rye-up.com/guide/faq/#windows-developer-mode").yellow() + style("https://rye.astral.sh/guide/faq/#windows-developer-mode").yellow() ); } @@ -495,23 +612,6 @@ fn perform_install( return Err(QuietExit(1).into()); } - // Use uv? - if config_doc - .get("behavior") - .and_then(|x| x.get("use-uv")) - .is_none() - && !matches!(mode, InstallMode::NoPrompts) - && dialoguer::Select::with_theme(tui_theme()) - .with_prompt("Select the preferred package installer") - .item("pip-tools (slow but stable)") - .item("uv (quick but experimental)") - .default(0) - .interact()? - == 1 - { - toml::ensure_table(config_doc, "behavior")["use-uv"] = toml_edit::value(true); - } - // If the global-python flag is not in the settings, ask the user if they want to turn // on global shims upon installation. if config_doc @@ -534,12 +634,17 @@ fn perform_install( // can fill in the default. if !matches!(mode, InstallMode::NoPrompts) { if toolchain_path.is_none() { - prompt_for_default_toolchain( + // Prompt the user for the default toolchain version. + let user_version_request = prompt_for_default_toolchain( toolchain_version_request .clone() .unwrap_or(SELF_PYTHON_TARGET_VERSION), config_doc, )?; + + // If the user has selected a toolchain version, we should use that as the default, + // unless the `RYE_TOOLCHAIN_VERSION` environment variable is set. + toolchain_version_request = toolchain_version_request.or(Some(user_version_request)); } else { prompt_for_toolchain_later = true; } @@ -687,7 +792,7 @@ fn add_rye_to_path(mode: &InstallMode, shims: &Path, ask: bool) -> Result<(), Er ); echo!(); } - echo!("For more information read https://rye-up.com/guide/installation/"); + echo!("For more information read https://rye.astral.sh/guide/installation/"); } } // On Windows, we add the rye directory to the user's PATH unconditionally. @@ -701,8 +806,8 @@ fn add_rye_to_path(mode: &InstallMode, shims: &Path, ask: bool) -> Result<(), Er fn prompt_for_default_toolchain( default_toolchain: PythonVersionRequest, - config_doc: &mut toml_edit::Document, -) -> Result<(), Error> { + config_doc: &mut toml_edit::DocumentMut, +) -> Result { let choice = dialoguer::Input::with_theme(tui_theme()) .with_prompt("Which version of Python should be used as default toolchain?") .default(default_toolchain.clone()) @@ -718,7 +823,7 @@ fn prompt_for_default_toolchain( }) .interact_text()?; toml::ensure_table(config_doc, "default")["toolchain"] = toml_edit::value(choice.to_string()); - Ok(()) + Ok(choice) } pub fn auto_self_install() -> Result { diff --git a/rye/src/cli/shim.rs b/rye/src/cli/shim.rs index b0d7e94a39..c4decafd2b 100644 --- a/rye/src/cli/shim.rs +++ b/rye/src/cli/shim.rs @@ -31,7 +31,7 @@ fn detect_shim(args: &[OsString]) -> Option { // rye is itself placed in the shims folder, so it must not // detect itself. - if shim_name == "rye" || shim_name == "rye.exe" { + if shim_name.eq_ignore_ascii_case("rye") || shim_name.eq_ignore_ascii_case("rye.exe") { return None; } @@ -132,7 +132,11 @@ fn is_pointless_windows_store_applink(path: &std::path::Path) -> bool { return false; } - let mut path_encoded = path.as_os_str().encode_wide().collect::>(); + let mut path_encoded = path + .as_os_str() + .encode_wide() + .chain(Some(0)) + .collect::>(); let reparse_handle = unsafe { CreateFileW( path_encoded.as_mut_ptr(), @@ -183,7 +187,7 @@ fn get_shim_target( ) -> Result>, Error> { // if we can find a project, we always look for a local virtualenv first for shims. if let Some(pyproject) = pyproject { - // However we only allow automatic synching, if we are rye managed. + // However we only allow automatic syncing, if we are rye managed. if pyproject.rye_managed() { let _guard = redirect_to_stderr(true); sync(SyncOptions::python_only()).context("sync ahead of shim resolution failed")?; @@ -323,7 +327,7 @@ pub fn execute_shim(args: &[OsString]) -> Result<(), Error> { "Target Python binary '{}' not found.\nYou are currently outside of a project. \ To resolve this, consider enabling global shims. \ Global shims allow for a Rye-managed Python installation.\n\ - For more information: https://rye-up.com/guide/shims/#global-shims", shim_name + For more information: https://rye.astral.sh/guide/shims/#global-shims", shim_name ); } } else { diff --git a/rye/src/cli/show.rs b/rye/src/cli/show.rs index adaeeb0142..1c5105db40 100644 --- a/rye/src/cli/show.rs +++ b/rye/src/cli/show.rs @@ -40,7 +40,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> { echo!("venv python: {}", style(&ver).cyan()); if let Some(actual) = get_current_venv_python_version(&project.venv_path()) { if actual != ver { - echo!("last synched venv python: {}", style(&actual).red()); + echo!("last synced venv python: {}", style(&actual).red()); } } } diff --git a/rye/src/cli/sync.rs b/rye/src/cli/sync.rs index aaaea1cc82..e324d8ed2e 100644 --- a/rye/src/cli/sync.rs +++ b/rye/src/cli/sync.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use anyhow::Error; use clap::Parser; -use crate::lock::LockOptions; +use crate::lock::{KeyringProvider, LockOptions}; use crate::sync::{sync, SyncMode, SyncOptions}; use crate::utils::CommandOutput; @@ -34,7 +34,7 @@ pub struct Args { /// Update to pre-release versions #[arg(long)] pre: bool, - /// Extras/features to enable when synching the workspace. + /// Extras/features to enable when syncing the workspace. #[arg(long)] features: Vec, /// Enables all features. @@ -43,12 +43,21 @@ pub struct Args { /// Set to true to lock with sources in the lockfile. #[arg(long)] with_sources: bool, + /// Attempt to use `keyring` for authentication for index URLs. + #[arg(long, value_enum, default_value_t)] + keyring_provider: KeyringProvider, + /// Set to true to lock with hashes in the lockfile. + #[arg(long)] + generate_hashes: bool, /// Use this pyproject.toml file #[arg(long, value_name = "PYPROJECT_TOML")] pyproject: Option, /// Do not reuse (reset) prior lock options. #[arg(long)] reset: bool, + /// Use universal lock files + #[arg(long)] + universal: bool, } pub fn execute(cmd: Args) -> Result<(), Error> { @@ -71,7 +80,10 @@ pub fn execute(cmd: Args) -> Result<(), Error> { all_features: cmd.all_features, with_sources: cmd.with_sources, reset: cmd.reset, + generate_hashes: cmd.generate_hashes, + universal: cmd.universal, }, + keyring_provider: cmd.keyring_provider, pyproject: cmd.pyproject, })?; Ok(()) diff --git a/rye/src/cli/test.rs b/rye/src/cli/test.rs new file mode 100644 index 0000000000..bb2a0042b1 --- /dev/null +++ b/rye/src/cli/test.rs @@ -0,0 +1,172 @@ +use std::env::consts::EXE_EXTENSION; +use std::ffi::OsString; +use std::path::PathBuf; +use std::process::Command; + +use anyhow::{bail, Error}; +use clap::Parser; +use console::style; +use same_file::is_same_file; + +use crate::config::Config; +use crate::consts::VENV_BIN; +use crate::lock::KeyringProvider; +use crate::pyproject::{locate_projects, normalize_package_name, DependencyKind, PyProject}; +use crate::sync::autosync; +use crate::utils::{CommandOutput, QuietExit}; + +/// Run the tests on the project. +/// +/// Today this will always run `pytest` for all projects. +#[derive(Parser, Debug)] +pub struct Args { + /// Perform the operation on all packages + #[arg(short, long)] + all: bool, + /// Perform the operation on a specific package + #[arg(short, long)] + package: Vec, + /// Use this pyproject.toml file + #[arg(long, value_name = "PYPROJECT_TOML")] + pyproject: Option, + /// Disable test output capture to stdout. + #[arg(long = "no-capture", short = 's')] + no_capture: bool, + /// Enables verbose diagnostics. + #[arg(short, long)] + verbose: bool, + /// Turns off all output. + #[arg(short, long, conflicts_with = "verbose")] + quiet: bool, + /// Extra arguments to pytest + #[arg(last = true)] + extra_args: Vec, + + /// Include pre-releases when automatically syncing the workspace. + #[arg(long)] + pre: bool, + /// Set to `true` to lock with sources in the lockfile when automatically syncing the workspace. + #[arg(long)] + with_sources: bool, + /// Set to `true` to lock with hashes in the lockfile when automatically syncing the workspace. + #[arg(long)] + generate_hashes: bool, + /// Attempt to use `keyring` for authentication for index URLs. + #[arg(long, value_enum, default_value_t)] + keyring_provider: KeyringProvider, +} + +pub fn execute(cmd: Args) -> Result<(), Error> { + let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose); + let project = PyProject::load_or_discover(cmd.pyproject.as_deref())?; + + let mut failed_with = None; + + // when working with workspaces we always want to know what other projects exist. + // for that we locate all those projects and their paths. This is later used to + // prevent accidentally recursing into the wrong projects. + let project_roots = if let Some(workspace) = project.workspace() { + workspace + .iter_projects() + .filter_map(|x| x.ok()) + .map(|x| x.root_path().to_path_buf()) + .collect() + } else { + vec![project.root_path().to_path_buf()] + }; + + let pytest = project + .venv_path() + .join(VENV_BIN) + .join("pytest") + .with_extension(EXE_EXTENSION); + + let projects = locate_projects(project, cmd.all, &cmd.package[..])?; + + if !pytest.is_file() { + let has_pytest = has_pytest_dependency(&projects)?; + if has_pytest { + if Config::current().autosync() { + autosync( + &projects[0], + output, + cmd.pre, + cmd.with_sources, + cmd.generate_hashes, + cmd.keyring_provider, + )?; + } else { + bail!("pytest not installed but in dependencies. Run `rye sync`.") + } + } else { + bail!("pytest not installed. Run `rye add --dev pytest`"); + } + } + + for (idx, project) in projects.iter().enumerate() { + if output != CommandOutput::Quiet { + if idx > 0 { + echo!(); + } + echo!( + "Running tests for {} ({})", + style(project.name().unwrap_or("")).cyan(), + style(project.root_path().display()).dim() + ); + } + + let mut pytest_cmd = Command::new(&pytest); + if cmd.no_capture { + pytest_cmd.arg("--capture=no"); + } + match output { + CommandOutput::Normal => {} + CommandOutput::Verbose => { + pytest_cmd.arg("-v"); + } + CommandOutput::Quiet => { + pytest_cmd.arg("-q"); + } + } + pytest_cmd.args(&cmd.extra_args); + pytest_cmd + .arg("--rootdir") + .arg(project.root_path().as_os_str()) + .current_dir(project.root_path()); + + // always ignore projects that are nested but not selected. + for path in &project_roots { + if !is_same_file(path, project.root_path()).unwrap_or(false) { + pytest_cmd.arg("--ignore").arg(path.as_os_str()); + } + } + + let status = pytest_cmd.status()?; + if !status.success() { + failed_with = Some(status.code().unwrap_or(1)); + } + } + + if let Some(code) = failed_with { + Err(Error::new(QuietExit(code))) + } else { + Ok(()) + } +} + +/// Does any of those projects have a pytest dependency? +fn has_pytest_dependency(projects: &[PyProject]) -> Result { + for project in projects { + for dep in project + .iter_dependencies(DependencyKind::Dev) + .chain(project.iter_dependencies(DependencyKind::Normal)) + { + if let Ok(req) = dep.expand(|name| std::env::var(name).ok()) { + if normalize_package_name(&req.name) == "pytest" { + return Ok(true); + } + } + } + } + Ok(false) +} diff --git a/rye/src/cli/toolchain.rs b/rye/src/cli/toolchain.rs index e35066ea0d..8081abd5a5 100644 --- a/rye/src/cli/toolchain.rs +++ b/rye/src/cli/toolchain.rs @@ -13,7 +13,6 @@ use serde::Deserialize; use serde::Serialize; use crate::installer::list_installed_tools; -use crate::piptools::get_pip_tools_venv_path; use crate::platform::{get_app_dir, get_canonical_py_path, list_known_toolchains}; use crate::pyproject::read_venv_marker; use crate::sources::py::{iter_downloadable, PythonVersion}; @@ -113,23 +112,37 @@ fn register(cmd: RegisterCommand) -> Result<(), Error> { fn check_in_use(ver: &PythonVersion) -> Result<(), Error> { // Check if used by rye itself. let app_dir = get_app_dir(); - for venv in &[app_dir.join("self"), get_pip_tools_venv_path(ver)] { - let venv_marker = read_venv_marker(venv); - if let Some(ref venv_marker) = venv_marker { - if &venv_marker.python == ver { - bail!("toolchain {} is still in use by rye itself", ver); - } + let venv_marker = read_venv_marker(app_dir.join("self").as_path()); + if let Some(ref venv_marker) = venv_marker { + if &venv_marker.python == ver { + bail!("toolchain {} is still in use by rye itself", ver); } } // Check if used by any tool. let installed_tools = list_installed_tools()?; - for (tool, info) in &installed_tools { - if let Some(ref venv_marker) = info.venv_marker { - if &venv_marker.python == ver { - bail!("toolchain {} is still in use by tool {}", ver, tool); + + let toolchain_refs = installed_tools + .iter() + .filter_map(|(tool, info)| { + if let Some(ref venv_marker) = info.venv_marker { + match &venv_marker.python == ver { + true => Some(tool.clone()), + false => None, + } + } else { + None } - } + }) + .collect::>(); + + if !toolchain_refs.is_empty() { + bail!( + "toolchain {} is still in use by tool{} {}", + ver, + if toolchain_refs.len() > 1 { "s:" } else { "" }, + toolchain_refs.join(", ") + ); } Ok(()) diff --git a/rye/src/cli/uninstall.rs b/rye/src/cli/uninstall.rs index 7039b30162..a4a689abeb 100644 --- a/rye/src/cli/uninstall.rs +++ b/rye/src/cli/uninstall.rs @@ -4,10 +4,10 @@ use clap::Parser; use crate::installer::uninstall; use crate::utils::CommandOutput; -/// Uninstalls a global tool. This is an alias of `rye tools uninstall`. +/// Uninstalls a global tool. #[derive(Parser, Debug)] pub struct Args { - /// The package to uninstall + /// The package to uninstall. name: String, /// Enables verbose diagnostics. #[arg(short, long)] diff --git a/rye/src/config.rs b/rye/src/config.rs index c6a1c9fc7c..de0073a086 100644 --- a/rye/src/config.rs +++ b/rye/src/config.rs @@ -6,7 +6,7 @@ use anyhow::{Context, Error}; use once_cell::sync::Lazy; use pep440_rs::Operator; use regex::Regex; -use toml_edit::Document; +use toml_edit::DocumentMut; use crate::platform::{get_app_dir, get_latest_cpython_version}; use crate::pyproject::{BuildSystem, SourceRef, SourceRefType}; @@ -23,7 +23,7 @@ pub fn load() -> Result<(), Error> { Config::from_path(&cfg_path)? } else { Config { - doc: Document::new(), + doc: DocumentMut::new(), path: cfg_path, } }; @@ -33,7 +33,7 @@ pub fn load() -> Result<(), Error> { #[derive(Clone)] pub struct Config { - doc: Document, + doc: DocumentMut, path: PathBuf, } @@ -49,12 +49,16 @@ impl Config { } /// Returns a clone of the internal doc. - pub fn doc_mut(&mut self) -> &mut Document { + pub fn doc_mut(&mut self) -> &mut DocumentMut { &mut self.doc } /// Saves changes back. pub fn save(&self) -> Result<(), Error> { + // try to make the parent folder if it does not exist. ignore the error though. + if let Some(parent) = self.path.parent() { + fs::create_dir_all(parent).ok(); + } fs::write(&self.path, self.doc.to_string()) .path_context(&self.path, "failed to save config")?; Ok(()) @@ -70,7 +74,7 @@ impl Config { let contents = fs::read_to_string(path).path_context(path, "failed to read config")?; Ok(Config { doc: contents - .parse::() + .parse::() .path_context(path, "failed to parse config")?, path: path.to_path_buf(), }) @@ -228,7 +232,8 @@ impl Config { if let Some(sources) = self.doc.get("sources").map(|x| toml::iter_tables(x)) { for source in sources { let source = source.context("invalid value for source in config.toml")?; - let source_ref = SourceRef::from_toml_table(source)?; + let source_ref = SourceRef::from_toml_table(source) + .context("invalid source definition in config.toml")?; if source_ref.name == "default" { need_default = false; } @@ -253,15 +258,28 @@ impl Config { .get("behavior") .and_then(|x| x.get("autosync")) .and_then(|x| x.as_bool()) - .unwrap_or_else(|| self.use_uv()) + .unwrap_or(true) } - /// Indicates if the experimental uv support should be used. + /// Indicates if uv should be used. + /// + /// This setting is deprecated, as pip-tools support was removed in Rye 0.40. pub fn use_uv(&self) -> bool { self.doc .get("behavior") .and_then(|x| x.get("use-uv")) .and_then(|x| x.as_bool()) + .unwrap_or(true) + } + + /// Fetches python installations with build info if possible. + /// + /// This used to be the default behavior in Rye prior to 0.31. + pub fn fetch_with_build_info(&self) -> bool { + self.doc + .get("behavior") + .and_then(|x| x.get("fetch-with-build-info")) + .and_then(|x| x.as_bool()) .unwrap_or(false) } } @@ -393,12 +411,4 @@ author = "John Doe ""#, .iter() .any(|src| src.name == "default" && src.url == "https://pypi.org/simple/")); } - - #[test] - fn test_use_uv() { - let (cfg_path, _temp_dir) = setup_config("[behavior]\nuse-uv = true"); - let cfg = Config::from_path(&cfg_path).expect("Failed to load config"); - // Assuming cfg!(windows) is false in this test environment - assert!(cfg.use_uv()); - } } diff --git a/rye/src/installer.rs b/rye/src/installer.rs index 0994d45457..3e814b7ff5 100644 --- a/rye/src/installer.rs +++ b/rye/src/installer.rs @@ -11,9 +11,10 @@ use regex::Regex; use same_file::is_same_file; use url::Url; -use crate::bootstrap::{ensure_self_venv, fetch}; +use crate::bootstrap::{ensure_self_venv, fetch, FetchOptions}; use crate::config::Config; use crate::consts::VENV_BIN; +use crate::lock::KeyringProvider; use crate::platform::get_app_dir; use crate::pyproject::{normalize_package_name, read_venv_marker, ExpandedSources}; use crate::sources::py::PythonVersionRequest; @@ -22,7 +23,7 @@ use crate::utils::{ get_short_executable_name, get_venv_python_bin, is_executable, symlink_file, CommandOutput, IoPathContext, }; -use crate::uv::Uv; +use crate::uv::{UvBuilder, UvInstallOptions}; const FIND_SCRIPT_SCRIPT: &str = r#" import os @@ -68,7 +69,7 @@ while to_resolve: print(json.dumps(result)) "#; static SUCCESSFULLY_DOWNLOADED_RE: Lazy = - Lazy::new(|| Regex::new("(?m)^Successfully downloaded (.*?)$").unwrap()); + Lazy::new(|| Regex::new("(?m)^Successfully downloaded (.*)").unwrap()); #[derive(Eq, PartialEq)] pub struct ToolInfo { @@ -109,6 +110,7 @@ pub fn install( include_deps: &[String], extra_requirements: &[Requirement], output: CommandOutput, + keyring_provider: KeyringProvider, ) -> Result<(), Error> { let config = Config::current(); let sources = ExpandedSources::from_sources(&config.sources()?)?; @@ -131,7 +133,7 @@ pub fn install( uninstall_helper(&target_venv_path, &shim_dir)?; // make sure we have a compatible python version - let py_ver = fetch(py_ver, output, false)?; + let py_ver = fetch(py_ver, FetchOptions::with_output(output))?; create_virtualenv( output, @@ -141,48 +143,24 @@ pub fn install( requirement.name.as_str(), )?; - let mut cmd = if Config::current().use_uv() { - let mut cmd = Uv::ensure_exists(output)?.cmd(); - cmd.arg("pip") - .arg("install") - .env("VIRTUAL_ENV", &target_venv_path) - .env("PYTHONWARNINGS", "ignore"); - cmd - } else { - let mut cmd = Command::new(self_venv.join(VENV_BIN).join("pip")); - cmd.arg("--python") - .arg(&py) - .arg("install") - .env("PYTHONWARNINGS", "ignore") - .env("PIP_DISABLE_PIP_VERSION_CHECK", "1"); - cmd - }; - - sources.add_as_pip_args(&mut cmd); - if output == CommandOutput::Verbose { - cmd.arg("--verbose"); - } else { - if output == CommandOutput::Quiet { - cmd.arg("-q"); - } - cmd.env("PYTHONWARNINGS", "ignore"); - } - cmd.arg("--").arg(&requirement.to_string()); - - // we don't support versions below 3.7, but for 3.7 we need importlib-metadata - // to be installed - if py_ver.major == 3 && py_ver.minor == 7 { - cmd.arg("importlib-metadata==6.6.0"); - } - - for extra in extra_requirements { - cmd.arg(extra.to_string()); - } - - let status = cmd.status()?; - if !status.success() { + let result = UvBuilder::new() + .with_output(output.quieter()) + .with_sources(sources) + .ensure_exists()? + .venv(&target_venv_path, &py, &py_ver, None)? + .with_output(output) + .install( + &requirement, + UvInstallOptions { + importlib_workaround: py_ver.major == 3 && py_ver.minor == 7, + extras: extra_requirements.to_vec(), + refresh: force, + keyring_provider, + }, + ); + if result.is_err() { uninstall_helper(&target_venv_path, &shim_dir)?; - bail!("tool installation failed"); + return result; } let out = Command::new(py) @@ -397,7 +375,7 @@ fn uninstall_helper(target_venv_path: &Path, shim_dir: &Path) -> Result<(), Erro let script = script?; if let Some(base_name) = script.path().file_name() { let shim_path = shim_dir.join(base_name); - if let Ok(true) = is_same_file(&shim_path, &script.path()) { + if let Ok(true) = is_same_file(&shim_path, script.path()) { fs::remove_file(&shim_path).ok(); } } diff --git a/rye/src/lock.rs b/rye/src/lock.rs index 350a487df2..55966583e4 100644 --- a/rye/src/lock.rs +++ b/rye/src/lock.rs @@ -6,7 +6,8 @@ use std::process::Command; use std::sync::Arc; use std::{env, fmt, fs}; -use anyhow::{anyhow, bail, Context, Error}; +use anyhow::{anyhow, Context, Error}; +use clap::ValueEnum; use minijinja::render; use once_cell::sync::Lazy; use pep508_rs::Requirement; @@ -15,18 +16,16 @@ use serde::Serialize; use tempfile::NamedTempFile; use url::Url; -use crate::config::Config; -use crate::piptools::{get_pip_compile, get_pip_tools_version, PipToolsVersion}; use crate::pyproject::{ normalize_package_name, DependencyKind, ExpandedSources, PyProject, Workspace, }; use crate::sources::py::PythonVersion; -use crate::utils::{set_proxy_variables, CommandOutput, IoPathContext}; -use crate::uv::Uv; +use crate::utils::{CommandOutput, IoPathContext}; +use crate::uv::{UvBuilder, UvPackageUpgrade}; static FILE_EDITABLE_RE: Lazy = Lazy::new(|| Regex::new(r"^-e (file://.*?)\s*$").unwrap()); static DEP_COMMENT_RE: Lazy = - Lazy::new(|| Regex::new(r"^ # (?:(via)|(?:via (.*?))|(?: (.*?)))$").unwrap()); + Lazy::new(|| Regex::new(r"^ # (?:via$|via (.*)| (.*))").unwrap()); static REQUIREMENTS_HEADER: &str = r#"# generated by rye # use `rye lock` or `rye sync` to update this lockfile # @@ -35,10 +34,13 @@ static REQUIREMENTS_HEADER: &str = r#"# generated by rye # features: {{ lock_options.features|tojson }} # all-features: {{ lock_options.all_features|tojson }} # with-sources: {{ lock_options.with_sources|tojson }} +# generate-hashes: {{ lock_options.generate_hashes|tojson }} +# universal: {{ lock_options.universal|tojson }} "#; -static PARAM_RE: Lazy = - Lazy::new(|| Regex::new(r"^# (pre|features|all-features|with_sources):\s*(.*?)$").unwrap()); +static PARAM_RE: Lazy = Lazy::new(|| { + Regex::new(r"^# (pre|features|all-features|with-sources|universal):\s*(.*)").unwrap() +}); #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum LockMode { @@ -59,6 +61,29 @@ impl fmt::Display for LockMode { } } +/// Keyring provider type to use for credential lookup. +#[derive(ValueEnum, Copy, Clone, Serialize, Debug, Default, PartialEq)] +#[value(rename_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum KeyringProvider { + /// Do not use keyring for credential lookup. + #[default] + Disabled, + /// Use the `keyring` command for credential lookup. + Subprocess, +} + +impl KeyringProvider { + pub fn add_as_pip_args(self, cmd: &mut Command) { + match self { + KeyringProvider::Disabled => {} + KeyringProvider::Subprocess => { + cmd.arg("--keyring-provider").arg("subprocess"); + } + } + } +} + /// Controls how locking should work. #[derive(Debug, Clone, Default, Serialize)] pub struct LockOptions { @@ -76,6 +101,10 @@ pub struct LockOptions { pub with_sources: bool, /// Do not reuse (reset) prior lock options. pub reset: bool, + /// Generate hashes in the lock file. + pub generate_hashes: bool, + /// Use universal lock files. + pub universal: bool, } impl LockOptions { @@ -111,7 +140,10 @@ impl LockOptions { "all-features" => { rv.all_features = rv.all_features || serde_json::from_str(value)? } - "with-sources" => rv.with_sources = serde_json::from_str(value)?, + "with-sources" => { + rv.with_sources = rv.with_sources || serde_json::from_str(value)? + } + "universal" => rv.universal = rv.universal || serde_json::from_str(value)?, _ => unreachable!(), } } @@ -126,6 +158,7 @@ impl LockOptions { } /// Creates lockfiles for all projects in the workspace. +#[allow(clippy::too_many_arguments)] pub fn update_workspace_lockfile( py_ver: &PythonVersion, workspace: &Arc, @@ -134,12 +167,12 @@ pub fn update_workspace_lockfile( output: CommandOutput, sources: &ExpandedSources, lock_options: &LockOptions, + keyring_provider: KeyringProvider, ) -> Result<(), Error> { - if output != CommandOutput::Quiet { - echo!("Generating {} lockfile: {}", lock_mode, lockfile.display()); - } + echo!(if output, "Generating {} lockfile: {}", lock_mode, lockfile.display()); - let features_by_project = collect_workspace_features(lock_options); + let lock_options = restore_lock_options(lockfile, lock_options)?; + let features_by_project = collect_workspace_features(&lock_options); let mut req_file = NamedTempFile::new()?; let mut local_projects = HashMap::new(); @@ -185,14 +218,28 @@ pub fn update_workspace_lockfile( req_file.path(), lockfile, sources, - lock_options, + &lock_options, &exclusions, true, + keyring_provider, )?; Ok(()) } +/// Tries to restore the lock options from the given lockfile. +fn restore_lock_options<'o>( + lockfile: &Path, + lock_options: &'o LockOptions, +) -> Result, Error> { + if lockfile.is_file() { + let requirements = fs::read_to_string(lockfile)?; + Ok(LockOptions::restore(&requirements, lock_options)?) + } else { + Ok(Cow::Borrowed(lock_options)) + } +} + fn format_project_extras<'a>( features_by_project: Option<&'a HashMap>>, project: &PyProject, @@ -294,6 +341,7 @@ fn dump_dependencies( } /// Updates the lockfile of the current project. +#[allow(clippy::too_many_arguments)] pub fn update_single_project_lockfile( py_ver: &PythonVersion, pyproject: &PyProject, @@ -302,16 +350,16 @@ pub fn update_single_project_lockfile( output: CommandOutput, sources: &ExpandedSources, lock_options: &LockOptions, + keyring_provider: KeyringProvider, ) -> Result<(), Error> { - if output != CommandOutput::Quiet { - echo!("Generating {} lockfile: {}", lock_mode, lockfile.display()); - } + echo!(if output, "Generating {} lockfile: {}", lock_mode, lockfile.display()); + let lock_options = restore_lock_options(lockfile, lock_options)?; let mut req_file = NamedTempFile::new()?; // virtual packages are themselves not installed if !pyproject.is_virtual() { - let features_by_project = collect_workspace_features(lock_options); + let features_by_project = collect_workspace_features(&lock_options); let applicable_extras = format_project_extras(features_by_project.as_ref(), pyproject)?; writeln!( req_file, @@ -340,9 +388,10 @@ pub fn update_single_project_lockfile( req_file.path(), lockfile, sources, - lock_options, + &lock_options, &exclusions, false, + keyring_provider, )?; Ok(()) @@ -358,94 +407,42 @@ fn generate_lockfile( sources: &ExpandedSources, lock_options: &LockOptions, exclusions: &HashSet, - no_deps: bool, + _no_deps: bool, + keyring_provider: KeyringProvider, ) -> Result<(), Error> { - let use_uv = Config::current().use_uv(); let scratch = tempfile::tempdir()?; let requirements_file = scratch.path().join("requirements.txt"); - let lock_options = if lockfile.is_file() { - let requirements = fs::read_to_string(lockfile)?; - fs::write(&requirements_file, &requirements) + if lockfile.is_file() { + fs::copy(lockfile, &requirements_file) .path_context(&requirements_file, "unable to restore requirements file")?; - LockOptions::restore(&requirements, lock_options)? - } else { - if !use_uv { - fs::write(&requirements_file, b"").path_context( - &requirements_file, - "unable to write empty requirements file", - )?; - } - Cow::Borrowed(lock_options) }; - let mut cmd = if use_uv { - let mut cmd = Uv::ensure_exists(output)?.cmd(); - cmd.arg("pip") - .arg("compile") - .arg("--no-header") - .env_remove("VIRTUAL_ENV") - .arg(format!( - "--python-version={}.{}.{}", - py_ver.major, py_ver.minor, py_ver.patch - )); - - if lock_options.pre { - cmd.arg("--prerelease=allow"); - } - // this primarily exists for testing - if let Ok(dt) = env::var("__RYE_UV_EXCLUDE_NEWER") { - cmd.arg("--exclude-newer").arg(dt); - } - cmd - } else { - let mut cmd = Command::new(get_pip_compile(py_ver, output)?); - // legacy pip tools requires some extra parameters - if get_pip_tools_version(py_ver) == PipToolsVersion::Legacy { - cmd.arg("--resolver=backtracking"); - } - cmd.arg("--strip-extras") - .arg("--allow-unsafe") - .arg("--no-header") - .arg("--annotate") - .arg("--pip-args") - .arg(format!( - "--python-version=\"{}.{}.{}\"{}", - py_ver.major, - py_ver.minor, - py_ver.patch, - if no_deps { " --no-deps" } else { "" } - )); - if lock_options.pre { - cmd.arg("--pre"); + let upgrade = { + if lock_options.update_all { + UvPackageUpgrade::All + } else if !lock_options.update.is_empty() { + UvPackageUpgrade::Packages(lock_options.update.clone()) + } else { + UvPackageUpgrade::Nothing } - cmd }; - cmd.arg(if output == CommandOutput::Verbose { - "--verbose" - } else { - "-q" - }) - .arg("-o") - .arg(&requirements_file) - .arg(requirements_file_in) - .current_dir(workspace_path) - .env("PYTHONWARNINGS", "ignore") - .env("PROJECT_ROOT", make_project_root_fragment(workspace_path)); - - for pkg in &lock_options.update { - cmd.arg("--upgrade-package"); - cmd.arg(pkg); - } - if lock_options.update_all { - cmd.arg("--upgrade"); - } - sources.add_as_pip_args(&mut cmd); - set_proxy_variables(&mut cmd); - let status = cmd.status().context("unable to run pip-compile")?; - if !status.success() { - bail!("failed to generate lockfile"); - }; + UvBuilder::new() + .with_output(output.quieter()) + .with_sources(sources.clone()) + .with_workdir(workspace_path) + .ensure_exists()? + .lockfile( + py_ver, + requirements_file_in, + &requirements_file, + lock_options.pre, + env::var("__RYE_UV_EXCLUDE_NEWER").ok(), + upgrade, + keyring_provider, + lock_options.generate_hashes, + lock_options.universal, + )?; finalize_lockfile( &requirements_file, @@ -453,7 +450,7 @@ fn generate_lockfile( workspace_path, exclusions, sources, - &lock_options, + lock_options, )?; Ok(()) @@ -477,12 +474,21 @@ fn finalize_lockfile( writeln!(rv)?; } + let mut exclude = false; for line in fs::read_to_string(generated) .path_context(generated, "unable to parse resolver output")? .lines() { - // we deal with this explicitly. - if line.trim().is_empty() + let trimmed = line.trim(); + + // if we're in an exclusion, continue to skip lines + if exclude { + exclude = trimmed.ends_with('\\'); + continue; + } + + // we deal with these settings explicitly. + if trimmed.is_empty() || line.starts_with("--index-url ") || line.starts_with("--extra-index-url ") || line.starts_with("--find-links ") @@ -490,37 +496,48 @@ fn finalize_lockfile( continue; } + // Strip trailing backslashes. + let stripped = trimmed + .strip_suffix('\\') + .map_or(trimmed, |rest| rest.trim()); + if let Some(m) = FILE_EDITABLE_RE.captures(line) { let url = Url::parse(&m[1]).context("invalid editable URL generated")?; if url.scheme() == "file" { let rel_url = make_relative_url(Path::new(url.path()), workspace_root)?; - writeln!(rv, "-e {}", rel_url)?; + writeln!(rv, "-e {rel_url}")?; continue; } - } else if let Ok(ref req) = line.trim().parse::() { + } else if let Ok(ref req) = stripped.parse::() { // TODO: this does not evaluate markers if exclusions.iter().any(|x| { normalize_package_name(&x.name) == normalize_package_name(&req.name) && (x.version_or_url.is_none() || x.version_or_url == req.version_or_url) }) { - // skip exclusions - writeln!(rv, "# {} (excluded)", line)?; + // skip exclusions. + writeln!(rv, "# {stripped} (excluded)")?; + + // if the exclusion is followed by hashes, we need to comment out the hashes too. + if trimmed.ends_with('\\') { + exclude = true; + } + continue; } } else if let Some(m) = DEP_COMMENT_RE.captures(line) { - if let Some(dep) = m.get(2).or_else(|| m.get(3)).map(|x| x.as_str()) { + if let Some(dep) = m.get(1).or_else(|| m.get(2)).map(|x| x.as_str()) { if !dep.starts_with("-r ") { // we cannot tell today based on the output where this comes from. This // can show up because it's a root dependency, because it's a dev dependency // or in some cases just because we declared it as a duplicate. - writeln!(rv, " # via {}", dep)?; + writeln!(rv, " # via {dep}")?; } }; continue; } else if line.starts_with('#') { continue; } - writeln!(rv, "{}", line)?; + writeln!(rv, "{line}")?; } Ok(()) } diff --git a/rye/src/main.rs b/rye/src/main.rs index 7976506914..6eb2fe754a 100644 --- a/rye/src/main.rs +++ b/rye/src/main.rs @@ -1,6 +1,6 @@ -use std::process; use std::sync::atomic::{AtomicBool, Ordering}; +use crate::utils::panic::trap_bad_pipe; use crate::utils::QuietExit; #[macro_use] @@ -12,7 +12,6 @@ mod config; mod consts; mod installer; mod lock; -mod piptools; mod platform; mod pyproject; mod sources; @@ -34,6 +33,8 @@ pub fn disable_ctrlc_handler() { } pub fn main() { + crate::utils::panic::set_panic_hook(); + ctrlc::set_handler(move || { if !DISABLE_CTRLC_HANDLER.load(Ordering::Relaxed) { let term = console::Term::stderr(); @@ -48,26 +49,27 @@ pub fn main() { }) .unwrap(); - let result = cli::execute(); - let status = match result { - Ok(()) => 0, - Err(err) => { - if let Some(err) = err.downcast_ref::() { - err.print().ok(); - err.exit_code() - } else if let Some(QuietExit(code)) = err.downcast_ref() { - *code - } else { - error!("{:?}", err); - 1 + trap_bad_pipe(|| { + let result = cli::execute(); + let status = match result { + Ok(()) => 0, + Err(err) => { + if let Some(err) = err.downcast_ref::() { + err.print().ok(); + err.exit_code() + } else if let Some(QuietExit(code)) = err.downcast_ref() { + *code + } else { + error!("{:?}", err); + 1 + } } - } - }; + }; - if SHOW_CONTINUE_PROMPT.load(Ordering::Relaxed) { - echo!("Press any key to continue"); - console::Term::buffered_stderr().read_key().ok(); - } - - process::exit(status); + if SHOW_CONTINUE_PROMPT.load(Ordering::Relaxed) { + echo!("Press any key to continue"); + console::Term::buffered_stderr().read_key().ok(); + } + status + }); } diff --git a/rye/src/piptools.rs b/rye/src/piptools.rs deleted file mode 100644 index 0c0b4aa930..0000000000 --- a/rye/src/piptools.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::fs; -use std::path::PathBuf; -use std::process::Command; - -use anyhow::{bail, Context, Error}; - -use crate::bootstrap::ensure_self_venv; -use crate::consts::VENV_BIN; -use crate::platform::get_app_dir; -use crate::sources::py::PythonVersion; -use crate::sync::create_virtualenv; -use crate::utils::{get_venv_python_bin, CommandOutput, IoPathContext}; - -// When changing these, also update `SELF_VERSION` in bootstrap.rs to ensure -// that the internals are re-created. -pub const LATEST_PIP: &str = "pip==23.3.2"; -const PIP_TOOLS_LATEST_REQ: &[&str] = &[LATEST_PIP, "pip-tools==7.3.0"]; -const PIP_TOOLS_LEGACY_REQ: &[&str] = &["pip==22.2.0", "pip-tools==6.14.0"]; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -/// Which version of piptools are in use? -pub enum PipToolsVersion { - Latest, - Legacy, -} - -impl PipToolsVersion { - fn requirements(&self) -> &'static [&'static str] { - match *self { - PipToolsVersion::Latest => PIP_TOOLS_LATEST_REQ, - PipToolsVersion::Legacy => PIP_TOOLS_LEGACY_REQ, - } - } -} - -fn get_pip_tools_bin(py_ver: &PythonVersion, output: CommandOutput) -> Result { - let self_venv = ensure_self_venv(output)?; - let venv = get_pip_tools_venv_path(py_ver); - - let py = get_venv_python_bin(&venv); - let version = get_pip_tools_version(py_ver); - - // if we have a python interpreter in the given path, let's use it - if venv.join(&py).is_file() { - return Ok(venv); - } - - // if however for some reason the virtualenv itself is already a folder - // it usually means that the symlink to the python is bad now. This can - // happen if someone wiped the toolchain of the pip-tools version. In - // that case wipe it first. - if venv.is_dir() { - fs::remove_dir_all(&venv) - .path_context(&venv, "unable to wipe old virtualenv for pip-tools")?; - } - - if output != CommandOutput::Quiet { - echo!("Creating virtualenv for pip-tools"); - } - create_virtualenv(output, &self_venv, py_ver, &venv, "pip-tools")?; - - let mut cmd = Command::new(self_venv.join(VENV_BIN).join("pip")); - cmd.arg("--python") - .arg(&py) - .arg("install") - .arg("--upgrade") - .args(version.requirements()) - .arg("-q") - .env("PIP_DISABLE_PIP_VERSION_CHECK", "1"); - if output == CommandOutput::Verbose { - cmd.arg("--verbose"); - } else { - cmd.arg("--quiet"); - cmd.env("PYTHONWARNINGS", "ignore"); - } - let status = cmd.status().context("unable to install pip-tools")?; - if !status.success() { - bail!("failed to initialize pip-tools venv (install dependencies)"); - } - Ok(venv) -} - -pub fn get_pip_tools_version(py_ver: &PythonVersion) -> PipToolsVersion { - if py_ver.major == 3 && py_ver.minor == 7 { - PipToolsVersion::Legacy - } else { - PipToolsVersion::Latest - } -} - -pub fn get_pip_tools_venv_path(py_ver: &PythonVersion) -> PathBuf { - let key = format!("{}@{}.{}", py_ver.name, py_ver.major, py_ver.minor); - get_app_dir().join("pip-tools").join(key) -} - -pub fn get_pip_sync(py_ver: &PythonVersion, output: CommandOutput) -> Result { - Ok(get_pip_tools_bin(py_ver, output)? - .join(VENV_BIN) - .join("pip-sync")) -} - -pub fn get_pip_compile(py_ver: &PythonVersion, output: CommandOutput) -> Result { - Ok(get_pip_tools_bin(py_ver, output)? - .join(VENV_BIN) - .join("pip-compile")) -} diff --git a/rye/src/platform.rs b/rye/src/platform.rs index 017b50b7fe..145c4ac4aa 100644 --- a/rye/src/platform.rs +++ b/rye/src/platform.rs @@ -62,7 +62,7 @@ pub fn get_canonical_py_path(version: &PythonVersion) -> Result /// Returns the path of the python binary for the given version. pub fn get_toolchain_python_bin(version: &PythonVersion) -> Result { - let mut p = get_canonical_py_path(version)?; + let p = get_canonical_py_path(version)?; // It's permissible to link Python binaries directly in two ways. It can either be // a symlink in which case it's used directly, it can be a non-executable text file @@ -83,26 +83,31 @@ pub fn get_toolchain_python_bin(version: &PythonVersion) -> Result PathBuf { + let mut path = path.to_path_buf(); // we support install/bin/python, install/python and bin/python - p.push("install"); - if !p.is_dir() { - p.pop(); + path.push("install"); + if !path.is_dir() { + path.pop(); } - p.push("bin"); - if !p.is_dir() { - p.pop(); + path.push("bin"); + if !path.is_dir() { + path.pop(); } #[cfg(unix)] { - p.push("python3"); + path.push("python3"); } #[cfg(windows)] { - p.push("python.exe"); + path.push("python.exe"); } - - Ok(p) + path } /// Returns a pinnable version for this version request. @@ -182,7 +187,7 @@ pub fn get_default_author_with_fallback(dir: &PathBuf) -> Option<(String, String .arg("config") .arg("--get-regexp") .current_dir(dir) - .arg("^user.(name|email)$") + .arg(r"^user\.(name|email)$") .stdout(Stdio::piped()) .output() { @@ -212,8 +217,7 @@ pub fn get_python_version_request_from_pyenv_pin(root: &Path) -> Option Option Option { + // Skip empty lines and comments. + let ver = contents.lines().find(|line| { + let trimmed = line.trim(); + !(trimmed.is_empty() || trimmed.starts_with('#')) + })?; + + // Parse the version. + let ver = ver.parse().ok()?; + + Some(ver) +} + /// Returns the most recent cpython release. pub fn get_latest_cpython_version() -> Result { latest_available_python_version(&PythonVersionRequest { @@ -249,7 +267,7 @@ pub fn get_latest_cpython_version() -> Result { /// [pypi] /// token = "" /// ``` -pub fn get_credentials() -> Result { +pub fn get_credentials() -> Result { let filepath = get_credentials_filepath()?; // If a credentials file doesn't exist create an empty one. TODO: Move to bootstrapping? @@ -258,13 +276,13 @@ pub fn get_credentials() -> Result { } let doc = fs::read_to_string(&filepath)? - .parse::() + .parse::() .path_context(&filepath, "failed to parse credentials")?; Ok(doc) } -pub fn write_credentials(doc: &toml_edit::Document) -> Result<(), Error> { +pub fn write_credentials(doc: &toml_edit::DocumentMut) -> Result<(), Error> { let path = get_credentials_filepath()?; std::fs::write(&path, doc.to_string()) .path_context(&path, "unable to write to the credentials file") @@ -273,3 +291,22 @@ pub fn write_credentials(doc: &toml_edit::Document) -> Result<(), Error> { pub fn get_credentials_filepath() -> Result { Ok(get_app_dir().join("credentials")) } + +#[cfg(test)] +mod test { + + #[test] + fn test_read_python_version() { + // Parse a simple version. + let ver = super::read_python_version("3.8.1\n"); + assert_eq!(ver, Some("3.8.1".parse().unwrap())); + + // Skip empty lines. + let ver = super::read_python_version("\n\n3.8.1\n"); + assert_eq!(ver, Some("3.8.1".parse().unwrap())); + + // Skip comments. + let ver = super::read_python_version("# comment\n3.8.1\n"); + assert_eq!(ver, Some("3.8.1".parse().unwrap())); + } +} diff --git a/rye/src/pyproject.rs b/rye/src/pyproject.rs index 3e5f4f6baa..4504d32a0c 100644 --- a/rye/src/pyproject.rs +++ b/rye/src/pyproject.rs @@ -30,7 +30,7 @@ use pep508_rs::Requirement; use python_pkginfo::Metadata; use regex::Regex; use serde::Serialize; -use toml_edit::{Array, Document, Formatted, Item, Table, TableLike, Value}; +use toml_edit::{Array, DocumentMut, Formatted, Item, Table, TableLike, Value}; use url::Url; static NORMALIZATION_SPLIT_RE: Lazy = Lazy::new(|| Regex::new(r"[-_.]+").unwrap()); @@ -58,7 +58,7 @@ pub enum DependencyKind<'a> { Optional(Cow<'a, str>), } -impl<'a> fmt::Display for DependencyKind<'a> { +impl fmt::Display for DependencyKind<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DependencyKind::Normal => f.write_str("regular"), @@ -152,14 +152,15 @@ impl SourceRef { .get("name") .and_then(|x| x.as_str()) .map(|x| x.to_string()) - .ok_or_else(|| anyhow!("expected name"))?; + .ok_or_else(|| anyhow!("expected source.name"))?; let url = source .get("url") .and_then(|x| x.as_str()) .map(|x| x.to_string()) - .ok_or_else(|| anyhow!("expected url"))?; + .ok_or_else(|| anyhow!("expected source.url"))?; let verify_ssl = source .get("verify_ssl") + .or_else(|| source.get("verify-ssl")) .and_then(|x| x.as_bool()) .unwrap_or(true); let username = source @@ -174,7 +175,7 @@ impl SourceRef { .get("type") .and_then(|x| x.as_str()) .map_or(Ok(SourceRefType::Index), |x| x.parse::()) - .context("invalid value for type")?; + .context("invalid value for source.type")?; Ok(SourceRef { name, url, @@ -203,14 +204,15 @@ impl SourceRef { } type EnvVars = HashMap; +type EnvFile = Option; /// A reference to a script #[derive(Clone, Debug)] pub enum Script { /// Call python module entry - Call(String, EnvVars), + Call(String, EnvVars, EnvFile), /// A command alias - Cmd(Vec, EnvVars), + Cmd(Vec, EnvVars, EnvFile), /// A multi-script execution Chain(Vec>), /// External script reference @@ -257,11 +259,19 @@ impl Script { env_vars } + fn get_env_file(detailed: &dyn TableLike) -> EnvFile { + detailed + .get("env-file") + .and_then(|x| x.as_str()) + .map(PathBuf::from) + } + if let Some(detailed) = item.as_table_like() { if let Some(call) = detailed.get("call") { let entry = call.as_str()?.to_string(); let env_vars = get_env_vars(detailed); - Some(Script::Call(entry, env_vars)) + let env_file = get_env_file(detailed); + Some(Script::Call(entry, env_vars, env_file)) } else if let Some(cmds) = detailed.get("chain").and_then(|x| x.as_array()) { Some(Script::Chain( cmds.iter().flat_map(toml_value_as_command_args).collect(), @@ -269,13 +279,14 @@ impl Script { } else if let Some(cmd) = detailed.get("cmd") { let cmd = toml_value_as_command_args(cmd.as_value()?)?; let env_vars = get_env_vars(detailed); - Some(Script::Cmd(cmd, env_vars)) + let env_file = get_env_file(detailed); + Some(Script::Cmd(cmd, env_vars, env_file)) } else { None } } else { toml_value_as_command_args(item.as_value()?) - .map(|cmd| Script::Cmd(cmd, EnvVars::default())) + .map(|cmd| Script::Cmd(cmd, EnvVars::default(), None)) } } } @@ -288,7 +299,7 @@ fn shlex_quote_unsafe(s: &str) -> Cow<'_, str> { impl fmt::Display for Script { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Script::Call(entry, env) => { + Script::Call(entry, env, env_file) => { write!(f, "{}", shlex_quote_unsafe(entry))?; if !env.is_empty() { write!(f, " (env: ")?; @@ -305,9 +316,12 @@ impl fmt::Display for Script { } write!(f, ")")?; } + if let Some(ref env_file) = env_file { + write!(f, " (env-file: {})", env_file.display())?; + } Ok(()) } - Script::Cmd(args, env) => { + Script::Cmd(args, env, env_file) => { let mut need_space = false; for (key, value) in env.iter() { if need_space { @@ -328,6 +342,9 @@ impl fmt::Display for Script { write!(f, "{}", shlex_quote_unsafe(arg))?; need_space = true; } + if let Some(ref env_file) = env_file { + write!(f, " (env-file: {})", env_file.display())?; + } Ok(()) } Script::Chain(cmds) => { @@ -355,13 +372,13 @@ impl fmt::Display for Script { #[derive(Debug)] pub struct Workspace { root: PathBuf, - doc: Document, + doc: DocumentMut, members: Option>, } impl Workspace { /// Loads a workspace from a pyproject.toml and path - fn try_load_from_toml(doc: &Document, path: &Path) -> Option { + fn try_load_from_toml(doc: &DocumentMut, path: &Path) -> Option { doc.get("tool") .and_then(|x| x.get("rye")) .and_then(|x| x.get("workspace")) @@ -389,7 +406,7 @@ impl Workspace { let project_file = here.join("pyproject.toml"); if project_file.is_file() { if let Ok(contents) = fs::read_to_string(&project_file) { - if let Ok(doc) = contents.parse::() { + if let Ok(doc) = contents.parse::() { if let Some(workspace) = Workspace::try_load_from_toml(&doc, here) { return Some(workspace); } @@ -519,6 +536,16 @@ impl Workspace { is_rye_managed(&self.doc) } + /// Should requirements.txt based locking include generating hashes? + pub fn generate_hashes(&self) -> bool { + generate_hashes(&self.doc) + } + + /// Should requirements.txt based locking be universal + pub fn universal(&self) -> bool { + universal(&self.doc) + } + /// Should requirements.txt based locking include a find-links reference? pub fn lock_with_sources(&self) -> bool { lock_with_sources(&self.doc) @@ -549,7 +576,14 @@ pub struct PyProject { root: PathBuf, basename: OsString, workspace: Option>, - doc: Document, + doc: DocumentMut, +} + +/// Returns an implicit table. +fn implicit() -> Item { + let mut table = Table::new(); + table.set_implicit(true); + Item::Table(table) } impl PyProject { @@ -578,7 +612,7 @@ impl PyProject { let root = filename.parent().unwrap_or(Path::new(".")); let doc = fs::read_to_string(filename) .path_context(filename, "failed to read pyproject.toml")? - .parse::() + .parse::() .path_context(filename, "failed to parse pyproject.toml")?; let mut workspace = Workspace::try_load_from_toml(&doc, root).map(Arc::new); @@ -618,7 +652,7 @@ impl PyProject { ) -> Result, Error> { let root = filename.parent().unwrap_or(Path::new(".")); let doc = fs::read_to_string(filename)? - .parse::() + .parse::() .with_context(|| { format!( "failed to parse pyproject.toml from '{}' in context of workspace {}", @@ -806,12 +840,21 @@ impl PyProject { .get("build-system") .and_then(|x| x.get("build-backend")) .and_then(|x| x.as_str()); - match backend { + let build_system = match backend { Some("hatchling.build") => Some(BuildSystem::Hatchling), Some("setuptools.build_meta") => Some(BuildSystem::Setuptools), Some("flit_core.buildapi") => Some(BuildSystem::Flit), Some("pdm.backend") => Some(BuildSystem::Pdm), _ => None, + }; + if self.is_virtual() && build_system.is_some() { + warn!( + "project '{}' is virtual but defines build-system", + self.name().unwrap_or("") + ); + None + } else { + build_system } } /// Looks up a script @@ -876,8 +919,14 @@ impl PyProject { ) -> Result<(), Error> { let dependencies = match kind { DependencyKind::Normal => &mut self.doc["project"]["dependencies"], - DependencyKind::Dev => &mut self.doc["tool"]["rye"]["dev-dependencies"], - DependencyKind::Excluded => &mut self.doc["tool"]["rye"]["excluded-dependencies"], + DependencyKind::Dev => self + .obtain_tool_config_table()? + .entry("dev-dependencies") + .or_insert(Item::Value(Value::Array(Array::new()))), + DependencyKind::Excluded => self + .obtain_tool_config_table()? + .entry("excluded-dependencies") + .or_insert(Item::Value(Value::Array(Array::new()))), DependencyKind::Optional(ref section) => { // add this as a proper non-inline table if it's missing let table = &mut self.doc["project"]["optional-dependencies"]; @@ -907,8 +956,14 @@ impl PyProject { ) -> Result, Error> { let dependencies = match kind { DependencyKind::Normal => &mut self.doc["project"]["dependencies"], - DependencyKind::Dev => &mut self.doc["tool"]["rye"]["dev-dependencies"], - DependencyKind::Excluded => &mut self.doc["tool"]["rye"]["excluded-dependencies"], + DependencyKind::Dev => self + .obtain_tool_config_table()? + .entry("dev-dependencies") + .or_insert(Item::Value(Value::Array(Array::new()))), + DependencyKind::Excluded => self + .obtain_tool_config_table()? + .entry("excluded-dependencies") + .or_insert(Item::Value(Value::Array(Array::new()))), DependencyKind::Optional(ref section) => { &mut self.doc["project"]["optional-dependencies"][section as &str] } @@ -981,7 +1036,23 @@ impl PyProject { .unwrap_or(false) } - /// Should requirements.txt based locking include a find-links reference? + /// Should requirements.txt-based locking include generating hashes? + pub fn generate_hashes(&self) -> bool { + match self.workspace { + Some(ref workspace) => workspace.generate_hashes(), + None => generate_hashes(&self.doc), + } + } + + /// Should requirements.txt-based locking be universal? + pub fn universal(&self) -> bool { + match self.workspace { + Some(ref workspace) => workspace.universal(), + None => universal(&self.doc), + } + } + + /// Should requirements.txt-based locking include a find-links reference? pub fn lock_with_sources(&self) -> bool { match self.workspace { Some(ref workspace) => workspace.lock_with_sources(), @@ -995,6 +1066,19 @@ impl PyProject { fs::write(&path, self.doc.to_string()).path_context(&path, "unable to write changes")?; Ok(()) } + + /// Gets or creates the [tool.rye] table in pyproject.toml + fn obtain_tool_config_table(&mut self) -> Result<&mut Table, Error> { + self.doc + .entry("tool") + .or_insert(implicit()) + .as_table_mut() + .ok_or(anyhow!("[tool.rye] in pyproject.toml is malformed"))? + .entry("rye") + .or_insert(implicit()) + .as_table_mut() + .ok_or(anyhow!("[tool.rye] in pyproject.toml is malformed")) + } } pub fn normalize_package_name(x: &str) -> String { @@ -1115,7 +1199,7 @@ pub fn latest_available_python_version( } fn resolve_target_python_version( - doc: &Document, + doc: &DocumentMut, root: &Path, venv_path: &Path, ) -> Option { @@ -1126,7 +1210,7 @@ fn resolve_target_python_version( } fn resolve_intended_venv_python_version( - doc: &Document, + doc: &DocumentMut, root: &Path, ) -> Result { let requested_version = get_python_version_request_from_pyenv_pin(root) @@ -1152,7 +1236,7 @@ fn resolve_intended_venv_python_version( } } -fn resolve_lower_bound_python_version(doc: &Document) -> Option { +fn resolve_lower_bound_python_version(doc: &DocumentMut) -> Option { doc.get("project") .and_then(|x| x.get("requires-python")) .and_then(|x| x.as_str()) @@ -1215,7 +1299,7 @@ fn is_unsafe_script(path: &Path) -> bool { } } -fn get_sources(doc: &Document) -> Result, Error> { +fn get_sources(doc: &DocumentMut) -> Result, Error> { let cfg = Config::current(); let mut rv = Vec::new(); @@ -1227,7 +1311,8 @@ fn get_sources(doc: &Document) -> Result, Error> { { for source in sources { let source = source.context("invalid value for pyproject.toml's tool.rye.sources")?; - let source_ref = SourceRef::from_toml_table(source)?; + let source_ref = SourceRef::from_toml_table(source) + .context("invalid source definition in pyproject.toml")?; rv.push(source_ref); } } @@ -1244,7 +1329,7 @@ fn get_sources(doc: &Document) -> Result, Error> { Ok(rv) } -fn is_rye_managed(doc: &Document) -> bool { +fn is_rye_managed(doc: &DocumentMut) -> bool { if Config::current().force_rye_managed() { return true; } @@ -1255,7 +1340,23 @@ fn is_rye_managed(doc: &Document) -> bool { .unwrap_or(false) } -fn lock_with_sources(doc: &Document) -> bool { +fn generate_hashes(doc: &DocumentMut) -> bool { + doc.get("tool") + .and_then(|x| x.get("rye")) + .and_then(|x| x.get("generate-hashes")) + .and_then(|x| x.as_bool()) + .unwrap_or(false) +} + +fn universal(doc: &DocumentMut) -> bool { + doc.get("tool") + .and_then(|x| x.get("rye")) + .and_then(|x| x.get("universal")) + .and_then(|x| x.as_bool()) + .unwrap_or(false) +} + +fn lock_with_sources(doc: &DocumentMut) -> bool { doc.get("tool") .and_then(|x| x.get("rye")) .and_then(|x| x.get("lock-with-sources")) @@ -1274,6 +1375,7 @@ fn get_project_metadata(path: &Path) -> Result { } serde_json::from_slice(&metadata.stdout).map_err(Into::into) } + /// Represents expanded sources. #[derive(Debug, Clone, Serialize)] pub struct ExpandedSources { @@ -1283,6 +1385,14 @@ pub struct ExpandedSources { } impl ExpandedSources { + pub fn empty() -> ExpandedSources { + ExpandedSources { + index_urls: Vec::new(), + find_links: Vec::new(), + trusted_hosts: HashSet::new(), + } + } + /// Takes some sources and expands them. pub fn from_sources(sources: &[SourceRef]) -> Result { let mut index_urls = Vec::new(); @@ -1311,17 +1421,25 @@ impl ExpandedSources { /// Attach common pip args to a command. pub fn add_as_pip_args(&self, cmd: &mut Command) { - for (url, default) in self.index_urls.iter() { - if *default { - cmd.arg("--index-url"); - } else { - cmd.arg("--extra-index-url"); - } - cmd.arg(&url.to_string()); + for url in self + .index_urls + .iter() + .filter_map(|(url, default)| if *default { Some(url) } else { None }) + { + cmd.arg("--index-url"); + cmd.arg(url.to_string()); + } + for url in self + .index_urls + .iter() + .filter_map(|(url, default)| if *default { None } else { Some(url) }) + { + cmd.arg("--extra-index-url"); + cmd.arg(url.to_string()); } for link in &self.find_links { cmd.arg("--find-links"); - cmd.arg(&link.to_string()); + cmd.arg(link.to_string()); } for host in &self.trusted_hosts { cmd.arg("--trusted-host"); @@ -1331,18 +1449,25 @@ impl ExpandedSources { /// Write the sources to a lockfile. pub fn add_to_lockfile(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> { - for (url, default) in self.index_urls.iter() { - if *default { - writeln!(out, "--index-url {}", url)?; - } else { - writeln!(out, "--extra-index-url {}", url)?; - } + for url in self + .index_urls + .iter() + .filter_map(|(url, default)| if *default { Some(url) } else { None }) + { + writeln!(out, "--index-url {url}")?; + } + for url in self + .index_urls + .iter() + .filter_map(|(url, default)| if *default { None } else { Some(url) }) + { + writeln!(out, "--extra-index-url {url}")?; } for link in &self.find_links { - writeln!(out, "--find-links {}", link)?; + writeln!(out, "--find-links {link}")?; } for host in &self.trusted_hosts { - writeln!(out, "--trusted-host {}", host)?; + writeln!(out, "--trusted-host {host}")?; } Ok(()) } @@ -1412,5 +1537,8 @@ pub fn locate_projects( } } } + + projects.sort_by(|a, b| a.name().cmp(&b.name())); + Ok(projects) } diff --git a/rye/src/sources/generated/python_downloads.inc b/rye/src/sources/generated/python_downloads.inc index 8e339fd86d..53213a89d6 100644 --- a/rye/src/sources/generated/python_downloads.inc +++ b/rye/src/sources/generated/python_downloads.inc @@ -2,6 +2,12 @@ // To regenerate, run `rye run find-downloads > rye/src/sources/generated/python_downloads.inc` from the root of the repository. use std::borrow::Cow; pub const PYTHON_VERSIONS: &[(PythonVersion, &str, Option<&str>)] = &[ + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 14, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.17-aarch64.tar.bz2", Some("53b6e5907df869c49e4eae7aca09fba16d150741097efb245892c1477d2395f2")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 14, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.17-macos_arm64.tar.bz2", Some("a050e25e8d686853dd5afc363e55625165825dacfb55f8753d8225ebe417cfd2")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 14, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.17-linux32.tar.bz2", Some("e534110e1047da37c1d586c392f74de3424f871d906a2083de6d41f2a8cc9164")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 14, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.17-linux64.tar.bz2", Some("fdcdb9b24f1a7726003586503fdeb264fd68fc37fbfcea022dcfe825a7fee18b")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 14, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.17-macos_x86_64.tar.bz2", Some("6c2c5f2300d7564e711421b4968abd63243cb96f76e363975dd648ebf4a362ee")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 14, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.17-win64.zip", Some("cab794a03ddda26238c72942ea6f225612e0dc17c76cac6652da83a95024e6e8")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 13, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.15-aarch64.tar.bz2", Some("52146fccaf64e87e71d178dda8de63c01577ec3923073dc69e1519622bcacb74")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 13, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.15-macos_arm64.tar.bz2", Some("d927c5105ea7880f7596fe459183e35cc17c853ef5105678b2ad62a8d000a548")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 13, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.15-linux32.tar.bz2", Some("75dd58c9abd8b9d78220373148355bc3119febcf27a2c781d64ad85e7232c4aa")), @@ -14,6 +20,12 @@ pub const PYTHON_VERSIONS: &[(PythonVersion, &str, Option<&str>)] = &[ (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 12, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.12-linux64.tar.bz2", Some("6c577993160b6f5ee8cab73cd1a807affcefafe2f7441c87bd926c10505e8731")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 12, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.12-macos_x86_64.tar.bz2", Some("dbc15d8570560d5f79366883c24bc42231a92855ac19a0f28cb0adeb11242666")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 12, suffix: None }, "https://downloads.python.org/pypy/pypy3.10-v7.3.12-win64.zip", Some("8c3b1d34fb99100e230e94560410a38d450dc844effbee9ea183518e4aff595c")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 19, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.16-aarch64.tar.bz2", Some("de3f2ed3581b30555ac0dd3e4df78a262ec736a36fb2e8f28259f8539b278ef4")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 19, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2", Some("88f824e7a2d676440d09bc90fc959ae0fd3557d7e2f14bfbbe53d41d159a47fe")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 19, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.16-linux32.tar.bz2", Some("583b6d6dd4e8c07cbc04da04a7ec2bdfa6674825289c2378c5e018d5abe779ea")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 19, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.16-linux64.tar.bz2", Some("16f9c5b808c848516e742986e826b833cdbeda09ad8764e8704595adbe791b23")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 19, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2", Some("fda015431621e7e5aa16359d114f2c45a77ed936992c1efff86302e768a6b21c")), + (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 19, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip", Some("06ec12a5e964dc0ad33e6f380185a4d295178dce6d6df512f508e7aee00a1323")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 18, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.15-aarch64.tar.bz2", Some("03e35fcba290454bb0ccf7ee57fb42d1e63108d10d593776a382c0a2fe355de0")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 18, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.15-macos_arm64.tar.bz2", Some("300541c32125767a91b182b03d9cc4257f04971af32d747ecd4d62549d72acfd")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 18, suffix: None }, "https://downloads.python.org/pypy/pypy3.9-v7.3.15-linux32.tar.bz2", Some("c6209380977066c9e8b96e8258821c70f996004ce1bc8659ae83d4fd5a89ff5c")), @@ -89,6 +101,54 @@ pub const PYTHON_VERSIONS: &[(PythonVersion, &str, Option<&str>)] = &[ (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("linux"), major: 3, minor: 7, patch: 9, suffix: None }, "https://downloads.python.org/pypy/pypy3.7-v7.3.3-linux32.tar.bz2", Some("7d81b8e9fcd07c067cfe2f519ab770ec62928ee8787f952cadf2d2786246efc8")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 7, patch: 9, suffix: None }, "https://downloads.python.org/pypy/pypy3.7-v7.3.3-linux64.tar.bz2", Some("37e2804c4661c86c857d709d28c7de716b000d31e89766599fdf5a98928b7096")), (PythonVersion { name: Cow::Borrowed("pypy"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 7, patch: 9, suffix: None }, "https://downloads.python.org/pypy/pypy3.7-v7.3.3-osx64.tar.bz2", Some("d72b27d5bb60813273f14f07378a08822186a66e216c5d1a768ad295b582438d")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 13, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.13.1%2B20241206-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("379a3b3ee9aec90cd031bf3c31972ca8a1925f863553c43e60cb2545e4157fee")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 13, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.13.1%2B20241206-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("ea1718a0e775bbd0ad800adeacb6c8deca3fa29c4639b204ef4a6975546af3b6")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 13, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.13.1%2B20241206-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("e4b0d9bfa0b8ecfc04625b530e58be0ff4ddcc9ce0c1ab3e509224ee6b362923")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 13, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.13.1%2B20241206-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("c0be5da5c8351fb7e607433726d9a1094cb91c07e6084c557fedc46bff37062e")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 13, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.13.1%2B20241206-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("8836cf9bad5fea6399ea4ac3e783519db87e9e1a3a7927da5bad21e1bc730594")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 13, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.13.1%2B20241206-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("3a97cd23c8470f3dc340538f3287cec8df8d168e3da67e4435d7697f5f57fc41")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 13, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.13.0%2B20241016-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("dab453a00cc921bb9b27db1ea23a7071381ee6b0fb04c5bc299ff9d03c020320")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 13, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.13.0%2B20241016-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("542e5ffac0b90dc1d872ba8157c0e96861b339b73a20eb21c53242f8c92e043c")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 13, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.13.0%2B20241016-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("0ef249cb7ba1e2c451e0a551df063f9caf2e2dc37e09d89f780f98991cd433db")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 13, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.13.0%2B20241016-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("078de2a3eef67377684e5054cbb059aa3f53d3189b2e19ab022f6b6242de014b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 13, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.13.0%2B20241016-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("1c1262a5105c1ad725c18b15979fb057b699e7b3c5c158d2af9b9c59ad29157b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 13, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.13.0%2B20241016-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("4b4940208e6c0475105fd2c8bfc2e29a4ccdb61350b26ef13172303930a23b4e")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.12.8%2B20241206-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("264d79b4b681f00d5a05ce65cb47feccf5ee410854f12ab036ee9ece374b655b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.12.8%2B20241206-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("67766ea5ade577e4bbf57afdd85db857b3b62d8e5a5ecfe4bbcef0e7fe6cab84")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.12.8%2B20241206-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("b14c6f94b6f7d3fe57edb22ec1ed6dc5acee15e7b3dc15af8f49d725fd75bdd8")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.12.8%2B20241206-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("80a3625b14e1bd1caf9cbe7fb24a7adcce27ee2150630e666ba1653a925915a9")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.12.8%2B20241206-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("ed3e01b105ae84de0eed9498c3013b5fb9eafae47fa07f437b3cb5fd32777617")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.12.8%2B20241206-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("7b4e910a414291389c5ec277abc532d7c09b158b5ec6ecb0e91920a193675eba")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 7, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.12.7%2B20241016-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("bd7688441ddf8d5dc2ccb288bb31fb862330999a4a3f1544aa4f999c93b85a6a")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 7, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.12.7%2B20241016-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("3f8bf335c97424d0be262687906b0f50511f22fede8b9be77363faa57c4b8e7f")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 7, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.12.7%2B20241016-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("199826ee08e52c9476a2376b43bb712da6b6464b26d56b0a1c165fddb3733770")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 7, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.12.7%2B20241016-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("a3709fb5350bff838bd7bb5dc18e67300c76dc3e5cd11f9c84e54c8aeac60784")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 7, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.12.7%2B20241016-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("4ae54498bd8d17fc8689fc57c7dadd59c625834af1b5f81fa21f943ff0e37643")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 7, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.12.7%2B20241016-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("bb0029bbe40c7c0851734765db1747589ff2ea85d87297c4e2d968e397c601e2")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 6, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240909/cpython-3.12.6%2B20240909-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("93a238ba9707c357c054d665a940e2ef33c7c2e2d4e454c248941e625c3050d9")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 6, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240909/cpython-3.12.6%2B20240909-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("b9b115e897e534bd6c1a4f4949fa3c75d662218c3c94bb47f87e61f8c6df430a")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 6, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240909/cpython-3.12.6%2B20240909-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("8e83f98c4b0f83a9ef4c8f90877513feb0bb7f0c2f2c6bc63077511d67e7b3ab")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 6, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240909/cpython-3.12.6%2B20240909-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("5b560c74201a5fc1d6771cdc12957b4b2f792dea76134b4d060178690c683d04")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 6, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240909/cpython-3.12.6%2B20240909-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("e38c3f031ebfd898f16f10bc73655f377787624f4915113f48d5f017ced0a9de")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 6, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240909/cpython-3.12.6%2B20240909-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("7566acba13d60fe059263c906a67f2450d7ae3d5749e524ffb21baa68e924cd3")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 5, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.12.5%2B20240814-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("bf5b434987f3eb7fb65111e7dbf24d82e961ef4e95400e54721035c0904b42c8")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 5, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.12.5%2B20240814-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("40b7a9bddca90217102e07f5bc2747c75534a1cced299d176a9c0901251a691b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 5, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.12.5%2B20240814-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("4b0f8e4bcd280fb595c2bbba8d1ae37831c989d2b301b5cefe9fba5e51b506b6")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 5, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.12.5%2B20240814-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("e4c7b70f1c8b8ff062f567e048777f55cc9d2a98dd6b71abaf8b10a0e1670906")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 5, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.12.5%2B20240814-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("0556dccef4c94637d6f4f7f645608b72db0a64c43c3a59cf0d9ec021ddf75a30")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 5, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.12.5%2B20240814-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("a7ddc238799f15c25ac6d4ff7bec3464d9fa8b5e8cf13dad434d89afd0697a1d")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 4, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240726/cpython-3.12.4%2B20240726-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("05cbc91569f94a07f96a7ae04b62e19a9d7a3e74400d7d6b5b042f17285d2601")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 4, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240726/cpython-3.12.4%2B20240726-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("3b017ab70e2f11b95d909317fc4e76c000ece588461058a642bf74db77cec267")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 4, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240726/cpython-3.12.4%2B20240726-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("4d321e97e499610c887da89682c796967dba9337a1102e9e5d98d65cef5dde52")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 4, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240726/cpython-3.12.4%2B20240726-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("164f9ca029de9220d6f473e835b06c14684905912aee73312c560238fc2051d6")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 4, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240726/cpython-3.12.4%2B20240726-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("60cfc2465326cb44303e3a8904d4e606ec54aaa41be378532e219f05f06ef37d")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 4, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240726/cpython-3.12.4%2B20240726-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("369d3a8a205cdbc754034f304ecedd4da0120cc9c71b6baac0147908aba15ece")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 3, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("a4f17d1e3b4ea0e4c2a3664f232c0857979522936af582f7de92b57050220f74")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 3, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("fa2b8c377f17dfb097a93c0fba217d93075a7ceba0cc877066e95be969e6b73d")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 3, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("31bb3f579f3dcbbf3bf1dc71a188112e821cdfc77d21c9dbfe82ea78538110e1")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 3, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("e51f6676a24c3551657347ef97963164eac801df0a62afcba8e0e28ebb62acee")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 3, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("e49da3f702da08a3e38d01c776cc2356e427217681964ff64a7880507e224a3c")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 3, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("776568c92c5f3b47dbf5f17c1c58578f70d75a32654419a158aa8bdc6f95b09a")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 2, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.12.2%2B20240224-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("2e87c0215aea1614e52ff8588b0ba41eb5ecf555e500094a179c0bbf1b25cbc7")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 2, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.12.2%2B20240224-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("2afcc8b25c55793f6ceb0bef2e547e101f53c9e25a0fe0332320e5381a1f0fdb")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 2, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.12.2%2B20240224-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("ee985ae6a6a98f4d5bd19fd8c59f45235911d19b64e1dbd026261b8103f15db5")), @@ -107,6 +167,24 @@ pub const PYTHON_VERSIONS: &[(PythonVersion, &str, Option<&str>)] = &[ (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 12, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.12.0%2B20231002-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("5ce861907a2751a3a7395b1aaada830c2b072acc03f3dd0bcbaaa2b7a9166fc0")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 12, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.12.0%2B20231002-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("3b4781e7fd4efabe574ba0954e54c35c7d5ac4dc5b2990b40796c1c6aec67d79")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 12, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.12.0%2B20231002-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("5bdff7ed56550d96f9b26a27a8c25f0cc58a03bff19e5f52bba84366183cab8b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 11, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.11.11%2B20241206-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("b34b46af478c3373b6b5428bd0d099d7096225d7aa7ea80825bca98d5dcab706")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 11, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.11.11%2B20241206-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("0533476f58b021bf115abb3dcfb171846cca7c07287d882a10ac0b28b512bf1f")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 11, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.11.11%2B20241206-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("e82e3ede645d3434c22db0ab3f6cc1aa287217f8e0803707eaaa12f401ac5b79")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 11, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.11.11%2B20241206-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("e3aadcdb1d8fc308f5e5881b6c0e77fa14f91fc7e0bbf058c34aff8ac4d1e985")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 11, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.11.11%2B20241206-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("d1e0cd6ede5ef690a19556250699c2efdb6ba711e8d67c308732471f7a66ad5b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 11, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.11.11%2B20241206-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("fc63b4dd0778536dbc171b121e1e1c6d6159d09f3f6a1ec4490c5ae958752888")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 10, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10%2B20241016-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("4aa77466ca61b0a3263e3bf55be911cc72f30fd33c81d545f13782b1a35daeec")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 10, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10%2B20241016-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("afac902c6498cda4095674344b6defbff4500a3687aea597b3710cd070b62756")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 10, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10%2B20241016-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("b802510e566aa7c58f6368ffde2b14fa7bd5147fc221e253c90253c2e88c6119")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 10, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10%2B20241016-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("36498e63788f5a03c0d6249a5df77fbca542bf066eaa6dbb8bbf516194f96e9b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 10, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10%2B20241016-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("672a3d428359c3edd2155bf9d9939e0f51803fe3c81f1643e021d9970f01fbdd")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 10, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10%2B20241016-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("11daa1c645cb8e76f513adc5998ab462daa6f1d939ef39710d5ca34f69648812")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 9, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.11.9%2B20240814-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("63cd27b797589b66689c5be6495c707311d8272b284ad20faff1814b00134ac7")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 9, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.11.9%2B20240814-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("6436b83fed284af320743f6f5148ab3decbdc054531b40848977a5fa2347ca44")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 9, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.11.9%2B20240814-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("88e3238b59aad1b624f0c45c058059e5c582e686563e3993b1b1dadddfa3fe1d")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 9, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.11.9%2B20240814-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("73b3bef1220efcfd61dec42af94b9792937fe388bcc7064017c8f3b8e4636187")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 9, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.11.9%2B20240814-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("2caa7756679ec65a795b99e306de00ea0a4069bd7b1d6ec45df89d6e37a29577")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 9, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.11.9%2B20240814-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("7766550c42ce59d53b1dd49e9d698d762c9e5a743c7a57d6d7114ff7d266e131")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.11.8%2B20240224-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("1d84ed69e5acce555513e9261ce4b78bed19969b06a51a26b2781a375d70083d")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.11.8%2B20240224-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("c0650884b929253b8688797d1955850f6e339bf0428b3d935f62ab3159f66362")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 8, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.11.8%2B20240224-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("c3e90962996177a027bd73dd9fd8c42a2d6ef832cda26db4ab4efc6105160537")), @@ -153,6 +231,24 @@ pub const PYTHON_VERSIONS: &[(PythonVersion, &str, Option<&str>)] = &[ (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 11, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1%2B20230116-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("02332441cb610b1e1aa2d2972e261e2910cc6a950b7973cac22c0759a93c5fcd")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 11, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1%2B20230116-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("0eb61be53ee13cf75a30b8a164ef513a2c7995b25b118a3a503245d46231b13a")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 11, patch: 1, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1%2B20230116-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("f5c46fffda7d7894b975af728f739b02d1cec50fd4a3ea49f69de9ceaae74b17")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 16, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.10.16%2B20241206-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("9f95aebbd0d80a7d2f0e1754401d9249e90f950278eebe549a0438b56176f0b9")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 16, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.10.16%2B20241206-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("4ec9b4248e2ee8e2186a9a0629cc9b3dd7133598811403429dd1eead083ea4e4")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 16, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.10.16%2B20241206-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("667edbcf1f1e26fb6b4e29f3b199294425e25c46f7b31e563c11015252396241")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 16, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.10.16%2B20241206-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("0b2e0d2769269fb8e5f3f5fda4371dfb0e30ad7bbda3dd4671619e7986bd9a98")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 16, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.10.16%2B20241206-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("6579270a213f23a64d659fb37dcf3a51edad66aa38dd84e3fc4478d3eeda752a")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 16, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.10.16%2B20241206-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("c1e5fe4b45ae5dd833c849dee20d91ca12b6dee2666fbfc344741bb59fe1fbf1")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 15, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.10.15%2B20241016-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("1ee866fe8c3a2fdf310defd9492aaa9d4d26b93b8ee9573525a860cdeacfc8fe")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 15, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.10.15%2B20241016-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("41c84c6a96f45e6eaa5ff012806ae180a5a758e0c8027b88295b7c2a28a1c82a")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 15, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.10.15%2B20241016-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("50301fa93816feeadddcbf0bc035870da1ef3006814c59b874ddf11252f19be3")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 15, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.10.15%2B20241016-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("8438e1c8df6180dbd4daa8363ac4b156de5cd9cbcdfd37e4a7052903457ae6b4")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 15, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.10.15%2B20241016-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("adaa44bb698b4b0a38335114bc9c2e53505f3562cb39aac35c87b7978175a9d1")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 15, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.10.15%2B20241016-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("ade7ce8507635e64b6d167da0dced735816a4e1c121433277a0ed86e42887d46")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 14, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14%2B20240814-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("56183ed135dbdaaa6b189a6b0e6b6bcf3f26074297721bdbe9648cdc5247937c")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 14, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14%2B20240814-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("ca1bf0cf71ef42a302bc9df6096ac6ae399b971b426e7b620481d86b00fffa8c")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 14, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14%2B20240814-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("49ba7ea8de4ca7369be9c3415712d789db3caaa4c6c0530ce94d2db5b4e145cd")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 14, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14%2B20240814-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("e80e39be6f3fe2620c210889d13041aac16573ebac103f7bbaafeedc0d8fc253")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 14, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14%2B20240814-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("75b7453594f41a4d6e50efb643b1717067642ad3cd85fc6151dec1d4fcb15810")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 14, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14%2B20240814-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("ffd0f253678f20580acb90680138d990f37518cbb622e5a4032759d5b06ff9fe")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 13, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.10.13%2B20240224-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("7f23a4afa4032a7c5a4e0ec926da37eea242472142613c2baa029ef61c3c493c")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 13, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.10.13%2B20240224-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("57b83a4aa32bdbe7611f1290313ef24f2574dff5fa59181c0ccb26c14c688b73")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 13, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20230826/cpython-3.10.13%2B20230826-i686-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("cc5625a16fbec682d4ce40c0d185318164bd181efaa7eaf945ca63015db9fea3")), @@ -237,6 +333,24 @@ pub const PYTHON_VERSIONS: &[(PythonVersion, &str, Option<&str>)] = &[ (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 10, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20211017/cpython-3.10.0-x86_64-unknown-linux-gnu-pgo%2Blto-20211017T1616.tar.zst", None), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 10, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20211017/cpython-3.10.0-x86_64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst", None), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 10, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20211017/cpython-3.10.0-x86_64-pc-windows-msvc-shared-pgo-20211017T1616.tar.zst", None), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 21, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.9.21%2B20241206-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("c121fab3cdcc38151cc7b6182a60d616e5fb05b238a3472fe38413bc4e3585ee")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 21, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.9.21%2B20241206-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("50d96c23cb9e7bb69d0bbcb30dcc0cd3431a016f9a8ad2f2e8f06e7b4d855934")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 21, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.9.21%2B20241206-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("8bd4428d7da08dd0a9e50820c8dd27a814d41776ed31c40a2c6dec46399a22cc")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 21, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.9.21%2B20241206-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("4609b1393d96e0e83a69e08a6da189158335a7af9319334e745f409f458d0648")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 21, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.9.21%2B20241206-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("92fb179be2a23a2ef38002349a2c336608e3c5d3e4097a107e298229e17f8d4f")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 21, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.9.21%2B20241206-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("0a8953af66e3cf2ea4124a22e01c62940797ad3d39ce27bf9a96b2f41c7e4777")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.9.20%2B20241016-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("7f832256d0f2f1d0dc89f71c3baf2f84bbd32538891d5017163bebae60837d89")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.9.20%2B20241016-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("61ad2810005b6b66465da0f3055163812b0c089979a96b67ef54c34906c6f560")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.9.20%2B20241016-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("9c56fa3257cfe2147a5c17f8bd55870a305ffa1ef405e3f189e22989df15c573")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.9.20%2B20241016-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("9873d6d3aa04918add2a7b428ab3d8b3baded2b0754c8fb97d6ed6b4201b0343")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.9.20%2B20241016-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("a8bb2129da3bf93ae074775efc720d8857ac76475344ce0c73f0076884847ea1")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.9.20%2B20241016-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("7b7ba312e704bd60202af8a45d1019a4402569e6bbc9fa4feee24c86919c33bf")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.9.19%2B20240814-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("0d5edd43f3219744be8476f3ff2ab93af852647ff13e344622e83ca26634688d")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.9.19%2B20240814-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("f3a918ec61e0c1676c56cb2e3a29fce733cf0a082bb2577ce12a27f7303c1098")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.9.19%2B20240814-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("077c61230bc95673e95edd1e9cf43a8b2953470f8c83c50cd89bad08e530aa2b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.9.19%2B20240814-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("ad5bd8e0eb95af34ba09c64a2aab1a5f3bdc0bf102501e0bb6a619c25583e55a")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.9.19%2B20240814-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("cb7df2cb1ccdb9b5d24a7dc4de2b1183ea0344c9048da2393bc0bd8e5dc96cca")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.9.19%2B20240814-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("f807820ac0f84735e8c4125d590093712252015398a1f4c7ff9795502f511ab4")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 18, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.9.18%2B20240224-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("93d7b15bf02a3191cfdee9d9d68bf2da782fc04cb142bcca6a4299fe524d9b37")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 18, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.9.18%2B20240224-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("579f9b68bbb3a915cbab9682e4d3c253bc96b0556b8a860982c49c25c61f974a")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 18, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20230826/cpython-3.9.18%2B20230826-i686-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("9e40a541b4eb6eb0a5e2f35724a18332aea91c61e18dec77ca40da5cf2496839")), @@ -345,6 +459,18 @@ pub const PYTHON_VERSIONS: &[(PythonVersion, &str, Option<&str>)] = &[ (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 9, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20201020/cpython-3.9.0-x86_64-unknown-linux-gnu-pgo-20201020T0627.tar.zst", None), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 9, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20201020/cpython-3.9.0-x86_64-apple-darwin-pgo-20201020T0626.tar.zst", None), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 9, patch: 0, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20201020/cpython-3.9.0-x86_64-pc-windows-msvc-shared-pgo-20201021T0245.tar.zst", None), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 8, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("05ed8e6403ed0692394ad0804e79bbc847d5d68cb3ad57b46b29065b7ad4a53b")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 8, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("68f99a2a8aa8d4c319f095a4d8ac1ffffd24ef31827ab81d290f2f0ae3f3a424")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 8, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("c19216a5e3040abd2d315e71dac607c991897f82d6b8c22860c9f2329c8bd6a3")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 8, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("25b45589eb04dfb6874e597f017b2ff180120330ca255317751238f682f34091")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 8, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("0b619b6d156244f1cbd1b91f2bf838cbcd69eeed4fb7edfcf4da4c6c526adc49")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 8, patch: 20, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("b84dd2228d36fc2381573af1002d0a9cb244a2a52c7c470c2fafec1f855459c3")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 8, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.8.19%2B20240814-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("d57b944f770579a2c24b34880843318135ddc816ccb67d9a7022b2c00b2c897e")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 8, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.8.19%2B20240814-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("43f3b6d7816448b44f86d2186dba1b7418533a3f4a37d07e5075bb934bcfba76")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 8, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.8.19%2B20240814-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("8f69364b8421a51a71da5e897d4f59d0659f18b354896d28c79756de6011c21c")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 3, minor: 8, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.8.19%2B20240814-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst", Some("adbe33a5f9a6d3cd05ef90ca2aed7db8d0002492cfdfe81c24cabf6e6e6aacee")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 3, minor: 8, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.8.19%2B20240814-x86_64-apple-darwin-pgo%2Blto-full.tar.zst", Some("04c2aca9f8ecceb4f72775d104586608489a016439e4a361d967748bde4949fd")), + (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 3, minor: 8, patch: 19, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.8.19%2B20240814-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst", Some("31ff501b345a4054a4eb6a886b4d2799bd11b15afe7338dc2f2e5f2a123f4dba")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 3, minor: 8, patch: 18, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.8.18%2B20240224-aarch64-unknown-linux-gnu-lto-full.tar.zst", Some("df66801678a5f4accee67784aff058f283fd52e42898527b7ff0e1cbc3e50e8c")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 3, minor: 8, patch: 18, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.8.18%2B20240224-aarch64-apple-darwin-pgo%2Blto-full.tar.zst", Some("c732c068cddcd6a008c1d6d8e35802f5bdc7323bd2eb64e77210d3d5fe4740c2")), (PythonVersion { name: Cow::Borrowed("cpython"), arch: Cow::Borrowed("x86"), os: Cow::Borrowed("windows"), major: 3, minor: 8, patch: 18, suffix: None }, "https://github.com/indygreg/python-build-standalone/releases/download/20240224/cpython-3.8.18%2B20240224-i686-pc-windows-msvc-shared-pgo-full.tar.zst", Some("9f94c7b54b97116cd308e73cda0b7a7b7fff4515932c5cbba18eeae9ec798351")), diff --git a/rye/src/sources/generated/uv_downloads.inc b/rye/src/sources/generated/uv_downloads.inc index 516afecfc6..5428d57a84 100644 --- a/rye/src/sources/generated/uv_downloads.inc +++ b/rye/src/sources/generated/uv_downloads.inc @@ -1,12 +1,12 @@ // Generated by rye-devtools. DO NOT EDIT. -// To regenerate, run `rye run uv-downloads > rye/src/generated/uv_downloads.inc` from the root of the repository. +// To regenerate, run `rye run uv-downloads > rye/src/sources/generated/uv_downloads.inc` from the root of the repository. use std::borrow::Cow; pub const UV_DOWNLOADS: &[UvDownload] = &[ - UvDownload {arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 0, minor: 1, patch: 11, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.1.11/uv-aarch64-apple-darwin.tar.gz"), sha256: Cow::Borrowed("64e0aeae1d5135684c65e5d41741f1b2eba5c60e574dfef7386712b259066d95") }, - UvDownload {arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 0, minor: 1, patch: 11, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.1.11/uv-aarch64-unknown-linux-gnu.tar.gz"), sha256: Cow::Borrowed("56a7edd9bfadab67100bde6aca3012c9d7d8619405ebb360eae50b88ba2c6dff") }, - UvDownload {arch: Cow::Borrowed("i686"), os: Cow::Borrowed("windows"), major: 0, minor: 1, patch: 11, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.1.11/uv-i686-pc-windows-msvc.zip"), sha256: Cow::Borrowed("75e74c43bf6aeb3cd83a7b7a1f578c2247c48a03fa421317ca7f27f881c0890e") }, - UvDownload {arch: Cow::Borrowed("i686"), os: Cow::Borrowed("linux"), major: 0, minor: 1, patch: 11, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.1.11/uv-i686-unknown-linux-gnu.tar.gz"), sha256: Cow::Borrowed("f8d63781a8a0d3452f7322268a4bfb3a6e06816b76deafde660d53bbc2d790d9") }, - UvDownload {arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 0, minor: 1, patch: 11, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.1.11/uv-x86_64-apple-darwin.tar.gz"), sha256: Cow::Borrowed("62f2c6696fdf8bf394662cad54244411b9e96908de9e52b3743955b0d94bd8d4") }, - UvDownload {arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 0, minor: 1, patch: 11, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.1.11/uv-x86_64-pc-windows-msvc.zip"), sha256: Cow::Borrowed("e3e49f27f032c195f01d33df30dff16c9ce7d6ac60512c1af117fc4597d0e632") }, - UvDownload {arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 0, minor: 1, patch: 11, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.1.11/uv-x86_64-unknown-linux-gnu.tar.gz"), sha256: Cow::Borrowed("8b1d098549f46814ed40975db6e88aba2eb541196bea54753b08f789b6db8384") }, + UvDownload {arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("macos"), major: 0, minor: 5, patch: 7, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.5.7/uv-aarch64-apple-darwin.tar.gz"), sha256: Cow::Borrowed("b8cab25ab2ec0714dbb34179f948c27aa4ab307be54e0628e9e1eef1d2264f9f") }, + UvDownload {arch: Cow::Borrowed("aarch64"), os: Cow::Borrowed("linux"), major: 0, minor: 5, patch: 7, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.5.7/uv-aarch64-unknown-linux-musl.tar.gz"), sha256: Cow::Borrowed("2c0fdf774ebfabecadf403c2fb966cc1a023b3db3907041fae34c77464a38a2b") }, + UvDownload {arch: Cow::Borrowed("i686"), os: Cow::Borrowed("windows"), major: 0, minor: 5, patch: 7, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.5.7/uv-i686-pc-windows-msvc.zip"), sha256: Cow::Borrowed("d207a2791a513e3d645779e5d86cc88f7040426ec5e4a5521498b87558f93db6") }, + UvDownload {arch: Cow::Borrowed("i686"), os: Cow::Borrowed("linux"), major: 0, minor: 5, patch: 7, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.5.7/uv-i686-unknown-linux-gnu.tar.gz"), sha256: Cow::Borrowed("01d7c0444812563712038f472fcac77981a5dac9c44d507ccbccd49ec1828a12") }, + UvDownload {arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("macos"), major: 0, minor: 5, patch: 7, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.5.7/uv-x86_64-apple-darwin.tar.gz"), sha256: Cow::Borrowed("b0ff9937005e9dbc68798f587e72cedfe488f0866bb47532a8c3273e7222090a") }, + UvDownload {arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("windows"), major: 0, minor: 5, patch: 7, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.5.7/uv-x86_64-pc-windows-msvc.zip"), sha256: Cow::Borrowed("cb2aea0d8f85ffe1c4e2a431cbbd6e5c8faeb732e7cf8e4bee1c10b7779e7352") }, + UvDownload {arch: Cow::Borrowed("x86_64"), os: Cow::Borrowed("linux"), major: 0, minor: 5, patch: 7, suffix: None, url: Cow::Borrowed("https://github.com/astral-sh/uv/releases/download/0.5.7/uv-x86_64-unknown-linux-gnu.tar.gz"), sha256: Cow::Borrowed("8a0a3e823684dec6e49ae17f31bf6483c778fd579671992d9156875210e5161e") }, ]; diff --git a/rye/src/sync.rs b/rye/src/sync.rs index 9846ca4313..d888ac2071 100644 --- a/rye/src/sync.rs +++ b/rye/src/sync.rs @@ -1,29 +1,21 @@ +use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; -use std::{env, fs}; use anyhow::{bail, Context, Error}; use console::style; use same_file::is_same_file; use serde::{Deserialize, Serialize}; -use tempfile::tempdir; -use crate::bootstrap::{ensure_self_venv, fetch, get_pip_module}; -use crate::config::Config; -use crate::consts::VENV_BIN; +use crate::bootstrap::{ensure_self_venv, fetch, FetchOptions}; use crate::lock::{ - make_project_root_fragment, update_single_project_lockfile, update_workspace_lockfile, - LockMode, LockOptions, + update_single_project_lockfile, update_workspace_lockfile, KeyringProvider, LockMode, + LockOptions, }; -use crate::piptools::{get_pip_sync, get_pip_tools_venv_path}; use crate::platform::get_toolchain_python_bin; -use crate::pyproject::{read_venv_marker, write_venv_marker, ExpandedSources, PyProject}; +use crate::pyproject::{read_venv_marker, ExpandedSources, PyProject}; use crate::sources::py::PythonVersion; -use crate::utils::{ - get_venv_python_bin, set_proxy_variables, symlink_dir, update_venv_sync_marker, CommandOutput, - IoPathContext, -}; -use crate::uv::Uv; +use crate::utils::{get_venv_python_bin, CommandOutput, IoPathContext}; +use crate::uv::{UvBuilder, UvSyncOptions}; /// Controls the sync mode #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] @@ -56,6 +48,8 @@ pub struct SyncOptions { pub lock_options: LockOptions, /// Explicit pyproject location (Only usable by PythonOnly mode) pub pyproject: Option, + /// Keyring provider to use for credential lookup. + pub keyring_provider: KeyringProvider, } impl SyncOptions { @@ -80,6 +74,12 @@ pub struct VenvMarker { pub venv_path: Option, } +impl VenvMarker { + pub fn is_compatible(&self, py_ver: &PythonVersion) -> bool { + self.python == *py_ver + } +} + /// Synchronizes a project's virtualenv. pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { let pyproject = PyProject::load_or_discover(cmd.pyproject.as_deref())?; @@ -97,6 +97,16 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { bail!("cannot sync or generate lockfile: package needs 'pyproject.toml'"); } + // Turn on generate_hashes if the project demands it. + if pyproject.generate_hashes() { + cmd.lock_options.generate_hashes = true; + } + + // Turn on universal locking if the project demands it. + if pyproject.universal() { + cmd.lock_options.universal = true; + } + // Turn on locking with sources if the project demands it. if pyproject.lock_with_sources() { cmd.lock_options.with_sources = true; @@ -109,33 +119,29 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { if venv.is_dir() { if let Some(marker) = read_venv_marker(&venv) { if marker.python != py_ver { - if cmd.output != CommandOutput::Quiet { - echo!( - "Python version mismatch (found {}, expected {}), recreating.", - marker.python, - py_ver - ); - } + echo!( + if cmd.output, + "Python version mismatch (found {}, expected {}), recreating.", + marker.python, + py_ver + ); recreate = true; } else if let Some(ref venv_path) = marker.venv_path { // for virtualenvs that have a location identifier, check if we need to // recreate it. On IO error we know that one of the paths is gone, so // something needs recreation. if !is_same_file(&venv, venv_path).unwrap_or(false) { - if cmd.output != CommandOutput::Quiet { - echo!( - "Detected relocated virtualenv ({} => {}), recreating.", - venv_path.display(), - venv.display(), - ); - } + echo!( + if cmd.output, + "Detected relocated virtualenv ({} => {}), recreating.", + venv_path.display(), + venv.display(), + ); recreate = true; } } } else if cmd.force { - if cmd.output != CommandOutput::Quiet { - echo!("Forcing re-creation of non-rye managed virtualenv"); - } + echo!(if cmd.output, "Forcing re-creation of non-rye managed virtualenv"); recreate = true; } else if cmd.mode == SyncMode::PythonOnly { // in python-only sync mode, don't complain about foreign venvs @@ -146,8 +152,8 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { } // make sure we have a compatible python version - let py_ver = - fetch(&py_ver.into(), output, false).context("failed fetching toolchain ahead of sync")?; + let py_ver = fetch(&py_ver.into(), FetchOptions::with_output(output)) + .context("failed fetching toolchain ahead of sync")?; // kill the virtualenv if it's there and we need to get rid of it. if recreate && venv.is_dir() { @@ -156,19 +162,16 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { if venv.is_dir() { // we only care about this output if regular syncs are used - if !matches!(cmd.mode, SyncMode::PythonOnly | SyncMode::LockOnly) - && output != CommandOutput::Quiet - { - echo!("Reusing already existing virtualenv"); + if !matches!(cmd.mode, SyncMode::PythonOnly | SyncMode::LockOnly) { + echo!(if output, "Reusing already existing virtualenv"); } } else { - if output != CommandOutput::Quiet { - echo!( - "Initializing new virtualenv in {}", - style(venv.display()).cyan() - ); - echo!("Python version: {}", style(&py_ver).cyan()); - } + echo!( + if output, + "Initializing new virtualenv in {}", + style(venv.display()).cyan() + ); + echo!(if output, "Python version: {}", style(&py_ver).cyan()); let prompt = pyproject.name().unwrap_or("venv"); create_virtualenv(output, &self_venv, &py_ver, &venv, prompt) .context("failed creating virtualenv ahead of sync")?; @@ -198,6 +201,7 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { cmd.output, &sources, &cmd.lock_options, + cmd.keyring_provider, ) .context("could not write production lockfile for workspace")?; update_workspace_lockfile( @@ -208,6 +212,7 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { cmd.output, &sources, &cmd.lock_options, + cmd.keyring_provider, ) .context("could not write dev lockfile for workspace")?; } else { @@ -220,6 +225,7 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { cmd.output, &sources, &cmd.lock_options, + cmd.keyring_provider, ) .context("could not write production lockfile for project")?; update_single_project_lockfile( @@ -230,145 +236,87 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> { cmd.output, &sources, &cmd.lock_options, + cmd.keyring_provider, ) .context("could not write dev lockfile for project")?; } // run pip install with the lockfile. if cmd.mode != SyncMode::LockOnly { - if output != CommandOutput::Quiet { - echo!("Installing dependencies"); - } + echo!(if output, "Installing dependencies"); - let tempdir = tempdir()?; - let mut sync_cmd = if Config::current().use_uv() { - let mut uv_sync_cmd = Uv::ensure_exists(output)?.cmd(); - uv_sync_cmd.arg("pip").arg("sync"); - let root = pyproject.workspace_path(); - - uv_sync_cmd - .env("PROJECT_ROOT", make_project_root_fragment(&root)) - .env("VIRTUAL_ENV", pyproject.venv_path().as_os_str()) - .current_dir(&root); - uv_sync_cmd + let target_lockfile = if cmd.dev && dev_lockfile.is_file() { + dev_lockfile } else { - let mut pip_sync_cmd = Command::new(get_pip_sync(&py_ver, output)?); - let root = pyproject.workspace_path(); - let py_path = get_venv_python_bin(&venv); - - // we need to run this after we have run the `get_pip_sync` command - // as this is what bootstraps or updates the pip tools installation. - // This is needed as on unix platforms we need to search the module path. - symlink_dir( - get_pip_module(&get_pip_tools_venv_path(&py_ver)) - .context("could not locate pip")?, - tempdir.path().join("pip"), - ) - .context("failed linking pip module into for pip-sync")?; - - pip_sync_cmd - .env("PROJECT_ROOT", make_project_root_fragment(&root)) - .env("PYTHONPATH", tempdir.path()) - .current_dir(&root) - .arg("--python-executable") - .arg(&py_path) - .arg("--pip-args") - .arg("--no-deps"); - - if output != CommandOutput::Quiet { - pip_sync_cmd.env("PYTHONWARNINGS", "ignore"); - } else if output == CommandOutput::Verbose && env::var("PIP_VERBOSE").is_err() { - pip_sync_cmd.env("PIP_VERBOSE", "2"); - } - pip_sync_cmd + lockfile }; - sources.add_as_pip_args(&mut sync_cmd); - - if cmd.dev && dev_lockfile.is_file() { - sync_cmd.arg(&dev_lockfile); - } else { - sync_cmd.arg(&lockfile); - } - - if output == CommandOutput::Verbose { - sync_cmd.arg("--verbose"); - } else if output == CommandOutput::Quiet { - sync_cmd.arg("-q"); - } - set_proxy_variables(&mut sync_cmd); - let status = sync_cmd.status().context("unable to run pip-sync")?; - - if !status.success() { - bail!("Installation of dependencies failed"); - } - } + let py_path = get_venv_python_bin(&venv); + let uv_options = UvSyncOptions { + keyring_provider: cmd.keyring_provider, + }; + UvBuilder::new() + .with_output(output.quieter()) + .with_workdir(&pyproject.workspace_path()) + .with_sources(sources) + .ensure_exists()? + .venv(&venv, &py_path, &py_ver, None)? + .with_output(output) + .sync(&target_lockfile, uv_options)?; + }; } - if output != CommandOutput::Quiet && cmd.mode != SyncMode::PythonOnly { - echo!("Done!"); + if cmd.mode != SyncMode::PythonOnly { + echo!(if output, "Done!"); } Ok(()) } /// Performs an autosync. -pub fn autosync(pyproject: &PyProject, output: CommandOutput) -> Result<(), Error> { +pub fn autosync( + pyproject: &PyProject, + output: CommandOutput, + pre: bool, + with_sources: bool, + generate_hashes: bool, + keyring_provider: KeyringProvider, +) -> Result<(), Error> { sync(SyncOptions { output, dev: true, mode: SyncMode::Regular, force: false, no_lock: false, - lock_options: LockOptions::default(), + lock_options: LockOptions { + pre, + with_sources, + generate_hashes, + ..Default::default() + }, pyproject: Some(pyproject.toml_path().to_path_buf()), + keyring_provider, }) } pub fn create_virtualenv( output: CommandOutput, - self_venv: &Path, + _self_venv: &Path, py_ver: &PythonVersion, venv: &Path, prompt: &str, ) -> Result<(), Error> { let py_bin = get_toolchain_python_bin(py_ver)?; - if Config::current().use_uv() { - // try to kill the empty venv if there is one as uv can't work otherwise. - fs::remove_dir(venv).ok(); - let uv = Uv::ensure_exists(output.quieter())? - .venv(venv, &py_bin, py_ver, Some(prompt)) - .context("failed to initialize virtualenv")?; - uv.write_marker()?; - uv.sync_marker(); - } else { - // create the venv folder first so we can manipulate some flags on it. - fs::create_dir_all(venv).path_context(venv, "unable to create virtualenv folder")?; - - update_venv_sync_marker(output, venv); - let mut venv_cmd = Command::new(self_venv.join(VENV_BIN).join("virtualenv")); - if output == CommandOutput::Verbose { - venv_cmd.arg("--verbose"); - } else { - venv_cmd.arg("-q"); - venv_cmd.env("PYTHONWARNINGS", "ignore"); - } - venv_cmd.arg("-p"); - venv_cmd.arg(&py_bin); - venv_cmd.arg("--no-seed"); - venv_cmd.arg("--prompt"); - venv_cmd.arg(prompt); - venv_cmd.arg("--").arg(venv); - let status = venv_cmd - .status() - .context("unable to invoke virtualenv command")?; - if !status.success() { - bail!("failed to initialize virtualenv"); - } - - write_venv_marker(venv, py_ver)?; - }; + // try to kill the empty venv if there is one as uv can't work otherwise. + fs::remove_dir(venv).ok(); + let uv = UvBuilder::new() + .with_output(output.quieter()) + .ensure_exists()? + .venv(venv, &py_bin, py_ver, Some(prompt)) + .context("failed to initialize virtualenv")?; + uv.write_marker()?; + uv.sync_marker(); // On UNIX systems Python is unable to find the tcl config that is placed // outside of the virtualenv. It also sometimes is entirely unable to find diff --git a/rye/src/templates/lib/maturin/lib.rs.j2 b/rye/src/templates/lib/maturin/lib.rs.j2 index baf9c5b4fe..e0ee798bdb 100644 --- a/rye/src/templates/lib/maturin/lib.rs.j2 +++ b/rye/src/templates/lib/maturin/lib.rs.j2 @@ -1,4 +1,4 @@ -const LIB_RS_TEMPLATE: &str = r#"use pyo3::prelude::*; +use pyo3::prelude::*; /// Prints a message. #[pyfunction] diff --git a/rye/src/templates/pyproject.toml.j2 b/rye/src/templates/pyproject.toml.j2 index b8d4b9d376..d3db4b9d97 100644 --- a/rye/src/templates/pyproject.toml.j2 +++ b/rye/src/templates/pyproject.toml.j2 @@ -29,7 +29,7 @@ classifiers = ["Private :: Do Not Upload"] {%- if is_script %} [project.scripts] -hello = {{ name_safe ~ ":main"}} +{{ name }} = {{ name_safe ~ ":main"}} {%- endif %} {%- if not is_virtual %} diff --git a/rye/src/tui.rs b/rye/src/tui.rs index 3464a1c171..4d092c0880 100644 --- a/rye/src/tui.rs +++ b/rye/src/tui.rs @@ -34,11 +34,27 @@ macro_rules! echo { () => { $crate::tui::_print(format_args!("")) }; + (if verbose $out:expr, $($arg:tt)+) => { + match $out { + $crate::utils::CommandOutput::Verbose => { + $crate::tui::_print(format_args!($($arg)*)) + } + _ => {} + } + }; + (if $out:expr, $($arg:tt)+) => { + match $out { + $crate::utils::CommandOutput::Normal | $crate::utils::CommandOutput::Verbose => { + $crate::tui::_print(format_args!($($arg)*)) + } + _ => {} + } + }; ($($arg:tt)+) => { // TODO: this is bloaty, but this way capturing of outputs // for stdout works in tests still. $crate::tui::_print(format_args!($($arg)*)) - } + }; } /// Like echo but always goes to stderr. diff --git a/rye/src/utils/mod.rs b/rye/src/utils/mod.rs index 7a9f7fbe32..04be999472 100644 --- a/rye/src/utils/mod.rs +++ b/rye/src/utils/mod.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::convert::Infallible; +use std::ffi::OsString; use std::io::{Cursor, Read}; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus, Stdio}; @@ -15,7 +16,7 @@ use sha2::{Digest, Sha256}; static ENV_VAR_RE: Lazy = Lazy::new(|| Regex::new(r"\$\{([A-Z0-9_]+)\}").unwrap()); #[cfg(unix)] -pub use std::os::unix::fs::{symlink as symlink_file, symlink as symlink_dir}; +pub use std::os::unix::fs::symlink as symlink_file; #[cfg(windows)] pub use std::os::windows::fs::symlink_file; @@ -34,6 +35,7 @@ pub(crate) mod windows; #[cfg(unix)] pub(crate) mod unix; +pub(crate) mod panic; pub(crate) mod ruff; pub(crate) mod toml; @@ -143,7 +145,7 @@ impl CommandOutput { } } - pub fn quieter(self) -> CommandOutput { + pub fn quieter(&self) -> CommandOutput { match self { CommandOutput::Normal => CommandOutput::Quiet, CommandOutput::Verbose => CommandOutput::Normal, @@ -164,7 +166,7 @@ pub fn is_executable(path: &Path) -> bool { } #[cfg(windows)] { - ["exe", "bat", "cmd"] + ["com", "exe", "bat", "cmd"] .iter() .any(|x| path.with_extension(x).is_file()) } @@ -179,7 +181,7 @@ pub fn get_short_executable_name(path: &Path) -> String { #[cfg(windows)] { let short_name = path.file_name().unwrap().to_string_lossy().to_lowercase(); - for ext in [".exe", ".bat", ".cmd"] { + for ext in [".com", ".exe", ".bat", ".cmd"] { if let Some(base_name) = short_name.strip_suffix(ext) { return base_name.into(); } @@ -192,7 +194,7 @@ pub fn get_short_executable_name(path: &Path) -> String { pub fn format_requirement(req: &Requirement) -> impl fmt::Display + '_ { struct Helper<'x>(&'x Requirement); - impl<'x> fmt::Display for Helper<'x> { + impl fmt::Display for Helper<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0.name)?; if let Some(extras) = &self.0.extras { @@ -233,6 +235,16 @@ where ENV_VAR_RE.replace_all(string, |m: &Captures| f(&m[1]).unwrap_or_default()) } +/// Prepend the given path to the current value of the $PATH environment variable +pub fn prepend_path_to_path_env(path: &Path) -> Result { + let mut paths = Vec::new(); + paths.push(path.to_owned()); + if let Some(existing_path) = std::env::var_os("PATH") { + paths.extend(std::env::split_paths(&existing_path)); + } + Ok(std::env::join_paths(paths)?) +} + #[derive(Copy, Clone, Debug)] enum ArchiveFormat { TarGz, diff --git a/rye/src/utils/panic.rs b/rye/src/utils/panic.rs new file mode 100644 index 0000000000..b4e724d806 --- /dev/null +++ b/rye/src/utils/panic.rs @@ -0,0 +1,32 @@ +use std::any::Any; +use std::{panic, process}; + +fn is_bad_pipe(payload: &dyn Any) -> bool { + payload + .downcast_ref::() + .map_or(false, |x| x.contains("failed printing to stdout: ")) +} + +/// Registers a panic hook that hides stdout printing failures. +pub fn set_panic_hook() { + let default_hook = panic::take_hook(); + panic::set_hook(Box::new(move |info| { + if !is_bad_pipe(info.payload()) { + default_hook(info) + } + })); +} + +/// Catches down panics that are caused by bad pipe errors. +pub fn trap_bad_pipe i32 + Send + Sync>(f: F) -> ! { + process::exit(match panic::catch_unwind(panic::AssertUnwindSafe(f)) { + Ok(status) => status, + Err(panic) => { + if is_bad_pipe(&panic) { + 1 + } else { + panic::resume_unwind(panic); + } + } + }); +} diff --git a/rye/src/utils/ruff.rs b/rye/src/utils/ruff.rs index 39e2ffe44e..e38387aa43 100644 --- a/rye/src/utils/ruff.rs +++ b/rye/src/utils/ruff.rs @@ -48,16 +48,18 @@ pub fn execute_ruff(args: RuffArgs, extra_args: &[&str]) -> Result<(), Error> { project.workspace_path().join(".ruff_cache"), ); } + ruff_cmd.args(extra_args); + match output { CommandOutput::Normal => {} CommandOutput::Verbose => { ruff_cmd.arg("--verbose"); } CommandOutput::Quiet => { - ruff_cmd.arg("-q"); + ruff_cmd.arg("--quiet"); } } - ruff_cmd.args(extra_args); + ruff_cmd.args(args.extra_args); ruff_cmd.arg("--"); diff --git a/rye/src/utils/toml.rs b/rye/src/utils/toml.rs index 087c316001..48fc655ecb 100644 --- a/rye/src/utils/toml.rs +++ b/rye/src/utils/toml.rs @@ -1,10 +1,10 @@ use anyhow::{anyhow, bail, Error}; -use toml_edit::{Array, Document, Item, RawString, Table, TableLike}; +use toml_edit::{Array, DocumentMut, Item, RawString, Table, TableLike}; /// Given a toml document, ensures that a given named table exists toplevel. /// /// The table is created as a non inline table which is the preferred style. -pub fn ensure_table<'a>(doc: &'a mut Document, name: &str) -> &'a mut Item { +pub fn ensure_table<'a>(doc: &'a mut DocumentMut, name: &str) -> &'a mut Item { if doc.as_item().get(name).is_none() { let mut tbl = Table::new(); tbl.set_implicit(true); @@ -48,7 +48,9 @@ pub fn reformat_array_multiline(deps: &mut Array) { rv.push_str(comment); } } - rv.push('\n'); + if !rv.is_empty() || !deps.is_empty() { + rv.push('\n'); + } rv }); deps.set_trailing_comma(true); diff --git a/rye/src/utils/windows.rs b/rye/src/utils/windows.rs index c260d433ee..f8472a9c52 100644 --- a/rye/src/utils/windows.rs +++ b/rye/src/utils/windows.rs @@ -174,7 +174,7 @@ pub(crate) fn add_to_programs(rye_home: &Path) -> Result<(), Error> { let mut uninstall_cmd = OsString::from("\""); uninstall_cmd.push(rye_home); - uninstall_cmd.push("\" self uninstall"); + uninstall_cmd.push("\\shims\\rye.exe\" self uninstall"); let reg_value = RegValue { bytes: to_winreg_bytes(uninstall_cmd.encode_wide().collect()), diff --git a/rye/src/uv.rs b/rye/src/uv.rs index d61172ff20..bba948dbb4 100644 --- a/rye/src/uv.rs +++ b/rye/src/uv.rs @@ -1,6 +1,7 @@ -use crate::bootstrap::download_url; +use crate::bootstrap::{download_url, SELF_REQUIREMENTS}; +use crate::lock::{make_project_root_fragment, KeyringProvider}; use crate::platform::get_app_dir; -use crate::pyproject::write_venv_marker; +use crate::pyproject::{read_venv_marker, write_venv_marker, ExpandedSources}; use crate::sources::py::PythonVersion; use crate::sources::uv::{UvDownload, UvRequest}; use crate::utils::{ @@ -8,18 +9,174 @@ use crate::utils::{ IoPathContext, }; use anyhow::{anyhow, Context, Error}; +use pep508_rs::Requirement; use std::fs::{self, remove_dir_all}; use std::io::Write; use std::path::{Path, PathBuf}; -use std::process::Command; +use std::process::{Command, Stdio}; use tempfile::NamedTempFile; +#[derive(Default)] +pub struct UvInstallOptions { + pub importlib_workaround: bool, + pub extras: Vec, + pub refresh: bool, + pub keyring_provider: KeyringProvider, +} + +pub enum UvPackageUpgrade { + /// Upgrade all packages. + All, + /// Upgrade the specific set of packages. + Packages(Vec), + /// Upgrade nothing (default). + Nothing, +} + +struct UvCompileOptions { + pub allow_prerelease: bool, + pub exclude_newer: Option, + pub upgrade: UvPackageUpgrade, + pub no_deps: bool, + pub no_header: bool, + pub keyring_provider: KeyringProvider, + pub generate_hashes: bool, + pub universal: bool, +} + +impl UvCompileOptions { + fn add_as_pip_args(self, cmd: &mut Command) { + if self.no_header { + cmd.arg("--no-header"); + } + + if self.no_deps { + cmd.arg("--no-deps"); + } + + if self.generate_hashes { + cmd.arg("--generate-hashes"); + } + + if self.allow_prerelease { + cmd.arg("--prerelease=allow"); + } + + if let Some(dt) = self.exclude_newer { + cmd.arg("--exclude-newer").arg(dt); + } + + if self.universal { + cmd.arg("--universal"); + } + + match self.upgrade { + UvPackageUpgrade::All => { + cmd.arg("--upgrade"); + } + UvPackageUpgrade::Packages(ref pkgs) => { + for pkg in pkgs { + cmd.arg("--upgrade-package").arg(pkg); + } + } + UvPackageUpgrade::Nothing => {} + } + + self.keyring_provider.add_as_pip_args(cmd); + } +} + +impl Default for UvCompileOptions { + fn default() -> Self { + Self { + allow_prerelease: false, + exclude_newer: None, + upgrade: UvPackageUpgrade::Nothing, + no_deps: false, + no_header: false, + generate_hashes: false, + keyring_provider: KeyringProvider::Disabled, + universal: false, + } + } +} + +pub struct UvSyncOptions { + pub keyring_provider: KeyringProvider, +} + +impl UvSyncOptions { + pub fn add_as_pip_args(self, cmd: &mut Command) { + self.keyring_provider.add_as_pip_args(cmd); + } +} + +impl Default for UvSyncOptions { + fn default() -> Self { + Self { + keyring_provider: KeyringProvider::Disabled, + } + } +} +pub struct UvBuilder { + workdir: Option, + sources: Option, + output: CommandOutput, +} + +impl UvBuilder { + pub fn new() -> Self { + Self { + workdir: None, + sources: None, + output: CommandOutput::Normal, + } + } + + pub fn with_workdir(self, workdir: &Path) -> Self { + Self { + workdir: Some(workdir.to_path_buf()), + ..self + } + } + + pub fn with_sources(self, sources: ExpandedSources) -> Self { + Self { + sources: Some(sources), + ..self + } + } + + pub fn with_output(self, output: CommandOutput) -> Self { + Self { output, ..self } + } + + pub fn ensure_exists(self) -> Result { + let workdir = self.workdir.unwrap_or(std::env::current_dir()?); + let sources = self.sources.unwrap_or_else(ExpandedSources::empty); + Uv::ensure(workdir, sources, self.output) + } +} + // Represents a uv binary and associated functions // to bootstrap rye using uv. #[derive(Clone)] pub struct Uv { output: CommandOutput, uv_bin: PathBuf, + workdir: PathBuf, + sources: ExpandedSources, +} + +impl Default for Uv { + fn default() -> Self { + Uv { + output: CommandOutput::Normal, + uv_bin: PathBuf::new(), + workdir: std::env::current_dir().unwrap_or_default(), + sources: ExpandedSources::empty(), + } + } } impl Uv { @@ -28,16 +185,11 @@ impl Uv { /// and bootstrap it into [RYE_HOME]/uv/[version]/uv. /// /// See [`Uv::cmd`] to get access to the uv binary in a safe way. - /// - /// Example: - /// ```rust - /// use rye::sources::uv::Uv; - /// use rye::utils::CommandOutput; - /// let uv = Uv::ensure_exists(CommandOutput::Normal).expect("Failed to ensure uv binary is available"); - /// let status = uv.cmd().arg("--version").status().expect("Failed to run uv"); - /// assert!(status.success()); - /// ``` - pub fn ensure_exists(output: CommandOutput) -> Result { + fn ensure( + workdir: PathBuf, + sources: ExpandedSources, + output: CommandOutput, + ) -> Result { // Request a download for the default uv binary for this platform. // For instance on aarch64 macos this will request a compatible uv version. let download = UvDownload::try_from(UvRequest::default())?; @@ -52,13 +204,23 @@ impl Uv { }; if uv_dir.exists() && uv_bin.is_file() { - return Ok(Self { uv_bin, output }); + return Ok(Uv { + output, + uv_bin, + workdir, + sources, + }); } Self::download(&download, &uv_dir, output)?; Self::cleanup_old_versions(&base_dir, &uv_dir)?; if uv_dir.exists() && uv_bin.is_file() { - return Ok(Self { uv_bin, output }); + return Ok(Uv { + output, + uv_bin, + workdir, + sources, + }); } Err(anyhow!("Failed to ensure uv binary is available")) @@ -105,10 +267,18 @@ impl Uv { Ok(()) } + /// Set the [`CommandOutput`] level for subsequent invocations of uv. + #[must_use] + pub fn with_output(self, output: CommandOutput) -> Self { + Self { output, ..self } + } + /// Returns a new command with the uv binary as the command to run. /// The command will have the correct proxy settings and verbosity level based on CommandOutput. pub fn cmd(&self) -> Command { let mut cmd = Command::new(&self.uv_bin); + cmd.current_dir(&self.workdir); + cmd.env("PROJECT_ROOT", make_project_root_fragment(&self.workdir)); match self.output { CommandOutput::Verbose => { @@ -125,15 +295,52 @@ impl Uv { cmd } - /// Ensures a venv is exists or is created at the given path. - /// Returns a UvWithVenv that can be used to run commands in the venv. + /// Ensures a venv exists, creating it at the given path if necessary. + /// + /// Returns a [`ReadWriteVenv`] that can be used to run commands in the venv. pub fn venv( &self, venv_dir: &Path, py_bin: &Path, version: &PythonVersion, prompt: Option<&str>, - ) -> Result { + ) -> Result { + match read_venv_marker(venv_dir) { + Some(venv) if venv.is_compatible(version) => { + Ok(ReadWriteVenv::new(self.clone(), venv_dir, version)) + } + _ => self.create_venv(venv_dir, py_bin, version, prompt), + } + } + + /// Returns a [`ReadOnlyVenv`] that can be used to run commands in the venv. + /// + /// Returns an error if the venv does not exist. + pub fn read_only_venv(&self, venv_dir: &Path) -> Result { + if venv_dir.is_dir() { + Ok(ReadOnlyVenv::new(self.clone(), venv_dir)) + } else { + Err(anyhow!( + "Virtualenv not found at path: {}", + venv_dir.display() + )) + } + } + + /// Get uv binary path + /// + /// Warning: Always use self.cmd() when at all possible + pub fn uv_bin(&self) -> &Path { + &self.uv_bin + } + + fn create_venv( + &self, + venv_dir: &Path, + py_bin: &Path, + version: &PythonVersion, + prompt: Option<&str>, + ) -> Result { let mut cmd = self.cmd(); cmd.arg("venv").arg("--python").arg(py_bin); if let Some(prompt) = prompt { @@ -144,7 +351,7 @@ impl Uv { format!( "unable to create self venv using {}. It might be that \ the used Python build is incompatible with this machine. \ - For more information see https://rye-up.com/guide/installation/", + For more information see https://rye.astral.sh/guide/installation/", py_bin.display() ) })?; @@ -156,62 +363,167 @@ impl Uv { status )); } - Ok(UvWithVenv::new(self.clone(), venv_dir, version)) + Ok(ReadWriteVenv::new(self.clone(), venv_dir, version)) + } + + #[allow(clippy::too_many_arguments)] + pub fn lockfile( + &self, + py_version: &PythonVersion, + source: &Path, + target: &Path, + allow_prerelease: bool, + exclude_newer: Option, + upgrade: UvPackageUpgrade, + keyring_provider: KeyringProvider, + generate_hashes: bool, + universal: bool, + ) -> Result<(), Error> { + let options = UvCompileOptions { + allow_prerelease, + exclude_newer, + upgrade, + no_deps: false, + no_header: true, + generate_hashes, + keyring_provider, + universal, + }; + + let mut cmd = self.cmd(); + cmd.arg("pip").arg("compile").env_remove("VIRTUAL_ENV"); + + self.sources.add_as_pip_args(&mut cmd); + options.add_as_pip_args(&mut cmd); + + cmd.arg("--python-version") + .arg(py_version.format_simple()) + .arg("--output-file") + .arg(target); + + cmd.arg(source); + + let status = cmd.status().with_context(|| { + format!( + "Unable to run uv pip compile and generate {}", + target.to_str().unwrap_or("") + ) + })?; + + if !status.success() { + return Err(anyhow!( + "Failed to run uv compile {}. uv exited with status: {}", + target.to_str().unwrap_or(""), + status + )); + } + Ok(()) } } -// Represents a venv generated and managed by uv -pub struct UvWithVenv { +/// Represents uv with any venv +/// +/// Methods on this type are not allowed to create or modify the underlying virtualenv +pub struct ReadOnlyVenv { + uv: Uv, + venv_path: PathBuf, +} + +/// Represents a venv generated and managed by uv +pub struct ReadWriteVenv { uv: Uv, venv_path: PathBuf, py_version: PythonVersion, } -impl UvWithVenv { - fn new(uv: Uv, venv_dir: &Path, version: &PythonVersion) -> Self { - UvWithVenv { - uv, - py_version: version.clone(), - venv_path: venv_dir.to_path_buf(), - } - } +pub trait Venv { + fn cmd(&self) -> Command; + + fn venv_path(&self) -> &Path; /// Returns a new command with the uv binary as the command to run. /// The command will have the correct proxy settings and verbosity level based on CommandOutput. /// The command will also have the VIRTUAL_ENV environment variable set to the venv path. - pub fn venv_cmd(&self) -> Command { - let mut cmd = self.uv.cmd(); - cmd.env("VIRTUAL_ENV", &self.venv_path); + fn venv_cmd(&self) -> Command { + let mut cmd = self.cmd(); + cmd.env("VIRTUAL_ENV", self.venv_path()); cmd } + /// Freezes the venv. + fn freeze(&self) -> Result<(), Error> { + let status = self + .venv_cmd() + .arg("pip") + .arg("freeze") + .status() + .with_context(|| format!("unable to freeze venv at {}", self.venv_path().display()))?; + + if !status.success() { + return Err(anyhow!( + "Failed to freeze venv at {}. uv exited with status: {}", + self.venv_path().display(), + status + )); + } + + Ok(()) + } +} + +impl Venv for ReadOnlyVenv { + fn cmd(&self) -> Command { + self.uv.cmd() + } + fn venv_path(&self) -> &Path { + &self.venv_path + } +} + +impl Venv for ReadWriteVenv { + fn cmd(&self) -> Command { + self.uv.cmd() + } + fn venv_path(&self) -> &Path { + &self.venv_path + } +} + +impl ReadOnlyVenv { + pub fn new(uv: Uv, venv_dir: &Path) -> Self { + Self { + uv, + venv_path: venv_dir.to_path_buf(), + } + } +} + +impl ReadWriteVenv { + pub fn new(uv: Uv, venv_dir: &Path, version: &PythonVersion) -> Self { + ReadWriteVenv { + uv, + py_version: version.clone(), + venv_path: venv_dir.to_path_buf(), + } + } + /// Writes a rye-venv.json for this venv. pub fn write_marker(&self) -> Result<(), Error> { write_venv_marker(&self.venv_path, &self.py_version) } - /// Updates the venv to the given pip version and requirements. - pub fn update(&self, pip_version: &str, requirements: &str) -> Result<(), Error> { - self.update_pip(pip_version)?; - self.update_requirements(requirements)?; - Ok(()) + /// Set the output level for subsequent invocations of uv. + pub fn with_output(self, output: CommandOutput) -> Self { + Self { + uv: Uv { output, ..self.uv }, + venv_path: self.venv_path, + py_version: self.py_version, + } } - /// Updates the pip version in the venv. - pub fn update_pip(&self, pip_version: &str) -> Result<(), Error> { - self.venv_cmd() - .arg("pip") - .arg("install") - .arg("--upgrade") - .arg(pip_version) - .status() - .with_context(|| { - format!( - "unable to update pip in venv at {}", - self.venv_path.display() - ) - })?; - + /// Install the bootstrap requirements in the venv. + pub fn bootstrap(&self) -> Result<(), Error> { + self.update_requirements(SELF_REQUIREMENTS)?; Ok(()) } @@ -237,6 +549,83 @@ impl UvWithVenv { Ok(()) } + /// Installs the given requirement in the venv. + /// + /// If you provide a list of extras, they will be installed as well. + /// For python 3.7 you are best off setting importlib_workaround to true. + pub fn install( + &self, + requirement: &Requirement, + options: UvInstallOptions, + ) -> Result<(), Error> { + let mut cmd = self.venv_cmd(); + + cmd.arg("pip").arg("install"); + + if options.refresh { + cmd.arg("--refresh"); + } + + options.keyring_provider.add_as_pip_args(&mut cmd); + + self.uv.sources.add_as_pip_args(&mut cmd); + + cmd.arg("--").arg(requirement.to_string()); + + for pkg in options.extras { + cmd.arg(pkg.to_string()); + } + + // We could also include this based on the python version, + // but we really want to leave this to the caller to decide. + if options.importlib_workaround { + cmd.arg("importlib-metadata==6.6.0"); + } + + let status = cmd.status().with_context(|| { + format!( + "unable to install {} in venv at {}", + requirement, + self.venv_path.display() + ) + })?; + + if !status.success() { + return Err(anyhow!( + "Installation of {} failed in venv at {}. uv exited with status: {}", + requirement, + self.venv_path.display(), + status + )); + } + + Ok(()) + } + + /// Syncs the venv + pub fn sync(&self, lockfile: &Path, options: UvSyncOptions) -> Result<(), Error> { + let mut cmd = self.venv_cmd(); + cmd.arg("pip").arg("sync"); + + options.add_as_pip_args(&mut cmd); + + self.uv.sources.add_as_pip_args(&mut cmd); + + let status = cmd + .arg(lockfile) + .status() + .with_context(|| format!("unable to run sync {}", self.venv_path.display()))?; + + if !status.success() { + return Err(anyhow!( + "Installation of dependencies failed in venv at {}. uv exited with status: {}", + self.venv_path.display(), + status + )); + } + Ok(()) + } + /// Writes the tool version to the venv. pub fn write_tool_version(&self, version: u64) -> Result<(), Error> { let tool_version_path = self.venv_path.join("tool-version.txt"); @@ -249,4 +638,63 @@ impl UvWithVenv { pub fn sync_marker(&self) { update_venv_sync_marker(self.uv.output, &self.venv_path) } + + /// Resolves the given requirement and returns the resolved requirement. + /// + /// This will spawn `uv` and read from its stdout. + pub fn resolve( + &self, + py_version: &PythonVersion, + requirement: &Requirement, + allow_prerelease: bool, + exclude_newer: Option, + keyring_provider: KeyringProvider, + ) -> Result { + let mut cmd = self.venv_cmd(); + let options = UvCompileOptions { + allow_prerelease, + exclude_newer, + upgrade: UvPackageUpgrade::Nothing, + no_deps: true, + no_header: true, + generate_hashes: false, + keyring_provider, + universal: false, + }; + + cmd.arg("pip").arg("compile"); + + self.uv.sources.add_as_pip_args(&mut cmd); + options.add_as_pip_args(&mut cmd); + + cmd.arg("--python-version").arg(py_version.format_simple()); + + // We are using stdin so we can create the requirements in memory and don't + // have to create a temporary file. + cmd.arg("-"); + + let mut child = cmd + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + // Write requirement to stdin + let child_stdin = child.stdin.as_mut().unwrap(); + writeln!(child_stdin, "{}", requirement)?; + + let rv = child.wait_with_output()?; + if !rv.status.success() { + let log = String::from_utf8_lossy(&rv.stderr); + return Err(anyhow!( + "Failed to run uv compile {}. uv exited with status: {}", + log, + rv.status + )); + } + + String::from_utf8_lossy(&rv.stdout) + .parse() + .context("unable to parse requirement from uv.") + } } diff --git a/rye/tests/common/mod.rs b/rye/tests/common/mod.rs index 3ffbb45b9f..305b47144e 100644 --- a/rye/tests/common/mod.rs +++ b/rye/tests/common/mod.rs @@ -11,6 +11,8 @@ pub static EXCLUDE_NEWER: &str = "2023-11-18T12:00:00Z"; #[allow(unused)] pub const INSTA_FILTERS: &[(&str, &str)] = &[ + // TODO(charlie): This should be `debug` logging in uv, but it's going to `stdout` on Windows. + (r"Using Python \d+\.\d+\.\d+ environment at: \.venv\n", ""), // general temp folders ( r"(\b[A-Z]:)?[\\/].*?[\\/]\.rye-tests---[^\\/]+[\\/]", @@ -27,7 +29,7 @@ pub const INSTA_FILTERS: &[(&str, &str)] = &[ (r"/tmp/\.tmp\S+", "[TEMP_FILE]"), // windows temp folders (r"\b[A-Z]:\\.*\\Local\\Temp\\\S+", "[TEMP_FILE]"), - (r" in (\d+\.)?\d+(ms|s)\b", " in [EXECUTION_TIME]"), + (r" in (\d+m )?(\d+\.)?\d+(ms|s)\b", " in [EXECUTION_TIME]"), (r"\\\\?([\w\d.])", "/$1"), (r"rye.exe", "rye"), ]; @@ -55,24 +57,39 @@ fn bootstrap_test_rye() -> PathBuf { use-uv = true [default] -toolchain = "cpython@3.12.2" +toolchain = "cpython@3.12.6" "#, ) .unwrap(); } - // fetch the most important interpreters - for version in ["cpython@3.8.17", "cpython@3.11.8", "cpython@3.12.2"] { + // fetch the most important interpreters. Fetch some with and some without + // build info to make sure we cover our grounds here. + for (version, build_info) in [ + ("cpython@3.8.20", false), + ("cpython@3.11.11", true), + ("cpython@3.12.8", false), + ("pypy@3.10.13", false), + ] { if home.join("py").join(version).is_dir() { continue; } let status = Command::new(get_bin()) .env("RYE_HOME", &home) .arg("fetch") + .arg(if build_info { + "--build-info" + } else { + "--no-build-info" + }) .arg(version) .status() .unwrap(); assert!(status.success()); + assert_eq!( + home.join("py").join(version).join("install").is_dir(), + build_info + ); } // make a dummy project to bootstrap it @@ -139,7 +156,7 @@ impl Space { } #[allow(unused)] - pub fn edit_toml, R, F: FnOnce(&mut toml_edit::Document) -> R>( + pub fn load_toml, R, F: FnOnce(&toml_edit::DocumentMut) -> R>( &self, path: P, f: F, @@ -148,7 +165,22 @@ impl Space { let mut doc = if p.is_file() { std::fs::read_to_string(&p).unwrap().parse().unwrap() } else { - toml_edit::Document::default() + toml_edit::DocumentMut::default() + }; + f(&doc) + } + + #[allow(unused)] + pub fn edit_toml, R, F: FnOnce(&mut toml_edit::DocumentMut) -> R>( + &self, + path: P, + f: F, + ) -> R { + let p = self.project_path().join(path.as_ref()); + let mut doc = if p.is_file() { + std::fs::read_to_string(&p).unwrap().parse().unwrap() + } else { + toml_edit::DocumentMut::default() }; let rv = f(&mut doc); fs::create_dir_all(p.parent().unwrap()).ok(); @@ -157,7 +189,7 @@ impl Space { } #[allow(unused)] - pub fn read_toml>(&self, path: P) -> toml_edit::Document { + pub fn read_toml>(&self, path: P) -> toml_edit::DocumentMut { let p = self.project_path().join(path.as_ref()); std::fs::read_to_string(p).unwrap().parse().unwrap() } diff --git a/rye/tests/test-list.rs b/rye/tests/test-list.rs new file mode 100644 index 0000000000..391c1a42ce --- /dev/null +++ b/rye/tests/test-list.rs @@ -0,0 +1,100 @@ +use crate::common::{rye_cmd_snapshot, Space}; +use toml_edit::value; + +mod common; + +#[test] +fn test_basic_list() { + let space = Space::new(); + space.init("my-project"); + + space + .rye_cmd() + .arg("add") + .arg("jinja2") + .status() + .expect("ok"); + + rye_cmd_snapshot!( + space.rye_cmd().arg("list"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + jinja2==3.1.2 + markupsafe==2.1.3 + -e file:[TEMP_PATH]/project + + ----- stderr ----- + "###); +} + +#[test] +fn test_list_not_rye_managed() { + let space = Space::new(); + space.init("my-project"); + + space.edit_toml("pyproject.toml", |doc| { + doc["tool"]["rye"]["managed"] = value(false); + }); + + space + .rye_cmd() + .arg("add") + .arg("jinja2") + .status() + .expect("Add package failed"); + + rye_cmd_snapshot!( + space.rye_cmd().arg("list"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + jinja2==3.1.2 + markupsafe==2.1.3 + -e file:[TEMP_PATH]/project + + ----- stderr ----- + "###); +} + +#[test] +fn test_list_never_overwrite() { + let space = Space::new(); + space.init("my-project"); + + space.rye_cmd().arg("sync").status().expect("Sync failed"); + + let venv_marker = space.read_string(".venv/rye-venv.json"); + assert!( + venv_marker.contains("@3.12"), + "asserting contents of venv marker: {}", + venv_marker + ); + + // Pick different python version + space + .rye_cmd() + .arg("pin") + .arg("3.11") + .status() + .expect("Sync failed"); + + // List keeps the existing virtualenv unchanged + + rye_cmd_snapshot!( + space.rye_cmd().arg("list"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + -e file:[TEMP_PATH]/project + + ----- stderr ----- + "###); + + let venv_marker = space.read_string(".venv/rye-venv.json"); + assert!( + venv_marker.contains("@3.12"), + "asserting contents of venv marker: {}", + venv_marker + ); +} diff --git a/rye/tests/test_add.rs b/rye/tests/test_add.rs index d53003ca6f..b07a76fa87 100644 --- a/rye/tests/test_add.rs +++ b/rye/tests/test_add.rs @@ -1,5 +1,5 @@ +use insta::assert_snapshot; use std::fs; - use toml_edit::{value, ArrayOfTables, Table}; use crate::common::{rye_cmd_snapshot, Space}; @@ -11,14 +11,14 @@ fn test_add_flask() { let space = Space::new(); space.init("my-project"); // add colorama to ensure we have this as a dependency on all platforms - rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("flask==3.0.0").arg("colorama"), @r###" + rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("flask").arg("colorama"), @r###" success: true exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 - Added colorama>=0.4.6 as regular dependency + Python version: cpython@3.12.8 Added flask>=3.0.0 as regular dependency + Added colorama>=0.4.6 as regular dependency Reusing already existing virtualenv Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock @@ -26,9 +26,8 @@ fn test_add_flask() { Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] - Resolved 8 packages in [EXECUTION_TIME] - Downloaded 8 packages in [EXECUTION_TIME] + Resolved 9 packages in [EXECUTION_TIME] + Prepared 9 packages in [EXECUTION_TIME] Installed 9 packages in [EXECUTION_TIME] + blinker==1.7.0 + click==8.1.7 @@ -40,6 +39,58 @@ fn test_add_flask() { + my-project==0.1.0 (from file:[TEMP_PATH]/project) + werkzeug==3.0.1 "###); + + assert_snapshot!(space.read_toml("pyproject.toml")["project"]["dependencies"], @r###" + [ + "flask>=3.0.0", + "colorama>=0.4.6", + ] + "### + ); +} + +#[test] +fn test_add_flask_dotenv() { + let space = Space::new(); + space.init("my-project"); + // add colorama to ensure we have this as a dependency on all platforms + rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("flask[dotenv]").arg("colorama"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Added flask[dotenv]>=3.0.0 as regular dependency + Added colorama>=0.4.6 as regular dependency + Reusing already existing virtualenv + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 10 packages in [EXECUTION_TIME] + Prepared 10 packages in [EXECUTION_TIME] + Installed 10 packages in [EXECUTION_TIME] + + blinker==1.7.0 + + click==8.1.7 + + colorama==0.4.6 + + flask==3.0.0 + + itsdangerous==2.1.2 + + jinja2==3.1.2 + + markupsafe==2.1.3 + + my-project==0.1.0 (from file:[TEMP_PATH]/project) + + python-dotenv==1.0.0 + + werkzeug==3.0.1 + "###); + + assert_snapshot!(space.read_toml("pyproject.toml")["project"]["dependencies"], @r###" + [ + "flask[dotenv]>=3.0.0", + "colorama>=0.4.6", + ] + "### + ); } #[test] @@ -61,9 +112,9 @@ fn test_add_from_find_links() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 - Added colorama>=0.4.6 as regular dependency + Python version: cpython@3.12.8 Added tqdm>=4.66.1 as regular dependency + Added colorama>=0.4.6 as regular dependency Reusing already existing virtualenv Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock @@ -71,14 +122,20 @@ fn test_add_from_find_links() { Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] - Resolved 2 packages in [EXECUTION_TIME] - Downloaded 2 packages in [EXECUTION_TIME] + Resolved 3 packages in [EXECUTION_TIME] + Prepared 3 packages in [EXECUTION_TIME] Installed 3 packages in [EXECUTION_TIME] + colorama==0.4.6 + my-project==0.1.0 (from file:[TEMP_PATH]/project) + tqdm==4.66.1 "###); + + assert_snapshot!(space.read_toml("pyproject.toml")["tool"]["rye"], @r###" + managed = true + dev-dependencies = [] + sources = [{ name = "extra", type = "find-links", url = "https://download.pytorch.org/whl/torch_stable.html" }] + "### + ); } #[test] @@ -93,9 +150,9 @@ fn test_add_flask_wrong_venv_exported() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 + Python version: cpython@3.12.8 + Added flask==3.0.0 as regular dependency Added colorama>=0.4.6 as regular dependency - Added flask>=3.0.0 as regular dependency Reusing already existing virtualenv Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock @@ -103,9 +160,8 @@ fn test_add_flask_wrong_venv_exported() { Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] - Resolved 8 packages in [EXECUTION_TIME] - Downloaded 8 packages in [EXECUTION_TIME] + Resolved 9 packages in [EXECUTION_TIME] + Prepared 9 packages in [EXECUTION_TIME] Installed 9 packages in [EXECUTION_TIME] + blinker==1.7.0 + click==8.1.7 @@ -119,3 +175,95 @@ fn test_add_flask_wrong_venv_exported() { "###); fs::remove_dir_all(&fake_venv).unwrap(); } + +#[test] +fn test_add_explicit_version_or_url() { + let space = Space::new(); + space.init("my-project"); + rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("werkZeug==3.0.0"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Added werkzeug==3.0.0 as regular dependency + Reusing already existing virtualenv + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 3 packages in [EXECUTION_TIME] + Prepared 3 packages in [EXECUTION_TIME] + Installed 3 packages in [EXECUTION_TIME] + + markupsafe==2.1.3 + + my-project==0.1.0 (from file:[TEMP_PATH]/project) + + werkzeug==3.0.0 + "###); + + let pip_url = "https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686"; + rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("pip").arg("--url").arg(pip_url), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Added pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686 as regular dependency + Reusing already existing virtualenv + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 4 packages in [EXECUTION_TIME] + Prepared 2 packages in [EXECUTION_TIME] + Uninstalled 1 package in [EXECUTION_TIME] + Installed 2 packages in [EXECUTION_TIME] + ~ my-project==0.1.0 (from file:[TEMP_PATH]/project) + + pip==1.3.1 (from https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686) + "###); +} + +#[test] +fn test_add_dev() { + let space = Space::new(); + space.init("my-project"); + // add colorama to ensure we have this as a dependency on all platforms + rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("flask").arg("colorama").arg("--dev"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Added flask>=3.0.0 as dev dependency + Added colorama>=0.4.6 as dev dependency + Reusing already existing virtualenv + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 9 packages in [EXECUTION_TIME] + Prepared 9 packages in [EXECUTION_TIME] + Installed 9 packages in [EXECUTION_TIME] + + blinker==1.7.0 + + click==8.1.7 + + colorama==0.4.6 + + flask==3.0.0 + + itsdangerous==2.1.2 + + jinja2==3.1.2 + + markupsafe==2.1.3 + + my-project==0.1.0 (from file:[TEMP_PATH]/project) + + werkzeug==3.0.1 + "###); + + assert_snapshot!(space.read_toml("pyproject.toml")["tool"]["rye"], @r###" + managed = true + dev-dependencies = [ + "flask>=3.0.0", + "colorama>=0.4.6", + ] + "### + ); +} diff --git a/rye/tests/test_cli.rs b/rye/tests/test_cli.rs new file mode 100644 index 0000000000..8db0b6a014 --- /dev/null +++ b/rye/tests/test_cli.rs @@ -0,0 +1,52 @@ +use std::fs; + +use toml_edit::value; + +use crate::common::{rye_cmd_snapshot, Space}; + +mod common; + +#[test] +fn test_dotenv() { + let space = Space::new(); + space.init("my-project"); + space.edit_toml("pyproject.toml", |doc| { + doc["tool"]["rye"]["scripts"]["hello"]["cmd"] = + value("python -c \"import os; print(os.environ['MY_COOL_VAR'], os.environ['MY_COOL_OTHER_VAR'])\""); + doc["tool"]["rye"]["scripts"]["hello"]["env-file"] = value(".other.env"); + }); + fs::write(space.project_path().join(".env"), "MY_COOL_VAR=42").unwrap(); + fs::write( + space.project_path().join(".other.env"), + "MY_COOL_OTHER_VAR=23", + ) + .unwrap(); + rye_cmd_snapshot!(space.rye_cmd().arg("sync"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 1 package in [EXECUTION_TIME] + Prepared 1 package in [EXECUTION_TIME] + Installed 1 package in [EXECUTION_TIME] + + my-project==0.1.0 (from file:[TEMP_PATH]/project) + "###); + rye_cmd_snapshot!(space.rye_cmd() + .arg("--env-file=.env") + .arg("run") + .arg("hello"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + 42 23 + + ----- stderr ----- + "###); +} diff --git a/rye/tests/test_config.rs b/rye/tests/test_config.rs index 3e04ecd952..6b6910adaa 100644 --- a/rye/tests/test_config.rs +++ b/rye/tests/test_config.rs @@ -1,3 +1,7 @@ +use std::fs; + +use insta::assert_snapshot; + use crate::common::{rye_cmd_snapshot, Space}; mod common; @@ -109,3 +113,24 @@ fn test_config_show_path_and_any_action() { error: an argument cannot be used with one or more of the other specified arguments "###); } + +#[test] +fn test_config_save_missing_folder() { + let space = Space::new(); + let fake_home = space.project_path().join("missing-thing"); + rye_cmd_snapshot!(space.rye_cmd() + .arg("config") + .arg("--set") + .arg("default.toolchain=cpython@3.12") + .env("RYE_HOME", fake_home.as_os_str()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "###); + assert_snapshot!(fs::read_to_string(fake_home.join("config.toml")).unwrap(), @r###" + [default] + toolchain = "cpython@3.12" + "###); +} diff --git a/rye/tests/test_init.rs b/rye/tests/test_init.rs index 9e636985d0..b203b5ab47 100644 --- a/rye/tests/test_init.rs +++ b/rye/tests/test_init.rs @@ -22,14 +22,15 @@ fn test_init_lib() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 + Python version: cpython@3.12.8 Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock Installing dependencies Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] + Resolved 1 package in [EXECUTION_TIME] + Prepared 1 package in [EXECUTION_TIME] Installed 1 package in [EXECUTION_TIME] + my-project==0.1.0 (from file:[TEMP_PATH]/project) "###); @@ -70,14 +71,15 @@ fn test_init_default() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 + Python version: cpython@3.12.8 Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock Installing dependencies Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] + Resolved 1 package in [EXECUTION_TIME] + Prepared 1 package in [EXECUTION_TIME] Installed 1 package in [EXECUTION_TIME] + my-project==0.1.0 (from file:[TEMP_PATH]/project) "###); @@ -119,19 +121,20 @@ fn test_init_script() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 + Python version: cpython@3.12.8 Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock Installing dependencies Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] + Resolved 1 package in [EXECUTION_TIME] + Prepared 1 package in [EXECUTION_TIME] Installed 1 package in [EXECUTION_TIME] + my-project==0.1.0 (from file:[TEMP_PATH]/project) "###); - rye_cmd_snapshot!(space.rye_cmd().arg("run").arg("hello"), @r###" + rye_cmd_snapshot!(space.rye_cmd().arg("run").arg("my-project"), @r###" success: true exit_code: 0 ----- stdout ----- diff --git a/rye/tests/test_publish.rs b/rye/tests/test_publish.rs new file mode 100644 index 0000000000..a5d4ebffa5 --- /dev/null +++ b/rye/tests/test_publish.rs @@ -0,0 +1,31 @@ +use crate::common::{rye_cmd_snapshot, Space}; + +mod common; + +#[test] +fn test_publish_outside_project() { + let space = Space::new(); + space.init("my-project"); + + let status = space.rye_cmd().arg("build").status().unwrap(); + assert!(status.success()); + + // Publish outside the project. + // Since we provide a fake token, the failure is expected. + rye_cmd_snapshot!(space + .rye_cmd() + .arg("publish") + .arg("--yes") + .arg("--token") + .arg("fake-token") + .arg("--quiet") + .current_dir(space.project_path().parent().unwrap()) + .arg(space.project_path().join("dist").join("*")), @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + error: failed to publish files + "###); +} diff --git a/rye/tests/test_ruff.rs b/rye/tests/test_ruff.rs index 1d2a9027bd..08b19f3404 100644 --- a/rye/tests/test_ruff.rs +++ b/rye/tests/test_ruff.rs @@ -9,7 +9,10 @@ fn test_lint_and_format() { let space = Space::new(); space.init("my-project"); space.write( - "src/my_project/__init__.py", + // `test.py` is used instead of `__init__.py` to make ruff consider it a fixable + // issue instead of requiring user intervention. + // ref: https://github.com/astral-sh/ruff/pull/11168 + "src/my_project/test.py", r#"import os def hello(): @@ -24,8 +27,22 @@ def hello(): success: false exit_code: 1 ----- stdout ----- - src/my_project/__init__.py:1:8: F401 [*] `os` imported but unused - src/my_project/__init__.py:6:25: E703 [*] Statement ends with an unnecessary semicolon + src/my_project/test.py:1:8: F401 [*] `os` imported but unused + | + 1 | import os + | ^^ F401 + 2 | + 3 | def hello(): + | + = help: Remove unused import: `os` + + src/my_project/test.py:6:25: E703 [*] Statement ends with an unnecessary semicolon + | + 6 | return "Hello World"; + | ^ E703 + | + = help: Remove unnecessary semicolon + Found 2 errors. [*] 2 fixable with the `--fix` option. @@ -39,7 +56,7 @@ def hello(): ----- stderr ----- "###); - assert_snapshot!(space.read_string("src/my_project/__init__.py"), @r###" + assert_snapshot!(space.read_string("src/my_project/test.py"), @r###" def hello(): @@ -48,12 +65,13 @@ def hello(): "###); // fmt next + // Already reformatted file mentioned bellow is `__init__.py` rye_cmd_snapshot!(space.rye_cmd().arg("fmt").arg("--check"), @r###" success: false exit_code: 1 ----- stdout ----- - Would reformat: src/my_project/__init__.py - 1 file would be reformatted + Would reformat: src/my_project/test.py + 1 file would be reformatted, 1 file already formatted ----- stderr ----- "###); @@ -61,11 +79,11 @@ def hello(): success: true exit_code: 0 ----- stdout ----- - 1 file reformatted + 1 file reformatted, 1 file left unchanged ----- stderr ----- "###); - assert_snapshot!(space.read_string("src/my_project/__init__.py"), @r###" + assert_snapshot!(space.read_string("src/my_project/test.py"), @r###" def hello(): return "Hello World" "###); diff --git a/rye/tests/test_scripts.rs b/rye/tests/test_scripts.rs new file mode 100644 index 0000000000..dcdc2e63f1 --- /dev/null +++ b/rye/tests/test_scripts.rs @@ -0,0 +1,39 @@ +use toml_edit::value; + +use crate::common::{rye_cmd_snapshot, Space}; + +mod common; + +#[test] +fn test_basic_script() { + let mut settings = insta::Settings::clone_current(); + settings.add_filter(r"(?m)(^py[a-z\d._]+$\r?\n)+", "[PYTHON SCRIPTS]\n"); + let _guard = settings.bind_to_scope(); + + let space = Space::new(); + space.init("my-project"); + space.edit_toml("pyproject.toml", |doc| { + doc["tool"]["rye"]["scripts"]["test-script"] = value("python -c 'print(\"Hello World\")'"); + }); + + rye_cmd_snapshot!(space.rye_cmd().arg("run").arg("test-script"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Hello World + + ----- stderr ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + "###); + + rye_cmd_snapshot!(space.rye_cmd().arg("run"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + [PYTHON SCRIPTS] + test-script (python -c 'print("Hello World")') + + ----- stderr ----- + "###); +} diff --git a/rye/tests/test_self.rs b/rye/tests/test_self.rs index 7fcd228a49..084d95f661 100644 --- a/rye/tests/test_self.rs +++ b/rye/tests/test_self.rs @@ -1,4 +1,6 @@ -use crate::common::Space; +use insta::Settings; + +use crate::common::{rye_cmd_snapshot, Space}; mod common; // This test is self-destructive, making other tests slow, ignore it by default. @@ -42,3 +44,29 @@ fn test_self_uninstall() { .collect(); assert!(leftovers.is_empty(), "leftovers: {:?}", leftovers); } + +#[test] +fn test_version() { + let space = Space::new(); + let _guard = space.lock_rye_home(); + + let mut settings = Settings::clone_current(); + settings.add_filter(r"(?m)^(rye )\d+\.\d+\.\d+$", "$1[VERSION]"); + settings.add_filter(r"(?m)^(commit: ).*", "$1[COMMIT]"); + settings.add_filter(r"(?m)^(platform: ).*", "$1[PLATFORM]"); + let _guard = settings.bind_to_scope(); + + rye_cmd_snapshot!(space.rye_cmd().arg("--version"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + rye [VERSION] + commit: [COMMIT] + platform: [PLATFORM] + self-python: cpython@3.12.8 + symlink support: true + uv enabled: true + + ----- stderr ----- + "###); +} diff --git a/rye/tests/test_sync.rs b/rye/tests/test_sync.rs index 5c35f1757f..797c408716 100644 --- a/rye/tests/test_sync.rs +++ b/rye/tests/test_sync.rs @@ -1,5 +1,7 @@ use std::fs; +use insta::{assert_snapshot, Settings}; + use crate::common::{rye_cmd_snapshot, Space}; mod common; @@ -13,14 +15,15 @@ fn test_empty_sync() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 + Python version: cpython@3.12.8 Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock Installing dependencies Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] + Resolved 1 package in [EXECUTION_TIME] + Prepared 1 package in [EXECUTION_TIME] Installed 1 package in [EXECUTION_TIME] + my-project==0.1.0 (from file:[TEMP_PATH]/project) "###); @@ -30,7 +33,7 @@ fn test_empty_sync() { { let script = space.venv_path().join("bin/activate"); let contents = fs::read_to_string(script).unwrap(); - assert!(contents.contains("VIRTUAL_ENV_PROMPT=\"my-project\"")); + assert!(contents.contains("VIRTUAL_ENV_PROMPT=\"(my-project) \"")); } #[cfg(windows)] { @@ -51,9 +54,9 @@ fn test_add_and_sync_no_auto_sync() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 + Python version: cpython@3.12.8 + Added flask==3.0.0 as regular dependency Added colorama>=0.4.6 as regular dependency - Added flask>=3.0.0 as regular dependency ----- stderr ----- "###); @@ -68,9 +71,8 @@ fn test_add_and_sync_no_auto_sync() { Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] - Resolved 8 packages in [EXECUTION_TIME] - Downloaded 8 packages in [EXECUTION_TIME] + Resolved 9 packages in [EXECUTION_TIME] + Prepared 9 packages in [EXECUTION_TIME] Installed 9 packages in [EXECUTION_TIME] + blinker==1.7.0 + click==8.1.7 @@ -94,9 +96,9 @@ fn test_add_autosync() { exit_code: 0 ----- stdout ----- Initializing new virtualenv in [TEMP_PATH]/project/.venv - Python version: cpython@3.12.2 + Python version: cpython@3.12.8 + Added flask==3.0.0 as regular dependency Added colorama>=0.4.6 as regular dependency - Added flask>=3.0.0 as regular dependency Reusing already existing virtualenv Generating production lockfile: [TEMP_PATH]/project/requirements.lock Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock @@ -104,9 +106,8 @@ fn test_add_autosync() { Done! ----- stderr ----- - Built 1 editable in [EXECUTION_TIME] - Resolved 8 packages in [EXECUTION_TIME] - Downloaded 8 packages in [EXECUTION_TIME] + Resolved 9 packages in [EXECUTION_TIME] + Prepared 9 packages in [EXECUTION_TIME] Installed 9 packages in [EXECUTION_TIME] + blinker==1.7.0 + click==8.1.7 @@ -119,3 +120,355 @@ fn test_add_autosync() { + werkzeug==3.0.1 "###); } + +// TODO(charlie): This started failing on Windows in https://github.com/astral-sh/rye/pull/1347, +// likely due to a difference in path canonicalization. +#[test] +#[cfg(unix)] +fn test_autosync_remember() { + // remove the dependency source markers since they are instable between platforms + let mut settings = Settings::clone_current(); + settings.add_filter(r"(?m)^\s+# via .*\r?\n", ""); + settings.add_filter(r"(?m)^(\s+)\d+\.\d+s( \d+ms)?", "$1[TIMING]"); + let _guard = settings.bind_to_scope(); + + let space = Space::new(); + space.init("my-project"); + rye_cmd_snapshot!(space.rye_cmd().arg("sync").arg("--with-sources").arg("--all-features"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 1 package in [EXECUTION_TIME] + Prepared 1 package in [EXECUTION_TIME] + Installed 1 package in [EXECUTION_TIME] + + my-project==0.1.0 (from file:[TEMP_PATH]/project) + "###); + + rye_cmd_snapshot!(space.rye_cmd() + .arg("add").arg("--optional=web").arg("flask==3.0.0").arg("colorama"), + @r###" + success: true + exit_code: 0 + ----- stdout ----- + Added flask==3.0.0 as optional (web) dependency + Added colorama>=0.4.6 as optional (web) dependency + Reusing already existing virtualenv + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 9 packages in [EXECUTION_TIME] + Prepared 9 packages in [EXECUTION_TIME] + Uninstalled 1 package in [EXECUTION_TIME] + Installed 9 packages in [EXECUTION_TIME] + + blinker==1.7.0 + + click==8.1.7 + + colorama==0.4.6 + + flask==3.0.0 + + itsdangerous==2.1.2 + + jinja2==3.1.2 + + markupsafe==2.1.3 + ~ my-project==0.1.0 (from file:[TEMP_PATH]/project) + + werkzeug==3.0.1 + "###); + assert_snapshot!(std::fs::read_to_string(space.project_path().join("requirements.lock")).unwrap(), @r###" + # generated by rye + # use `rye lock` or `rye sync` to update this lockfile + # + # last locked with the following flags: + # pre: false + # features: [] + # all-features: true + # with-sources: true + # generate-hashes: false + # universal: false + + --index-url https://pypi.org/simple/ + + -e file:. + blinker==1.7.0 + click==8.1.7 + colorama==0.4.6 + flask==3.0.0 + itsdangerous==2.1.2 + jinja2==3.1.2 + markupsafe==2.1.3 + werkzeug==3.0.1 + "###); + + rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("urllib3"), + @r###" + success: true + exit_code: 0 + ----- stdout ----- + Added urllib3>=2.1.0 as regular dependency + Reusing already existing virtualenv + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 10 packages in [EXECUTION_TIME] + Prepared 2 packages in [EXECUTION_TIME] + Uninstalled 1 package in [EXECUTION_TIME] + Installed 2 packages in [EXECUTION_TIME] + ~ my-project==0.1.0 (from file:[TEMP_PATH]/project) + + urllib3==2.1.0 + "###); + + // would be nice to assert on the non quiet output here but unfortunately + // on CI we seem to have some flakage on this command with regards to + // rebuilding the editable. + rye_cmd_snapshot!(space.rye_cmd().arg("sync").arg("-q"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "###); + + assert_snapshot!(std::fs::read_to_string(space.project_path().join("requirements.lock")).unwrap(), @r###" + # generated by rye + # use `rye lock` or `rye sync` to update this lockfile + # + # last locked with the following flags: + # pre: false + # features: [] + # all-features: true + # with-sources: true + # generate-hashes: false + # universal: false + + --index-url https://pypi.org/simple/ + + -e file:. + blinker==1.7.0 + click==8.1.7 + colorama==0.4.6 + flask==3.0.0 + itsdangerous==2.1.2 + jinja2==3.1.2 + markupsafe==2.1.3 + urllib3==2.1.0 + werkzeug==3.0.1 + "###); +} + +#[test] +fn test_exclude_hashes() { + let space = Space::new(); + space.init("my-project"); + + fs::write( + space.project_path().join("pyproject.toml"), + r###" + [project] + name = "exclude-rye-test" + version = "0.1.0" + dependencies = ["anyio==4.0.0"] + readme = "README.md" + requires-python = ">= 3.8" + + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [tool.rye] + generate-hashes = true + excluded-dependencies = ["idna"] + + [tool.hatch.metadata] + allow-direct-references = true + + [tool.hatch.build.targets.wheel] + packages = ["src/exclude_rye_test"] + "###, + ) + .unwrap(); + + rye_cmd_snapshot!(space.rye_cmd().arg("sync"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 3 packages in [EXECUTION_TIME] + Prepared 3 packages in [EXECUTION_TIME] + Installed 3 packages in [EXECUTION_TIME] + + anyio==4.0.0 + + exclude-rye-test==0.1.0 (from file:[TEMP_PATH]/project) + + sniffio==1.3.0 + "###); + + assert_snapshot!(space.read_string(space.project_path().join("requirements.lock")), @r###" + # generated by rye + # use `rye lock` or `rye sync` to update this lockfile + # + # last locked with the following flags: + # pre: false + # features: [] + # all-features: false + # with-sources: false + # generate-hashes: true + # universal: false + + -e file:. + anyio==4.0.0 \ + --hash=sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f \ + --hash=sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a + # via exclude-rye-test + # idna==3.4 (excluded) + # via anyio + sniffio==1.3.0 \ + --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \ + --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384 + # via anyio + "###); +} + +#[test] +fn test_lockfile() { + let space = Space::new(); + space.init("my-project"); + + rye_cmd_snapshot!(space.rye_cmd().arg("add").arg("anyio==4.0.0"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Added anyio==4.0.0 as regular dependency + Reusing already existing virtualenv + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 4 packages in [EXECUTION_TIME] + Prepared 4 packages in [EXECUTION_TIME] + Installed 4 packages in [EXECUTION_TIME] + + anyio==4.0.0 + + idna==3.4 + + my-project==0.1.0 (from file:[TEMP_PATH]/project) + + sniffio==1.3.0 + "###); + + assert_snapshot!(space.read_string(space.project_path().join("requirements.lock")), @r###" + # generated by rye + # use `rye lock` or `rye sync` to update this lockfile + # + # last locked with the following flags: + # pre: false + # features: [] + # all-features: false + # with-sources: false + # generate-hashes: false + # universal: false + + -e file:. + anyio==4.0.0 + # via my-project + idna==3.4 + # via anyio + sniffio==1.3.0 + # via anyio + "###); +} + +#[test] +fn test_generate_hashes() { + let space = Space::new(); + space.init("my-project"); + + fs::write( + space.project_path().join("pyproject.toml"), + r###" + [project] + name = "exclude-rye-test" + version = "0.1.0" + dependencies = ["anyio==4.0.0"] + readme = "README.md" + requires-python = ">= 3.8" + + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [tool.rye] + generate-hashes = true + + [tool.hatch.metadata] + allow-direct-references = true + + [tool.hatch.build.targets.wheel] + packages = ["src/exclude_rye_test"] + "###, + ) + .unwrap(); + + rye_cmd_snapshot!(space.rye_cmd().arg("sync"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + + ----- stderr ----- + Resolved 4 packages in [EXECUTION_TIME] + Prepared 4 packages in [EXECUTION_TIME] + Installed 4 packages in [EXECUTION_TIME] + + anyio==4.0.0 + + exclude-rye-test==0.1.0 (from file:[TEMP_PATH]/project) + + idna==3.4 + + sniffio==1.3.0 + "###); + + assert_snapshot!(space.read_string(space.project_path().join("requirements.lock")), @r###" + # generated by rye + # use `rye lock` or `rye sync` to update this lockfile + # + # last locked with the following flags: + # pre: false + # features: [] + # all-features: false + # with-sources: false + # generate-hashes: true + # universal: false + + -e file:. + anyio==4.0.0 \ + --hash=sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f \ + --hash=sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a + # via exclude-rye-test + idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via anyio + sniffio==1.3.0 \ + --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \ + --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384 + # via anyio + "###); +} diff --git a/rye/tests/test_test.rs b/rye/tests/test_test.rs new file mode 100644 index 0000000000..f829d859d4 --- /dev/null +++ b/rye/tests/test_test.rs @@ -0,0 +1,151 @@ +use std::fs; + +use insta::Settings; +use toml_edit::{value, Array}; + +use crate::common::{rye_cmd_snapshot, Space}; + +mod common; + +const BASIC_TEST: &str = r#" +def test_okay(): + pass + +def test_fail(): + 1 / 0 +"#; + +// pytest for weird reasons has different formatting behavior on +// different platforms -.- +#[cfg(windows)] +const PYTEST_COLS: &str = "80"; +#[cfg(unix)] +const PYTEST_COLS: &str = "79"; + +#[test] +fn test_basic_tool_behavior() { + // fixes issues for rendering between platforms + let mut settings = Settings::clone_current(); + settings.add_filter(r"(?m)^(platform )(.*?)( --)", "$1[PLATFORM]$3"); + settings.add_filter(r"(?m)\s+(\[\d+%\])\s*?$", " $1"); + let _guard = settings.bind_to_scope(); + + let space = Space::new(); + space.init("foo"); + space.edit_toml("pyproject.toml", |doc| { + let mut deps = Array::new(); + deps.push("pytest>=7.0.0"); + deps.push("colorama==0.4.6"); + let mut workspace_members = Array::new(); + workspace_members.push("."); + workspace_members.push("child-dep"); + doc["tool"]["rye"]["dev-dependencies"] = value(deps); + doc["tool"]["rye"]["workspace"]["members"] = value(workspace_members); + }); + let status = space + .rye_cmd() + .arg("init") + .arg("-q") + .arg(space.project_path().join("child-dep")) + .status() + .unwrap(); + assert!(status.success()); + + let root_tests = space.project_path().join("tests"); + fs::create_dir_all(&root_tests).unwrap(); + fs::write(root_tests.join("test_foo.py"), BASIC_TEST).unwrap(); + + let child_tests = space.project_path().join("child-dep").join("tests"); + fs::create_dir_all(&child_tests).unwrap(); + fs::write(child_tests.join("test_child.py"), BASIC_TEST).unwrap(); + + rye_cmd_snapshot!(space.rye_cmd().arg("test").env("COLUMNS", PYTEST_COLS), @r###" + success: false + exit_code: 1 + ----- stdout ----- + Initializing new virtualenv in [TEMP_PATH]/project/.venv + Python version: cpython@3.12.8 + Generating production lockfile: [TEMP_PATH]/project/requirements.lock + Generating dev lockfile: [TEMP_PATH]/project/requirements-dev.lock + Installing dependencies + Done! + Running tests for foo ([TEMP_PATH]/project) + ============================= test session starts ============================= + platform [PLATFORM] -- Python 3.12.8, pytest-7.4.3, pluggy-1.3.0 + rootdir: [TEMP_PATH]/project + collected 2 items + + tests/test_foo.py .F [100%] + + ================================== FAILURES =================================== + __________________________________ test_fail __________________________________ + + def test_fail(): + > 1 / 0 + E ZeroDivisionError: division by zero + + tests/test_foo.py:6: ZeroDivisionError + =========================== short test summary info =========================== + FAILED tests/test_foo.py::test_fail - ZeroDivisionError: division by zero + ========================= 1 failed, 1 passed in [EXECUTION_TIME] ========================= + + ----- stderr ----- + Resolved 7 packages in [EXECUTION_TIME] + Prepared 7 packages in [EXECUTION_TIME] + Installed 7 packages in [EXECUTION_TIME] + + child-dep==0.1.0 (from file:[TEMP_PATH]/project/child-dep) + + colorama==0.4.6 + + foo==0.1.0 (from file:[TEMP_PATH]/project) + + iniconfig==2.0.0 + + packaging==23.2 + + pluggy==1.3.0 + + pytest==7.4.3 + "###); + + rye_cmd_snapshot!(space.rye_cmd().arg("test").arg("--all").env("COLUMNS", PYTEST_COLS), @r###" + success: false + exit_code: 1 + ----- stdout ----- + Running tests for child-dep ([TEMP_PATH]/project/child-dep) + ============================= test session starts ============================= + platform [PLATFORM] -- Python 3.12.8, pytest-7.4.3, pluggy-1.3.0 + rootdir: [TEMP_PATH]/project/child-dep + collected 2 items + + tests/test_child.py .F [100%] + + ================================== FAILURES =================================== + __________________________________ test_fail __________________________________ + + def test_fail(): + > 1 / 0 + E ZeroDivisionError: division by zero + + tests/test_child.py:6: ZeroDivisionError + =========================== short test summary info =========================== + FAILED tests/test_child.py::test_fail - ZeroDivisionError: division by zero + ========================= 1 failed, 1 passed in [EXECUTION_TIME] ========================= + + Running tests for foo ([TEMP_PATH]/project) + ============================= test session starts ============================= + platform [PLATFORM] -- Python 3.12.8, pytest-7.4.3, pluggy-1.3.0 + rootdir: [TEMP_PATH]/project + collected 2 items + + tests/test_foo.py .F [100%] + + ================================== FAILURES =================================== + __________________________________ test_fail __________________________________ + + def test_fail(): + > 1 / 0 + E ZeroDivisionError: division by zero + + tests/test_foo.py:6: ZeroDivisionError + =========================== short test summary info =========================== + FAILED tests/test_foo.py::test_fail - ZeroDivisionError: division by zero + ========================= 1 failed, 1 passed in [EXECUTION_TIME] ========================= + + ----- stderr ----- + "###); +} diff --git a/rye/tests/test_tools.rs b/rye/tests/test_tools.rs index 43cee4a9d8..93c3ae3f62 100644 --- a/rye/tests/test_tools.rs +++ b/rye/tests/test_tools.rs @@ -1,5 +1,6 @@ use std::env::consts::EXE_EXTENSION; use std::fs; +use tempfile::TempDir; use crate::common::{rye_cmd_snapshot, Space}; @@ -9,6 +10,9 @@ mod common; fn test_basic_tool_behavior() { let space = Space::new(); + // Cache alongside the home directory, so that the cache lives alongside the tools directory. + let cache_dir = TempDir::new_in(space.rye_home()).unwrap(); + // in case we left things behind from last run. fs::remove_dir_all(space.rye_home().join("tools")).ok(); fs::remove_file( @@ -22,6 +26,7 @@ fn test_basic_tool_behavior() { rye_cmd_snapshot!( space.rye_cmd() + .env("UV_CACHE_DIR", cache_dir.path()) .arg("tools") .arg("install") .arg("pycowsay") @@ -35,14 +40,16 @@ fn test_basic_tool_behavior() { - pycowsay ----- stderr ----- + Using Python 3.11.11 environment at: [RYE_HOME]/tools/pycowsay Resolved 1 package in [EXECUTION_TIME] - Downloaded 1 package in [EXECUTION_TIME] + Prepared 1 package in [EXECUTION_TIME] Installed 1 package in [EXECUTION_TIME] + pycowsay==0.0.0.2 "###); rye_cmd_snapshot!( space.rye_cmd() + .env("UV_CACHE_DIR", cache_dir.path()) .arg("tools") .arg("list"), @r###" success: true @@ -55,32 +62,35 @@ fn test_basic_tool_behavior() { rye_cmd_snapshot!( space.rye_cmd() + .env("UV_CACHE_DIR", cache_dir.path()) .arg("tools") .arg("list") .arg("--include-version"), @r###" success: true exit_code: 0 ----- stdout ----- - pycowsay 0.0.0.2 (cpython@3.11.8) + pycowsay 0.0.0.2 (cpython@3.11.11) ----- stderr ----- "###); rye_cmd_snapshot!( space.rye_cmd() + .env("UV_CACHE_DIR", cache_dir.path()) .arg("toolchain") .arg("remove") - .arg("cpython@3.11.8"), @r###" + .arg("cpython@3.11.11"), @r###" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- - error: toolchain cpython@3.11.8 is still in use by tool pycowsay + error: toolchain cpython@3.11.11 is still in use by tool pycowsay "###); rye_cmd_snapshot!( space.rye_cmd() + .env("UV_CACHE_DIR", cache_dir.path()) .arg("tools") .arg("uninstall") .arg("pycowsay"), @r###" diff --git a/rye/tests/test_version.rs b/rye/tests/test_version.rs new file mode 100644 index 0000000000..44114c7376 --- /dev/null +++ b/rye/tests/test_version.rs @@ -0,0 +1,49 @@ +use crate::common::{rye_cmd_snapshot, Space}; + +mod common; + +#[test] +fn test_version_show() { + let space = Space::new(); + space.init("my-project"); + rye_cmd_snapshot!(space.rye_cmd().arg("version"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + 0.1.0 + + ----- stderr ----- + "###); +} + +#[test] +fn test_version_bump() { + let space = Space::new(); + space.init("my-project"); + rye_cmd_snapshot!(space.rye_cmd().arg("version").arg("--bump").arg("patch"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + version bumped to 0.1.1 + + ----- stderr ----- + "###); + + rye_cmd_snapshot!(space.rye_cmd().arg("version").arg("--bump").arg("minor"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + version bumped to 0.2.0 + + ----- stderr ----- + "###); + + rye_cmd_snapshot!(space.rye_cmd().arg("version").arg("--bump").arg("major"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + version bumped to 1.0.0 + + ----- stderr ----- + "###); +} diff --git a/scripts/sha256.py b/scripts/sha256.py index 448a02bfe4..ddd80d1fa6 100644 --- a/scripts/sha256.py +++ b/scripts/sha256.py @@ -1,5 +1,5 @@ -import sys import hashlib +import sys h = hashlib.sha256() diff --git a/scripts/summarize-release.py b/scripts/summarize-release.py index 00eea3f102..91657861c6 100644 --- a/scripts/summarize-release.py +++ b/scripts/summarize-release.py @@ -1,7 +1,6 @@ +import json import os import sys -import json - version = sys.argv[1] base = sys.argv[2]