diff --git a/.github/workflows/artifact.yaml b/.github/workflows/artifact.yaml index eae5339d..17647db3 100644 --- a/.github/workflows/artifact.yaml +++ b/.github/workflows/artifact.yaml @@ -11,6 +11,7 @@ jobs: sdist: runs-on: ubuntu-22.04 + timeout-minutes: 10 strategy: fail-fast: false env: @@ -77,7 +78,7 @@ jobs: { cc: "clang", cflags: "-Os -fstrict-aliasing -fno-plt -flto=full -emit-llvm", - features: "avx512,no-panic,unstable-simd,yyjson", + features: "avx512,unstable-simd,yyjson", ldflags: "-fuse-ld=lld -Wl,-plugin-opt=also-emit-llvm -Wl,--as-needed -Wl,-zrelro,-znow", rustflags: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z threads=4 -D warnings", tag: null, @@ -85,17 +86,17 @@ jobs: }, ] env: - PYTHON: "${{ matrix.python.interpreter }}" - PYTHON_PACKAGE: "${{ matrix.python.package }}" - TARGET: "${{ matrix.arch.target }}" + CARGO_TARGET_DIR: "/tmp/orjson" CC: "${{ matrix.arch.cc }}" - VENV: ".venv" - FEATURES: "${{ matrix.arch.features }}" CFLAGS: "${{ matrix.arch.cflags }}" + COMPATIBILITY: "${{ matrix.python.compatibility }}" + FEATURES: "${{ matrix.arch.features }}" LDFLAGS: "${{ matrix.arch.ldflags }}" + PYTHON: "${{ matrix.python.interpreter }}" + PYTHON_PACKAGE: "${{ matrix.python.package }}" RUSTFLAGS: "${{ matrix.arch.rustflags }}" - CARGO_TARGET_DIR: "/tmp/orjson" - COMPATIBILITY: "${{ matrix.python.compatibility }}" + TARGET: "${{ matrix.arch.target }}" + VENV: ".venv" steps: - name: cpuinfo @@ -167,7 +168,7 @@ jobs: { cc: "clang", cflags: "-Os -fstrict-aliasing -fno-plt -flto=full -emit-llvm", - features: "no-panic,unstable-simd,yyjson", + features: "unstable-simd,yyjson", ldflags: "-fuse-ld=lld -Wl,-plugin-opt=also-emit-llvm -Wl,--as-needed -Wl,-zrelro,-znow", rustflags: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z threads=4 -D warnings", tag: "aarch64", @@ -175,17 +176,17 @@ jobs: }, ] env: - PYTHON: "${{ matrix.python.interpreter }}" - PYTHON_PACKAGE: "${{ matrix.python.package }}" - TARGET: "${{ matrix.arch.target }}" + CARGO_TARGET_DIR: "/tmp/orjson" CC: "${{ matrix.arch.cc }}" - VENV: ".venv" - FEATURES: "${{ matrix.arch.features }}" CFLAGS: "${{ matrix.arch.cflags }}" + COMPATIBILITY: "${{ matrix.python.compatibility }}" + FEATURES: "${{ matrix.arch.features }}" LDFLAGS: "${{ matrix.arch.ldflags }}" + PYTHON: "${{ matrix.python.interpreter }}" + PYTHON_PACKAGE: "${{ matrix.python.package }}" RUSTFLAGS: "${{ matrix.arch.rustflags }}" - CARGO_TARGET_DIR: "/tmp/orjson" - COMPATIBILITY: "${{ matrix.python.compatibility }}" + TARGET: "${{ matrix.arch.target }}" + VENV: ".venv" steps: - name: cpuinfo @@ -241,6 +242,7 @@ jobs: musllinux: runs-on: ubuntu-24.04 + timeout-minutes: 10 strategy: fail-fast: false matrix: @@ -256,11 +258,19 @@ jobs: - target: aarch64-unknown-linux-musl arch: aarch64 platform: linux/arm64 - features: no-panic,unstable-simd,unwind,yyjson + features: unstable-simd,unwind,yyjson + - target: armv7-unknown-linux-musleabihf + arch: armv7l + platform: linux/amd64 + features: unstable-simd,unwind,yyjson - target: x86_64-unknown-linux-musl arch: x86_64 platform: linux/amd64 - features: avx512,no-panic,unstable-simd,unwind,yyjson + features: avx512,unstable-simd,unwind,yyjson + - target: i686-unknown-linux-musl + arch: i686 + platform: linux/i686 + features: unstable-simd,unwind,yyjson steps: - uses: actions/checkout@v4 @@ -317,6 +327,7 @@ jobs: manylinux_non_amd64: runs-on: ubuntu-24.04 + timeout-minutes: 20 strategy: fail-fast: false matrix: @@ -331,28 +342,35 @@ jobs: { arch: 'aarch64', cflags: '-Os -flto=full -fstrict-aliasing', - features: 'no-panic,unstable-simd,yyjson', + features: 'unstable-simd,yyjson', rustflags: '-Z mir-opt-level=4 -C lto=fat -D warnings', target: 'aarch64-unknown-linux-gnu', }, + { + arch: 'i686', + cflags: '-Os -flto -fstrict-aliasing', + features: 'unstable-simd,yyjson', + rustflags: '-Z mir-opt-level=4 -C lto=fat -D warnings', + target: 'i686-unknown-linux-gnu', + }, { arch: 'armv7', cflags: '-Os -flto=full -fstrict-aliasing', - features: 'no-panic,yyjson', # no SIMD + features: 'yyjson', # no SIMD rustflags: '-Z mir-opt-level=4 -C lto=fat -D warnings -C opt-level=s', target: 'armv7-unknown-linux-gnueabihf', }, { arch: 'ppc64le', cflags: '-Os -flto=full -fstrict-aliasing', - features: 'no-panic,unstable-simd,yyjson', + features: 'unstable-simd,yyjson', rustflags: '-Z mir-opt-level=4 -C lto=fat -D warnings', target: 'powerpc64le-unknown-linux-gnu', }, { arch: 's390x', cflags: '-Os -flto=full -fstrict-aliasing -march=z10', - features: 'no-panic,yyjson', + features: 'yyjson', rustflags: '-Z mir-opt-level=4 -C lto=fat -D warnings -C target-cpu=z10', target: 's390x-unknown-linux-gnu', }, @@ -389,7 +407,8 @@ jobs: retention-days: 1 macos_aarch64: - runs-on: macos-14 + runs-on: macos-15 + timeout-minutes: 10 strategy: fail-fast: false matrix: @@ -414,7 +433,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "${{ matrix.python.version }}" - allow-prereleases: true - uses: dtolnay/rust-toolchain@master with: @@ -426,6 +444,8 @@ jobs: run: | cargo fetch --target aarch64-apple-darwin & + export PATH=$HOME/.cargo/bin:$HOME/.local/bin:$PATH + curl -LsSf https://astral.sh/uv/install.sh | sh uv venv --python python${{ matrix.python.version }} uv pip install --upgrade "maturin>=1,<2" -r test/requirements.txt -r integration/requirements.txt @@ -435,11 +455,12 @@ jobs: - name: maturin run: | - PATH=$HOME/.cargo/bin:$PATH \ + export PATH=$HOME/.cargo/bin:$HOME/.local/bin:$PATH + MACOSX_DEPLOYMENT_TARGET="${{ matrix.python.macosx_target }}" \ PYO3_CROSS_LIB_DIR=$(python -c "import sysconfig;print(sysconfig.get_config_var('LIBDIR'))") \ maturin build --release --strip \ - --features=no-panic,unstable-simd,yyjson \ + --features=unstable-simd,yyjson \ --interpreter python${{ matrix.python.version }} \ --target=universal2-apple-darwin uv pip install target/wheels/orjson*.whl @@ -463,6 +484,7 @@ jobs: macos_amd64: runs-on: macos-13 + timeout-minutes: 10 strategy: fail-fast: false matrix: @@ -496,6 +518,8 @@ jobs: run: | cargo fetch --target aarch64-apple-darwin & + export PATH=$HOME/.cargo/bin:$HOME/.local/bin:$PATH + curl -LsSf https://astral.sh/uv/install.sh | sh uv venv --python python${{ matrix.python.version }} uv pip install --upgrade "maturin>=1,<2" -r test/requirements.txt -r integration/requirements.txt @@ -505,11 +529,12 @@ jobs: - name: maturin run: | - PATH=$HOME/.cargo/bin:$PATH \ + export PATH=$HOME/.cargo/bin:$HOME/.local/bin:$PATH + MACOSX_DEPLOYMENT_TARGET="${{ matrix.python.macosx_target }}" \ PYO3_CROSS_LIB_DIR=$(python -c "import sysconfig;print(sysconfig.get_config_var('LIBDIR'))") \ maturin build --release --strip \ - --features=no-panic,unstable-simd,yyjson \ + --features=unstable-simd,yyjson \ --interpreter python${{ matrix.python.version }} \ --target=universal2-apple-darwin uv pip install target/wheels/orjson*.whl @@ -531,9 +556,75 @@ jobs: overwrite: true retention-days: 1 + windows: + runs-on: windows-2022 + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + python: [ + { version: '3.13' }, + { version: '3.12' }, + { version: '3.11' }, + { version: '3.10' }, + { version: '3.9' }, + { version: '3.8' }, + ] + platform: [ + { arch: "x64", target: "x86_64-pc-windows-msvc" }, + { arch: "x86", target: "i686-pc-windows-msvc" }, + ] + env: + CFLAGS: "-Os" + LDFLAGS: "-Wl,--as-needed" + RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -D warnings" + steps: + + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "${{ matrix.python.version }}" + architecture: "${{ matrix.platform.arch }}" + + - uses: dtolnay/rust-toolchain@master + with: + toolchain: "${{ env.RUST_TOOLCHAIN }}" + targets: "${{ matrix.platform.target }}" + components: "rust-src" + + - name: Build environment + run: | + cargo fetch --target "${{ matrix.platform.target }}" & + + python.exe -m pip install --upgrade pip "maturin>=1,<2" wheel + python.exe -m pip install -r test\requirements.txt -r integration\requirements.txt + + mkdir .cargo + cp ci\config.toml .cargo\config.toml + + - name: maturin + run: | + maturin.exe build --release --strip --features=unstable-simd,yyjson --target="${{ matrix.platform.target }}" + python.exe -m pip install orjson --no-index --find-links target\wheels + + - run: python.exe -m pytest -s -rxX -v test + env: + PYTHONMALLOC: "debug" + + - name: Store wheels + if: "startsWith(github.ref, 'refs/tags/')" + uses: actions/upload-artifact@v4 + with: + name: orjson_windows_${{ matrix.platform.arch }}_${{ matrix.python.version }} + path: target\wheels + overwrite: true + retention-days: 1 + pypi: name: PyPI runs-on: ubuntu-24.04 + timeout-minutes: 10 if: "startsWith(github.ref, 'refs/tags/')" needs: [ macos_aarch64, @@ -543,6 +634,7 @@ jobs: manylinux_non_amd64, musllinux, sdist, + windows, ] environment: name: pypi diff --git a/.github/workflows/debug.yaml b/.github/workflows/debug.yaml index 863135ac..d4b4d7c7 100644 --- a/.github/workflows/debug.yaml +++ b/.github/workflows/debug.yaml @@ -10,7 +10,6 @@ jobs: profile: [ { rust: "1.72", features: "" }, { rust: "1.72", features: "--features=yyjson" }, - { rust: "nightly-2024-09-25", features: "--features=yyjson,unstable-simd" }, { rust: "nightly-2024-09-25", features: "--features=avx512,yyjson,unstable-simd" }, ] python: [ diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index ef9d5310..3dc0091d 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -10,7 +10,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: days-before-stale: 7 days-before-close: 1 diff --git a/Cargo.lock b/Cargo.lock index fde9db8d..42588100 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,9 +89,6 @@ name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -dependencies = [ - "no-panic", -] [[package]] name = "itoap" @@ -117,17 +114,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "no-panic" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8540b7d99a20166178b42a05776aef900cdbfec397f861dfc7819bf1d7760b3d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "once_cell" version = "1.20.2" @@ -206,9 +192,6 @@ name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -dependencies = [ - "no-panic", -] [[package]] name = "serde" diff --git a/Cargo.toml b/Cargo.toml index 516f4d0e..9453beb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,11 +38,6 @@ unstable-simd = [] # Include runtime-detected functions that use AVX512VL. Requires unstable-simd and amd64. avx512 = [] -no-panic = [ - "itoa/no-panic", - "ryu/no-panic", -] - # Avoid bundling libgcc on musl. unwind = ["unwinding"] diff --git a/README.md b/README.md index 942597ef..9de3ccfd 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ file-like objects orjson supports CPython 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, and 3.14. -It distributes amd64/x86_64, aarch64/armv8, arm7, POWER/ppc64le, and s390x -wheels for Linux, amd64 and aarch64 wheels for macOS, and amd64 +It distributes amd64/x86_64, i686/x86, aarch64/armv8, arm7, POWER/ppc64le, +and s390x wheels for Linux, amd64 and aarch64 wheels for macOS, and amd64 and i686/x86 wheels for Windows. orjson does not and will not support PyPy, embedded Python builds for diff --git a/build.rs b/build.rs index ad6d5b9c..b3fc40c9 100644 --- a/build.rs +++ b/build.rs @@ -51,11 +51,10 @@ fn main() { } } - #[cfg(all( - target_pointer_width = "64", - any(target_arch = "x86_64", target_arch = "aarch64") - ))] - println!("cargo:rustc-cfg=feature=\"inline_int\""); + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + if let Some(64) = python_config.pointer_width { + println!("cargo:rustc-cfg=feature=\"inline_int\""); + } if env::var("ORJSON_DISABLE_YYJSON").is_ok() { if env::var("CARGO_FEATURE_YYJSON").is_ok() { diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml deleted file mode 100644 index 8382734e..00000000 --- a/ci/azure-pipelines.yml +++ /dev/null @@ -1,196 +0,0 @@ -variables: - toolchain: nightly-2024-09-25 - -jobs: - -- job: win_python313_amd64 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.13.0\x64\python.exe - rustup: https://win.rustup.rs/x86_64 - target: x86_64-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.13.0' - addToPath: true - architecture: 'x64' - - checkout: self - - template: ./azure-win.yml - -- job: win_python312_amd64 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.12.2\x64\python.exe - rustup: https://win.rustup.rs/x86_64 - target: x86_64-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.12.2' - addToPath: true - architecture: 'x64' - - checkout: self - - template: ./azure-win.yml - -- job: win_python311_amd64 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.11.4\x64\python.exe - rustup: https://win.rustup.rs/x86_64 - target: x86_64-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.11.4' - addToPath: true - architecture: 'x64' - - checkout: self - - template: ./azure-win.yml - -- job: win_python310_amd64 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.10.8\x64\python.exe - rustup: https://win.rustup.rs/x86_64 - target: x86_64-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.10.8' - addToPath: true - architecture: 'x64' - - checkout: self - - template: ./azure-win.yml - -- job: win_python39_amd64 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.9.13\x64\python.exe - rustup: https://win.rustup.rs/x86_64 - target: x86_64-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.9.13' - addToPath: true - architecture: 'x64' - - checkout: self - - template: ./azure-win.yml - -- job: win_python38_amd64 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.8.10\x64\python.exe - rustup: https://win.rustup.rs/x86_64 - target: x86_64-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.8.10' - addToPath: true - architecture: 'x64' - - checkout: self - - template: ./azure-win.yml - -- job: win_python313_x86 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.13.0\x86\python.exe - rustup: https://win.rustup.rs/x86 - target: i686-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.13.0' - addToPath: true - architecture: 'x86' - - checkout: self - - template: ./azure-win.yml - -- job: win_python312_x86 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.12.2\x86\python.exe - rustup: https://win.rustup.rs/x86 - target: i686-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.12.2' - addToPath: true - architecture: 'x86' - - checkout: self - - template: ./azure-win.yml - -- job: win_python311_x86 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.11.4\x86\python.exe - rustup: https://win.rustup.rs/x86 - target: i686-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.11.4' - addToPath: true - architecture: 'x86' - - checkout: self - - template: ./azure-win.yml - -- job: win_python310_x86 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.10.8\x86\python.exe - rustup: https://win.rustup.rs/x86 - target: i686-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.10.8' - addToPath: true - architecture: 'x86' - - checkout: self - - template: ./azure-win.yml - -- job: win_python39_x86 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.9.13\x86\python.exe - rustup: https://win.rustup.rs/x86 - target: i686-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.9.13' - addToPath: true - architecture: 'x86' - - checkout: self - - template: ./azure-win.yml - -- job: win_python38_x86 - pool: - vmImage: windows-2022 - variables: - interpreter: C:\hostedtoolcache\windows\Python\3.8.10\x86\python.exe - rustup: https://win.rustup.rs/x86 - target: i686-pc-windows-msvc - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.8.10' - addToPath: true - architecture: 'x86' - - checkout: self - - template: ./azure-win.yml diff --git a/ci/azure-win.yml b/ci/azure-win.yml deleted file mode 100644 index 655eab96..00000000 --- a/ci/azure-win.yml +++ /dev/null @@ -1,41 +0,0 @@ -parameters: - interpreter: '' - rustup: '' - target: '' - toolchain: '' - -steps: -- script: | - curl $(rustup) -o rustup-init.exe - rustup-init.exe -y --default-host $(target) --default-toolchain $(toolchain)-$(target) --profile minimal - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustup default $(toolchain)-$(target) - rustup component add rust-src - mkdir .cargo - cp ci/config.toml .cargo/config.toml - echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" - displayName: rustup -- script: python.exe -m pip install --upgrade pip "maturin>=1,<2" wheel - displayName: build dependencies -- script: python.exe -m pip install -r test\requirements.txt -r integration\requirements.txt - displayName: test dependencies -- script: maturin.exe build --release --features=no-panic,unstable-simd,yyjson --strip --interpreter $(interpreter) --target $(target) - displayName: build - env: - CFLAGS: "-Os -flto" - LDFLAGS: "-Wl,--as-needed" - RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -D warnings" - CARGO_UNSTABLE_SPARSE_REGISTRY: "true" - UNSAFE_PYO3_SKIP_VERSION_CHECK: "1" -- script: python.exe -m pip install orjson --no-index --find-links=D:\a\1\s\target\wheels - displayName: install -- script: python.exe -m pytest -s -rxX -v test - env: - PYTHONMALLOC: "debug" - displayName: pytest -- script: python.exe integration\thread - displayName: thread -- script: python.exe integration\init - displayName: init -- bash: ./ci/deploy /d/a/1/s/target/wheels/*.whl - displayName: deploy diff --git a/ci/deploy b/ci/deploy deleted file mode 100755 index 1b34b539..00000000 --- a/ci/deploy +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -set -eou pipefail - -if [ -z ${DRONE_TAG+x} ]; then - tag=$(git name-rev --tags --name-only $(git rev-parse HEAD)) -else - tag="$DRONE_TAG" -fi - -echo "$tag" - -if [[ "$tag" == "undefined" ]]; then - echo "not on a tag" - exit 0 -fi - -maturin upload --skip-existing "$1" diff --git a/script/install-fedora b/script/install-fedora index 19a5a455..6d14860e 100755 --- a/script/install-fedora +++ b/script/install-fedora @@ -14,7 +14,7 @@ export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target}" rm /etc/yum.repos.d/fedora-cisco-openh264.repo || true -dnf install --setopt=install_weak_deps=false -y rustup clang lld "${PYTHON_PACKAGE}" +dnf install --setopt=install_weak_deps=false -y rustup clang lld "${PYTHON_PACKAGE}" python3-uv rustup-init --default-toolchain "${RUST_TOOLCHAIN}-${TARGET}" --profile minimal --component rust-src -y source "${HOME}/.cargo/env" @@ -24,7 +24,6 @@ cp ci/config.toml .cargo/config.toml cargo fetch --target="${TARGET}" & -curl -LsSf https://astral.sh/uv/install.sh | sh rm -rf "${VENV}" uv venv --python "${PYTHON}" "${VENV}" source "${VENV}/bin/activate"