astral-sh-uv-9252ba6/000077500000000000000000000000001521221614700143755ustar00rootroot00000000000000astral-sh-uv-9252ba6/.cargo/000077500000000000000000000000001521221614700155465ustar00rootroot00000000000000astral-sh-uv-9252ba6/.cargo/config.toml000066400000000000000000000005021521221614700177050ustar00rootroot00000000000000[alias] dev = "run --package uv-dev --features dev" # statically link the C runtime so the executable does not depend on # that shared/dynamic library. # # See: https://github.com/astral-sh/ruff/issues/11503 [target.'cfg(all(target_env = "msvc", target_os = "windows"))'] rustflags = ["-C", "target-feature=+crt-static"] astral-sh-uv-9252ba6/.claude/000077500000000000000000000000001521221614700157105ustar00rootroot00000000000000astral-sh-uv-9252ba6/.claude/settings.json000066400000000000000000000007351521221614700204500ustar00rootroot00000000000000{ "hooks": { "SessionStart": [ { "matcher": "startup", "hooks": [ { "type": "command", "command": "bash scripts/hooks/session-start.sh" } ] } ], "PostToolUse": [ { "matcher": "Edit|Write|MultiEdit", "hooks": [ { "type": "command", "command": "uv run scripts/hooks/post-edit-format.py" } ] } ] } } astral-sh-uv-9252ba6/.codex/000077500000000000000000000000001521221614700155555ustar00rootroot00000000000000astral-sh-uv-9252ba6/.codex/hooks.json000066400000000000000000000007351521221614700176000ustar00rootroot00000000000000{ "hooks": { "SessionStart": [ { "matcher": "startup", "hooks": [ { "type": "command", "command": "bash scripts/hooks/session-start.sh" } ] } ], "PostToolUse": [ { "matcher": "Edit|Write|MultiEdit", "hooks": [ { "type": "command", "command": "uv run scripts/hooks/post-edit-format.py" } ] } ] } } astral-sh-uv-9252ba6/.config/000077500000000000000000000000001521221614700157205ustar00rootroot00000000000000astral-sh-uv-9252ba6/.config/nextest.toml000066400000000000000000000022721521221614700203120ustar00rootroot00000000000000experimental = ["setup-scripts"] [profile.default] # Mark tests that take longer than 10s as slow. # Terminate after 120s as a stop-gap measure to terminate on deadlock. slow-timeout = { period = "10s", terminate-after = 12 } [profile.ci] test-threads = 20 status-level = "skip" final-status-level = "slow" failure-output = "immediate-final" fail-fast = false [profile.ci.junit] path = "junit.xml" [profile.ci-linux] inherits = "ci" [profile.ci-macos] inherits = "ci" test-threads = 12 [profile.ci-windows] inherits = "ci" [test-groups] serial = { max-threads = 1 } io-bound = { max-threads = 1 } [[profile.default.overrides]] filter = 'test(native_auth)' test-group = 'serial' [[profile.default.overrides]] filter = 'package(uv-keyring)' test-group = 'serial' [[profile.ci-windows.overrides]] platform = 'cfg(target_os = "windows")' filter = 'test(/^python_install::/) + test(/^python_update::/) + test(/^python_find::/)' test-group = 'io-bound' priority = 1 # Start earlier so these can complete before the end [scripts.setup.setup-hook-unix] command = "scripts/nextest-setup-hook-unix.sh" [[profile.default.scripts]] platform = { host = "cfg(unix)" } filter = "all()" setup = "setup-hook-unix" astral-sh-uv-9252ba6/.editorconfig000066400000000000000000000006371521221614700170600ustar00rootroot00000000000000# Check http://editorconfig.org for more information # This is the main config file for this project: root = true [*] charset = utf-8 trim_trailing_whitespace = true end_of_line = lf indent_style = space insert_final_newline = true indent_size = 2 [*.{rs,py,pyi}] indent_size = 4 [*.snap] trim_trailing_whitespace = false [crates/uv/tests/help.rs] trim_trailing_whitespace = false [*.md] max_line_length = 100 astral-sh-uv-9252ba6/.gitattributes000066400000000000000000000001511521221614700172650ustar00rootroot00000000000000* text=auto eol=lf *.inc linguist-language=Rust uv.schema.json linguist-generated=true text=auto eol=lf astral-sh-uv-9252ba6/.github/000077500000000000000000000000001521221614700157355ustar00rootroot00000000000000astral-sh-uv-9252ba6/.github/ISSUE_TEMPLATE/000077500000000000000000000000001521221614700201205ustar00rootroot00000000000000astral-sh-uv-9252ba6/.github/ISSUE_TEMPLATE/1_bug_report.yaml000066400000000000000000000026721521221614700234030ustar00rootroot00000000000000name: Bug report description: Report an error or unexpected behavior labels: ["bug"] body: - type: markdown attributes: value: | **Please review [our guide on interacting with the issue tracker](https://github.com/astral-sh/uv/issues/9452) before opening a new issue.** - type: textarea attributes: label: Summary description: | A clear and concise description of the bug, including [a minimal reproducible example](https://docs.astral.sh/uv/reference/troubleshooting/reproducible-examples/). If we cannot reproduce the bug, it is unlikely that we will be able to help you. Please include the full output of uv with the complete error message. validations: required: true - type: input attributes: label: Platform description: What operating system and architecture are you using? (see `uname -orsm`) placeholder: e.g., macOS 14 arm64, Windows 11 x86_64, Ubuntu 20.04 amd64 validations: required: true - type: input attributes: label: Version description: What version of uv are you using? (see `uv self version`) placeholder: e.g., uv 0.5.20 (1c17662b3 2025-01-15) validations: required: true - type: input attributes: label: Python version description: What version of Python are you using? (see `uv run python --version`) placeholder: e.g., Python 3.12.6 validations: required: false astral-sh-uv-9252ba6/.github/ISSUE_TEMPLATE/2_feature_request.yaml000066400000000000000000000014211521221614700244260ustar00rootroot00000000000000name: Feature request description: Suggest a new feature or improvement labels: ["enhancement"] body: - type: markdown attributes: value: | **Please review [our guide on interacting with the issue tracker](https://github.com/astral-sh/uv/issues/9452) before opening a new issue.** - type: textarea attributes: label: Summary description: | A clear and concise description of what new feature or behavior you would like to see. If applicable, please describe the current behavior as well. validations: required: true - type: textarea attributes: label: Example description: Provide an example of how the user experience would change or how the new feature would be used. validations: required: false astral-sh-uv-9252ba6/.github/ISSUE_TEMPLATE/3_question.yaml000066400000000000000000000016311521221614700230760ustar00rootroot00000000000000name: Question description: Ask a question about uv labels: ["question"] body: - type: markdown attributes: value: | **Please review [our guide on interacting with the issue tracker](https://github.com/astral-sh/uv/issues/9452) before opening a new issue.** - type: textarea attributes: label: Question description: Describe your question in detail. validations: required: true - type: input attributes: label: Platform description: What operating system and architecture are you using? (see `uname -orsm`) placeholder: e.g., macOS 14 arm64, Windows 11 x86_64, Ubuntu 20.04 amd64 validations: required: false - type: input attributes: label: Version description: What version of uv are you using? (see `uv self version`) placeholder: e.g., uv 0.5.20 (1c17662b3 2025-01-15) validations: required: false astral-sh-uv-9252ba6/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000004651521221614700221150ustar00rootroot00000000000000blank_issues_enabled: true contact_links: - name: Documentation url: https://docs.astral.sh/uv about: Please consult the documentation before creating an issue. - name: Community url: https://discord.com/invite/astral-sh about: Join our Discord community to ask questions and collaborate. astral-sh-uv-9252ba6/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000006501521221614700215370ustar00rootroot00000000000000 ## Summary ## Test Plan astral-sh-uv-9252ba6/.github/renovate.json5000066400000000000000000000154671521221614700205550ustar00rootroot00000000000000{ $schema: "https://docs.renovatebot.com/renovate-schema.json", dependencyDashboard: true, suppressNotifications: ["prEditedNotification"], extends: [ "github>astral-sh/renovate-config", // For tool versions defined in GitHub Actions: "customManagers:githubActionsVersions", ], labels: ["internal", "build:skip-docker", "build:skip-release"], schedule: ["* 0-3 * * 1"], semanticCommits: "disabled", separateMajorMinor: false, enabledManagers: ["github-actions", "pre-commit", "cargo", "custom.regex"], cargo: { // See https://docs.renovatebot.com/configuration-options/#rangestrategy rangeStrategy: "update-lockfile", managerFilePatterns: ["/^Cargo\\.toml$/", "/^crates/.*Cargo\\.toml$/"], }, "pre-commit": { enabled: true, }, packageRules: [ // Pin GitHub Actions to immutable SHAs. { matchDepTypes: ["action"], pinDigests: true, }, // Annotate GitHub Actions SHAs with a SemVer version. { extends: ["helpers:pinGitHubActionDigests"], extractVersion: "^(?v?\\d+\\.\\d+\\.\\d+)$", versioning: "regex:^v?(?\\d+)(\\.(?\\d+)\\.(?\\d+))?$", }, { // Disable updates of `zip-rs`; intentionally pinned for now due to ownership change // See: https://github.com/astral-sh/uv/issues/3642 matchPackageNames: ["/zip/"], matchManagers: ["cargo"], enabled: false, }, { // Disable updates of actions used by `.github/workflows/release.yml`; // these action pins are managed by dist-workspace.toml matchFileNames: [".github/workflows/release.yml"], matchManagers: ["github-actions"], enabled: false, }, { // Create dedicated branches to update references to dependencies in the documentation. matchFileNames: ["docs/**/*.md"], commitMessageTopic: "documentation references to {{{depName}}}", semanticCommitType: "docs", semanticCommitScope: null, additionalBranchPrefix: "docs-", }, { // Group upload/download artifact updates, the versions are dependent groupName: "Artifact GitHub Actions dependencies", matchManagers: ["github-actions"], matchDatasources: ["gitea-tags", "github-tags"], matchPackageNames: ["/actions/.*-artifact/"], description: "Weekly update of artifact-related GitHub Actions dependencies", }, { // This package rule disables updates for GitHub runners: // we'd only pin them to a specific version // if there was a deliberate reason to do so groupName: "GitHub runners", matchManagers: ["github-actions"], matchDatasources: ["github-runners"], description: "Disable PRs updating GitHub runners (e.g. 'runs-on: macos-14')", enabled: false, }, { // Exclude test-system.yml from Python version updates, as it intentionally // tests multiple Python versions and should not be auto-updated. matchFileNames: [".github/workflows/test-system.yml"], matchDepNames: ["python"], description: "Disable Python version updates in test-system.yml", enabled: false, }, { // Exclude test-integration.yml from Python version updates, as it intentionally // tests specific Python versions and should not be auto-updated. matchFileNames: [".github/workflows/test-integration.yml"], matchDepNames: ["python"], description: "Disable Python version updates in test-integration.yml", enabled: false, }, { matchFileNames: [".github/workflows/test-system.yml"], extends: ["schedule:monthly"], description: "Monthly update of test-system.yml dependencies", }, { groupName: "pre-commit dependencies", matchManagers: ["pre-commit"], description: "Weekly update of pre-commit dependencies", }, { groupName: "Rust dev-dependencies", matchManagers: ["cargo"], matchDepTypes: ["devDependencies"], description: "Weekly update of Rust development dependencies", }, { // We don't really use PyO3 in this project; it's pulled in as an optional feature // of the PEP 440 and PEP 508 crates, which we vendored and forked. groupName: "pyo3", matchManagers: ["cargo"], matchPackageNames: ["/pyo3/"], description: "Weekly update of pyo3 dependencies", enabled: false, }, { groupName: "pubgrub", matchManagers: ["cargo"], matchDepNames: ["pubgrub", "version-ranges"], description: "version-ranges and pubgrub are in the same Git repository", }, { commitMessageTopic: "MSRV", matchManagers: ["custom.regex"], matchDepNames: ["msrv"], // We have a rolling support policy for the MSRV // 2 releases back * 6 weeks per release * 7 days per week + 1 minimumReleaseAge: "85 days", internalChecksFilter: "strict", groupName: "MSRV", }, { matchManagers: ["custom.regex"], matchDepNames: ["rust"], commitMessageTopic: "Rust", }, { matchManagers: ["custom.regex"], matchDepNames: ["maturin"], commitMessageTopic: "maturin", // Maturin upgrades can change release wheel contents, so keep release // binary validation enabled. labels: ["internal", "build:skip-docker"], }, ], customManagers: [ // Update major GitHub actions references in documentation. { customType: "regex", managerFilePatterns: ["/^docs/.*\\.md$/"], matchStrings: [ "\\suses: (?[\\w-]+/[\\w-]+)(?/.*)?@(?.+?)\\s", ], datasourceTemplate: "github-tags", versioningTemplate: "regex:^v(?\\d+)$", }, // Maturin version used in maturin-action { customType: "regex", managerFilePatterns: [ "/.github/workflows/build-release-binaries\\.yml$/", ], matchStrings: ["maturin-version: (?v\\d+\\.\\d+\\.\\d+)"], depNameTemplate: "maturin", packageNameTemplate: "PyO3/maturin", datasourceTemplate: "github-releases", }, // Minimum supported Rust toolchain version { customType: "regex", managerFilePatterns: ["/(^|/)Cargo\\.toml?$/"], matchStrings: [ 'rust-version\\s*=\\s*"(?\\d+\\.\\d+(\\.\\d+)?)"', ], depNameTemplate: "msrv", packageNameTemplate: "rust-lang/rust", datasourceTemplate: "github-releases", }, // Rust toolchain version { customType: "regex", managerFilePatterns: ["/(^|/)rust-toolchain\\.toml?$/"], matchStrings: [ 'channel\\s*=\\s*"(?\\d+\\.\\d+(\\.\\d+)?)"', ], depNameTemplate: "rust", packageNameTemplate: "rust-lang/rust", datasourceTemplate: "github-releases", }, ], vulnerabilityAlerts: { commitMessageSuffix: "", labels: ["internal", "security"], }, } astral-sh-uv-9252ba6/.github/workflows/000077500000000000000000000000001521221614700177725ustar00rootroot00000000000000astral-sh-uv-9252ba6/.github/workflows/bench.yml000066400000000000000000000110561521221614700215770ustar00rootroot00000000000000on: workflow_call: inputs: save-rust-cache: required: false type: string default: "true" permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: benchmarks-walltime-build: name: "walltime build" # codspeed-macro doesn't support Ubuntu 24.04 yet runs-on: depot-ubuntu-22.04-arm-4 if: ${{ github.repository == 'astral-sh/uv' }} timeout-minutes: 20 steps: - name: "Checkout Branch" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Install Rust toolchain" run: rustup show - name: "Install codspeed" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-codspeed - name: "Install requirements and prime cache" run: | sudo apt-get update sudo apt-get install -y libsasl2-dev libldap2-dev libkrb5-dev cargo run --bin uv -- venv --cache-dir .cache cargo run --bin uv -- pip compile test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache - name: "Build benchmarks" run: cargo codspeed build -m walltime --profile profiling -p uv-bench - name: "Create artifact archive" run: tar -cvf benchmarks-walltime.tar target/codspeed target/debug/uv .cache - name: "Upload benchmark artifacts" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: benchmarks-walltime path: benchmarks-walltime.tar retention-days: 1 benchmarks-walltime-run: name: "walltime on aarch64 linux" runs-on: codspeed-macro needs: benchmarks-walltime-build timeout-minutes: 20 steps: - name: "Checkout Branch" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install codspeed" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-codspeed - name: "Download benchmark artifacts" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: benchmarks-walltime - name: "Extract artifact archive" run: tar -xvf benchmarks-walltime.tar - name: "Create venv" run: ./target/debug/uv venv --cache-dir .cache - name: "Run benchmarks" uses: CodSpeedHQ/action@9d332c4d90b43981c3e55ae8e38e68709996240f # v4.17.0 with: run: cargo codspeed run mode: walltime token: ${{ secrets.CODSPEED_TOKEN }} benchmarks-simulated: name: "simulated" runs-on: depot-ubuntu-24.04-4 if: ${{ github.repository == 'astral-sh/uv' }} timeout-minutes: 20 steps: - name: "Checkout Branch" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Install Rust toolchain" run: rustup show - name: "Install codspeed" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-codspeed - name: "Install requirements and prime cache" run: | sudo apt-get update sudo apt-get install -y libsasl2-dev libldap2-dev libkrb5-dev cargo run --bin uv -- venv --cache-dir .cache cargo run --bin uv -- pip compile test/requirements/jupyter.in --universal --exclude-newer 2024-08-08 --cache-dir .cache cargo run --bin uv -- pip compile test/requirements/airflow.in --universal --exclude-newer 2024-08-08 --cache-dir .cache - name: "Build benchmarks" run: cargo codspeed build --profile profiling -p uv-bench - name: "Run benchmarks" uses: CodSpeedHQ/action@9d332c4d90b43981c3e55ae8e38e68709996240f # v4.17.0 with: run: cargo codspeed run mode: simulation token: ${{ secrets.CODSPEED_TOKEN }} astral-sh-uv-9252ba6/.github/workflows/build-dev-binaries.yml000066400000000000000000000324261521221614700241710ustar00rootroot00000000000000on: workflow_call: inputs: save-rust-cache: required: false type: string default: "true" permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: build-binary-linux-libc: name: "linux libc" timeout-minutes: 10 runs-on: github-ubuntu-24.04-x86_64-8 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install mold" run: ./scripts/install-mold.sh - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Build" run: cargo build --profile no-debug - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-linux-libc-${{ github.sha }} path: | ./target/no-debug/uv ./target/no-debug/uvx retention-days: 1 build-binary-linux-aarch64: name: "linux aarch64" timeout-minutes: 10 runs-on: github-ubuntu-24.04-aarch64-4 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install mold" run: ./scripts/install-mold.sh - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Build" run: cargo build --profile no-debug - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-linux-aarch64-${{ github.sha }} path: | ./target/no-debug/uv ./target/no-debug/uvx retention-days: 1 build-binary-linux-armv7-gnueabihf: name: "linux armv7 gnueabihf" timeout-minutes: 15 runs-on: github-ubuntu-24.04-x86_64-8 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install mold" run: ./scripts/install-mold.sh - name: "Setup armv7" run: | sudo apt-get update sudo apt-get install gcc-arm-linux-gnueabihf rustup target add armv7-unknown-linux-gnueabihf - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Build" env: CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc run: cargo build --profile no-debug --target armv7-unknown-linux-gnueabihf --bin uv --bin uvx - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-linux-armv7-gnueabihf-${{ github.sha }} path: | ./target/armv7-unknown-linux-gnueabihf/no-debug/uv ./target/armv7-unknown-linux-gnueabihf/no-debug/uvx retention-days: 1 build-binary-linux-musl: name: "linux musl" timeout-minutes: 10 runs-on: github-ubuntu-24.04-x86_64-8 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install mold" run: ./scripts/install-mold.sh - name: "Setup musl" run: | sudo apt-get install musl-tools rustup target add x86_64-unknown-linux-musl - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Build" run: cargo build --profile no-debug --target x86_64-unknown-linux-musl --bin uv --bin uvx - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-linux-musl-${{ github.sha }} path: | ./target/x86_64-unknown-linux-musl/no-debug/uv ./target/x86_64-unknown-linux-musl/no-debug/uvx retention-days: 1 build-binary-macos-aarch64: name: "macos aarch64" timeout-minutes: 10 runs-on: macos-14 # github-macos-14-aarch64-3 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} cache-bin: false - name: "Build" run: cargo build --profile no-debug --bin uv --bin uvx - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-macos-aarch64-${{ github.sha }} path: | ./target/no-debug/uv ./target/no-debug/uvx retention-days: 1 build-binary-macos-x86_64: name: "macos x86_64" timeout-minutes: 10 runs-on: macos-latest-large # github-macos-14-x86_64-12 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} cache-bin: false - name: "Build" run: cargo build --profile no-debug --bin uv --bin uvx - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-macos-x86_64-${{ github.sha }} path: | ./target/no-debug/uv ./target/no-debug/uvx retention-days: 1 build-binary-windows-x86_64: name: "windows x86_64" timeout-minutes: 10 runs-on: windows-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Dev Drive run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1 # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone... - name: Copy Git Repo to Dev Drive run: | Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: ${{ env.UV_WORKSPACE }} save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Build" working-directory: ${{ env.UV_WORKSPACE }} run: cargo build --profile no-debug --bin uv --bin uvx - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-windows-x86_64-${{ github.sha }} path: | ${{ env.UV_WORKSPACE }}/target/no-debug/uv.exe ${{ env.UV_WORKSPACE }}/target/no-debug/uvx.exe retention-days: 1 build-binary-windows-aarch64: name: "windows aarch64" timeout-minutes: 10 runs-on: windows-11-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Dev Drive run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1 # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone... - name: Copy Git Repo to Dev Drive run: | Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: ${{ env.UV_WORKSPACE }} save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Build" working-directory: ${{ env.UV_WORKSPACE }} run: cargo build --profile no-debug --bin uv --bin uvx - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-windows-aarch64-${{ github.sha }} path: | ${{ env.UV_WORKSPACE }}/target/no-debug/uv.exe ${{ env.UV_WORKSPACE }}/target/no-debug/uvx.exe retention-days: 1 build-binary-msrv: name: "msrv" runs-on: github-ubuntu-24.04-x86_64-8 timeout-minutes: 10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Read MSRV from Cargo.toml" id: msrv run: | MSRV=$(grep -m1 'rust-version' Cargo.toml | sed 's/.*"\([^"]*\)".*/\1/') echo "value=$MSRV" >> "$GITHUB_OUTPUT" - name: "Install Rust toolchain" run: rustup default ${MSRV} env: MSRV: ${{ steps.msrv.outputs.value }} - name: "Install mold" run: ./scripts/install-mold.sh - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - run: cargo +${MSRV} build --profile no-debug env: MSRV: ${{ steps.msrv.outputs.value }} - run: ./target/no-debug/uv --version build-binary-android-aarch64: name: "android aarch64" timeout-minutes: 10 runs-on: github-ubuntu-24.04-x86_64-8 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Setup Android NDK" run: | # Use ANDROID_NDK_ROOT if set, otherwise find the latest installed NDK if [ -z "${ANDROID_NDK_ROOT}" ]; then NDK_ROOT=$(ls -d "${ANDROID_HOME}/ndk/"* 2>/dev/null | sort -V | tail -n1) else NDK_ROOT="${ANDROID_NDK_ROOT}" fi TOOLCHAIN="${NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64" echo "CC_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV" echo "CXX_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang++" >> "$GITHUB_ENV" echo "AR_aarch64_linux_android=${TOOLCHAIN}/bin/llvm-ar" >> "$GITHUB_ENV" echo "RANLIB_aarch64_linux_android=${TOOLCHAIN}/bin/llvm-ranlib" >> "$GITHUB_ENV" echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV" echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_RANLIB=${TOOLCHAIN}/bin/llvm-ranlib" >> "$GITHUB_ENV" # NDK 23+ removed libgcc, provide a stub that redirects to libunwind LIBDIR=$(echo "${TOOLCHAIN}"/lib/clang/*/lib/linux/aarch64) if [ ! -f "${LIBDIR}/libgcc.a" ]; then echo 'INPUT(-lunwind)' | sudo tee "${LIBDIR}/libgcc.a" > /dev/null fi rustup target add aarch64-linux-android - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Build" run: cargo build --profile no-debug --target aarch64-linux-android --bin uv --bin uvx - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: uv-android-aarch64-${{ github.sha }} path: | ./target/aarch64-linux-android/no-debug/uv ./target/aarch64-linux-android/no-debug/uvx retention-days: 1 build-binary-freebsd: name: "freebsd" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Cross build" run: | # Install cross from `freebsd-firecracker` wget -q -O cross https://github.com/acj/freebsd-firecracker/releases/download/v0.0.10/cross chmod +x cross mv cross /usr/local/bin/cross cross build --target x86_64-unknown-freebsd --profile no-debug - name: Test in Firecracker VM uses: acj/freebsd-firecracker-action@bab3e77871573c7943b80816f1641b6c1ce36896 # v0.9.1 with: verbose: false checkout: false pre-run: | # The exclude `*` prevents examination of directories so we need to # include each parent directory of the binary include_path="$(mktemp)" cat < $include_path target target/x86_64-unknown-freebsd target/x86_64-unknown-freebsd/no-debug target/x86_64-unknown-freebsd/no-debug/uv EOF rsync -r -e "ssh" \ --relative \ --copy-links \ --include-from "$include_path" \ --exclude "*" \ . firecracker: run-in-vm: | mv target/x86_64-unknown-freebsd/no-debug/uv uv chmod +x uv ./uv --version astral-sh-uv-9252ba6/.github/workflows/build-docker.yml000066400000000000000000000511161521221614700230650ustar00rootroot00000000000000# Build and publish Docker images. # # Uses Depot for multi-platform builds. Includes both a `uv` base image, which # is just the binary in a scratch image, and a set of extra, common images with # the uv binary installed. # # On pull requests, triggered via CI workflow when Docker-related files change # (e.g., Dockerfile, Cargo.toml, rust-toolchain.toml). Images are built but not # pushed, to verify the build still works. # # On release, assumed to run as a subworkflow of .github/workflows/release.yml; # specifically, as a local artifacts job within `cargo-dist`. In this case, # images are published based on the `plan`. # # TODO(charlie): Ideally, the publish step would happen as a publish job within # `cargo-dist`, but sharing the built image as an artifact between jobs is # challenging. name: "Docker images" on: workflow_call: inputs: plan: required: false type: string default: "" push-dev: required: false type: boolean default: false env: UV_GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ inputs.push-dev && 'uv-dev' || 'uv' }} UV_DOCKERHUB_IMAGE: ${{ !inputs.push-dev && 'docker.io/astral/uv' || '' }} permissions: {} jobs: docker-plan: name: plan runs-on: ubuntu-latest timeout-minutes: 2 outputs: login: ${{ steps.plan.outputs.login }} push: ${{ steps.plan.outputs.push }} push-version: ${{ steps.plan.outputs.push-version }} tag: ${{ steps.plan.outputs.tag }} action: ${{ steps.plan.outputs.action }} extra-images: ${{ steps.extra-images.outputs.matrix }} steps: - name: Set push variable env: DRY_RUN: ${{ inputs.plan == '' || fromJson(inputs.plan).announcement_tag_is_implicit }} TAG: ${{ inputs.plan != '' && fromJson(inputs.plan).announcement_tag }} PUSH_DEV: ${{ inputs.push-dev }} IS_LOCAL_PR: ${{ github.event.pull_request.head.repo.full_name == 'astral-sh/uv' }} id: plan run: | if [ "${PUSH_DEV}" == "true" ] && [ "${IS_LOCAL_PR}" == "true" ]; then echo "login=true" >> "$GITHUB_OUTPUT" echo "push=true" >> "$GITHUB_OUTPUT" echo "push-version=false" >> "$GITHUB_OUTPUT" echo "tag=sha" >> "$GITHUB_OUTPUT" echo "action=build and publish to uv-dev" >> "$GITHUB_OUTPUT" elif [ "${DRY_RUN}" == "false" ]; then echo "login=true" >> "$GITHUB_OUTPUT" echo "push=true" >> "$GITHUB_OUTPUT" echo "push-version=true" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" echo "action=build and publish" >> "$GITHUB_OUTPUT" else echo "login=${IS_LOCAL_PR}" >> "$GITHUB_OUTPUT" echo "push=false" >> "$GITHUB_OUTPUT" echo "push-version=false" >> "$GITHUB_OUTPUT" echo "tag=dry-run" >> "$GITHUB_OUTPUT" echo "action=build" >> "$GITHUB_OUTPUT" fi - name: Generate extra image matrix id: extra-images env: LOGIN: ${{ steps.plan.outputs.login }} run: | # Each entry is: base-image,tag1,tag2,... # DHI (Docker Hardened Images) require authentication to pull, so they # are excluded when login is unavailable (e.g., PRs from forks). images=( "alpine:3.23,alpine3.23,alpine" "alpine:3.22,alpine3.22" "debian:trixie-slim,trixie-slim,debian-slim" "buildpack-deps:trixie,trixie,debian" "python:3.14-alpine3.23,python3.14-alpine3.23,python3.14-alpine" "python:3.13-alpine3.23,python3.13-alpine3.23,python3.13-alpine" "python:3.12-alpine3.23,python3.12-alpine3.23,python3.12-alpine" "python:3.11-alpine3.23,python3.11-alpine3.23,python3.11-alpine" "python:3.10-alpine3.23,python3.10-alpine3.23,python3.10-alpine" "python:3.9-alpine3.22,python3.9-alpine3.22,python3.9-alpine" "python:3.14-trixie,python3.14-trixie" "python:3.13-trixie,python3.13-trixie" "python:3.12-trixie,python3.12-trixie" "python:3.11-trixie,python3.11-trixie" "python:3.10-trixie,python3.10-trixie" "python:3.9-trixie,python3.9-trixie" "python:3.14-slim-trixie,python3.14-trixie-slim" "python:3.13-slim-trixie,python3.13-trixie-slim" "python:3.12-slim-trixie,python3.12-trixie-slim" "python:3.11-slim-trixie,python3.11-trixie-slim" "python:3.10-slim-trixie,python3.10-trixie-slim" "python:3.9-slim-trixie,python3.9-trixie-slim" ) if [ "${LOGIN}" == "true" ]; then images+=( "dhi.io/alpine-base:3.23,alpine3.23-dhi,alpine-dhi" "dhi.io/debian-base:trixie-debian13,trixie-dhi,debian-dhi" "dhi.io/python:3.14,python3.14-dhi" "dhi.io/python:3.13,python3.13-dhi" "dhi.io/python:3.12,python3.12-dhi" "dhi.io/python:3.11,python3.11-dhi" "dhi.io/python:3.10,python3.10-dhi" ) fi json=$(printf '%s\n' "${images[@]}" | jq -R . | jq -sc '{"image-mapping": .}') echo "matrix=${json}" >> "$GITHUB_OUTPUT" docker-publish-base: name: ${{ needs.docker-plan.outputs.action }} uv needs: - docker-plan runs-on: ubuntu-latest timeout-minutes: 20 permissions: contents: read id-token: write # for Depot OIDC and GHCR signing packages: write # for GHCR image pushes attestations: write # for GHCR attestations environment: name: ${{ needs.docker-plan.outputs.push-version == 'true' && 'release' || (needs.docker-plan.outputs.push == 'true' && 'release-test' || '') }} deployment: ${{ needs.docker-plan.outputs.push-version == 'true' }} outputs: image-tags: ${{ steps.meta.outputs.tags }} image-annotations: ${{ steps.meta.outputs.annotations }} image-digest: ${{ steps.build.outputs.digest }} image-version: ${{ steps.meta.outputs.version }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false # Login to DockerHub (when not pushing, it's to avoid rate-limiting) - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 if: ${{ needs.docker-plan.outputs.login == 'true' }} with: username: ${{ needs.docker-plan.outputs.push == 'true' && 'astral' || 'astralshbot' }} password: ${{ needs.docker-plan.outputs.push == 'true' && secrets.DOCKERHUB_TOKEN_RW || secrets.DOCKERHUB_TOKEN_RO }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 if: ${{ needs.docker-plan.outputs.push == 'true' }} with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1 - name: Check tag consistency if: ${{ needs.docker-plan.outputs.push == 'true' }} run: | if [ "${PUSH_DEV}" == "true" ]; then echo "Building at $(git rev-parse HEAD)" exit 0 fi version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g') if [ "${TAG}" != "${version}" ]; then echo "The input tag does not match the version from pyproject.toml:" >&2 echo "${TAG}" >&2 echo "${version}" >&2 exit 1 fi echo "Releasing ${version}" env: TAG: ${{ needs.docker-plan.outputs.tag }} PUSH_DEV: ${{ inputs.push-dev }} - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 env: DOCKER_METADATA_ANNOTATIONS_LEVELS: index with: images: | ${{ env.UV_GHCR_IMAGE }} ${{ env.UV_DOCKERHUB_IMAGE }} # Defining this makes sure the org.opencontainers.image.version OCI label becomes the actual release version and not the branch name tags: | type=raw,value=dry-run,enable=${{ needs.docker-plan.outputs.push == 'false' }} type=pep440,pattern={{ version }},value=${{ needs.docker-plan.outputs.tag }},enable=${{ needs.docker-plan.outputs.push-version == 'true' }} type=pep440,pattern={{ major }}.{{ minor }},value=${{ needs.docker-plan.outputs.tag }},enable=${{ needs.docker-plan.outputs.push-version == 'true' }} type=sha,enable=${{ inputs.push-dev }} - name: Build and push by digest id: build uses: depot/build-push-action@98e78adca7817480b8185f474a400b451d74e287 # v1.18.0 with: project: 7hd4vdzmw5 # astral-sh/uv context: . platforms: linux/amd64,linux/arm64 push: ${{ needs.docker-plan.outputs.push }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} provenance: mode=max sbom: true # TODO(zanieb): Annotations are not supported by Depot yet and are ignored annotations: ${{ steps.meta.outputs.annotations }} - name: Generate artifact attestation for base image if: ${{ needs.docker-plan.outputs.push == 'true' }} uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ${{ env.UV_GHCR_IMAGE }} subject-digest: ${{ steps.build.outputs.digest }} docker-publish-extra: name: ${{ needs.docker-plan.outputs.action }} ${{ matrix.image-mapping }} runs-on: ubuntu-latest timeout-minutes: 5 environment: name: ${{ needs.docker-plan.outputs.push-version == 'true' && 'release' || (needs.docker-plan.outputs.push == 'true' && 'release-test' || '') }} deployment: ${{ needs.docker-plan.outputs.push-version == 'true' }} needs: - docker-plan - docker-publish-base permissions: id-token: write # for Depot OIDC and GHCR signing packages: write # for GHCR image pushes attestations: write # for GHCR attestations strategy: fail-fast: false matrix: ${{ fromJson(needs.docker-plan.outputs.extra-images) }} steps: # Login to DockerHub (when not pushing, it's to avoid rate-limiting) - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 if: ${{ needs.docker-plan.outputs.login == 'true' }} with: username: ${{ needs.docker-plan.outputs.push == 'true' && 'astral' || 'astralshbot' }} password: ${{ needs.docker-plan.outputs.push == 'true' && secrets.DOCKERHUB_TOKEN_RW || secrets.DOCKERHUB_TOKEN_RO }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 if: ${{ needs.docker-plan.outputs.login == 'true' }} with: registry: dhi.io username: astralshbot password: ${{ secrets.DOCKERHUB_TOKEN_RO }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1 - name: Generate Dynamic Dockerfile Tags shell: bash run: | set -euo pipefail # Extract the image and tags from the matrix variable IFS=',' read -r BASE_IMAGE BASE_TAGS <<< "${IMAGE_MAPPING}" # Generate Dockerfile content cat < Dockerfile FROM ${BASE_IMAGE} COPY --from=${UV_GHCR_IMAGE}:${UV_BASE_TAG} /uv /uvx /usr/local/bin/ ENV UV_TOOL_BIN_DIR="/usr/local/bin" ENTRYPOINT [] CMD ["/usr/local/bin/uv"] EOF # Initialize a variable to store all tag docker metadata patterns TAG_PATTERNS="" # Loop through all base tags and append its docker metadata pattern to the list # Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version IFS=','; for TAG in ${BASE_TAGS}; do if [ "${PUSH_DEV}" == "true" ]; then TAG_PATTERNS="${TAG_PATTERNS}type=sha,suffix=-${TAG}\n" else TAG_PATTERNS="${TAG_PATTERNS}type=pep440,pattern={{ version }},suffix=-${TAG},value=${VERSION}\n" TAG_PATTERNS="${TAG_PATTERNS}type=pep440,pattern={{ major }}.{{ minor }},suffix=-${TAG},value=${VERSION}\n" TAG_PATTERNS="${TAG_PATTERNS}type=raw,value=${TAG}\n" fi done # Remove the trailing newline from the pattern list TAG_PATTERNS="${TAG_PATTERNS%\\n}" # Export tag patterns using the multiline env var syntax { echo "TAG_PATTERNS<> $GITHUB_ENV env: VERSION: ${{ needs.docker-plan.outputs.tag }} PUSH_DEV: ${{ inputs.push-dev }} # Use the tag from the base image we just pushed; fall back to `latest` on dry-runs # since the base image isn't pushed to the registry. UV_BASE_TAG: ${{ needs.docker-plan.outputs.push == 'true' && needs.docker-publish-base.outputs.image-version || 'latest' }} IMAGE_MAPPING: ${{ matrix.image-mapping }} - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 # ghcr.io prefers index level annotations env: DOCKER_METADATA_ANNOTATIONS_LEVELS: index with: images: | ${{ env.UV_GHCR_IMAGE }} ${{ env.UV_DOCKERHUB_IMAGE }} flavor: | latest=false tags: | ${{ env.TAG_PATTERNS }} - name: Build and push id: build-and-push uses: depot/build-push-action@98e78adca7817480b8185f474a400b451d74e287 # v1.18.0 with: context: . project: 7hd4vdzmw5 # astral-sh/uv platforms: linux/amd64,linux/arm64 push: ${{ needs.docker-plan.outputs.push }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} provenance: mode=max sbom: true # TODO(zanieb): Annotations are not supported by Depot yet and are ignored annotations: ${{ steps.meta.outputs.annotations }} - name: Generate artifact attestation if: ${{ needs.docker-plan.outputs.push == 'true' }} uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ${{ env.UV_GHCR_IMAGE }} subject-digest: ${{ steps.build-and-push.outputs.digest }} # Push annotations manually. # See `docker-annotate-base` for details. - name: Add annotations to images if: ${{ needs.docker-plan.outputs.push == 'true' }} env: IMAGES: "${{ env.UV_GHCR_IMAGE }} ${{ env.UV_DOCKERHUB_IMAGE }}" DIGEST: ${{ steps.build-and-push.outputs.digest }} TAGS: ${{ steps.meta.outputs.tags }} ANNOTATIONS: ${{ steps.meta.outputs.annotations }} run: | set -x readarray -t lines <<< "$ANNOTATIONS"; annotations=(); for line in "${lines[@]}"; do annotations+=(--annotation "$line"); done for image in $IMAGES; do readarray -t lines < <(grep "^${image}:" <<< "$TAGS"); tags=(); for line in "${lines[@]}"; do tags+=(-t "$line"); done docker buildx imagetools create \ "${annotations[@]}" \ "${tags[@]}" \ "${image}@${DIGEST}" done # See `docker-annotate-base` for details. - name: Export manifest digest id: manifest-digest if: ${{ needs.docker-plan.outputs.push == 'true' }} env: IMAGE: ${{ env.UV_GHCR_IMAGE }} VERSION: ${{ steps.meta.outputs.version }} run: | digest="$( docker buildx imagetools inspect \ "${IMAGE}:${VERSION}" \ --format '{{json .Manifest}}' \ | jq -r '.digest' )" echo "digest=${digest}" >> "$GITHUB_OUTPUT" # See `docker-annotate-base` for details. - name: Generate artifact attestation if: ${{ needs.docker-plan.outputs.push == 'true' }} uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ${{ env.UV_GHCR_IMAGE }} subject-digest: ${{ steps.manifest-digest.outputs.digest }} # Annotate the base image docker-annotate-base: name: annotate uv runs-on: ubuntu-latest timeout-minutes: 2 permissions: contents: read id-token: write # for GHCR signing packages: write # for GHCR image pushes attestations: write # for GHCR attestations environment: name: ${{ needs.docker-plan.outputs.push-version == 'true' && 'release' || (needs.docker-plan.outputs.push == 'true' && 'release-test' || '') }} deployment: ${{ needs.docker-plan.outputs.push-version == 'true' }} needs: - docker-plan - docker-publish-base - docker-publish-extra if: ${{ needs.docker-plan.outputs.push == 'true' }} steps: - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: username: astral password: ${{ secrets.DOCKERHUB_TOKEN_RW }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} # Depot doesn't support annotating images, so we need to do so manually # afterwards. Mutating the manifest is desirable regardless, because we # want to bump the base image to appear at the top of the list on GHCR. # However, once annotation support is added to Depot, this step can be # minimized to just touch the GHCR manifest. - name: Add annotations to images env: IMAGES: "${{ env.UV_GHCR_IMAGE }} ${{ env.UV_DOCKERHUB_IMAGE }}" DIGEST: ${{ needs.docker-publish-base.outputs.image-digest }} TAGS: ${{ needs.docker-publish-base.outputs.image-tags }} ANNOTATIONS: ${{ needs.docker-publish-base.outputs.image-annotations }} # The readarray part is used to make sure the quoting and special characters are preserved on expansion (e.g. spaces) # The final command becomes `docker buildx imagetools create --annotation 'index:foo=1' --annotation 'index:bar=2' ... -t tag1 -t tag2 ... @sha256:` run: | set -x readarray -t lines <<< "$ANNOTATIONS"; annotations=(); for line in "${lines[@]}"; do annotations+=(--annotation "$line"); done for image in $IMAGES; do readarray -t lines < <(grep "^${image}:" <<< "$TAGS"); tags=(); for line in "${lines[@]}"; do tags+=(-t "$line"); done docker buildx imagetools create \ "${annotations[@]}" \ "${tags[@]}" \ "${image}@${DIGEST}" done # Now that we've modified the manifest, we need to attest it again. # Note we only generate an attestation for GHCR. - name: Export manifest digest id: manifest-digest env: IMAGE: ${{ env.UV_GHCR_IMAGE }} VERSION: ${{ needs.docker-publish-base.outputs.image-version }} # To sign the manifest, we need it's digest. Unfortunately "docker # buildx imagetools create" does not (yet) have a clean way of sharing # the digest of the manifest it creates (see docker/buildx#2407), so # we use a separate command to retrieve it. # imagetools inspect [TAG] --format '{{json .Manifest}}' gives us # the machine readable JSON description of the manifest, and the # jq command extracts the digest from this. The digest is then # sent to the Github step output file for sharing with other steps. run: | digest="$( docker buildx imagetools inspect \ "${IMAGE}:${VERSION}" \ --format '{{json .Manifest}}' \ | jq -r '.digest' )" echo "digest=${digest}" >> "$GITHUB_OUTPUT" - name: Generate artifact attestation uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ${{ env.UV_GHCR_IMAGE }} subject-digest: ${{ steps.manifest-digest.outputs.digest }} astral-sh-uv-9252ba6/.github/workflows/build-release-binaries.yml000066400000000000000000001434141521221614700250330ustar00rootroot00000000000000# Build uv on all platforms. # # Generates both wheels (for PyPI) and archived binaries (for GitHub releases). # # Called from: # - .github/workflows/ci.yml (when release-relevant files change) # - .github/workflows/release.yml (as a local artifacts job within `cargo-dist`) name: "Build release binaries" on: workflow_call: inputs: plan: required: false type: string concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: PACKAGE_NAME: uv MODULE_NAME: uv PYTHON_VERSION: "3.11" CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 permissions: {} jobs: sdist: name: sdist runs-on: depot-ubuntu-24.04-4 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} # uv - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi - name: "Build sdist" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 command: sdist args: --out dist - name: "Test sdist" run: | # We can't use `--find-links` here, since we need maturin, which means no `--no-index`, and without that option # we run the risk that pip pull uv from PyPI instead. pip install dist/${PACKAGE_NAME}-*.tar.gz --force-reinstall ${MODULE_NAME} --help python -m ${MODULE_NAME} --help uvx --help - name: "Upload sdist" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-sdist path: dist # uv-build - name: "Build sdist uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 command: sdist args: --out crates/uv-build/dist -m crates/uv-build/Cargo.toml - name: "Fix Cargo.lock in sdist uv-build" run: python scripts/repair-sdist-cargo-lock.py crates/uv-build/dist/${PACKAGE_NAME}_build-*.tar.gz - name: "Test sdist uv-build" run: | pip install crates/uv-build/dist/${PACKAGE_NAME}_build-*.tar.gz --force-reinstall ${MODULE_NAME}-build --help python -m ${MODULE_NAME}_build --help - name: "Upload sdist uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-sdist path: crates/uv-build/dist macos-x86_64: name: x86_64-apple-darwin runs-on: depot-macos-15 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi - name: "Install cargo extensions" shell: bash run: scripts/install-cargo-extensions.sh # uv - name: "Build wheels - x86_64" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: x86_64 args: --release --locked --out dist --features self-update --compatibility pypi env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # Use Rust's bundled Mach-O LLD, which supports ICF. # ICF reduces the macOS x86_64 uv binary size by ~2%. RUSTFLAGS: "-C linker=rust-lld -C linker-flavor=ld64.lld -C link-arg=--icf=safe" - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-macos-x86_64 path: dist - name: "Archive binary" run: | TARGET=x86_64-apple-darwin ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-macos-x86_64 path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build - x86_64" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: x86_64 args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # Use Rust's bundled Mach-O LLD, which supports ICF. # ICF reduces the macOS x86_64 uv-build binary size by ~0.7%. RUSTFLAGS: "-C linker=rust-lld -C linker-flavor=ld64.lld -C link-arg=--icf=safe" - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-macos-x86_64 path: crates/uv-build/dist macos-aarch64: name: aarch64-apple-darwin runs-on: depot-macos-15 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: arm64 - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi - name: "Install cargo extensions" shell: bash run: scripts/install-cargo-extensions.sh # uv - name: "Build wheels - aarch64" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: aarch64 manylinux: 2_17 args: --release --locked --out dist --features self-update --compatibility pypi env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # Use Rust's bundled Mach-O LLD, which supports ICF. # ICF reduces the macOS aarch64 uv binary size by ~1.4%. RUSTFLAGS: "-C linker=rust-lld -C linker-flavor=ld64.lld -C link-arg=--icf=safe" - name: "Test wheel - aarch64" run: | pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall ${MODULE_NAME} --help python -m ${MODULE_NAME} --help uvx --help - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-aarch64-apple-darwin path: dist - name: "Archive binary" run: | TARGET=aarch64-apple-darwin ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-aarch64-apple-darwin path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build - aarch64" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: aarch64 args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # Use Rust's bundled Mach-O LLD, which supports ICF. # ICF reduces the macOS aarch64 uv-build binary size by ~0.7%. RUSTFLAGS: "-C linker=rust-lld -C linker-flavor=ld64.lld -C link-arg=--icf=safe" - name: "Test wheel - aarch64" run: | pip install ${PACKAGE_NAME}_build --no-index --find-links crates/uv-build/dist --force-reinstall ${MODULE_NAME}-build --help python -m ${MODULE_NAME}_build --help - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-aarch64-apple-darwin path: crates/uv-build/dist windows: name: ${{ matrix.platform.target }} runs-on: ${{ matrix.platform.runner }} strategy: matrix: platform: - target: x86_64-pc-windows-msvc arch: x64 runner: github-windows-2025-x86_64-8 - target: i686-pc-windows-msvc arch: x86 runner: github-windows-2025-x86_64-8 - target: aarch64-pc-windows-msvc arch: arm64 runner: github-windows-11-aarch64-8 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: ${{ matrix.platform.arch }} - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi - name: "Install cargo extensions" shell: bash run: scripts/install-cargo-extensions.sh - name: "Install NASM" # NASM is required for x86/x86-64 Windows targets by aws-lc-sys. # On aarch64-pc-windows-msvc, it uses clang-cl instead. # See: https://aws.github.io/aws-lc-rs/requirements/windows.html#build-requirements if: contains(matrix.platform.target, 'x86') || contains(matrix.platform.target, 'i686') run: | winget install NASM.NASM --accept-source-agreements --accept-package-agreements echo "C:\Program Files\NASM" | Out-File -FilePath $env:GITHUB_PATH -Append # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} args: --release --locked --out dist --features self-update,windows-gui-bin --compatibility pypi env: CARGO: ${{ github.workspace }}/scripts/cargo.cmd # Disable prebuilt NASM objects so we always compile assembly from source. AWS_LC_SYS_PREBUILT_NASM: "0" - name: "Test wheel" shell: bash run: | pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall ${MODULE_NAME} --help python -m ${MODULE_NAME} --help uvx --help uvw --help - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_FILE=uv-${PLATFORM_TARGET}.zip 7z a $ARCHIVE_FILE ./target/${PLATFORM_TARGET}/release/uv.exe 7z a $ARCHIVE_FILE ./target/${PLATFORM_TARGET}/release/uvx.exe 7z a $ARCHIVE_FILE ./target/${PLATFORM_TARGET}/release/uvw.exe sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: PLATFORM_TARGET: ${{ matrix.platform.target }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.platform.target }} path: | *.zip *.sha256 # uv-build - name: "Build wheels uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi env: CARGO: ${{ github.workspace }}/scripts/cargo.cmd - name: "Test wheel uv-build" shell: bash run: | pip install ${PACKAGE_NAME}_build --no-index --find-links crates/uv-build/dist --force-reinstall ${MODULE_NAME}-build --help python -m ${MODULE_NAME}_build --help - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.platform.target }} path: crates/uv-build/dist linux: name: ${{ matrix.target }} runs-on: depot-ubuntu-latest-4 strategy: matrix: include: - { target: "i686-unknown-linux-gnu", cc: "gcc -m32" } - { target: "x86_64-unknown-linux-gnu", cc: "gcc" } steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.target }} # Generally, we try to build in a target docker container. In this case however, a # 32-bit compiler runs out of memory (4GB memory limit for 32-bit), so we cross compile # from 64-bit version of the container, breaking the pattern from other builds. container: quay.io/pypa/manylinux2014 manylinux: 2_17 docker-options: -e CARGO args: --release --locked --out dist --features self-update --compatibility pypi before-script-linux: | # Install the 32-bit cross target on 64-bit (noop if we're already on 64-bit) rustup target add ${{ matrix.target }} # If we're running on rhel centos, install needed packages. if command -v yum &> /dev/null; then yum update -y && yum install -y pkgconfig libatomic # Install cross build requirements if [[ "${{ matrix.target }}" == "i686-unknown-linux-gnu" ]]; then yum install -y glibc-devel.i686 libstdc++-devel.i686 libatomic.i686 fi # Symlink libatomic so the linker can find it with -latomic. if [[ -f "/usr/lib/libatomic.so.1" && ! -f "/usr/lib/libatomic.so" ]]; then ln -s /usr/lib/libatomic.so.1 /usr/lib/libatomic.so fi else # If we're running on debian-based system. apt update -y && apt-get install -y pkg-config fi # Install cargo extensions as a static musl binary so it runs in any container. scripts/install-cargo-extensions.sh env: CC: ${{ matrix.cc }} CARGO: ${{ github.workspace }}/scripts/cargo.sh - name: "Test wheel" if: ${{ startsWith(matrix.target, 'x86_64') }} run: | pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall ${MODULE_NAME} --help python -m ${MODULE_NAME} --help uvx --help - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: TARGET: ${{ matrix.target }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.target }} path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.target }} manylinux: 2_17 docker-options: -e CARGO args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh - name: "Test wheel uv-build" if: ${{ startsWith(matrix.target, 'x86_64') }} run: | pip install ${PACKAGE_NAME}_build --no-index --find-links crates/uv-build/dist --force-reinstall ${MODULE_NAME}-build --help python -m ${MODULE_NAME}_build --help - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.target }} path: crates/uv-build/dist linux-arm: name: ${{ matrix.platform.target }} runs-on: depot-ubuntu-24.04-8 timeout-minutes: 30 strategy: matrix: platform: - target: aarch64-unknown-linux-gnu arch: aarch64 # see https://github.com/astral-sh/ruff/issues/3791 # and https://github.com/gnzlbg/jemallocator/issues/170#issuecomment-1503228963 maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16 # Build fails with 2_17 container: https://github.com/astral-sh/uv/actions/runs/20850906093/job/59905482208?pr=17358 manylinux: 2_28 - target: armv7-unknown-linux-gnueabihf arch: armv7 manylinux: 2_17 - target: arm-unknown-linux-musleabihf arch: arm # Special case: armv6l is linux_armv6l, no manylinux or musllinux. # "auto" instead of "off" to get the cross container manylinux: auto steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: ${{ matrix.platform.manylinux }} docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --release --locked --out dist --features self-update --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel" with: arch: ${{ matrix.platform.arch == 'arm' && 'armv6' || matrix.platform.arch }} distro: ${{ matrix.platform.arch == 'arm' && 'bullseye' || 'ubuntu20.04' }} install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall ${MODULE_NAME} --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME} --help uvx --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: TARGET: ${{ matrix.platform.target }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.platform.target }} path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: ${{ matrix.platform.manylinux }} docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel uv-build" with: arch: ${{ matrix.platform.arch == 'arm' && 'armv6' || matrix.platform.arch }} distro: ${{ matrix.platform.arch == 'arm' && 'bullseye' || 'ubuntu20.04' }} install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME}_build --no-index --find-links crates/uv-build/dist --force-reinstall ${MODULE_NAME}-build --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME}_build --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.platform.target }} path: crates/uv-build/dist # Like `linux-arm`. linux-s390x: name: ${{ matrix.platform.target }} timeout-minutes: 30 runs-on: depot-ubuntu-latest-4 strategy: matrix: platform: - target: s390x-unknown-linux-gnu arch: s390x steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: 2_17 docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --release --locked --out dist --features self-update --compatibility pypi rust-toolchain: ${{ matrix.platform.toolchain || null }} before-script-linux: | scripts/install-cargo-extensions.sh # Install the s390x cross target on x86_64 rustup target add ${{ matrix.platform.target }} apt-get update && apt-get install -y gcc-s390x-linux-gnu binutils-s390x-linux-gnu env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel" with: arch: ${{ matrix.platform.arch }} distro: ubuntu20.04 install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall ${MODULE_NAME} --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME} --help uvx --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: TARGET: ${{ matrix.platform.target }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.platform.target }} path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: 2_17 docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel uv-build" with: arch: ${{ matrix.platform.arch }} distro: ubuntu20.04 install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME}-build --no-index --find-links crates/uv-build/dist --force-reinstall ${MODULE_NAME}-build --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME}-build --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.platform.target }} path: crates/uv-build/dist # Like `linux-arm`, but install the `gcc-powerpc64-linux-gnu` package. linux-powerpc: name: ${{ matrix.platform.target }} runs-on: depot-ubuntu-24.04-4 strategy: matrix: platform: - target: powerpc64le-unknown-linux-gnu arch: ppc64le # see https://github.com/astral-sh/uv/issues/6528 maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: 2_17 docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --release --locked --out dist --features self-update --compatibility pypi before-script-linux: | if command -v yum &> /dev/null; then yum update -y yum -y install epel-release yum repolist yum install -y gcc-powerpc64-linux-gnu fi scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(charlie): Re-enable testing for PPC wheels. # - uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1 # name: "Test wheel" # with: # arch: ${{ matrix.platform.arch }} # distro: ubuntu20.04 # install: | # apt-get update # apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 # pip3 install -U pip # run: | # pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall # ${MODULE_NAME} --help # #(konsti) TODO: Enable this test on all platforms,currently `find_uv_bin` is failingto discover uv here. # # python -m ${MODULE_NAME} --helppython -m ${MODULE_NAME} --help # uvx --help - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: TARGET: ${{ matrix.platform.target }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.platform.target }} path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: 2_17 docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi before-script-linux: | if command -v yum &> /dev/null; then yum update -y yum -y install epel-release yum repolist yum install -y gcc-powerpc64-linux-gnu fi scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(charlie): Re-enable testing for PPC wheels. - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.platform.target }} path: crates/uv-build/dist # Like `linux-arm`. linux-riscv64: name: ${{ matrix.platform.target }} timeout-minutes: 30 runs-on: depot-ubuntu-latest-4 strategy: matrix: platform: - target: riscv64gc-unknown-linux-gnu arch: riscv64 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: 2_31 docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --release --locked --out dist --features self-update --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel" with: arch: ${{ matrix.platform.arch }} distro: ubuntu20.04 githubToken: ${{ github.token }} install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall ${MODULE_NAME} --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME} --help uvx --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: TARGET: ${{ matrix.platform.target }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.platform.target }} path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: 2_31 docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel uv-build" with: arch: ${{ matrix.platform.arch }} distro: ubuntu20.04 githubToken: ${{ github.token }} install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME}-build --no-index --find-links crates/uv-build/dist --force-reinstall ${MODULE_NAME}-build --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME}-build --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.platform.target }} path: crates/uv-build/dist musllinux: name: ${{ matrix.target }} runs-on: depot-ubuntu-24.04-4 strategy: matrix: target: - x86_64-unknown-linux-musl - i686-unknown-linux-musl steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.target }} manylinux: musllinux_1_1 docker-options: -e CARGO args: --release --locked --out dist --features self-update --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh - name: "Test wheel" if: matrix.target == 'x86_64-unknown-linux-musl' run: | docker run --rm -v ${{ github.workspace }}:/io -w /io --env MODULE_NAME --env PACKAGE_NAME alpine:3.12 sh -c " apk add python3 py3-pip; python3 -m venv .venv; .venv/bin/pip install --upgrade pip; .venv/bin/pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall; .venv/bin/${MODULE_NAME} --help; # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # .venv/bin/python -m ${MODULE_NAME} --help; .venv/bin/uvx --help; " - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/release/uv $ARCHIVE_NAME/uv cp target/$TARGET/release/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: TARGET: ${{ matrix.target }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.target }} path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels uv-build" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.target }} manylinux: musllinux_1_1 docker-options: -e CARGO args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh - name: "Test wheel uv-build" if: matrix.target == 'x86_64-unknown-linux-musl' run: | docker run --rm -v ${{ github.workspace }}:/io -w /io --env MODULE_NAME --env PACKAGE_NAME alpine:3.12 sh -c " apk add python3 py3-pip; python3 -m venv .venv; .venv/bin/pip install --upgrade pip; .venv/bin/pip install ${PACKAGE_NAME}-build --no-index --find-links crates/uv-build/dist --force-reinstall; .venv/bin/${MODULE_NAME}-build --help; # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # .venv/bin/python -m ${MODULE_NAME}_build --help; " - name: "Upload wheels uv-build" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.target }} path: crates/uv-build/dist musllinux-cross: name: ${{ matrix.platform.target }} runs-on: depot-ubuntu-24.04-8 env: CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_MUSL_RUSTFLAGS: "-C target-feature=+crt-static" strategy: matrix: platform: - target: aarch64-unknown-linux-musl arch: aarch64 maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16 - target: armv7-unknown-linux-musleabihf arch: armv7 - target: riscv64gc-unknown-linux-musl arch: riscv64 fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi # uv - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: musllinux_1_1 # Tag the musl builds as manylinux 2_17 fallback cause the aarch64 build only support 2_28 args: --release --locked --out dist --features self-update --compatibility ${{ matrix.platform.arch == 'riscv64' && '2_31' || '2_17'}} --compatibility pypi docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} rust-toolchain: ${{ matrix.platform.toolchain || null }} before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel" with: arch: ${{ matrix.platform.arch }} distro: alpine_latest install: | apk add python3 run: | python -m venv .venv .venv/bin/pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall .venv/bin/${MODULE_NAME} --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # .venv/bin/python -m ${MODULE_NAME} --help .venv/bin/uvx --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel (manylinux)" if: matrix.platform.arch == 'aarch64' with: arch: aarch64 distro: ubuntu20.04 install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall ${MODULE_NAME} --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME} --help uvx --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_NAME=uv-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp target/$TARGET/$PROFILE/uv $ARCHIVE_NAME/uv cp target/$TARGET/$PROFILE/uvx $ARCHIVE_NAME/uvx tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 env: TARGET: ${{ matrix.platform.target }} PROFILE: ${{ matrix.platform.arch == 'ppc64le' && 'release-no-lto' || 'release' }} - name: "Upload binary" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: artifacts-${{ matrix.platform.target }} path: | *.tar.gz *.sha256 # uv-build - name: "Build wheels" uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: maturin-version: v1.13.1 target: ${{ matrix.platform.target }} manylinux: musllinux_1_1 args: --profile minimal-size --locked ${{ matrix.platform.arch == 'aarch64' && '--compatibility 2_17' || ''}} --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi docker-options: -e CARGO ${{ matrix.platform.maturin_docker_options }} rust-toolchain: ${{ matrix.platform.toolchain || null }} before-script-linux: | scripts/install-cargo-extensions.sh env: CARGO: ${{ github.workspace }}/scripts/cargo.sh # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel" with: arch: ${{ matrix.platform.arch }} distro: alpine_latest install: | apk add python3 run: | python -m venv .venv .venv/bin/pip install ${PACKAGE_NAME}-build --no-index --find-links crates/uv-build/dist --force-reinstall .venv/bin/${MODULE_NAME}-build --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # .venv/bin/python -m ${MODULE_NAME}_build --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 name: "Test wheel (manylinux)" if: matrix.platform.arch == 'aarch64' with: arch: aarch64 distro: ubuntu20.04 install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip python-is-python3 pip3 install -U pip run: | pip install ${PACKAGE_NAME}-build --no-index --find-links crates/uv-build/dist --force-reinstall ${MODULE_NAME}-build --help # TODO(konsti): Enable this test on all platforms, currently `find_uv_bin` is failing to discover uv here. # python -m ${MODULE_NAME}_build --help env: | PACKAGE_NAME: ${{ env.PACKAGE_NAME }} MODULE_NAME: ${{ env.MODULE_NAME }} - name: "Upload wheels" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels_uv_build-${{ matrix.platform.target }} path: crates/uv-build/dist check-wheels: name: "Check wheel contents" runs-on: ubuntu-slim needs: - macos-x86_64 - macos-aarch64 - windows - linux - linux-arm - linux-s390x - linux-powerpc - linux-riscv64 - musllinux - musllinux-cross steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: wheels_*-* path: wheels merge-multiple: true - name: "Check wheel contents" run: uv run --no-project scripts/check_uv_wheel_contents.py wheels/* astral-sh-uv-9252ba6/.github/workflows/check-docs.yml000066400000000000000000000017031521221614700225210ustar00rootroot00000000000000on: workflow_call: permissions: {} jobs: docs: timeout-minutes: 10 name: "mkdocs" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ github.ref == 'refs/heads/main' }} - name: "Generate reference documentation" run: | cargo dev generate-options-reference cargo dev generate-cli-reference cargo dev generate-env-vars-reference - name: "Build docs" run: uv run --only-group docs mkdocs build --strict -f mkdocs.yml astral-sh-uv-9252ba6/.github/workflows/check-fmt.yml000066400000000000000000000017411521221614700223610ustar00rootroot00000000000000on: workflow_call: permissions: {} jobs: rust: timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Rustfmt" run: rustup component add rustfmt - run: cargo fmt --all --check prettier: timeout-minutes: 10 runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - run: npx prettier --check . python: timeout-minutes: 10 runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - run: uvx ruff format --diff . astral-sh-uv-9252ba6/.github/workflows/check-generated-files.yml000066400000000000000000000023231521221614700246260ustar00rootroot00000000000000on: workflow_call: inputs: schema-changed: required: true type: string save-rust-cache: required: false type: string default: "true" permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: cargo-dev-generate-all: timeout-minutes: 10 runs-on: ubuntu-latest name: "cargo dev generate-all" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Generate all" run: cargo dev generate-all --mode dry-run - name: "Check sysconfig mappings" run: cargo dev generate-sysconfig-metadata --mode check - name: "Install Rustfmt" run: rustup component add rustfmt - name: "Check Packse scenario tests" run: cargo dev generate-scenario-tests --mode check - name: "Check JSON schema" if: ${{ inputs.schema-changed == 'true' }} run: cargo dev generate-json-schema --mode check astral-sh-uv-9252ba6/.github/workflows/check-lint.yml000066400000000000000000000130521521221614700225370ustar00rootroot00000000000000on: workflow_call: inputs: code-changed: required: true type: string save-rust-cache: required: false type: string default: "true" permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: ruff: timeout-minutes: 10 runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - run: uvx ruff check . ty: timeout-minutes: 10 runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - run: | uvx ty check python/uv uvx \ --directory crates/uv-python \ --with-requirements fetch-download-metadata.py \ ty check --python-version 3.13 fetch-download-metadata.py shellcheck: timeout-minutes: 10 runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install shellcheck" run: | # renovate: datasource=github-releases depName=koalaman/shellcheck SHELLCHECK_VERSION="v0.11.0" curl -sSL "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar -xJf - sudo mv "shellcheck-${SHELLCHECK_VERSION}/shellcheck" /usr/local/bin/ - name: "Run shellcheck" run: find . -name '*.sh' -type f | xargs shellcheck --shell bash --severity style validate-pyproject: timeout-minutes: 10 runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - run: uvx --from 'validate-pyproject[all,store]' validate-pyproject pyproject.toml readme: timeout-minutes: 10 runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.14 - run: python scripts/transform_readme.py --target pypi clippy-ubuntu: name: "clippy on linux" timeout-minutes: 10 if: ${{ inputs.code-changed == 'true' || github.ref == 'refs/heads/main' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Check uv_build dependencies" uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20 with: command: check bans manifest-path: crates/uv-build/Cargo.toml - name: "Install Rust toolchain" run: rustup component add clippy - name: "Clippy" run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings clippy-windows: name: "clippy on windows" timeout-minutes: 15 if: ${{ inputs.code-changed == 'true' || github.ref == 'refs/heads/main' }} runs-on: namespace-profile-windows-2022-x86-64-4 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Dev Drive run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1 # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone... - name: Copy Git Repo to Dev Drive run: | Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: ${{ env.UV_WORKSPACE }} save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Install Rust toolchain" run: rustup component add clippy - name: "Clippy" working-directory: ${{ env.UV_WORKSPACE }} run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings shear: name: "cargo shear" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install cargo shear" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-shear@1.12.4 - run: cargo shear --deny-warnings typos: runs-on: ubuntu-slim steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: crate-ci/typos@f8a58b6b53f2279f71eb605f03a4ae4d10608f45 # v1.47.0 astral-sh-uv-9252ba6/.github/workflows/check-lock.yml000066400000000000000000000012611521221614700225200ustar00rootroot00000000000000on: workflow_call: permissions: {} jobs: lock: timeout-minutes: 10 runs-on: ubuntu-slim name: "uv lockfiles" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - name: "Check project lockfile" run: uv lock --check - name: "Check script lockfiles" run: | while IFS= read -r lockfile; do uv lock --script "${lockfile%.lock}" --check done < <(git ls-files '*.py.lock') astral-sh-uv-9252ba6/.github/workflows/check-publish.yml000066400000000000000000000012131521221614700232330ustar00rootroot00000000000000on: workflow_call: permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: cargo-publish-dry-run: timeout-minutes: 20 runs-on: depot-ubuntu-24.04-8 name: "cargo publish dry-run" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ github.ref == 'refs/heads/main' }} - name: "cargo publish dry-run" run: cargo publish --workspace --dry-run astral-sh-uv-9252ba6/.github/workflows/check-release.yml000066400000000000000000000021331521221614700232070ustar00rootroot00000000000000on: workflow_call: env: CARGO_DIST_VERSION: "0.31.0" CARGO_DIST_CHECKSUM: "cd355dab0b4c02fb59038fef87655550021d07f45f1d82f947a34ef98560abb8" jobs: dist-plan: name: "dist plan" runs-on: ubuntu-latest env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false submodules: recursive - name: Install dist shell: bash run: | curl --proto '=https' --tlsv1.2 -LsSf "https://github.com/axodotdev/cargo-dist/releases/download/v${CARGO_DIST_VERSION}/cargo-dist-x86_64-unknown-linux-gnu.tar.xz" -o /tmp/cargo-dist.tar.xz echo "${CARGO_DIST_CHECKSUM} /tmp/cargo-dist.tar.xz" | sha256sum -c - tar -xf /tmp/cargo-dist.tar.xz -C /tmp install /tmp/cargo-dist-x86_64-unknown-linux-gnu/dist ~/.cargo/bin/ - name: Run dist plan run: | dist plan --output-format=json > plan-dist-manifest.json echo "dist plan completed successfully" cat plan-dist-manifest.json astral-sh-uv-9252ba6/.github/workflows/check-zizmor.yml000066400000000000000000000005511521221614700231230ustar00rootroot00000000000000on: workflow_call: permissions: {} jobs: zizmor: runs-on: ubuntu-latest permissions: security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 astral-sh-uv-9252ba6/.github/workflows/ci.yml000066400000000000000000000512631521221614700211170ustar00rootroot00000000000000name: CI on: push: branches: [main] pull_request: workflow_dispatch: permissions: {} concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true jobs: plan: runs-on: depot-ubuntu-24.04 outputs: test-code: ${{ steps.plan.outputs.test_code }} check-schema: ${{ steps.plan.outputs.check_schema }} build-release-binaries: ${{ steps.plan.outputs.build_release_binaries }} run-checks: ${{ steps.plan.outputs.run_checks }} test-publish: ${{ steps.plan.outputs.test_publish }} test-windows-trampoline: ${{ steps.plan.outputs.test_windows_trampoline }} test-windows-trampoline-check-binary: ${{ steps.plan.outputs.test_windows_trampoline_check_binary }} save-rust-cache: ${{ steps.plan.outputs.save_rust_cache }} run-bench: ${{ steps.plan.outputs.run_bench }} test-smoke: ${{ steps.plan.outputs.test_smoke }} test-ecosystem: ${{ steps.plan.outputs.test_ecosystem }} test-integration: ${{ steps.plan.outputs.test_integration }} test-system: ${{ steps.plan.outputs.test_system }} test-macos: ${{ steps.plan.outputs.test_macos }} build-docker: ${{ steps.plan.outputs.build_docker }} push-docker: ${{ steps.plan.outputs.push_docker }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - name: "Plan" id: plan shell: bash env: GH_REF: ${{ github.ref }} HAS_SKIP_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'test:skip') }} HAS_INTEGRATION_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'test:integration') }} HAS_SYSTEM_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'test:system') }} HAS_EXTENDED_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'test:extended') }} HAS_MACOS_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'test:macos') }} HAS_PUBLISH_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'test:publish') }} HAS_BUILD_SKIP_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'build:skip') }} HAS_BUILD_SKIP_DOCKER_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'build:skip-docker') }} HAS_BUILD_SKIP_RELEASE_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'build:skip-release') }} HAS_BUILD_RELEASE_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'build:release') }} HAS_BUILD_PUSH_DOCKER_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'build:push-docker') }} HAS_BUILD_WINDOWS_TRAMPOLINE_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'build:windows-trampoline') }} BASE_SHA: ${{ github.event.pull_request.base.sha }} run: | [[ "$GH_REF" == "refs/heads/main" ]] && on_main_branch=1 [[ "$HAS_SKIP_LABEL" == "true" ]] && has_skip_label=1 [[ "$HAS_INTEGRATION_LABEL" == "true" ]] && has_integration_label=1 [[ "$HAS_SYSTEM_LABEL" == "true" ]] && has_system_label=1 [[ "$HAS_EXTENDED_LABEL" == "true" ]] && has_extended_label=1 [[ "$HAS_MACOS_LABEL" == "true" ]] && has_macos_label=1 [[ "$HAS_PUBLISH_LABEL" == "true" ]] && has_publish_label=1 [[ "$HAS_BUILD_SKIP_LABEL" == "true" ]] && has_build_skip_label=1 [[ "$HAS_BUILD_SKIP_DOCKER_LABEL" == "true" ]] && has_build_skip_docker_label=1 [[ "$HAS_BUILD_SKIP_RELEASE_LABEL" == "true" ]] && has_build_skip_release_label=1 [[ "$HAS_BUILD_RELEASE_LABEL" == "true" ]] && has_build_release_label=1 [[ "$HAS_BUILD_PUSH_DOCKER_LABEL" == "true" ]] && has_build_push_docker_label=1 [[ "$HAS_BUILD_WINDOWS_TRAMPOLINE_LABEL" == "true" ]] && has_build_windows_trampoline_label=1 # Detect changed files while IFS= read -r file; do [[ -z "$file" ]] && continue [[ "$file" =~ \.rs$ ]] && rust_code_changed=1 [[ "$file" == "Cargo.toml" || "$file" == "Cargo.lock" || "$file" =~ ^crates/.*/Cargo\.toml$ ]] && rust_deps_changed=1 [[ "$file" == "rust-toolchain.toml" || "$file" == "crates/uv-build/rust-toolchain.toml" || "$file" =~ ^\.cargo/ ]] && rust_config_changed=1 [[ "$file" == "pyproject.toml" || "$file" =~ ^crates/.*/pyproject\.toml$ ]] && python_config_changed=1 [[ "$file" =~ ^\.github/workflows/.*\.yml$ ]] && workflow_changed=1 [[ "$file" == ".github/workflows/build-release-binaries.yml" || "$file" == ".github/workflows/release.yml" ]] && release_workflow_changed=1 [[ "$file" == "scripts/check_uv_wheel_contents.py" || "$file" == "scripts/patch-dist-manifest-checksums.py" || "$file" == "scripts/repair-sdist-cargo-lock.py" ]] && release_build_changed=1 [[ "$file" == ".github/workflows/ci.yml" ]] && ci_workflow_changed=1 [[ "$file" == "uv.schema.json" ]] && schema_changed=1 [[ "$file" =~ ^crates/uv-publish/ || "$file" =~ ^scripts/publish/ || "$file" == "crates/uv/src/commands/publish.rs" ]] && publish_code_changed=1 [[ "$file" == ".github/workflows/test-windows-trampolines.yml" ]] && trampoline_workflow_changed=1 [[ "$file" =~ ^crates/uv-trampoline/ || "$file" =~ ^crates/uv-trampoline-builder/ ]] && trampoline_code_changed=1 [[ "$file" =~ ^crates/uv-trampoline/.*\.rs$ || "$file" =~ ^crates/uv-trampoline-builder/trampolines/ ]] && trampoline_repro_changed=1 [[ "$file" == "scripts/build-trampolines.sh" || "$file" == "scripts/build-trampolines-in-docker.sh" ]] && trampoline_build_scripts_changed=1 [[ "$file" == "scripts/build-trampolines.sh" || "$file" == "scripts/build-trampolines-in-docker.sh" || "$file" == "scripts/check-trampoline-version-consistency.py" ]] && trampoline_scripts_changed=1 [[ "$file" =~ ^crates/uv-build/ ]] && uv_build_changed=1 [[ "$file" == "Dockerfile" ]] && dockerfile_changed=1 [[ "$file" == ".github/workflows/build-docker.yml" ]] && docker_workflow_changed=1 [[ "$file" == ".github/workflows/bench.yml" ]] && bench_workflow_changed=1 [[ "$file" == ".github/workflows/test-integration.yml" || "$file" =~ ^test/integration/ || "$file" == "scripts/check_registry.py" || "$file" == "scripts/check_cache_compat.py" || "$file" == "scripts/registries-test.py" ]] && integration_changed=1 [[ "$file" == ".github/workflows/test-system.yml" ]] && system_workflow_changed=1 [[ "$file" == "scripts/check_system_python.py" || "$file" == "scripts/check_embedded_python.py" ]] && system_test_changed=1 [[ "$file" =~ ^docs/ || "$file" =~ ^mkdocs.*\.yml$ || "$file" =~ \.md$ || "$file" =~ ^bin/ || "$file" =~ ^assets/ ]] && continue any_code_changed=1 done <<< "$(git diff --name-only "${BASE_SHA:-origin/main}...HEAD")" # Derived groups [[ $rust_code_changed || $rust_deps_changed || $rust_config_changed ]] && any_rust_changed=1 [[ $python_config_changed || $rust_deps_changed || $rust_config_changed || $uv_build_changed || $release_workflow_changed ]] && release_build_changed=1 [[ $publish_code_changed || $ci_workflow_changed ]] && publish_changed=1 [[ $rust_deps_changed || $rust_config_changed || $workflow_changed ]] && cache_relevant_changed=1 [[ $python_config_changed || $rust_deps_changed || $rust_config_changed || $dockerfile_changed || $docker_workflow_changed ]] && docker_build_changed=1 # Decisions [[ ! $has_skip_label && ($any_code_changed || $on_main_branch) ]] && test_code=1 [[ $schema_changed ]] && check_schema=1 [[ ! $has_skip_label && ! $has_build_skip_label && ! $has_build_skip_release_label && ($release_build_changed || $has_build_release_label) ]] && build_release_binaries=1 [[ ! $has_skip_label ]] && run_checks=1 [[ $publish_changed || $has_publish_label || $has_extended_label || $on_main_branch ]] && test_publish=1 [[ ! $has_skip_label && ($trampoline_code_changed || $trampoline_scripts_changed || $trampoline_workflow_changed || $rust_deps_changed || $has_build_windows_trampoline_label || $on_main_branch) ]] && test_windows_trampoline=1 [[ ! $has_skip_label && ($trampoline_repro_changed || $trampoline_build_scripts_changed || $has_build_windows_trampoline_label) ]] && test_windows_trampoline_check_binary=1 [[ $on_main_branch || $cache_relevant_changed ]] && save_rust_cache=1 [[ ! $has_skip_label && ($any_rust_changed || $bench_workflow_changed || $on_main_branch) ]] && run_bench=1 [[ ! $has_skip_label ]] && test_smoke=1 [[ ! $has_skip_label ]] && test_ecosystem=1 [[ $has_integration_label || $has_extended_label || $on_main_branch || $integration_changed ]] && test_integration=1 [[ $has_system_label || $has_extended_label || $on_main_branch || $system_workflow_changed || $system_test_changed ]] && test_system=1 [[ $has_macos_label || $has_extended_label || $on_main_branch || $build_release_binaries ]] && test_macos=1 [[ ! $has_build_skip_label && ! $has_build_skip_docker_label && ($docker_build_changed || $has_build_push_docker_label) ]] && build_docker=1 [[ $has_build_push_docker_label ]] && push_docker=1 # Output (convert 1/empty to true/false for GHA) out() { [[ "$2" ]] && echo "$1=true" || echo "$1=false"; } { out test_code "$test_code" out check_schema "$check_schema" out build_release_binaries "$build_release_binaries" out run_checks "$run_checks" out test_publish "$test_publish" out test_windows_trampoline "$test_windows_trampoline" out test_windows_trampoline_check_binary "$test_windows_trampoline_check_binary" out save_rust_cache "$save_rust_cache" out run_bench "$run_bench" out test_smoke "$test_smoke" out test_ecosystem "$test_ecosystem" out test_integration "$test_integration" out test_system "$test_system" out test_macos "$test_macos" out build_docker "$build_docker" out push_docker "$push_docker" } >> "$GITHUB_OUTPUT" check-fmt: uses: ./.github/workflows/check-fmt.yml check-lint: needs: plan uses: ./.github/workflows/check-lint.yml with: code-changed: ${{ needs.plan.outputs.test-code }} save-rust-cache: ${{ needs.plan.outputs.save-rust-cache }} check-docs: needs: plan if: ${{ needs.plan.outputs.run-checks == 'true' }} uses: ./.github/workflows/check-docs.yml secrets: inherit check-zizmor: needs: plan if: ${{ needs.plan.outputs.run-checks == 'true' }} uses: ./.github/workflows/check-zizmor.yml permissions: contents: read security-events: write check-publish: needs: plan if: ${{ needs.plan.outputs.test-code == 'true' }} uses: ./.github/workflows/check-publish.yml check-release: needs: plan if: ${{ needs.plan.outputs.run-checks == 'true' }} uses: ./.github/workflows/check-release.yml check-lock: needs: plan if: ${{ needs.plan.outputs.run-checks == 'true' }} uses: ./.github/workflows/check-lock.yml check-generated-files: needs: plan if: ${{ needs.plan.outputs.test-code == 'true' }} uses: ./.github/workflows/check-generated-files.yml with: schema-changed: ${{ needs.plan.outputs.check-schema }} save-rust-cache: ${{ needs.plan.outputs.save-rust-cache }} test: needs: plan if: ${{ needs.plan.outputs.test-code == 'true' }} uses: ./.github/workflows/test.yml with: save-rust-cache: ${{ needs.plan.outputs.save-rust-cache }} test-macos: ${{ needs.plan.outputs.test-macos }} test-windows-trampolines: needs: plan if: ${{ needs.plan.outputs.test-windows-trampoline == 'true' }} uses: ./.github/workflows/test-windows-trampolines.yml with: trampoline-build: ${{ needs.plan.outputs.test-windows-trampoline-check-binary }} build-dev-binaries: needs: plan if: ${{ needs.plan.outputs.test-code == 'true' }} uses: ./.github/workflows/build-dev-binaries.yml with: save-rust-cache: ${{ needs.plan.outputs.save-rust-cache }} test-smoke: needs: - plan - build-dev-binaries if: ${{ needs.plan.outputs.test-smoke == 'true' }} uses: ./.github/workflows/test-smoke.yml with: sha: ${{ github.sha }} test-integration: needs: - plan - build-dev-binaries if: ${{ needs.plan.outputs.test-integration == 'true' }} uses: ./.github/workflows/test-integration.yml secrets: inherit permissions: id-token: write with: sha: ${{ github.sha }} test-system: needs: - plan - build-dev-binaries if: ${{ needs.plan.outputs.test-system == 'true' }} uses: ./.github/workflows/test-system.yml with: sha: ${{ github.sha }} test-ecosystem: needs: - plan - build-dev-binaries if: ${{ needs.plan.outputs.test-ecosystem == 'true' }} uses: ./.github/workflows/test-ecosystem.yml with: sha: ${{ github.sha }} build-release-binaries: needs: plan if: ${{ needs.plan.outputs.build-release-binaries == 'true' }} uses: ./.github/workflows/build-release-binaries.yml secrets: inherit build-docker: needs: plan if: ${{ needs.plan.outputs.build-docker == 'true' }} uses: ./.github/workflows/build-docker.yml with: push-dev: ${{ needs.plan.outputs.push-docker == 'true' }} secrets: inherit permissions: contents: read id-token: write packages: write attestations: write bench: needs: plan if: ${{ needs.plan.outputs.run-bench == 'true' }} uses: ./.github/workflows/bench.yml secrets: inherit with: save-rust-cache: ${{ needs.plan.outputs.save-rust-cache }} # This job cannot be moved into a reusable workflow because it includes coverage for uploading # attestations and PyPI does not support attestations in reusable workflows. test-publish: name: "test uv publish" timeout-minutes: 20 needs: - plan - build-dev-binaries runs-on: ubuntu-latest # Only the main repository is a trusted publisher if: ${{ github.repository == 'astral-sh/uv' && github.event.pull_request.head.repo.fork != true && needs.plan.outputs.test-publish == 'true' }} environment: name: uv-test-publish deployment: false env: # No dbus in GitHub Actions PYTHON_KEYRING_BACKEND: keyrings.alt.file.PlaintextKeyring PYTHON_VERSION: 3.12 permissions: # For trusted publishing id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "${{ env.PYTHON_VERSION }}" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ github.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Build astral-test-pypa-gh-action" shell: bash -eo pipefail {0} run: | # Build a yet unused version of `astral-test-pypa-gh-action` mkdir astral-test-pypa-gh-action cd astral-test-pypa-gh-action ../uv init --package --no-workspace # Get the latest patch version patch_version=$(curl https://test.pypi.org/simple/astral-test-pypa-gh-action/?format=application/vnd.pypi.simple.v1+json | jq --raw-output '[.files[].filename | select(endswith(".tar.gz"))] | last' | grep -oP '(?<=astral_test_pypa_gh_action-0\.1\.)\d+(?=\.tar\.gz)') # Set the current version to one higher (which should be unused) sed -i "s/0.1.0/0.1.$((patch_version + 1))/g" pyproject.toml ../uv build - name: "Publish astral-test-pypa-gh-action" uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: # With this GitHub action, we can't do as rigid checks as with our custom Python script, so we publish more # leniently skip-existing: "true" verbose: "true" repository-url: "https://test.pypi.org/legacy/" packages-dir: "astral-test-pypa-gh-action/dist" - name: "Request GitLab OIDC tokens for impersonation" uses: digital-blueprint/gitlab-pipeline-trigger-action@c59b56e9d2688ab42c1304322ac8831a4ef6f7d2 # v1.4.0 with: host: gitlab.com id: astral-test-publish/astral-test-gitlab-pypi-tp ref: main trigger_token: ${{ secrets.GITLAB_TEST_PUBLISH_TRIGGER_TOKEN }} access_token: ${{ secrets.GITLAB_TEST_PUBLISH_ACCESS_TOKEN }} download_artifacts: true fail_if_no_artifacts: true download_path: ./gitlab-artifacts - name: "Load GitLab OIDC tokens from GitLab job artifacts" id: load-gitlab-oidc-token run: | # we expect ./gitlab-artifacts/*/artifacts/pypi-id-token to exist pypi_id_token_file=$(find ./gitlab-artifacts -type f -name pypi-id-token | head -n 1) if [ -z "${pypi_id_token_file}" ]; then echo "No pypi-id-token file found in GitLab artifacts" exit 1 fi GITLAB_PYPI_OIDC_TOKEN=$(cat "${pypi_id_token_file}") # we expect ./gitlab-artifacts/*/artifacts/pyx-id-token to exist pyx_id_token_file=$(find ./gitlab-artifacts -type f -name pyx-id-token | head -n 1) if [ -z "${pyx_id_token_file}" ]; then echo "No pyx-id-token file found in GitLab artifacts" exit 1 fi GITLAB_PYX_OIDC_TOKEN=$(cat "${pyx_id_token_file}") # Add secret masks for the tokens. echo "::add-mask::$GITLAB_PYPI_OIDC_TOKEN" echo "::add-mask::$GITLAB_PYX_OIDC_TOKEN" echo "GITLAB_PYPI_OIDC_TOKEN=${GITLAB_PYPI_OIDC_TOKEN}" >> "${GITHUB_OUTPUT}" echo "GITLAB_PYX_OIDC_TOKEN=${GITLAB_PYX_OIDC_TOKEN}" >> "${GITHUB_OUTPUT}" - name: "Add password to keyring" run: | # `keyrings.alt` contains the plaintext keyring ./uv tool install --with keyrings.alt keyring echo $UV_TEST_PUBLISH_KEYRING | keyring set https://test.pypi.org/legacy/?astral-test-keyring __token__ env: UV_TEST_PUBLISH_KEYRING: ${{ secrets.UV_TEST_PUBLISH_KEYRING }} - name: "Add password to uv text store" run: | ./uv auth login https://test.pypi.org/legacy/?astral-test-text-store --token ${UV_TEST_PUBLISH_TEXT_STORE} env: UV_TEST_PUBLISH_TEXT_STORE: ${{ secrets.UV_TEST_PUBLISH_TEXT_STORE }} - name: "Publish test packages" # `-p 3.12` prefers the python we just installed over the one locked in `.python_version`. run: ./uv run --no-project -p "${PYTHON_VERSION}" scripts/publish/test_publish.py --uv ./uv all env: RUST_LOG: uv=debug,uv_publish=trace UV_TEST_PUBLISH_TOKEN: ${{ secrets.UV_TEST_PUBLISH_TOKEN }} UV_TEST_PUBLISH_PASSWORD: ${{ secrets.UV_TEST_PUBLISH_PASSWORD }} UV_TEST_PUBLISH_GITLAB_PAT: ${{ secrets.UV_TEST_PUBLISH_GITLAB_PAT }} UV_TEST_PUBLISH_CODEBERG_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CODEBERG_TOKEN }} UV_TEST_PUBLISH_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CLOUDSMITH_TOKEN }} UV_TEST_PUBLISH_PYX_TOKEN: ${{ secrets.UV_TEST_PUBLISH_PYX_TOKEN }} UV_TEST_PUBLISH_PYTHON_VERSION: ${{ env.PYTHON_VERSION }} UV_TEST_PUBLISH_GITLAB_PYPI_OIDC_TOKEN: ${{ steps.load-gitlab-oidc-token.outputs.GITLAB_PYPI_OIDC_TOKEN }} UV_TEST_PUBLISH_GITLAB_PYX_OIDC_TOKEN: ${{ steps.load-gitlab-oidc-token.outputs.GITLAB_PYX_OIDC_TOKEN }} required-checks-passed: name: "all required jobs passed" if: always() needs: - check-fmt - check-lint - check-docs - check-generated-files - test - build-dev-binaries runs-on: ubuntu-slim steps: - name: "Check required jobs passed" run: | failing=$(echo "$NEEDS_JSON" | jq -r 'to_entries[] | select(.value.result != "success" and .value.result != "skipped") | "\(.key): \(.value.result)"') if [ -n "$failing" ]; then echo "$failing" exit 1 fi env: NEEDS_JSON: ${{ toJSON(needs) }} astral-sh-uv-9252ba6/.github/workflows/publish-crates.yml000066400000000000000000000026471521221614700234530ustar00rootroot00000000000000# Publish a release to crates.io. # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a publish job # within `cargo-dist`. name: "Publish to crates.io" on: workflow_call: inputs: plan: required: true type: string jobs: crates-publish-uv: name: Upload uv to crates.io runs-on: ubuntu-latest environment: name: release permissions: contents: read id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4 id: auth - name: Install nightly toolchain # Nightly is required for the unstable `-Zpublish-timeout` flag, which lets us # raise the per-crate wait above the 60s default. crates.io indexing has been # known to lag long enough to exceed that during workspace publishes. run: rustup toolchain install nightly-2026-04-15 --profile minimal --no-self-update - name: Publish workspace crates # Note `--no-verify` is safe because we do a publish dry-run elsewhere in CI run: python3 scripts/publish-crates.py --cargo cargo +nightly-2026-04-15 --no-verify -- -Zpublish-timeout --config 'publish.timeout=600' env: CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} astral-sh-uv-9252ba6/.github/workflows/publish-docs.yml000066400000000000000000000113071521221614700231130ustar00rootroot00000000000000# Publish the uv documentation. # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce # job within `cargo-dist`. name: mkdocs on: workflow_dispatch: inputs: ref: description: "The commit SHA, tag, or branch to publish. Uses the default branch if not specified." default: "" type: string workflow_call: inputs: plan: required: true type: string permissions: {} jobs: mkdocs: environment: name: release runs-on: ubuntu-latest env: VERSION: ${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || inputs.ref }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.ref }} fetch-depth: 0 persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.14 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - name: "Generate reference documentation" run: | cargo dev generate-options-reference cargo dev generate-cli-reference cargo dev generate-env-vars-reference - name: "Set docs display name" run: | version="${VERSION}" # if version is missing, use 'latest' if [ -z "$version" ]; then echo "Using 'latest' as version" version="latest" fi # Use version as display name for now display_name="$version" echo "DISPLAY_NAME=$display_name" >> $GITHUB_ENV - name: "Set branch name" run: | version="${VERSION}" display_name="${DISPLAY_NAME}" timestamp="$(date +%s)" # create branch_display_name from display_name by replacing all # characters disallowed in git branch names with hyphens branch_display_name="$(echo "$display_name" | tr -c '[:alnum:]._' '-' | tr -s '-')" echo "BRANCH_NAME=update-docs-$branch_display_name-$timestamp" >> $GITHUB_ENV echo "TIMESTAMP=$timestamp" >> $GITHUB_ENV - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - name: "Build docs" run: uv run --only-group docs mkdocs build --strict -f mkdocs.yml - name: "Clone docs repo" run: | version="${VERSION}" git clone https://${ASTRAL_DOCS_PAT}@github.com/astral-sh/docs.git astral-docs env: ASTRAL_DOCS_PAT: ${{ secrets.ASTRAL_DOCS_PAT }} - name: "Copy docs" run: rm -rf astral-docs/site/uv && mkdir -p astral-docs/site && cp -r site/uv astral-docs/site/ - name: "Commit docs" working-directory: astral-docs run: | branch_name="${BRANCH_NAME}" git config user.name "astral-docs-bot" git config user.email "176161322+astral-docs-bot@users.noreply.github.com" git checkout -b $branch_name git add site/uv git commit -m "Update uv documentation for $version" - name: "Create Pull Request" working-directory: astral-docs env: GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }} run: | version="${VERSION}" display_name="${DISPLAY_NAME}" branch_name="${BRANCH_NAME}" # set the PR title pull_request_title="Update uv documentation for $display_name" # Delete any existing pull requests that are open for this version # by checking against pull_request_title because the new PR will # supersede the old one. gh pr list --state open --json title --jq '.[] | select(.title == "$pull_request_title") | .number' | \ xargs -I {} gh pr close {} # push the branch to GitHub git push origin $branch_name # create the PR gh pr create --base main --head $branch_name \ --title "$pull_request_title" \ --body "Automated documentation update for $display_name" \ --label "documentation" - name: "Merge Pull Request" if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} working-directory: astral-docs env: GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }} run: | branch_name="${BRANCH_NAME}" # auto-merge the PR if the build was triggered by a release. Manual builds should be reviewed by a human. # give the PR a few seconds to be created before trying to auto-merge it sleep 10 gh pr merge --squash $branch_name astral-sh-uv-9252ba6/.github/workflows/publish-mirror.yml000066400000000000000000000044051521221614700234760ustar00rootroot00000000000000# Publish uv releases to a mirror # # Assumed to run as a subworkflow of .github/workflows/release.yml as a custom publish job name: publish-mirror on: workflow_call: inputs: plan: required: true type: string permissions: {} jobs: publish-mirror: runs-on: ubuntu-latest environment: name: release env: VERSION: ${{ fromJson(inputs.plan).announcement_tag }} steps: - name: "Download GitHub Artifacts" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: "Upload to R2" env: AWS_ACCESS_KEY_ID: ${{ secrets.MIRROR_R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.MIRROR_R2_SECRET_ACCESS_KEY }} AWS_ENDPOINT_URL: https://${{ secrets.MIRROR_R2_CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com AWS_DEFAULT_REGION: auto R2_BUCKET: ${{ secrets.MIRROR_R2_BUCKET_NAME }} PROJECT: uv run: | aws s3 cp --recursive --output table --color on \ --exclude '*' \ --include '*.zip' --include '*.zip.sha256' \ --include '*.tar.gz' --include '*.tar.gz.sha256' \ --include sha256.sum --include '*.ps1' --include '*.sh' \ --cache-control "public, max-age=31536000, immutable" \ artifacts/ \ s3://${R2_BUCKET}/github/$PROJECT/releases/download/$VERSION/ - name: "Upload latest installers to R2" if: ${{ !fromJson(inputs.plan).announcement_is_prerelease }} env: AWS_ACCESS_KEY_ID: ${{ secrets.MIRROR_R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.MIRROR_R2_SECRET_ACCESS_KEY }} AWS_ENDPOINT_URL: https://${{ secrets.MIRROR_R2_CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com AWS_DEFAULT_REGION: auto R2_BUCKET: ${{ secrets.MIRROR_R2_BUCKET_NAME }} run: | for installer in uv-installer.sh uv-installer.ps1; do aws s3 cp --output table --color on \ --cache-control "public, max-age=300" \ "artifacts/${installer}" \ "s3://${R2_BUCKET}/installers/uv/latest/${installer}" done astral-sh-uv-9252ba6/.github/workflows/publish-pypi.yml000066400000000000000000000026311521221614700231440ustar00rootroot00000000000000# Publish a release to PyPI. # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a publish job # within `cargo-dist`. name: "Publish to PyPI" on: workflow_call: inputs: plan: required: true type: string jobs: pypi-publish-uv: name: Upload uv to PyPI runs-on: ubuntu-latest environment: name: release permissions: id-token: write # For PyPI's trusted publishing steps: - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: wheels_uv-* path: wheels_uv merge-multiple: true - name: Publish to PyPI run: uv publish -v wheels_uv/* pypi-publish-uv-build: name: Upload uv-build to PyPI runs-on: ubuntu-latest environment: name: release permissions: id-token: write # For PyPI's trusted publishing steps: - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: wheels_uv_build-* path: wheels_uv_build merge-multiple: true - name: Publish to PyPI run: uv publish -v wheels_uv_build/* astral-sh-uv-9252ba6/.github/workflows/publish-versions.yml000066400000000000000000000050001521221614700240240ustar00rootroot00000000000000# Publish uv version information to the versions repository. # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce # job within `cargo-dist`. name: publish-versions on: workflow_call: inputs: plan: required: true type: string permissions: {} jobs: publish-versions: runs-on: ubuntu-latest environment: name: release env: VERSION: ${{ fromJson(inputs.plan).announcement_tag }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Set branch name" run: echo "BRANCH_NAME=update-versions-$VERSION-$(date +%s)" >> $GITHUB_ENV - name: "Clone versions repo" run: git clone https://${{ secrets.ASTRAL_VERSIONS_PAT }}@github.com/astral-sh/versions.git astral-versions - name: "Install uv" uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - name: "Update versions" env: PLAN: ${{ inputs.plan }} run: printf '%s' "$PLAN" | uv run astral-versions/scripts/convert-cargo-dist-plan.py | uv run astral-versions/scripts/insert-versions.py --name uv - name: "Commit versions" working-directory: astral-versions run: | git config user.name "astral-versions-bot" git config user.email "176161322+astral-versions-bot@users.noreply.github.com" git checkout -b "$BRANCH_NAME" git add -A git commit -m "Update uv to version $VERSION" - name: "Create Pull Request" working-directory: astral-versions env: GITHUB_TOKEN: ${{ secrets.ASTRAL_VERSIONS_PAT }} run: | pull_request_title="Add uv $VERSION" gh pr list --state open --json title --jq ".[] | select(.title == \"$pull_request_title\") | .number" | \ xargs -I {} gh pr close {} git push origin "$BRANCH_NAME" gh pr create --base main --head "$BRANCH_NAME" \ --title "$pull_request_title" \ --body "Automated versions update for $VERSION" \ --label "automation" - name: "Merge Pull Request" if: ${{ !fromJson(inputs.plan).announcement_tag_is_implicit }} working-directory: astral-versions env: GITHUB_TOKEN: ${{ secrets.ASTRAL_VERSIONS_PAT }} run: | # Wait for PR to be created before merging sleep 10 gh pr merge --squash "$BRANCH_NAME" astral-sh-uv-9252ba6/.github/workflows/release-prepare.yml000066400000000000000000000037421521221614700235770ustar00rootroot00000000000000# Bump the uv version and create a pull request. name: "Prepare release" on: workflow_dispatch: inputs: bump: description: "The version bump type (auto-detected from PR labels if not specified)" required: false type: string version: description: "The exact version to set (overrides bump)" required: false type: string permissions: {} jobs: release: if: github.repository == 'astral-sh/uv' runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "latest" enable-cache: true - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: false - name: "Configure Git" run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: "Bump version" run: | if [ -n "$INPUT_VERSION" ]; then ./scripts/release.sh --version "$INPUT_VERSION" elif [ -n "$INPUT_BUMP" ]; then ./scripts/release.sh --bump "$INPUT_BUMP" else ./scripts/release.sh fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} INPUT_VERSION: ${{ inputs.version }} INPUT_BUMP: ${{ inputs.bump }} - name: "Create Pull Request" run: | branch="$(git branch --show-current)" git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:"$branch" gh pr create \ --base main \ --head "$branch" \ --fill env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} astral-sh-uv-9252ba6/.github/workflows/release.yml000066400000000000000000000375111521221614700221440ustar00rootroot00000000000000# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you workflow_dispatch with a tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: workflow_dispatch: inputs: tag: description: Release Tag required: true default: dry-run type: string env: CARGO_DIST_VERSION: "0.31.0" CARGO_DIST_CHECKSUM: "cd355dab0b4c02fb59038fef87655550021d07f45f1d82f947a34ef98560abb8" jobs: release-gate: # N.B. This name should not change, it is used for downstream checks. name: release-gate if: ${{ inputs.tag != 'dry-run' }} runs-on: ubuntu-latest # This environment requires a 2-factor approval, i.e., the workflow must be approved by another # team member. GitHub fires approval events on every job that deploys to an environment, so we # have a dedicated environment for this purpose instead of using the `release` environment. # We use a GitHub App with a deployment protection rule webhook to ensure that the `release` # environment is only approved when the `release-gate` job succeeds. environment: name: release-gate deployment: true steps: - run: echo "Release approved" # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "depot-ubuntu-latest-4" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ (inputs.tag != 'dry-run' && inputs.tag) || '' }} tag-flag: ${{ inputs.tag && inputs.tag != 'dry-run' && format('--tag={0}', inputs.tag) || '' }} publishing: ${{ inputs.tag && inputs.tag != 'dry-run' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false submodules: recursive - name: Install dist shell: bash run: | curl --proto '=https' --tlsv1.2 -LsSf "https://github.com/axodotdev/cargo-dist/releases/download/v${CARGO_DIST_VERSION}/cargo-dist-x86_64-unknown-linux-gnu.tar.xz" -o /tmp/cargo-dist.tar.xz echo "${CARGO_DIST_CHECKSUM} /tmp/cargo-dist.tar.xz" | sha256sum -c - tar -xf /tmp/cargo-dist.tar.xz -C /tmp install /tmp/cargo-dist-x86_64-unknown-linux-gnu/dist ~/.cargo/bin/ - name: Cache dist uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json custom-build-release-binaries: needs: - plan if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} uses: ./.github/workflows/build-release-binaries.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit custom-build-docker: needs: - plan - release-gate if: ${{ always() && needs.plan.result == 'success' && (needs.release-gate.result == 'success' || needs.release-gate.result == 'skipped') && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run') }} uses: ./.github/workflows/build-docker.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit permissions: "attestations": "write" "contents": "read" "id-token": "write" "packages": "write" generate-checksum-manifest: needs: - plan - custom-build-release-binaries if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} runs-on: "depot-ubuntu-latest-4" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false submodules: recursive - name: Install cached dist uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist - name: Fetch local artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - name: Generate local dist manifest shell: bash env: TAG: ${{ needs.plan.outputs.tag-flag }} run: | temp_manifest=target/local-dist-manifest.json.tmp dist manifest "$TAG" --output-format=json --no-local-paths --artifacts=local > "$temp_manifest" python3 scripts/patch-dist-manifest-checksums.py --manifest "$temp_manifest" --artifacts-dir target/distrib mv "$temp_manifest" target/distrib/local-dist-manifest.json - name: Upload synthesized local dist manifest uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-build-local-manifest path: target/distrib/local-dist-manifest.json # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - custom-build-release-binaries - custom-build-docker - generate-checksum-manifest runs-on: "depot-ubuntu-latest-4" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false submodules: recursive - name: Install cached dist uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - custom-build-release-binaries - custom-build-docker - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-release-binaries.result == 'skipped' || needs.custom-build-release-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "depot-ubuntu-latest-4" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false submodules: recursive - name: Install cached dist uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true # This is a harmless no-op for GitHub Releases, hosting for that happens in "announce" - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json custom-publish-pypi: needs: - plan - host - release-gate if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} uses: ./.github/workflows/publish-pypi.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit # publish jobs get escalated permissions permissions: "id-token": "write" "packages": "write" custom-publish-crates: needs: - plan - host - release-gate - custom-publish-pypi # DIRTY: see #16989 if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} uses: ./.github/workflows/publish-crates.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit # publish jobs get escalated permissions permissions: "contents": "read" "id-token": "write" # Create a GitHub Release while uploading all files to it announce: needs: - plan - host - release-gate - custom-publish-pypi # use "always() && ..." to allow publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! # `custom-publish-crates` is intentionally not a dependency: it's not # critical to the release and isn't idempotent on retry. if: ${{ always() && needs.host.result == 'success' && needs.release-gate.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') }} runs-on: "depot-ubuntu-latest-4" environment: name: release permissions: "attestations": "write" "contents": "write" "id-token": "write" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false submodules: recursive # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Attest uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 with: subject-path: | artifacts/*.json artifacts/*.sh artifacts/*.ps1 artifacts/*.zip artifacts/*.tar.gz - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(needs.host.outputs.val).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(needs.host.outputs.val).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(needs.host.outputs.val).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* custom-publish-docs: needs: - plan - announce # Use `always() && ...` so this job gates only on whether `announce` # completed successfully. if: ${{ always() && needs.announce.result == 'success' }} uses: ./.github/workflows/publish-docs.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit custom-publish-versions: needs: - plan - announce if: ${{ always() && needs.announce.result == 'success' }} uses: ./.github/workflows/publish-versions.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit custom-publish-mirror: needs: - plan - announce if: ${{ always() && needs.announce.result == 'success' }} uses: ./.github/workflows/publish-mirror.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit permissions: "contents": "read" astral-sh-uv-9252ba6/.github/workflows/setup-dev-drive.ps1000066400000000000000000000063461521221614700234530ustar00rootroot00000000000000# Configures a drive for testing in CI. # # When using standard GitHub Actions runners, a `D:` drive is present and has # similar or better performance characteristics than a ReFS dev drive. Sometimes # using a larger runner is still more performant (e.g., when running the test # suite) and we need to create a dev drive. This script automatically configures # the appropriate drive. # # When using GitHub Actions' "larger runners", the `D:` drive is not present and # we create a DevDrive mount on `C:`. This is purported to be more performant # than an ReFS drive, though we did not see a change when we switched over. # # When using Depot runners, the underling infrastructure is EC2, which does not # support Hyper-V. The `New-VHD` commandlet only works with Hyper-V, but we can # create a ReFS drive using `diskpart` and `format` directory. We cannot use a # DevDrive, as that also requires Hyper-V. The Depot runners use `D:` already, # so we must check if it's a Depot runner first, and we use `V:` as the target # instead. if ($env:DEPOT_RUNNER -eq "1") { Write-Output "DEPOT_RUNNER detected, setting up custom dev drive..." # Create VHD and configure drive using diskpart $vhdPath = "C:\uv_dev_drive.vhdx" @" create vdisk file="$vhdPath" maximum=25600 type=expandable attach vdisk create partition primary active assign letter=V "@ | diskpart # Format the drive as ReFS format V: /fs:ReFS /q /y $Drive = "V:" Write-Output "Custom dev drive created at $Drive" } elseif (Test-Path "D:\") { # Note `Get-PSDrive` is not sufficient because the drive letter is assigned. Write-Output "Using existing drive at D:" $Drive = "D:" } else { # The size (25 GB) is chosen empirically to be large enough for our # workflows; larger drives can take longer to set up. $Volume = New-VHD -Path C:/uv_dev_drive.vhdx -SizeBytes 25GB | Mount-VHD -Passthru | Initialize-Disk -Passthru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -DevDrive -Confirm:$false -Force $Drive = "$($Volume.DriveLetter):" # Set the drive as trusted # See https://learn.microsoft.com/en-us/windows/dev-drive/#how-do-i-designate-a-dev-drive-as-trusted fsutil devdrv trust $Drive # Disable antivirus filtering on dev drives # See https://learn.microsoft.com/en-us/windows/dev-drive/#how-do-i-configure-additional-filters-on-dev-drive fsutil devdrv enable /disallowAv # Remount so the changes take effect Dismount-VHD -Path C:/uv_dev_drive.vhdx Mount-VHD -Path C:/uv_dev_drive.vhdx # Show some debug information Write-Output $Volume fsutil devdrv query $Drive Write-Output "Using Dev Drive at $Volume" } $Tmp = "$($Drive)\uv-tmp" # Create the directory ahead of time in an attempt to avoid race-conditions New-Item $Tmp -ItemType Directory # Move Cargo to the dev drive New-Item -Path "$($Drive)/.cargo/bin" -ItemType Directory -Force if (Test-Path "C:/Users/runneradmin/.cargo") { Copy-Item -Path "C:/Users/runneradmin/.cargo/*" -Destination "$($Drive)/.cargo/" -Recurse -Force } Write-Output ` "DEV_DRIVE=$($Drive)" ` "TMP=$($Tmp)" ` "TEMP=$($Tmp)" ` "RUSTUP_HOME=$($Drive)/.rustup" ` "CARGO_HOME=$($Drive)/.cargo" ` "UV_WORKSPACE=$($Drive)/uv" ` "PATH=$($Drive)/.cargo/bin;$env:PATH" ` >> $env:GITHUB_ENV astral-sh-uv-9252ba6/.github/workflows/sync-python-releases.yml000066400000000000000000000037771521221614700246270ustar00rootroot00000000000000# Sync Python releases and create a pull request. # # Based on: https://github.com/astral-sh/rye/blob/57b7c089e494138aae29a130afb2e17f447970bf/.github/workflows/sync-python-releases.yml name: "Sync Python downloads" on: workflow_dispatch: schedule: - cron: "0 0 * * *" permissions: {} jobs: sync: if: github.repository == 'astral-sh/uv' runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "latest" enable-cache: true - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: false - name: Sync Python Releases run: | uv run -- fetch-download-metadata.py working-directory: ./crates/uv-python env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Sync Sysconfig Targets run: ${GITHUB_WORKSPACE}/crates/uv-dev/sync_sysconfig_targets.sh working-directory: ./crates/uv-dev env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Update Python version constants" run: uv run --no-project scripts/sync-python-version-constants.py - name: "Create Pull Request" uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: commit-message: "Sync latest Python releases" add-paths: | crates/uv-python/download-metadata.json crates/uv-dev/src/generate_sysconfig_mappings.rs crates/uv-python/src/sysconfig/generated_mappings.rs crates/uv-test/src/lib.rs branch: "sync-python-releases" title: "Sync latest Python releases" body: "Automated update for Python releases." base: "main" draft: true astral-sh-uv-9252ba6/.github/workflows/test-ecosystem.yml000066400000000000000000000034511521221614700235100ustar00rootroot00000000000000on: workflow_call: inputs: sha: required: true type: string permissions: {} jobs: ecosystem-test: name: "${{ matrix.repo }}" timeout-minutes: 10 runs-on: ubuntu-latest strategy: matrix: include: - repo: "prefecthq/prefect" ref: "7f25bbdf45fc81cca6dc23fb6a7377d436b70c83" commands: - "uv venv" - "uv pip install -e '.[dev]'" python: "3.9" - repo: "pallets/flask" ref: "b78b5a210bde49e7e04b62a2a4f453ca10e0048c" commands: - "uv venv" - "uv pip install -r requirements/dev.txt" python: "3.12" - repo: "pydantic/pydantic-core" ref: "d03bf4a01ca3b378cc8590bd481f307e82115bc6" commands: - "uv sync --group all" - "uv lock --upgrade" python: "3.12" fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: ${{ matrix.repo }} ref: ${{ matrix.ref }} persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python }} - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Test" run: | echo '${{ toJSON(matrix.commands) }}' | jq -r '.[]' | while read cmd; do echo "+ $cmd" >&2 if [[ $cmd == uv* ]]; then ./$cmd else $cmd fi done astral-sh-uv-9252ba6/.github/workflows/test-integration.yml000066400000000000000000001170171521221614700240240ustar00rootroot00000000000000on: workflow_call: inputs: sha: description: "The commit SHA to use for artifact names" required: true type: string permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always PYTHON_VERSION: "3.12" RUSTUP_MAX_RETRIES: 10 jobs: integration-test-nushell: name: "nushell" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Install nushell env: # This token only needs read access to the GitHub repository nushell/nushell. # This token is used (via gh-cli) to avoid hitting GitHub REST API rate limits. GITHUB_TOKEN: ${{ github.token }} run: |- # get latest nushell tag name nu_latest=$(gh release list --repo nushell/nushell --limit 1 --exclude-pre-releases --exclude-drafts --json "tagName" --jq '.[0].tagName') # trim any trailing whitespace from output nu_tag=${nu_latest%%[[:space:]]*} # download binary for x86_64-unknown-linux-gnu target gh release download ${nu_tag} --repo nushell/nushell --pattern "nu-${nu_tag}-x86_64-unknown-linux-gnu.tar.gz" # extract nu binary from tar.gz tar -xf "nu-${nu_tag}-x86_64-unknown-linux-gnu.tar.gz" # make the binary executable chmod +x "./nu-${nu_tag}-x86_64-unknown-linux-gnu/nu" # add it to PATH echo "${{ github.workspace }}/nu-${nu_tag}-x86_64-unknown-linux-gnu" >> "${GITHUB_PATH}" - name: Download binary uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: Prepare binary run: chmod +x ./uv - name: Create venv # The python version is arbitrary for this test. # We only want to ensure the activation script behaves properly run: ./uv venv - name: Activate venv shell: nu {0} run: overlay use ${{ github.workspace }}/.venv/bin/activate.nu integration-test-conda: name: "conda on linux" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 with: miniconda-version: latest activate-environment: uv python-version: "3.12" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: Conda info shell: bash -el {0} run: conda info - name: "Install a package" shell: bash -el {0} run: | echo "$CONDA_PREFIX" ./uv pip install anyio integration-test-conda-macos-x86_64: name: "conda on macos x86-64" timeout-minutes: 10 runs-on: macos-15-intel # github-macos-15-x86_64-4 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 with: miniconda-version: latest activate-environment: uv python-version: "3.12" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-macos-x86_64-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: Conda info shell: bash -el {0} run: conda info - name: "Install a package with macOS macOS 11.0 wheels" shell: bash -el {0} run: | # greenlet has `macosx_11_0_universal2` wheels. Previously, conda interpreters on Intel # macOS reported the incorrect platform tag `macosx_10_16` causing uv to consider the # wheel incompatible. See https://github.com/astral-sh/uv/issues/14267 # # Using `--only-binary` ensures this fails if the platform tags are wrong instead of # falling back to a source build. ./uv pip install --no-build 'greenlet==3.3.2' integration-test-deadsnakes-39-linux: name: "deadsnakes python3.9 on ubuntu" timeout-minutes: 15 runs-on: ubuntu-latest steps: - name: "Install python3.9" run: | for i in {1..5}; do sudo add-apt-repository ppa:deadsnakes && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; } if [ $i -eq 5 ]; then echo "Failed to add repository after 5 attempts" exit 1 fi done sudo apt-get update sudo apt-get install python3.9 - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Check missing distutils" run: | ./uv venv -p 3.9 --python-preference only-system -v 2>&1 | tee log.txt || true # We should report that distutils is missing grep 'Python installation is missing `distutils`' log.txt - name: "Install distutils" run: | sudo apt-get install python3.9-distutils - name: "Create a virtualenv" run: | ./uv venv -p 3.9 --python-preference only-system -v - name: "Check version" run: | .venv/bin/python --version - name: "Check install" run: | ./uv pip install -v anyio integration-test-linux-armv7-on-aarch64: name: "armv7 on aarch64 linux" timeout-minutes: 20 runs-on: github-ubuntu-24.04-aarch64-4 env: UV_PYTHON_INSTALL_DIR: ${{ github.workspace }}/.python steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-armv7-gnueabihf-${{ inputs.sha }} - name: "Install armhf runtime" run: | sudo dpkg --add-architecture armhf echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries > /dev/null sudo apt install -y --update libc6:armhf libgcc-s1:armhf - name: "Prepare binary" run: | chmod +x ./uv chmod +x ./uvx - name: "Install Python via uv" run: | ./uv python install -v 3.13.12 - name: "Verify hard-float variant selected" run: | python_path=$(./uv python find 3.13) expected="$GITHUB_WORKSPACE/.python/cpython-3.13-linux-armv7-gnueabihf/bin/python3.13" if [[ "$python_path" != "$expected" ]]; then echo "Expected: $expected" echo "Found: $python_path" exit 1 fi - name: "Create a virtual environment" run: | ./uv venv -c -p 3.13.12 --managed-python --no-python-downloads integration-test-free-threaded-windows-x86_64: name: "free-threaded on windows" timeout-minutes: 10 runs-on: windows-latest steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Install free-threaded Python via uv" run: | ./uv python install -v 3.13t - name: "Create a virtual environment (stdlib)" run: | & (./uv python find 3.13t) -m venv .venv - name: "Check version (stdlib)" run: | .venv/Scripts/python --version - name: "Create a virtual environment (uv)" run: | ./uv venv -c -p 3.13t --managed-python - name: "Check version (uv)" run: | .venv/Scripts/python --version - name: "Check is free-threaded" run: | .venv/Scripts/python -c "import sys; exit(1) if sys._is_gil_enabled() else exit(0)" - name: "Check install" run: | ./uv pip install -v anyio - name: "Check uv run" run: | ./uv run python -c "" ./uv run -p 3.13t python -c "" integration-test-windows-aarch64-implicit: name: "aarch64 windows implicit" timeout-minutes: 10 runs-on: windows-11-arm steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-aarch64-${{ inputs.sha }} - name: "Install Python via uv (implicitly select x64)" run: | ./uv python install -v 3.13 - name: "Create a virtual environment (stdlib)" run: | & (./uv python find 3.13) -m venv .venv - name: "Check version (stdlib)" run: | .venv/Scripts/python --version - name: "Create a virtual environment (uv)" run: | ./uv venv -c -p 3.13 --managed-python - name: "Check version (uv)" run: | .venv/Scripts/python --version - name: "Check is x64" run: | .venv/Scripts/python -c "import sys; exit(1) if 'AMD64' not in sys.version else exit(0)" - name: "Check install" run: | ./uv pip install -v anyio - name: "Check uv run" run: | ./uv run python -c "" ./uv run -p 3.13 python -c "" integration-test-windows-aarch64-explicit: name: "aarch64 windows explicit" timeout-minutes: 10 runs-on: windows-11-arm steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-aarch64-${{ inputs.sha }} - name: "Install Python via uv (explicitly select aarch64)" run: | ./uv python install -v cpython-3.13-windows-aarch64-none - name: "Create a virtual environment (stdlib)" run: | & (./uv python find 3.13) -m venv .venv - name: "Check version (stdlib)" run: | .venv/Scripts/python --version - name: "Create a virtual environment (uv)" run: | ./uv venv -c -p 3.13 --managed-python - name: "Check version (uv)" run: | .venv/Scripts/python --version - name: "Check is NOT x64" run: | .venv/Scripts/python -c "import sys; exit(1) if 'AMD64' in sys.version else exit(0)" - name: "Check install" run: | ./uv pip install -v anyio - name: "Check uv run" run: | ./uv run python -c "" ./uv run -p 3.13 python -c "" integration-test-windows-python-install-manager: name: "windows python install manager" timeout-minutes: 10 runs-on: windows-latest steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Install Python via Python Install manager" run: | # https://www.python.org/downloads/release/pymanager-250/ winget install --accept-package-agreements --accept-source-agreements 9NQ7512CXL7T # Call Python Install Manager's py.exe by full path to avoid legacy py.exe & "$env:LOCALAPPDATA\Microsoft\WindowsApps\py.exe" install 3.14 # https://github.com/astral-sh/uv/issues/16204 - name: "Check temporary environment creation" run: | ./uv run -p $env:LOCALAPPDATA\Python\pythoncore-3.14-64\python.exe --with numpy python -c "import sys; print(sys.executable)" # Test PEP 514 integration that installs Python into the Windows registry. integration-test-windows-registry: name: "windows registry" timeout-minutes: 10 runs-on: windows-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Test PEP 514 registration" run: python ./scripts/check_registry.py --uv ./uv.exe integration-test-pypy-linux: name: "pypy on linux" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Install PyPy" run: ./uv python install -v pypy3.9 - name: "Create a virtual environment" run: | ./uv venv -p pypy3.9 --managed-python - name: "Check for executables" run: | check_in_bin() { local executable_name=$1 local bin_path=".venv/bin" if [[ -x "$bin_path/$executable_name" ]]; then return 0 else echo "Executable '$executable_name' not found in folder '$bin_path'." return 1 fi } executables=("pypy" "pypy3" "python") all_found=true for executable_name in "${executables[@]}"; do check_in_bin "$executable_name" "$folder_path" result=$? if [[ $result -ne 0 ]]; then all_found=false fi done if ! $all_found; then echo "One or more expected executables were not found." exit 1 fi - name: "Check version" run: | .venv/bin/pypy --version .venv/bin/pypy3 --version .venv/bin/python --version - name: "Check install" run: | ./uv pip install anyio integration-test-python-install-wine: name: "python install under wine" timeout-minutes: 15 runs-on: ubuntu-latest env: WINEARCH: win64 WINEDEBUG: -all WINEPREFIX: /home/runner/uv-wine-prefix steps: - name: "Install Wine" run: | echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries > /dev/null sudo apt-get update sudo apt-get install -y --no-install-recommends wine wine64 - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Initialize Wine" run: | wine --version wineboot --init - name: "Install Python" # Regression test for https://github.com/astral-sh/uv/issues/19187. run: | mkdir -p "$RUNNER_TEMP/uv-cache" "$RUNNER_TEMP/uv-python" export UV_CACHE_DIR="$(winepath -w "$RUNNER_TEMP/uv-cache")" export UV_PYTHON_INSTALL_DIR="$(winepath -w "$RUNNER_TEMP/uv-python")" wine ./uv.exe python install -v --no-registry --no-bin 3.12.13 integration-test-pypy-windows-x86_64: name: "pypy on windows" timeout-minutes: 10 runs-on: windows-latest steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Install PyPy" run: .\uv.exe python install pypy3.9 - name: "Create a virtual environment" run: | .\uv.exe venv -p pypy3.9 --managed-python - name: "Check for executables" shell: python run: | import sys from pathlib import Path def binary_exist(binary): binaries_path = Path(".venv\\Scripts") if (binaries_path / binary).exists(): return True print(f"Executable '{binary}' not found in folder '{binaries_path}'.") all_found = True expected_binaries = [ "pypy3.9.exe", "pypy3.9w.exe", "pypy3.exe", "pypyw.exe", "python.exe", "python3.9.exe", "python3.exe", "pythonw.exe", ] for binary in expected_binaries: if not binary_exist(binary): all_found = False if not all_found: print("One or more expected executables were not found.") sys.exit(1) - name: "Check version" run: | & .venv\Scripts\pypy3.9.exe --version & .venv\Scripts\pypy3.exe --version & .venv\Scripts\python.exe --version - name: "Check install" run: | .\uv.exe pip install anyio integration-test-graalpy-linux: name: "graalpy on linux" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Install GraalPy" run: ./uv python install -v graalpy - name: "Create a virtual environment" run: | ./uv venv -p graalpy --managed-python - name: "Check for executables" run: | check_in_bin() { local executable_name=$1 local bin_path=".venv/bin" if [[ -x "$bin_path/$executable_name" ]]; then return 0 else echo "Executable '$executable_name' not found in folder '$bin_path'." return 1 fi } executables=("graalpy" "python3" "python") all_found=true for executable_name in "${executables[@]}"; do check_in_bin "$executable_name" "$folder_path" result=$? if [[ $result -ne 0 ]]; then all_found=false fi done if ! $all_found; then echo "One or more expected executables were not found." exit 1 fi - name: "Check version" run: | .venv/bin/graalpy --version .venv/bin/python3 --version .venv/bin/python --version - name: "Check install" run: | ./uv pip install anyio - name: "Check a GraalPy dev version (different version parsing)" run: | curl -sLf https://github.com/graalvm/graal-languages-ea-builds/releases/download/graalpy-25.0.0-ea.31/graalpy-25.0.0-ea.31-linux-amd64.tar.gz | tar xz ./uv run --no-project -p ./graalpy-25.0.0-dev-linux-amd64/bin/python python --version integration-test-graalpy-windows-x86_64: name: "graalpy on windows" timeout-minutes: 10 runs-on: windows-latest steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Install GraalPy" run: .\uv.exe python install graalpy - name: "Create a virtual environment" run: | .\uv.exe venv -p graalpy --managed-python - name: "Check for executables" shell: python run: | import sys from pathlib import Path def binary_exist(binary): binaries_path = Path(".venv\\Scripts") if (binaries_path / binary).exists(): return True print(f"Executable '{binary}' not found in folder '{binaries_path}'.") all_found = True expected_binaries = [ "graalpy.exe", "python.exe", "python3.exe", ] for binary in expected_binaries: if not binary_exist(binary): all_found = False if not all_found: print("One or more expected executables were not found.") sys.exit(1) - name: "Check version" run: | & .venv\Scripts\graalpy.exe --version & .venv\Scripts\python3.exe --version & .venv\Scripts\python.exe --version - name: "Check install" run: | .\uv.exe pip install anyio integration-test-pyodide-linux: name: "pyodide on linux" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Create a native virtual environment" run: | ./uv venv venv-native -p 3.12 # We use features added in 0.30.3 but there is no known breakage in # newer versions. ./uv pip install -p venv-native/bin/python pyodide-build==0.30.7 pip - name: "Install Pyodide interpreter" run: | source ./venv-native/bin/activate pyodide xbuildenv install 0.27.5 PYODIDE_PYTHON=$(pyodide config get interpreter) PYODIDE_INDEX=$(pyodide config get package_index) echo "PYODIDE_PYTHON=$PYODIDE_PYTHON" >> $GITHUB_ENV echo "PYODIDE_INDEX=$PYODIDE_INDEX" >> $GITHUB_ENV - name: "Create Pyodide virtual environment" run: | ./uv venv -p $PYODIDE_PYTHON venv-pyodide source ./venv-pyodide/bin/activate ./uv pip install --extra-index-url=$PYODIDE_INDEX --no-build numpy python -c 'import numpy' - name: "Install Pyodide with uv python" run: | ./uv python install cpython-3.13.2-emscripten-wasm32-musl - name: "Create a Pyodide virtual environment using uv installed Python" run: | ./uv venv -p cpython-3.13.2-emscripten-wasm32-musl venv-pyodide2 # TODO: be able to install Emscripten wheels here... source ./venv-pyodide2/bin/activate ./uv pip install packaging python -c 'import packaging' integration-test-pyodide-windows: name: "pyodide on windows" timeout-minutes: 10 runs-on: windows-latest # Github Actions Windows runner uses D drive as pwd. # Pyodide CLI has an issue running cross-drive, # So ensure that cache and Pyodide paths are on D drive as well. env: UV_CACHE_DIR: D:\uv-cache PYODIDE_XBUILDENV_PATH: D:\pyodide-xbuildenv steps: - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Create a native virtual environment" run: | .\uv.exe venv venv-native -p 3.13 .\uv.exe pip install -p venv-native\Scripts\python.exe pyodide-build==0.31.1 pip - name: "Install Pyodide interpreter" run: | & .\venv-native\Scripts\Activate.ps1 pyodide xbuildenv install 0.29.2 $env:PYODIDE_PYTHON = pyodide config get interpreter $env:PYODIDE_INDEX = pyodide config get package_index echo "PYODIDE_PYTHON=$env:PYODIDE_PYTHON" >> $env:GITHUB_ENV echo "PYODIDE_INDEX=$env:PYODIDE_INDEX" >> $env:GITHUB_ENV - name: "Create Pyodide virtual environment" run: | .\uv.exe venv -p $env:PYODIDE_PYTHON venv-pyodide & .\venv-pyodide\Scripts\Activate.ps1 .\uv.exe pip install --extra-index-url=$env:PYODIDE_INDEX --no-build numpy python -c 'import numpy' integration-test-termux: name: "termux on android" timeout-minutes: 15 runs-on: ubuntu-24.04 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Run uv on Termux" run: | docker run --rm \ -v "$PWD/uv:/uv" \ -v "$PWD/test/integration/termux.sh:/test-termux.sh" \ ghcr.io/astral-sh/termux-python:latest \ /entrypoint.sh bash /test-termux.sh integration-test-github-actions: name: "github actions" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12.7" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Install a package without system opt-in" run: | ./uv pip install anyio && exit 1 || echo "Failed as expected" - name: "Install a package with system opt-in" run: | ./uv pip install anyio --system - name: Configure uv to use the system Python by default run: echo "UV_SYSTEM_PYTHON=1" >> $GITHUB_ENV - name: "Install a package with system opt-in via the environment" run: | ./uv pip install anyio --reinstall - name: "Create a project" run: | # Use Python 3.11 as the minimum required version ./uv init --python 3.11 ./uv add anyio - name: "Sync to the system Python" run: ./uv sync -v --python 3.12 env: UV_PROJECT_ENVIRONMENT: "/opt/hostedtoolcache/Python/3.12.7/x64" - name: "Attempt to sync to the system Python with an incompatible version" run: | ./uv sync -v --python 3.11 && { echo "ci: Error; should not succeed"; exit 1; } || { echo "ci: Ok; expected failure"; exit 0; } env: UV_PROJECT_ENVIRONMENT: "/opt/hostedtoolcache/Python/3.12.7/x64" - name: "Attempt to sync to a non-Python environment directory" run: | mkdir -p /home/runner/example touch /home/runner/example/some-file ./uv sync -v && { echo "ci: Error; should not succeed"; exit 1; } || { echo "ci: Ok; expected failure"; exit 0; } env: UV_PROJECT_ENVIRONMENT: "/home/runner/example" integration-test-github-actions-freethreaded: name: "free-threaded on github actions" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13t" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Install a package without system opt-in" run: | ./uv pip install anyio && exit 1 || echo "Failed as expected" - name: "Install a package with system opt-in but without free-threaded opt-in" run: | ./uv pip install anyio --system --python 3.13 || echo "Failed as expected" # (we need to request 3.13 or we'll discover 3.12 on the system) - name: "Install a package with system and free-threaded opt-in" run: | ./uv pip install anyio --system --python 3.13t - name: "Create a virtual environment" run: | ./uv venv -p 3.13t --python-preference only-system - name: "Check is free-threaded" run: | .venv/bin/python -c "import sys; exit(1) if sys._is_gil_enabled() else exit(0)" integration-test-wsl: name: "pyenv on wsl${{ matrix.wsl-version }}" timeout-minutes: 15 runs-on: ${{ matrix.runner }} if: ${{ github.event.pull_request.head.repo.fork != true }} strategy: fail-fast: false matrix: include: - runner: windows-2022 wsl-version: 1 - runner: windows-2025 wsl-version: 2 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - name: "Setup WSL" uses: Vampire/setup-wsl@d1da7f2c0322a5ee4f24975344f67fc0f5baf364 # v7.0.0 with: distribution: Ubuntu-22.04 wsl-version: ${{ matrix.wsl-version }} - name: "Install pyenv-win" shell: pwsh run: | Write-Host "Installing pyenv-win..." Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1" .\install-pyenv-win.ps1 # Add pyenv-win to PATH for this session $env:PYENV = "$env:USERPROFILE\.pyenv\pyenv-win" $env:PATH = "$env:PYENV\bin;$env:PYENV\shims;$env:PATH" # Add to GITHUB_PATH so WSL can find the shims echo "$env:PYENV\bin" | Out-File -FilePath $env:GITHUB_PATH -Append echo "$env:PYENV\shims" | Out-File -FilePath $env:GITHUB_PATH -Append Write-Host "Installing Python 3.11.9 via pyenv-win..." & "$env:PYENV\bin\pyenv.bat" install 3.11.9 & "$env:PYENV\bin\pyenv.bat" global 3.11.9 Write-Host "Verifying pyenv-win installation..." & "$env:PYENV\bin\pyenv.bat" versions - name: "Test uv" shell: wsl-bash {0} run: | set -x chmod +x ./uv # Check that we don't fail on `pyenv-win` shims ./uv python list -v integration-test-registries: name: "registries" timeout-minutes: 10 runs-on: ubuntu-latest if: ${{ github.event.pull_request.head.repo.fork != true }} environment: name: uv-test-registries deployment: false env: PYTHON_VERSION: 3.12 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "${{ env.PYTHON_VERSION }}" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Configure AWS credentials" uses: aws-actions/configure-aws-credentials@99214aa6889fcddfa57764031d71add364327e59 # v6.1.3 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - name: "Get AWS CodeArtifact token" run: | UV_TEST_AWS_TOKEN=$(aws codeartifact get-authorization-token \ --domain tests \ --domain-owner ${{ secrets.AWS_ACCOUNT_ID }} \ --region us-east-1 \ --query authorizationToken \ --output text) echo "::add-mask::$UV_TEST_AWS_TOKEN" echo "UV_TEST_AWS_TOKEN=$UV_TEST_AWS_TOKEN" >> $GITHUB_ENV - name: "Authenticate with GCP" id: "auth" uses: "google-github-actions/auth@fc2174804b84f912b1f6d334e9463f484f1c552d" with: credentials_json: "${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}" - name: "Set up GCP SDK" uses: "google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db" - name: "Get GCP Artifact Registry token" id: get_token run: | UV_TEST_GCP_TOKEN=$(gcloud auth print-access-token) echo "::add-mask::$UV_TEST_GCP_TOKEN" echo "UV_TEST_GCP_TOKEN=$UV_TEST_GCP_TOKEN" >> $GITHUB_ENV - name: "Run registry tests with environment variable backend" run: ./uv run --no-project -p "${PYTHON_VERSION}" scripts/registries-test.py --uv ./uv --color always --all --auth-method env env: RUST_LOG: uv=debug UV_TEST_ARTIFACTORY_TOKEN: ${{ secrets.UV_TEST_ARTIFACTORY_TOKEN }} UV_TEST_ARTIFACTORY_URL: ${{ secrets.UV_TEST_ARTIFACTORY_URL }} UV_TEST_ARTIFACTORY_USERNAME: ${{ secrets.UV_TEST_ARTIFACTORY_USERNAME }} UV_TEST_AWS_URL: ${{ secrets.UV_TEST_AWS_URL }} UV_TEST_AWS_USERNAME: aws UV_TEST_AZURE_TOKEN: ${{ secrets.UV_TEST_AZURE_TOKEN }} UV_TEST_AZURE_URL: ${{ secrets.UV_TEST_AZURE_URL }} UV_TEST_AZURE_USERNAME: dummy UV_TEST_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_CLOUDSMITH_TOKEN }} UV_TEST_CLOUDSMITH_URL: ${{ secrets.UV_TEST_CLOUDSMITH_URL }} UV_TEST_CLOUDSMITH_USERNAME: ${{ secrets.UV_TEST_CLOUDSMITH_USERNAME }} UV_TEST_GCP_URL: ${{ secrets.UV_TEST_GCP_URL }} UV_TEST_GCP_USERNAME: oauth2accesstoken UV_TEST_GEMFURY_TOKEN: ${{ secrets.UV_TEST_GEMFURY_TOKEN }} UV_TEST_GEMFURY_URL: ${{ secrets.UV_TEST_GEMFURY_URL }} UV_TEST_GEMFURY_USERNAME: ${{ secrets.UV_TEST_GEMFURY_USERNAME }} UV_TEST_GITLAB_TOKEN: ${{ secrets.UV_TEST_GITLAB_TOKEN }} UV_TEST_GITLAB_URL: ${{ secrets.UV_TEST_GITLAB_URL }} UV_TEST_GITLAB_USERNAME: token - name: "Run registry tests with text store backend" run: ./uv run --no-project -p "${PYTHON_VERSION}" scripts/registries-test.py --uv ./uv --color always --all --auth-method text-store env: RUST_LOG: uv=debug UV_TEST_ARTIFACTORY_TOKEN: ${{ secrets.UV_TEST_ARTIFACTORY_TOKEN }} UV_TEST_ARTIFACTORY_URL: ${{ secrets.UV_TEST_ARTIFACTORY_URL }} UV_TEST_ARTIFACTORY_USERNAME: ${{ secrets.UV_TEST_ARTIFACTORY_USERNAME }} UV_TEST_AWS_URL: ${{ secrets.UV_TEST_AWS_URL }} UV_TEST_AWS_USERNAME: aws UV_TEST_AZURE_TOKEN: ${{ secrets.UV_TEST_AZURE_TOKEN }} UV_TEST_AZURE_URL: ${{ secrets.UV_TEST_AZURE_URL }} UV_TEST_AZURE_USERNAME: dummy UV_TEST_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_CLOUDSMITH_TOKEN }} UV_TEST_CLOUDSMITH_URL: ${{ secrets.UV_TEST_CLOUDSMITH_URL }} UV_TEST_CLOUDSMITH_USERNAME: ${{ secrets.UV_TEST_CLOUDSMITH_USERNAME }} UV_TEST_GCP_URL: ${{ secrets.UV_TEST_GCP_URL }} UV_TEST_GCP_USERNAME: oauth2accesstoken UV_TEST_GEMFURY_TOKEN: ${{ secrets.UV_TEST_GEMFURY_TOKEN }} UV_TEST_GEMFURY_URL: ${{ secrets.UV_TEST_GEMFURY_URL }} UV_TEST_GEMFURY_USERNAME: ${{ secrets.UV_TEST_GEMFURY_USERNAME }} UV_TEST_GITLAB_TOKEN: ${{ secrets.UV_TEST_GITLAB_TOKEN }} UV_TEST_GITLAB_URL: ${{ secrets.UV_TEST_GITLAB_URL }} UV_TEST_GITLAB_USERNAME: token integration-uv-build-backend: name: "uv_build" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "${{ env.PYTHON_VERSION }}" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: | chmod +x ./uv chmod +x ./uvx - name: "Test uv_build package" run: | # Build the Python package, which is not covered by uv's integration tests since they can't depend on having # a Python package (only the binary itself is built before running Rust's tests) ./uv build -v crates/uv-build # Test the main path (`build_wheel`) through pip ./uv venv -v --seed ./uv run --no-project python -m pip install -v test/packages/built-by-uv --find-links crates/uv-build/dist --no-index --no-deps ./uv run --no-project python -c "from built_by_uv import greet; print(greet())" # Test both `build_wheel` and `build_sdist` through uv ./uv venv -c -v ./uv build -v --force-pep517 test/packages/built-by-uv --find-links crates/uv-build/dist --offline ./uv pip install -v test/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps ./uv run --no-project python -c "from built_by_uv import greet; print(greet())" # Test both `build_wheel` and `build_sdist` through the official `build` rm -rf test/packages/built-by-uv/dist/ ./uv venv -c -v ./uv pip install build # Add the uv binary to PATH for `build` to find PATH="$(pwd):$PATH" UV_OFFLINE=1 UV_FIND_LINKS=crates/uv-build/dist ./uv run --no-project python -m build -v --installer uv test/packages/built-by-uv ./uv pip install -v test/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps ./uv run --no-project python -c "from built_by_uv import greet; print(greet())" cache-test-ubuntu: name: "cache on linux" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.14" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Download binary for last version" run: curl -LsSf "https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-gnu.tar.gz" | tar -xvz - name: "Check cache compatibility" run: python scripts/check_cache_compat.py --uv-current ./uv --uv-previous ./uv-x86_64-unknown-linux-gnu/uv cache-test-macos-aarch64: name: "cache on macos aarch64" timeout-minutes: 10 runs-on: macos-14 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.14" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-macos-aarch64-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Download binary for last version" run: curl -LsSf "https://github.com/astral-sh/uv/releases/latest/download/uv-aarch64-apple-darwin.tar.gz" | tar -xvz - name: "Check cache compatibility" run: python scripts/check_cache_compat.py --uv-current ./uv --uv-previous ./uv-aarch64-apple-darwin/uv astral-sh-uv-9252ba6/.github/workflows/test-smoke.yml000066400000000000000000000122261521221614700226130ustar00rootroot00000000000000on: workflow_call: inputs: sha: description: "The commit SHA to use for artifact names" required: true type: string permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: smoke-test-linux: name: "linux" timeout-minutes: 10 runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: | chmod +x ./uv chmod +x ./uvx - name: "Smoke test" run: | ./uv run --no-project scripts/smoke-test - name: "Test shell completions" run: | eval "$(./uv generate-shell-completion bash)" eval "$(./uvx --generate-shell-completion bash)" smoke-test-linux-aarch64: name: "linux aarch64" timeout-minutes: 10 runs-on: github-ubuntu-24.04-aarch64-2 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-aarch64-${{ inputs.sha }} - name: "Prepare binary" run: | chmod +x ./uv chmod +x ./uvx - name: "Smoke test" run: | ./uv run --no-project scripts/smoke-test - name: "Test shell completions" run: | eval "$(./uv generate-shell-completion bash)" eval "$(./uvx --generate-shell-completion bash)" smoke-test-linux-musl: name: "linux musl" timeout-minutes: 10 runs-on: ubuntu-latest container: alpine:latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: | chmod +x ./uv chmod +x ./uvx - name: "Smoke test" run: | ./uv run --no-project scripts/smoke-test smoke-test-macos: name: "macos" timeout-minutes: 10 runs-on: macos-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-macos-x86_64-${{ inputs.sha }} - name: "Prepare binary" run: | chmod +x ./uv chmod +x ./uvx - name: "Smoke test" run: | ./uv run --no-project scripts/smoke-test - name: "Test shell completions" run: | eval "$(./uv generate-shell-completion bash)" eval "$(./uvx --generate-shell-completion bash)" smoke-test-windows-x86_64: name: "windows x86_64" timeout-minutes: 10 runs-on: windows-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Smoke test" working-directory: ${{ env.UV_WORKSPACE }} run: | ./uv run --no-project scripts/smoke-test - name: "Test uv shell completions" working-directory: ${{ env.UV_WORKSPACE }} run: | (& ./uv generate-shell-completion powershell) | Out-String | Invoke-Expression - name: "Test uvx shell completions" working-directory: ${{ env.UV_WORKSPACE }} run: | (& ./uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression smoke-test-windows-aarch64: name: "windows aarch64" timeout-minutes: 10 runs-on: windows-11-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-aarch64-${{ inputs.sha }} - name: "Smoke test" working-directory: ${{ env.UV_WORKSPACE }} run: | ./uv run --no-project scripts/smoke-test - name: "Test uv shell completions" working-directory: ${{ env.UV_WORKSPACE }} run: | (& ./uv generate-shell-completion powershell) | Out-String | Invoke-Expression - name: "Test uvx shell completions" working-directory: ${{ env.UV_WORKSPACE }} run: | (& ./uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression astral-sh-uv-9252ba6/.github/workflows/test-system.yml000066400000000000000000000655251521221614700230330ustar00rootroot00000000000000on: workflow_call: inputs: sha: description: "The commit SHA to use for artifact names" required: true type: string permissions: {} jobs: system-test-debian: timeout-minutes: 10 name: "python on debian" runs-on: ubuntu-latest container: debian:trixie-20260421@sha256:35b8ff74ead4880f22090b617372daff0ccae742eb5674455d542bef71ef1999 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Python" run: apt-get update && apt-get install -y python3 python3-pip python3-venv python3-debian - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv --externally-managed - name: "Test `uv run` with system Python" run: | ./uv run --no-project -p python3 -v python -c "import debian" ./uv run --no-project -p python3 -v --with anyio python -c "import debian" system-test-fedora: timeout-minutes: 10 name: "python on fedora" runs-on: ubuntu-latest container: fedora:45@sha256:af8cdc432037f5e8e288bbc26c2b55b96000a911ec5b37959cd464d830f6cc5b steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Python" run: dnf install python3 which -y && python3 -m ensurepip - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv system-test-ubuntu: timeout-minutes: 10 name: "python3.12 via setup-python" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python) - name: "Validate global Python install" run: python scripts/check_system_python.py --uv ./uv system-test-python-36: timeout-minutes: 10 name: "python3.6 on debian buster" runs-on: ubuntu-latest container: python:3.6-buster steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv system-test-python-37: timeout-minutes: 10 name: "python3.7 on debian buster" runs-on: ubuntu-latest container: python:3.7-buster steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv # Currently failing, see https://github.com/astral-sh/uv/issues/13811 # system-test-opensuse: # timeout-minutes: 5 # name: "python on opensuse" # runs-on: ubuntu-latest # container: opensuse/tumbleweed # steps: # - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # - name: "Install Python" # run: > # until # zypper install -y python310 which && python3.10 -m ensurepip && mv /usr/bin/python3.10 /usr/bin/python3; # do sleep 10; # done # # We retry because `zypper` can fail during remote repository updates # # The above will not sleep forever due to the job level timeout # - name: "Download binary" # uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # with: # name: uv-linux-libc-${{ inputs.sha }} # - name: "Prepare binary" # run: chmod +x ./uv # - name: "Print Python path" # run: echo $(which python3) # - name: "Validate global Python install" # run: python3 scripts/check_system_python.py --uv ./uv # Note: rockylinux is a 1-1 code compatible distro to rhel # rockylinux mimics centos but with added maintenance stability # and avoids issues with centos stream uptime concerns system-test-rocky-linux: timeout-minutes: 10 name: "python on rocky linux ${{ matrix.rocky-version }}" runs-on: ubuntu-latest container: rockylinux/rockylinux:${{ matrix.rocky-version }} strategy: fail-fast: false matrix: rocky-version: ["8", "9", "10"] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Python" if: matrix.rocky-version == '8' run: | for i in {1..5}; do dnf install python39 python39-pip which -y && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; } if [ $i -eq 5 ]; then echo "Failed to install Python after 5 attempts" exit 1 fi done - name: "Install Python" if: matrix.rocky-version == '9' run: | for i in {1..5}; do dnf install python3.9 python3.9-pip which -y && break || { echo "Attempt $i failed, retrying in 10 seconds..."; sleep 10; } if [ $i -eq 5 ]; then echo "Failed to install Python after 5 attempts" exit 1 fi done - name: "Install Python" if: matrix.rocky-version == '10' run: | dnf install python3 python3-pip which -y - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) # Needed for building Pydantic - name: "Install build tools" run: dnf install -y gcc - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv system-test-graalpy: timeout-minutes: 10 name: "graalpy on linux" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "graalpy24.1" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which graalpy) - name: "Validate global Python install" run: graalpy scripts/check_system_python.py --uv ./uv system-test-pypy: timeout-minutes: 10 name: "pypy on linux" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "pypy3.9" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which pypy) - name: "Validate global Python install" run: pypy scripts/check_system_python.py --uv ./uv system-test-pyston: timeout-minutes: 10 name: "pyston on linux" runs-on: ubuntu-latest container: pyston/pyston:2.3.5 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which pyston) - name: "Validate global Python install" run: pyston scripts/check_system_python.py --uv ./uv system-test-chainguard-dev: timeout-minutes: 10 name: "python on chainguard-dev" runs-on: ubuntu-latest container: image: cgr.dev/chainguard/python:latest-dev@sha256:866419cbf80d962a159689e82e1127cdec06564db23bd2dbc10f629bc404c060 options: --user root --entrypoint /bin/sh steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python) - name: "Validate global Python install" run: python scripts/check_system_python.py --uv ./uv # The Chainguard distroless image has no shell, so we can't use `container:` # directly. Instead, we run on ubuntu-latest and use `docker run` to execute # inside the image. system-test-chainguard: timeout-minutes: 10 name: "python on chainguard" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Validate global Python install" run: | docker run --rm --user root \ -v "${{ github.workspace }}:/app" \ -w /app \ cgr.dev/chainguard/python:latest@sha256:c789723622cfc4a6b5d604a59250e3f708d0b4bb64cabb39a17c47119a224179 \ scripts/check_system_python.py --uv ./uv system-test-alpine: timeout-minutes: 10 name: "python on alpine" runs-on: ubuntu-latest container: alpine:latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Python" run: apk add --update --no-cache python3 py3-pip - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv --externally-managed system-test-macos-aarch64: timeout-minutes: 10 name: "python on macos aarch64" runs-on: macos-14 # github-macos-14-aarch64-3 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-macos-aarch64-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv --externally-managed system-test-macos-aarch64-homebrew: timeout-minutes: 10 name: "homebrew python on macos aarch64" runs-on: macos-14 # github-macos-14-aarch64-3 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Python" run: brew install python3 - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-macos-aarch64-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv --externally-managed system-test-macos-aarch64-emulated: timeout-minutes: 10 name: "x86-64 python on macos aarch64" runs-on: macos-14 # github-macos-14-aarch64-3 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.13 architecture: x64 - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-macos-aarch64-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv --externally-managed system-test-macos-x86_64: timeout-minutes: 10 name: "python on macos x86-64" runs-on: macos-15-intel # github-macos-15-x86_64-4 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false # We test with GitHub's Python as a regression test for # https://github.com/astral-sh/uv/issues/2450 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-macos-x86_64-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv --externally-managed system-test-windows-python-310: timeout-minutes: 10 name: "python3.10 on windows x86-64" runs-on: windows-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.10" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Print Python path" run: echo $(which python) - name: "Validate global Python install" run: py -3.10 ./scripts/check_system_python.py --uv ./uv.exe system-test-windows-x86_64-python-310: timeout-minutes: 10 name: "python3.10 on windows x86" runs-on: windows-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.10" architecture: "x86" - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Print Python path" run: echo $(which python) - name: "Validate global Python install" run: python ./scripts/check_system_python.py --uv ./uv.exe system-test-windows-x86_64-python-313: timeout-minutes: 10 name: "python3.13 on windows x86-64" runs-on: windows-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" allow-prereleases: true - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} - name: "Print Python path" run: echo $(which python) - name: "Validate global Python install" run: py -3.13 ./scripts/check_system_python.py --uv ./uv.exe system-test-windows-aarch64-x86-python-313: timeout-minutes: 10 name: "x86-64 python3.13 on windows aarch64" runs-on: windows-11-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" architecture: "x64" allow-prereleases: true - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-aarch64-${{ inputs.sha }} - name: "Validate global Python install" run: py -3.13 ./scripts/check_system_python.py --uv ./uv.exe system-test-windows-aarch64-aarch64-python-313: timeout-minutes: 10 name: "aarch64 python3.13 on windows aarch64" runs-on: windows-11-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" architecture: "arm64" allow-prereleases: true - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-aarch64-${{ inputs.sha }} - name: "Validate global Python install" run: py -3.13-arm64 ./scripts/check_system_python.py --uv ./uv.exe system-test-choco: timeout-minutes: 10 name: "python3.9 via chocolatey" runs-on: windows-latest env: TEST_PYTHON_VERSION: "3.9.13" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Python" run: choco install python3 --verbose --version=$env:TEST_PYTHON_VERSION - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} # Chocolatey updates the registry but doesn't update `GITHUB_PATH` and since figuring out what # it added would be more work than it's worth, it's simpler to just reload the values from the # registry. - name: "Validate global Python install" run: | $env:PATH = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") python ./scripts/check_system_python.py --uv ./uv.exe --check-python-version $env:TEST_PYTHON_VERSION --check-path system-test-pyenv: timeout-minutes: 10 name: "python3.9 via pyenv" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install pyenv" run: | # Install pyenv curl https://pyenv.run | bash # Set up environment variables for current step export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" # Install Python 3.9 pyenv install 3.9 pyenv global 3.9 # Make environment variables persist across steps echo "PYENV_ROOT=$HOME/.pyenv" >> $GITHUB_ENV echo "$HOME/.pyenv/bin" >> $GITHUB_PATH echo "$HOME/.pyenv/shims" >> $GITHUB_PATH - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3.9) - name: "Validate global Python install" run: python3.9 scripts/check_system_python.py --uv ./uv system-test-linux-313: timeout-minutes: 10 name: "python3.13 via setup-python" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.13 allow-prereleases: true - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-libc-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3.13) - name: "Validate global Python install" run: python3.13 scripts/check_system_python.py --uv ./uv system-test-conda: timeout-minutes: 10 name: "conda${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.arch }}" runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: os: ["linux", "windows", "macos"] python-version: ["3.8", "3.11"] include: - { os: "linux", target: "linux-libc", runner: "ubuntu-latest", arch: "x86-64", } - { os: "windows", target: "windows-x86_64", runner: "windows-latest", arch: "x86-64", } - { os: "macos", target: "macos-aarch64", runner: "macos-14", arch: "aarch64", } steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false # TODO(zanieb): Find an alternative for this action; it uses EOL Node 20 - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 with: miniconda-version: "latest" activate-environment: uv python-version: ${{ matrix.python-version }} - name: Conda info shell: bash -el {0} run: conda info - name: Conda list shell: pwsh run: conda list - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-${{ matrix.target }}-${{ inputs.sha }} - name: "Prepare binary" if: ${{ matrix.os != 'windows' }} run: chmod +x ./uv - name: "Print Python path" shell: bash -el {0} run: echo $(which python) - name: "Validate global Python install" shell: bash -el {0} run: python ./scripts/check_system_python.py --uv ./uv system-test-amazonlinux: timeout-minutes: 10 name: "python on amazonlinux" runs-on: ubuntu-latest container: amazonlinux:2023 steps: - name: "Install base requirements" run: | # Needed for `actions/checkout` yum install tar gzip which -y - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install Python" run: yum install python3 python3-pip -y - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-linux-musl-${{ inputs.sha }} - name: "Prepare binary" run: chmod +x ./uv - name: "Print Python path" run: echo $(which python3) - name: Install build tools run: yum install -y gcc - name: "Validate global Python install" run: python3 scripts/check_system_python.py --uv ./uv system-test-windows-embedded-python-310: timeout-minutes: 10 name: "embedded python3.10 on windows x86-64" runs-on: windows-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Download binary" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: uv-windows-x86_64-${{ inputs.sha }} # Download embedded Python. - name: "Download embedded Python" run: curl -LsSf https://www.python.org/ftp/python/3.11.8/python-3.11.8-embed-amd64.zip -o python-3.11.8-embed-amd64.zip - name: "Unzip embedded Python" run: 7z x python-3.11.8-embed-amd64.zip -oembedded-python - name: "Show embedded Python contents" run: ls embedded-python - name: "Set PATH" run: echo "${{ github.workspace }}\embedded-python" >> $env:GITHUB_PATH - name: "Print Python path" run: echo $(which python) - name: "Validate embedded Python install" run: python ./scripts/check_embedded_python.py --uv ./uv.exe astral-sh-uv-9252ba6/.github/workflows/test-windows-trampolines.yml000066400000000000000000000150411521221614700255200ustar00rootroot00000000000000on: workflow_call: inputs: trampoline-build: description: "Whether to check the committed trampoline binaries" required: false type: string default: "false" permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: # Verify windows crate version matches between workspaces windows-version-check: timeout-minutes: 5 runs-on: ubuntu-latest name: "check windows crate version" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - name: "Check windows crate versions match" run: uv run --no-project scripts/check-trampoline-version-consistency.py # Verify committed trampolines were built with the Docker image windows-trampoline-check-binary: # Only run when trampoline sources changed, the trampoline binaries changed # or the `build:windows-trampoline` label is set. Dependency bumps would # otherwise force a trampoline rebuild on every dependency update. if: ${{ inputs.trampoline-build == 'true' }} timeout-minutes: 30 runs-on: ubuntu-latest name: "check binary" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Set up Docker Buildx" uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: "Build trampolines in Docker" run: scripts/build-trampolines.sh --cache-from type=gha --cache-to type=gha - name: "Check committed trampolines match build" run: git diff --exit-code crates/uv-trampoline-builder/trampolines/ # Separate jobs for the nightly crate windows-trampoline-check: timeout-minutes: 15 runs-on: windows-latest name: "check on ${{ matrix.target-arch }}" strategy: fail-fast: false matrix: target-arch: ["x86_64", "i686", "aarch64"] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Dev Drive run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1 # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone... - name: Copy Git Repo to Dev Drive run: | Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline - name: "Install Rust toolchain" working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline run: | rustup target add ${{ matrix.target-arch }}-pc-windows-msvc rustup component add rust-src --target ${{ matrix.target-arch }}-pc-windows-msvc - name: "Install cargo-bloat" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-bloat - name: "rustfmt" working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline run: cargo fmt --check - name: "Clippy" working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline run: cargo clippy --all-features --locked --target x86_64-pc-windows-msvc --tests -- -D warnings - name: "Bloat Check" working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline run: | $output = cargo bloat --release --target x86_64-pc-windows-msvc $filteredOutput = $output | Select-String -Pattern 'core::fmt::write|core::fmt::getcount' -NotMatch $containsPatterns = $filteredOutput | Select-String -Pattern 'core::fmt|std::panicking|std::backtrace_rs' if ($containsPatterns) { Exit 1 } else { Exit 0 } # Separate jobs for the nightly crate windows-trampoline-test: timeout-minutes: 10 runs-on: ${{ matrix.runner }} name: "test on ${{ matrix.target-arch }}" strategy: fail-fast: false matrix: include: - { runner: windows-latest, target-arch: "x86_64" } - { runner: windows-latest, target-arch: "i686" } - { runner: windows-11-arm, target-arch: "aarch64" } steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Dev Drive run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1 # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone... - name: Copy Git Repo to Dev Drive run: | Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline - name: "Install Rust toolchain" working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline run: | rustup target add ${{ matrix.target-arch }}-pc-windows-msvc rustup component add rust-src --target ${{ matrix.target-arch }}-pc-windows-msvc - name: "Test committed binaries" working-directory: ${{ env.UV_WORKSPACE }} run: | rustup target add ${{ matrix.target-arch }}-pc-windows-msvc cargo test -p uv-trampoline-builder --target ${{ matrix.target-arch }}-pc-windows-msvc # Build and copy the new binaries - name: "Build" working-directory: ${{ env.UV_WORKSPACE }}/crates/uv-trampoline run: | cargo build --target ${{ matrix.target-arch }}-pc-windows-msvc cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-console.exe ../uv-trampoline-builder/trampolines/uv-trampoline-${{ matrix.target-arch }}-console.exe cp target/${{ matrix.target-arch }}-pc-windows-msvc/debug/uv-trampoline-gui.exe ../uv-trampoline-builder/trampolines/uv-trampoline-${{ matrix.target-arch }}-gui.exe - name: "Test new binaries" working-directory: ${{ env.UV_WORKSPACE }} run: | # We turn off the default "production" test feature since these are debug binaries cargo test -p uv-trampoline-builder --target ${{ matrix.target-arch }}-pc-windows-msvc --no-default-features astral-sh-uv-9252ba6/.github/workflows/test.yml000066400000000000000000000251341521221614700215010ustar00rootroot00000000000000on: workflow_call: inputs: save-rust-cache: required: false type: string default: "true" test-macos: required: false type: string default: "false" permissions: {} env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always PYTHON_VERSION: "3.12" RUSTUP_MAX_RETRIES: 10 jobs: # We use the large GitHub actions runners # For Ubuntu and Windows, this requires Organization-level configuration # See: https://docs.github.com/en/actions/using-github-hosted-runners/about-larger-runners/about-larger-runners#about-ubuntu-and-windows-larger-runners cargo-test-linux: timeout-minutes: 10 runs-on: depot-ubuntu-24.04-16 name: "cargo test on linux" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Install mold" run: ./scripts/install-mold.sh - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Install Rust toolchain" run: rustup show - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - name: "Install required Python versions" run: uv python install - name: "Install secret service" run: | echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries > /dev/null sudo apt install -y gnome-keyring - name: "Start gnome-keyring" # run gnome-keyring with 'foobar' as password for the login keyring # this will create a new login keyring and unlock it # the login password doesn't matter, but the keyring must be unlocked for the tests to work run: gnome-keyring-daemon --components=secrets --daemonize --unlock <<< 'foobar' - name: "Create btrfs filesystem" run: | sudo apt install -y btrfs-progs truncate -s 1G /tmp/btrfs.img mkfs.btrfs /tmp/btrfs.img sudo mkdir /btrfs sudo mount -o loop /tmp/btrfs.img /btrfs sudo chown "$(id -u):$(id -g)" /btrfs - name: "Create tmpfs filesystem" run: | sudo mkdir /tmpfs sudo mount -t tmpfs -o size=256m tmpfs /tmpfs sudo chown "$(id -u):$(id -g)" /tmpfs - name: "Create minix filesystem (low hardlink limit)" run: | truncate -s 16M /tmp/minix.img mkfs.minix /tmp/minix.img sudo mkdir /minix sudo mount -o loop /tmp/minix.img /minix sudo chown "$(id -u):$(id -g)" /minix - name: "Install cargo nextest" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-nextest - name: "Cargo test" env: # Retry more than default to reduce flakes in CI UV_HTTP_RETRIES: 5 RUST_BACKTRACE: 1 UV_INTERNAL__TEST_COW_FS: /btrfs UV_INTERNAL__TEST_NOCOW_FS: /tmpfs UV_INTERNAL__TEST_ALT_FS: /tmpfs UV_INTERNAL__TEST_LOWLINKS_FS: /minix # Write pending snapshots to a separate directory for artifact upload INSTA_UPDATE: new INSTA_PENDING_DIR: ${{ github.workspace }}/pending-snapshots run: | cargo nextest run \ --cargo-profile fast-build \ --features test-python-patch,native-auth,secret-service \ --workspace \ --profile ci-linux - name: "Upload pending snapshots" if: ${{ failure() }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: pending-snapshots-linux path: pending-snapshots/ if-no-files-found: ignore include-hidden-files: true retention-days: 14 - name: "Upload JUnit results" if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: junit-results-linux path: target/nextest/ci*/junit.xml if-no-files-found: ignore retention-days: 14 cargo-test-macos: timeout-minutes: 20 # Only run macOS tests on main without opt-in if: ${{ inputs.test-macos == 'true' }} runs-on: depot-macos-15 name: "cargo test on macos" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Install Rust toolchain" run: rustup show - name: "Create HFS+ disk image (no reflink support)" run: | hdiutil create -size 256m -fs HFS+ -volname NoReflink /tmp/noreflink.dmg hdiutil attach /tmp/noreflink.dmg echo "HFS_MOUNT=/Volumes/NoReflink" >> "$GITHUB_ENV" - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - name: "Install required Python versions" run: uv python install - name: "Install cargo nextest" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-nextest - name: "Cargo test" env: # Retry more than default to reduce flakes in CI UV_HTTP_RETRIES: 5 RUST_BACKTRACE: 1 # macOS tmpdir is on APFS which supports reflink UV_INTERNAL__TEST_COW_FS: ${{ runner.temp }} # HFS+ RAM disk does not support copy-on-write and is on a different device UV_INTERNAL__TEST_NOCOW_FS: ${{ env.HFS_MOUNT }} UV_INTERNAL__TEST_ALT_FS: ${{ env.HFS_MOUNT }} # Write pending snapshots to a separate directory for artifact upload INSTA_UPDATE: new INSTA_PENDING_DIR: ${{ github.workspace }}/pending-snapshots run: | cargo nextest run \ --cargo-profile fast-build \ --no-default-features \ --features test-python,test-python-managed,test-pypi,test-git,test-git-lfs,performance,test-crates-io,native-auth,apple-native \ --workspace \ --profile ci-macos - name: "Upload pending snapshots" if: ${{ failure() }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: pending-snapshots-macos path: pending-snapshots/ if-no-files-found: ignore include-hidden-files: true retention-days: 14 - name: "Upload JUnit results" if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: junit-results-macos path: target/nextest/ci*/junit.xml if-no-files-found: ignore retention-days: 14 cargo-test-windows: timeout-minutes: 15 runs-on: namespace-profile-windows-2022-x86-64-16 name: "cargo test on windows ${{ matrix.partition }} of 3" strategy: fail-fast: false matrix: partition: [1, 2, 3] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Dev Drive run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1 # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone... - name: Copy Git Repo to Dev Drive run: | Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: "0.11.19" - name: "Install required Python versions" run: uv python install - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: ${{ env.UV_WORKSPACE }} # Use the same cache entry across the partitions add-job-id-key: false save-if: ${{ inputs.save-rust-cache == 'true' }} - name: "Install Rust toolchain" working-directory: ${{ env.UV_WORKSPACE }} run: rustup show - name: "Create NTFS test directory (low hardlink limit)" run: New-Item -Path "C:\uv" -ItemType Directory -Force - name: "Create SMB test share (alternative filesystem)" run: | New-Item -Path "C:\uv-smb" -ItemType Directory -Force New-SmbShare -Name "uv-alt-fs" -Path "C:\uv-smb" -FullAccess "Everyone" - name: "Install cargo nextest" uses: taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2.81.1 with: tool: cargo-nextest - name: "Cargo test" working-directory: ${{ env.UV_WORKSPACE }} env: # Retry more than default to reduce flakes in CI UV_HTTP_RETRIES: 5 # Avoid permission errors during concurrent tests # See https://github.com/astral-sh/uv/issues/6940 UV_LINK_MODE: copy RUST_BACKTRACE: 1 UV_INTERNAL__TEST_ALT_FS: "\\\\localhost\\uv-alt-fs" UV_INTERNAL__TEST_SMB_FS: "\\\\localhost\\uv-alt-fs" UV_INTERNAL__TEST_LOWLINKS_FS: "C:\\uv" # Write pending snapshots to a separate directory for artifact upload INSTA_UPDATE: new INSTA_PENDING_DIR: ${{ github.workspace }}/pending-snapshots shell: bash run: | cargo nextest run \ --cargo-profile fast-build \ --no-default-features \ --features test-python,test-pypi,test-python-managed,test-windows-registry,native-auth,windows-native \ --workspace \ --profile ci-windows \ --partition hash:${{ matrix.partition }}/3 - name: "Upload pending snapshots" if: ${{ failure() }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: pending-snapshots-windows-${{ matrix.partition }} path: pending-snapshots/ if-no-files-found: ignore include-hidden-files: true retention-days: 14 - name: "Upload JUnit results" if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: junit-results-windows-${{ matrix.partition }} path: ${{ env.UV_WORKSPACE }}/target/nextest/ci*/junit.xml if-no-files-found: ignore retention-days: 14 astral-sh-uv-9252ba6/.gitignore000066400000000000000000000014561521221614700163730ustar00rootroot00000000000000# Insta snapshots. *.pending-snap # Generated by Cargo # will have compiled files and executables /vendor/ debug/ target-alpine/ target/ # Bootstrapped Python versions /bin/ # These are backup files generated by rustfmt **/*.rs.bk # MSVC Windows builds of rustc generate these, which store debugging information *.pdb # Python tmp files __pycache__ # GitHub issue mirror .prism/ # Maturin builds, and other native editable builds *.so *.pyd *.dll /dist /crates/uv-build/dist # Profiling flamegraph.svg perf.data perf.data.old profile.json profile.json.gz # MkDocs /site # Generated reference docs (use `cargo dev generate-all` to regenerate) /docs/reference/cli.md /docs/reference/environment.md /docs/reference/settings.md # macOS **/.DS_Store # IDE .idea .vscode # Codex .codex/* !.codex/hooks.json astral-sh-uv-9252ba6/.ignore000066400000000000000000000000131521221614700156530ustar00rootroot00000000000000!/.github/ astral-sh-uv-9252ba6/.known-crates000066400000000000000000000015651521221614700170200ustar00rootroot00000000000000# GENERATED-BY scripts/setup-crates-io-publish.py uv uv-audit uv-auth uv-bin-install uv-build uv-build-backend uv-build-frontend uv-cache uv-cache-info uv-cache-key uv-cli uv-client uv-configuration uv-console uv-dirs uv-dispatch uv-distribution uv-distribution-filename uv-distribution-types uv-errors uv-extract uv-fastid uv-flags uv-fs uv-git uv-git-types uv-globfilter uv-install-wheel uv-installer uv-keyring uv-logging uv-macros uv-metadata uv-netrc uv-normalize uv-once-map uv-options-metadata uv-pep440 uv-pep508 uv-performance-memory-allocator uv-platform uv-platform-tags uv-preview uv-publish uv-pypi-types uv-python uv-redacted uv-requirements uv-requirements-txt uv-resolver uv-scripts uv-settings uv-shell uv-small-str uv-state uv-static uv-test uv-toml uv-tool uv-torch uv-trampoline-builder uv-types uv-unix uv-version uv-virtualenv uv-warnings uv-windows uv-workspace astral-sh-uv-9252ba6/.pre-commit-config.yaml000066400000000000000000000020671521221614700206630ustar00rootroot00000000000000fail_fast: true exclude: | (?x)^( .*/(snapshots)/.*| )$ repos: - repo: https://github.com/abravalheri/validate-pyproject rev: v0.24.1 hooks: - id: validate-pyproject - repo: https://github.com/crate-ci/typos rev: v1.42.3 hooks: - id: typos - repo: local hooks: - id: rustfmt name: rustfmt entry: rustfmt language: system types: [rust] - repo: local hooks: - id: cargo-dev-generate-all name: cargo dev generate-all entry: cargo dev generate-all language: system types: [rust] pass_filenames: false files: ^crates/(uv-cli|uv-settings)/ - repo: local hooks: - id: prettier name: prettier entry: prettier --write --ignore-unknown language: node additional_dependencies: ["prettier@3"] types_or: [yaml, json5] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.14 hooks: - id: ruff-format - id: ruff args: [--fix, --exit-non-zero-on-fix] astral-sh-uv-9252ba6/.prettierignore000066400000000000000000000003161521221614700174400ustar00rootroot00000000000000.venv .prism CHANGELOG.md PREVIEW-CHANGELOG.md docs/reference/cli.md docs/reference/settings.md docs/reference/environment.md test/ecosystem/home-assistant-core/LICENSE.md docs/guides/integration/gitlab.md astral-sh-uv-9252ba6/.prettierrc000066400000000000000000000001671521221614700165650ustar00rootroot00000000000000{ "overrides": [ { "files": ["*.md"], "options": { "proseWrap": "always" } } ] } astral-sh-uv-9252ba6/.python-versions000066400000000000000000000015431521221614700175700ustar00rootroot00000000000000# These are versions of Python required for running uv's own test suite. You can add or remove # versions here as needed for tests; this doesn't impact uv's own functionality. They can be # installed through any means you like, e.g. `uv python install` if you already have a build of uv, # `cargo run python install`, or through some other installer. # # In uv's CI in GitHub Actions, they are bootstrapped by an existing released version of uv, # installed by the astral-sh/setup-uv action If you need a newer or different version, you will # first need to complete a uv release capable of installing that version, get it picked up by # astral-sh/setup-uv, and update its hash in .github/workflows. 3.14.0 3.13.2 3.12.9 3.11.11 3.10.16 3.9.21 3.8.20 # The following is needed for `==3.13` request tests 3.13.0 # A pre-release version required for testing 3.14.0rc2 astral-sh-uv-9252ba6/AGENTS.md000066400000000000000000000026161521221614700157050ustar00rootroot00000000000000- Read CONTRIBUTING.md for guidelines on how to run tools - ALWAYS attempt to add a test case for changed behavior - PREFER integration tests, e.g., at `it/...` over unit tests - PREFER `insta` snapshots following patterns in nearby tests over substring assertions - When making changes for Windows from Unix, use `cargo xwin clippy` to check compilation - NEVER perform builds with the release profile, unless asked or reproducing performance issues - PREFER running specific tests over running the entire test suite - AVOID using `panic!`, `unreachable!`, `.unwrap()`, unsafe code, and clippy rule ignores - PREFER patterns like `if let` to handle fallibility - ALWAYS write `SAFETY` comments following our usual style when writing `unsafe` code - PREFER `#[expect()]` over `[allow()]` if clippy must be disabled - PREFER let chains (`if let` combined with `&&`) over nested `if let` statements - NEVER update all dependencies in the lockfile and ALWAYS use `cargo update --precise` to make lockfile changes - NEVER assume clippy warnings are pre-existing, it is very rare that `main` has warnings - ALWAYS read and copy the style of similar tests when adding new cases - PREFER top-level imports over local imports or fully qualified names - AVOID shortening variable names, e.g., use `version` instead of `ver`, and `requires_python` instead of `rp` - PREFER [`TypeName`] references when writing Rust doc comments astral-sh-uv-9252ba6/BENCHMARKS.md000066400000000000000000000107741521221614700163450ustar00rootroot00000000000000# Benchmarks All benchmarks were computed on macOS using Python 3.12.4 (for non-uv tools), and come with a few important caveats: - Benchmark performance may vary dramatically across different operating systems and filesystems. In particular, uv uses different installation strategies based on the underlying filesystem's capabilities. (For example, uv uses reflinking on macOS, and hardlinking on Linux.) - Benchmark performance may vary dramatically depending on the set of packages being installed. For example, a resolution that requires building a single intensive source distribution may appear very similar across tools, since the bottleneck is tool-agnostic. This document benchmarks against Trio's `docs-requirements.in`, as a representative example of a real-world project. In each case, a smaller bar (i.e., lower) is better. ## Warm Installation Benchmarking package installation (e.g., `uv sync`) with a warm cache. This is equivalent to removing and recreating a virtual environment, and then populating it with dependencies that you've installed previously on the same machine. ![install-warm](https://github.com/user-attachments/assets/84118aaa-d030-4e29-8f1e-9483091ceca3) ## Cold Installation Benchmarking package installation (e.g., `uv sync`) with a cold cache. This is equivalent to running `uv sync` on a new machine or in CI (assuming that the package manager cache is not shared across runs). ![install-cold](https://github.com/user-attachments/assets/e7f5b203-7e84-452b-8c56-1ff6531c9898) ## Warm Resolution Benchmarking dependency resolution (e.g., `uv lock`) with a warm cache, but no existing lockfile. This is equivalent to blowing away an existing `requirements.txt` file to regenerate it from a `requirements.in` file. ![resolve-warm](https://github.com/user-attachments/assets/e1637a08-8b27-4077-8138-b3849e53eb04) ## Cold Resolution Benchmarking dependency resolution (e.g., `uv lock`) with a cold cache. This is equivalent to running `uv lock` on a new machine or in CI (assuming that the package manager cache is not shared across runs). ![resolve-cold](https://github.com/user-attachments/assets/b578c264-c209-45ab-b4c3-54073d871e86) ## Reproduction All benchmarks were generated using the `scripts/benchmark` package, which wraps [`hyperfine`](https://github.com/sharkdp/hyperfine) to facilitate benchmarking uv against a variety of other tools. The benchmark script itself has a several requirements: - A local uv release build (`cargo build --release`). - An installation of the production `uv` binary in your path. - The [`hyperfine`](https://github.com/sharkdp/hyperfine) command-line tool installed on your system. To benchmark resolution against pip-compile, Poetry, and PDM: ```shell uv run resolver \ --uv-project \ --poetry \ --pdm \ --pip-compile \ --benchmark resolve-warm --benchmark resolve-cold \ --json \ ../requirements/trio.in ``` To benchmark installation against pip-sync, Poetry, and PDM: ```shell uv run resolver \ --uv-project \ --poetry \ --pdm \ --pip-sync \ --benchmark install-warm --benchmark install-cold \ --json \ ../requirements/compiled/trio.txt ``` Both commands should be run from the `scripts/benchmark` directory. After running the benchmark script, you can generate the corresponding graph via: ```shell cargo run -p uv-dev --all-features render-benchmarks resolve-warm.json --title "Warm Resolution" cargo run -p uv-dev --all-features render-benchmarks resolve-cold.json --title "Cold Resolution" cargo run -p uv-dev --all-features render-benchmarks install-warm.json --title "Warm Installation" cargo run -p uv-dev --all-features render-benchmarks install-cold.json --title "Cold Installation" ``` You need to install the [Roboto Font](https://fonts.google.com/specimen/Roboto) if the labels are missing in the generated graph. ## Acknowledgements The inclusion of this `BENCHMARKS.md` file was inspired by the excellent benchmarking documentation in [Orogene](https://github.com/orogene/orogene/blob/472e481b4fc6e97c2b57e69240bf8fe995dfab83/BENCHMARKS.md). ## Troubleshooting ### Flaky benchmarks If you're seeing high variance when running the cold benchmarks, then it's likely that you're running into throttling or DDoS prevention from your ISP. In that case, ISPs forcefully terminate TCP connections with a TCP reset. We believe this is due to the benchmarks making the exact same requests in a very short time (especially true for `uv`). A possible workaround is to connect to VPN to bypass your ISPs filtering mechanism. astral-sh-uv-9252ba6/CHANGELOG.md000066400000000000000000001611041521221614700162110ustar00rootroot00000000000000# Changelog ## 0.11.20 Released on 2026-06-10. ### Enhancements - Add `--emit-index-url` and `--emit-find-links` to `uv export` ([#18370](https://github.com/astral-sh/uv/pull/18370)) - Add `--find-links` support for `uv pip list` ([#16103](https://github.com/astral-sh/uv/pull/16103)) - Group executable install errors during `uv python install` ([#19691](https://github.com/astral-sh/uv/pull/19691)) - Use ICF in macOS release builds to reduce binary sizes ([#19615](https://github.com/astral-sh/uv/pull/19615)) ### Preview features - Add initial hidden `uv upgrade` command ([#19678](https://github.com/astral-sh/uv/pull/19678)) - Reject Git revisions in `uv upgrade` ([#19742](https://github.com/astral-sh/uv/pull/19742)) ### Configuration - Recognize `UV_NO_INSTALL_PROJECT`, `UV_NO_INSTALL_WORKSPACE`, `UV_NO_INSTALL_LOCAL` ([#19323](https://github.com/astral-sh/uv/pull/19323)) ### Performance - Speed up discovery of large workspaces ([#18311](https://github.com/astral-sh/uv/pull/18311)) ### Bug fixes - Allow unknown preview flags with a warning again ([#19669](https://github.com/astral-sh/uv/pull/19669)) - Apply dependency exclusions to direct requirements ([#19699](https://github.com/astral-sh/uv/pull/19699)) - Avoid following external symlinks during cache clean ([#19682](https://github.com/astral-sh/uv/pull/19682)) - Avoid following symlinks during cache prune ([#19543](https://github.com/astral-sh/uv/pull/19543)) - Fix Git cache keys for worktrees and packed refs ([#19706](https://github.com/astral-sh/uv/pull/19706)) - Make resolver error handling iterative to avoid stack overflows ([#19695](https://github.com/astral-sh/uv/pull/19695)) - Pass `VIRTUAL_ENV` through `cygpath` inside `fish` on Windows ([#19703](https://github.com/astral-sh/uv/pull/19703)) - Rebuild explicit local directory tool installs ([#19591](https://github.com/astral-sh/uv/pull/19591)) - Validate egg top-level entries as identifiers ([#19679](https://github.com/astral-sh/uv/pull/19679)) ### Documentation - Document `--find-links` caching behavior ([#19585](https://github.com/astral-sh/uv/pull/19585)) - Add a small section for malware checks ([#19680](https://github.com/astral-sh/uv/pull/19680)) ## 0.11.19 Released on 2026-06-03. ### Python - Add CPython 3.15.0b2 ([#19531](https://github.com/astral-sh/uv/pull/19531)) ### Enhancements - Always compute SHA256 for remote distributions ([#19662](https://github.com/astral-sh/uv/pull/19662)) - Add PyEmscripten platform (PEP 783) ([#19629](https://github.com/astral-sh/uv/pull/19629)) - Add Pyodide 2025 target triple ([#19653](https://github.com/astral-sh/uv/pull/19653)) ### Preview features - Make preview features for commands have names that aren't ambiguous with the command ([#19645](https://github.com/astral-sh/uv/pull/19645)) - Respect `--isolated` in `uv check` ([#19666](https://github.com/astral-sh/uv/pull/19666)) ### Bug fixes - Continue tool uninstall after dangling receipts ([#19623](https://github.com/astral-sh/uv/pull/19623)) - Skip Unix-specific installation steps when cross-installing Windows Python distributions ([#19424](https://github.com/astral-sh/uv/pull/19424)) ## 0.11.18 Released on 2026-06-01. ### Performance - Fix performance regression in unzip of local wheels ([#19637](https://github.com/astral-sh/uv/pull/19637)) ### Preview - Add `uv check` to run `ty` from uv ([#19605](https://github.com/astral-sh/uv/pull/19605)) ### Bug fixes - Update activation scripts with upstream fixes ([#19628](https://github.com/astral-sh/uv/pull/19628)) ### Other changes - Bump MSRV to 1.94 ([#19600](https://github.com/astral-sh/uv/pull/19600)) ## 0.11.17 Released on 2026-05-28. ### Enhancements - Add a diagnostic for `uv add` with standard library modules ([#19572](https://github.com/astral-sh/uv/pull/19572)) - Expose `uv workspace` and its `list` subcommand in help output ([#19533](https://github.com/astral-sh/uv/pull/19533)) - Improve the "403 forbidden" hint to suggest `ignore-error-codes` when applicable ([#19521](https://github.com/astral-sh/uv/pull/19521)) - Skip direct URL lock freshness checks while offline ([#19596](https://github.com/astral-sh/uv/pull/19596)) - Add `import-names` and `import-namespaces` support to `uv-build` ([PEP 794](https://peps.python.org/pep-0794/)) ([#19380](https://github.com/astral-sh/uv/pull/19380)) - Add a `--no-editable-package` flag to various commands ([#19584](https://github.com/astral-sh/uv/pull/19584)) - Infer Python version requests from source trees in `uv tool` invocations ([#19577](https://github.com/astral-sh/uv/pull/19577)) ### Preview features - Add module owners to `uv workspace metadata` ([#19122](https://github.com/astral-sh/uv/pull/19122)) - Do not allow `uv venv --clear` to remove non-virtual environments ([#19595](https://github.com/astral-sh/uv/pull/19595)) ### Bug fixes - Improve the performance of large entries in `tool.uv.conflicts` ([#19538](https://github.com/astral-sh/uv/pull/19538)) - Avoid modifying the parent process' env with `--env-file` in `uv run` ([#19567](https://github.com/astral-sh/uv/pull/19567)) - Fix script environment creation for scripts with long filenames ([#19539](https://github.com/astral-sh/uv/pull/19539)) - Fix transitive Git archive dependencies in lockfiles ([#19589](https://github.com/astral-sh/uv/pull/19589)) - Preserve Git repository URLs in direct URL metadata ([#19590](https://github.com/astral-sh/uv/pull/19590)) - Support redirects in `--check-url` ([#19594](https://github.com/astral-sh/uv/pull/19594)) - Accept case-insensitive HTML tags in `--find-links` parsing ([#19537](https://github.com/astral-sh/uv/pull/19537)) - Reject duplicate script metadata blocks ([#19544](https://github.com/astral-sh/uv/pull/19544)) - Ban names like "python3" as script entry points ([#19535](https://github.com/astral-sh/uv/pull/19535), [#19536](https://github.com/astral-sh/uv/pull/19536)) - Validate Git LFS artifacts for Git archives ([#19592](https://github.com/astral-sh/uv/pull/19592)) - Use a relative path when creating symlinks in cache to improve relocatability ([#19033](https://github.com/astral-sh/uv/pull/19033)) ### Documentation - Fix malformed positional anchors in the CLI reference ([#19575](https://github.com/astral-sh/uv/pull/19575)) ## 0.11.16 Released on 2026-05-21. ### Enhancements - Add support for direct archive dependencies in Git ([#10072](https://github.com/astral-sh/uv/pull/10072)) - Adjust hint rendering ([#18090](https://github.com/astral-sh/uv/pull/18090)) ### Preview features - uv audit: specialize malformed OSV error ([#19515](https://github.com/astral-sh/uv/pull/19515)) - Reject locked malware installations ([#18936](https://github.com/astral-sh/uv/pull/18936)) ### Configuration - Allow disabling reading the system config with `UV_NO_SYSTEM_CONFIG` ([#19476](https://github.com/astral-sh/uv/pull/19476)) ### Bug fixes - Allow environment variables that take a list to be empty ([#19503](https://github.com/astral-sh/uv/pull/19503)) - Ensure that incompatible wheel hints do not leak secrets ([#19504](https://github.com/astral-sh/uv/pull/19504)) - Reject unsafe entry points in `uv-build` ([#19495](https://github.com/astral-sh/uv/pull/19495)) - Restrict delimiters in entry point parsing ([#19471](https://github.com/astral-sh/uv/pull/19471)) - uv-netrc: fix multi-word no-space comment lines causing parse errors ([#19494](https://github.com/astral-sh/uv/pull/19494)) ### Documentation - Document and test relative exclude-newer support for uv pip ([#19475](https://github.com/astral-sh/uv/pull/19475)) ## 0.11.15 Released on 2026-05-18. ### Security - Fix a TAR parser differential, see [GHSA-3cv2-h65g-fgmm](https://github.com/astral-sh/tokio-tar/security/advisories/GHSA-3cv2-h65g-fgmm) ([#19463](https://github.com/astral-sh/uv/pull/19463)) - Enforce that entry points cannot escape in the scripts directory, see [GHSA-4gg8-gxpx-9rph](https://github.com/astral-sh/uv/security/advisories/GHSA-4gg8-gxpx-9rph) ([#19464](https://github.com/astral-sh/uv/pull/19464)) ### Enhancements - Add TOML v1.1 -> v1.0 backwards compatibility for source distributions ([#18741](https://github.com/astral-sh/uv/pull/18741)) - Add support for Azure request signing ([#19421](https://github.com/astral-sh/uv/pull/19421)) - Apply stricter validation to all wheel filename segments ([#19364](https://github.com/astral-sh/uv/pull/19364)) - Reject empty strings as an invalid package name ([#19435](https://github.com/astral-sh/uv/pull/19435)) - Use structured errors for signing authentication failures ([#19422](https://github.com/astral-sh/uv/pull/19422)) ### Preview - uv audit: Add JSON output ([#19305](https://github.com/astral-sh/uv/pull/19305)) ### Configuration - Respect `required-environments` in `uv pip compile` ([#19378](https://github.com/astral-sh/uv/pull/19378)) ### Performance - Avoid parsing JSON manifest when local Python is available ([#19398](https://github.com/astral-sh/uv/pull/19398)) - Avoid walking nested directories in linker conflict registration ([#19382](https://github.com/astral-sh/uv/pull/19382)) - Optimize async wheel ZIP writing ([#19383](https://github.com/astral-sh/uv/pull/19383)) - Fix dead "already trimmed" fast-path in `Version::only_release_trimmed` ([#19425](https://github.com/astral-sh/uv/pull/19425)) ### Bug fixes - Apply workspace-member `[tool.uv.sources]` credentials under `uv sync --frozen` ([#19423](https://github.com/astral-sh/uv/pull/19423)) - Skip empty directories in uv build outputs ([#19437](https://github.com/astral-sh/uv/pull/19437)) - Fix Git submodule handling when using relative paths ([#12156](https://github.com/astral-sh/uv/pull/12156)) - Fix line number reporting in netrc parsing ([#19452](https://github.com/astral-sh/uv/pull/19452)) ### Documentation - Move Bazel auth helper setup into integration guide ([#19392](https://github.com/astral-sh/uv/pull/19392)) ## 0.11.14 Released on 2026-05-12. ### Enhancements - Add Astral mirror URL override ([#19206](https://github.com/astral-sh/uv/pull/19206)) - Ignore `top_level.txt` entries in uninstall that are not valid Python identifiers ([#19340](https://github.com/astral-sh/uv/pull/19340)) ### Bug fixes - Avoid applying `.env` files in parent process ([#19343](https://github.com/astral-sh/uv/pull/19343)) - Filter ANSI codes in logging output ([#19311](https://github.com/astral-sh/uv/pull/19311)) - Fix `uv tree` showing extra-conditional deps for packages required without extras ([#19332](https://github.com/astral-sh/uv/pull/19332)) - Respect build options (e.g., `--no-build`) during lock validation ([#19366](https://github.com/astral-sh/uv/pull/19366)) ## 0.11.13 Released on 2026-05-10. ### Bug fixes - Include data files in editable builds ([#19312](https://github.com/astral-sh/uv/pull/19312)) - Respect `--require-hashes` when installing from `pylock.toml` files ([#19334](https://github.com/astral-sh/uv/pull/19334)) ### Python ### Python - Add CPython 3.14.5 ## 0.11.12 Released on 2026-05-08. ### Python - Add CPython 3.15.0b1 ### Enhancements - Add `--no-editable` support to `uv pip install` ([#19306](https://github.com/astral-sh/uv/pull/19306)) - Require git refs in URLs to be percent-encoded ([#19320](https://github.com/astral-sh/uv/pull/19320)) ### Bug fixes - Respect `--no-dev` over `UV_DEV=1` ([#19313](https://github.com/astral-sh/uv/pull/19313)) - Don't suggest non-existent `--no-frozen` flag (#19290) ([#19294](https://github.com/astral-sh/uv/pull/19294)) ### Documentation - Fix bug from inconsistent workflow name in GHA-PyPI guide example ([#19309](https://github.com/astral-sh/uv/pull/19309)) ## 0.11.11 Released on 2026-05-06. ### Bug fixes - Accept legacy ID format from pre-0.11.9 cache entries ([#19301](https://github.com/astral-sh/uv/pull/19301)) ## 0.11.10 Released on 2026-05-05. ### Bug fixes - Allow pre-release Python requests with non-zero patch versions ([#19286](https://github.com/astral-sh/uv/pull/19286)) ## 0.11.9 Released on 2026-05-04. This release includes a special release candidate for the next Python 3.14 patch release. Python 3.14 included a new garbage collection implementation, which reduced pause times but caused significant unexpected memory pressure in production environments. In 3.14.5 and 3.15, the previous garbage collection implementation will be restored. We would greatly appreciate if you tested the 3.14.5rc1 version included in this release. The stable version is expected to be released soon and any feedback on potential issues would be helpful to the Python development team. For more context, see the [announcement](https://discuss.python.org/t/reverting-the-incremental-gc-in-python-3-14-and-3-15/107014), [issue](https://github.com/python/cpython/issues/148726), and [pull request](https://github.com/python/cpython/pull/148720). Issues with the new release can be reported in the uv or CPython issue trackers. ### Python - Upgrade PyPy to v7.3.22 - Add CPython 3.14.5rc1 - On macOS, CPython statically links `libpython` to match Linux ### Enhancements - Omit compatible release desugaring for pre-release hints ([#19267](https://github.com/astral-sh/uv/pull/19267)) - Fix file locks on Android ([#18323](https://github.com/astral-sh/uv/pull/18323)) ### Preview - `uv audit` add reporting for adverse project statuses ([#19128](https://github.com/astral-sh/uv/pull/19128)) ### Bug fixes - Discover versioned Python executables when `requires-python` pins a version ([#18700](https://github.com/astral-sh/uv/pull/18700)) - Fix URL prefix matching to require path boundaries ([#19154](https://github.com/astral-sh/uv/pull/19154)) - Fix transitive Git path dependencies in lockfiles ([#19269](https://github.com/astral-sh/uv/pull/19269)) - Handle incorrect unlock error in `LockedFile::drop` on Wine ([#19229](https://github.com/astral-sh/uv/pull/19229)) - Prevent uninstalling site-packages for empty `top_level.txt` in `.egg-info` ([#19114](https://github.com/astral-sh/uv/pull/19114)) - Use symlinks instead of junctions on Wine ([#19213](https://github.com/astral-sh/uv/pull/19213)) - Fix floating-point environment handling on ARMv7 ([#19157](https://github.com/astral-sh/uv/pull/19157)) - Redact credentials from remote requirements URL in offline errors ([#19216](https://github.com/astral-sh/uv/pull/19216)) - Windows tramplolines no longer set `PYTHONHOME` and only set `__PYVENV_LAUNCHER__` for virtual environments ([#19199](https://github.com/astral-sh/uv/pull/19199)) ### Documentation - Mark `--native-tls` and `UV_NATIVE_TLS` as deprecated ([#18705](https://github.com/astral-sh/uv/pull/18705)) - Re-add `pytorch-triton-rocm` to PyTorch ROCm docs ([#19241](https://github.com/astral-sh/uv/pull/19241)) - Tweak changelog entries for 0.11.8 ([#19188](https://github.com/astral-sh/uv/pull/19188)) - Add 'Exporting lockfiles' to the Concepts->Projects index ([#19209](https://github.com/astral-sh/uv/pull/19209)) - Clarify that `uv init` creates git files / folders in the projects guide ([#19183](https://github.com/astral-sh/uv/pull/19183)) ## 0.11.8 Released on 2026-04-27. ### Enhancements - Add `--python-downloads-json-url` to `python pin` ([#19092](https://github.com/astral-sh/uv/pull/19092)) - Fetch uv from Astral mirror during self-update ([#18682](https://github.com/astral-sh/uv/pull/18682)) - Support `pip uninstall -y` ([#19082](https://github.com/astral-sh/uv/pull/19082)) - Allow `exclude-newer` to be missing from the lockfile when `exclude-newer-span` is present ([#19024](https://github.com/astral-sh/uv/pull/19024)) - Only show the version number in `uv self version --short` ([#19019](https://github.com/astral-sh/uv/pull/19019)) - Silence warnings on empty `SSL_CERT_DIR` directory ([#19018](https://github.com/astral-sh/uv/pull/19018)) - Use a sentinel timestamp for relative `exclude-newer` and `exclude-newer-package` values in lockfiles ([#19022](https://github.com/astral-sh/uv/pull/19022), [#19101](https://github.com/astral-sh/uv/pull/19101)) ### Configuration - Add `UV_PYTHON_NO_REGISTRY` ([#19035](https://github.com/astral-sh/uv/pull/19035)) - Add an environment variable for `UV_NO_PROJECT` ([#19052](https://github.com/astral-sh/uv/pull/19052)) - Expose `UV_PYTHON_SEARCH_PATH` for Python discovery `PATH` overrides ([#19034](https://github.com/astral-sh/uv/pull/19034)) ### Bug fixes - Add `rust-toolchain.toml` to uv-build sdist ([#19131](https://github.com/astral-sh/uv/pull/19131)) - Ensure uv invocations of git do not inherit repository location environment variables ([#19088](https://github.com/astral-sh/uv/pull/19088)) - Redact pre-signed upload URLs in verbose output ([#19146](https://github.com/astral-sh/uv/pull/19146)) - Handle transitive URL dependencies in PEP 517 build requirements ([#19076](https://github.com/astral-sh/uv/pull/19076), [#19086](https://github.com/astral-sh/uv/pull/19086)) - Support `uv lock` on a `pyproject.toml` that only contains dependency-groups ([#19087](https://github.com/astral-sh/uv/pull/19087)) - Disable transparent Python upgrades in projects when a patch version is requested via `.python-version` ([#19102](https://github.com/astral-sh/uv/pull/19102)) - Fix Python variant tagging in the Windows registry ([#19012](https://github.com/astral-sh/uv/pull/19012)) - Ban external symlinks in `.tar.zst` wheels ([#19144](https://github.com/astral-sh/uv/pull/19144)) ### Distributions - Remove deprecated license classifiers from uv-build and add Python 3.14 classifier ([#19130](https://github.com/astral-sh/uv/pull/19130)) ### Documentation - Bump astral-sh/setup-uv version in docs ([#19030](https://github.com/astral-sh/uv/pull/19030)) - Update PyTorch documentation for PyTorch 2.11 ([#19095](https://github.com/astral-sh/uv/pull/19095)) ## 0.11.7 Released on 2026-04-15. ### Python - Upgrade CPython build to 20260414 including an OpenSSL security upgrade ([#19004](https://github.com/astral-sh/uv/pull/19004)) ### Enhancements - Elevate configuration errors to `required-version` mismatches ([#18977](https://github.com/astral-sh/uv/pull/18977)) - Further improve TLS certificate validation messages ([#18933](https://github.com/astral-sh/uv/pull/18933)) - Improve `--exclude-newer` hints ([#18952](https://github.com/astral-sh/uv/pull/18952)) ### Preview features - Fix `--script` handling in `uv audit` ([#18970](https://github.com/astral-sh/uv/pull/18970)) - Fix traversal of extras in `uv audit` ([#18970](https://github.com/astral-sh/uv/pull/18970)) ### Bug fixes - De-quote `workspace metadata` in linehaul data ([#18966](https://github.com/astral-sh/uv/pull/18966)) - Avoid installing tool workspace member dependencies as editable ([#18891](https://github.com/astral-sh/uv/pull/18891)) - Emit JSON report for `uv sync --check` failures ([#18976](https://github.com/astral-sh/uv/pull/18976)) - Filter and warn on invalid TLS certificates ([#18951](https://github.com/astral-sh/uv/pull/18951)) - Fix equality comparisons for version specifiers with `~=` operators ([#18960](https://github.com/astral-sh/uv/pull/18960)) - Fix stale Python upgrade preview feature check in project environment construction ([#18961](https://github.com/astral-sh/uv/pull/18961)) - Improve Windows path normalization ([#18945](https://github.com/astral-sh/uv/pull/18945)) ## 0.11.6 Released on 2026-04-09. This release resolves a low severity security advisory in which wheels with malformed RECORD entries could delete arbitrary files on uninstall. See [GHSA-pjjw-68hj-v9mw](https://github.com/astral-sh/uv/security/advisories/GHSA-pjjw-68hj-v9mw) for details. ### Bug fixes - Do not remove files outside the venv on uninstall ([#18942](https://github.com/astral-sh/uv/pull/18942)) - Validate and heal wheel `RECORD` during installation ([#18943](https://github.com/astral-sh/uv/pull/18943)) - Avoid `uv cache clean` errors due to Win32 path normalization ([#18856](https://github.com/astral-sh/uv/pull/18856)) ## 0.11.5 Released on 2026-04-08. ### Python - Add CPython 3.13.13, 3.14.4, and 3.15.0a8 ([#18908](https://github.com/astral-sh/uv/pull/18908)) ### Enhancements - Fix `build_system.requires` error message ([#18911](https://github.com/astral-sh/uv/pull/18911)) - Remove trailing path separators in path normalization ([#18915](https://github.com/astral-sh/uv/pull/18915)) - Improve error messages for unsupported or invalid TLS certificates ([#18924](https://github.com/astral-sh/uv/pull/18924)) ### Preview features - Add `exclude-newer` to `[[tool.uv.index]]` ([#18839](https://github.com/astral-sh/uv/pull/18839)) - `uv audit`: add context/warnings for ignored vulnerabilities ([#18905](https://github.com/astral-sh/uv/pull/18905)) ### Bug fixes - Normalize persisted fork markers before lock equality checks ([#18612](https://github.com/astral-sh/uv/pull/18612)) - Clear junction properly when uninstalling Python versions on Windows ([#18815](https://github.com/astral-sh/uv/pull/18815)) - Report error cleanly instead of panicking on TLS certificate error ([#18904](https://github.com/astral-sh/uv/pull/18904)) ### Documentation - Remove the legacy `PIP_COMPATIBILITY.md` redirect file ([#18928](https://github.com/astral-sh/uv/pull/18928)) - Fix `uv init example-bare --bare` examples ([#18822](https://github.com/astral-sh/uv/pull/18822), [#18925](https://github.com/astral-sh/uv/pull/18925)) ## 0.11.4 Released on 2026-04-07. ### Enhancements - Add support for `--upgrade-group` ([#18266](https://github.com/astral-sh/uv/pull/18266)) - Merge repeated archive URL hashes by version ID ([#18841](https://github.com/astral-sh/uv/pull/18841)) - Require all direct URL hash algorithms to match ([#18842](https://github.com/astral-sh/uv/pull/18842)) ### Bug fixes - Avoid panics in environment finding via cycle detection ([#18828](https://github.com/astral-sh/uv/pull/18828)) - Enforce direct URL hashes for `pyproject.toml` dependencies ([#18786](https://github.com/astral-sh/uv/pull/18786)) - Error on `--locked` and `--frozen` when script lockfile is missing ([#18832](https://github.com/astral-sh/uv/pull/18832)) - Fix `uv export` extra resolution for workspace member and conflicting extras ([#18888](https://github.com/astral-sh/uv/pull/18888)) - Include conflicts defined in virtual workspace root ([#18886](https://github.com/astral-sh/uv/pull/18886)) - Recompute relative `exclude-newer` values during `uv tree --outdated` ([#18899](https://github.com/astral-sh/uv/pull/18899)) - Respect `--exclude-newer` in `uv tool list --outdated` ([#18861](https://github.com/astral-sh/uv/pull/18861)) - Sort by comparator to break specifier ties ([#18850](https://github.com/astral-sh/uv/pull/18850)) - Store relative timestamps in tool receipts ([#18901](https://github.com/astral-sh/uv/pull/18901)) - Track newly-activated extras when determining conflicts ([#18852](https://github.com/astral-sh/uv/pull/18852)) - Patch `Cargo.lock` in `uv-build` source distributions ([#18831](https://github.com/astral-sh/uv/pull/18831)) ### Documentation - Clarify that `--exclude-newer` compares artifact upload times ([#18830](https://github.com/astral-sh/uv/pull/18830)) ## 0.11.3 Released on 2026-04-01. ### Enhancements - Add progress bar for hashing phase in uv publish ([#18752](https://github.com/astral-sh/uv/pull/18752)) - Add support for ROCm 7.2 ([#18730](https://github.com/astral-sh/uv/pull/18730)) - Emit abi3t tags for every abi3 version ([#18777](https://github.com/astral-sh/uv/pull/18777)) - Expand `uv workspace metadata` with dependency information from the lock ([#18356](https://github.com/astral-sh/uv/pull/18356)) - Implement support for PEP 803 ([#18767](https://github.com/astral-sh/uv/pull/18767)) - Pretty-print platform in built wheel errors ([#18738](https://github.com/astral-sh/uv/pull/18738)) - Publish installers to `/installers/uv/latest` on the mirror ([#18725](https://github.com/astral-sh/uv/pull/18725)) - Show free-threaded Python in built-wheel errors ([#18740](https://github.com/astral-sh/uv/pull/18740)) ### Preview features - Add `--ignore` and `--ignore-until-fixed` to `uv audit` ([#18737](https://github.com/astral-sh/uv/pull/18737)) ### Bug fixes - Bump simple API cache ([#18797](https://github.com/astral-sh/uv/pull/18797)) - Don't drop `blake2b` hashes ([#18794](https://github.com/astral-sh/uv/pull/18794)) - Handle broken range request implementations ([#18780](https://github.com/astral-sh/uv/pull/18780)) - Remove `powerpc64-unknown-linux-gnu` from release build targets ([#18800](https://github.com/astral-sh/uv/pull/18800)) - Respect dependency metadata overrides in `uv pip check` ([#18742](https://github.com/astral-sh/uv/pull/18742)) - Support debug CPython ABI tags in environment compatibility ([#18739](https://github.com/astral-sh/uv/pull/18739)) ### Documentation - Document `false` opt-out for `exclude-newer-package` ([#18768](https://github.com/astral-sh/uv/pull/18768), [#18803](https://github.com/astral-sh/uv/pull/18803)) ## 0.11.2 Released on 2026-03-26. ### Enhancements - Add a dedicated Windows PE editing error ([#18710](https://github.com/astral-sh/uv/pull/18710)) - Make `uv self update` fetch the manifest from the mirror first ([#18679](https://github.com/astral-sh/uv/pull/18679)) - Use uv reqwest client for self update ([#17982](https://github.com/astral-sh/uv/pull/17982)) - Show `uv self update` success and failure messages with `--quiet` ([#18645](https://github.com/astral-sh/uv/pull/18645)) ### Preview features - Evaluate extras and groups when determining auditable packages ([#18511](https://github.com/astral-sh/uv/pull/18511)) ### Bug fixes - Skip redundant project configuration parsing for `uv run` ([#17890](https://github.com/astral-sh/uv/pull/17890)) ## 0.11.1 Released on 2026-03-24. ### Bug fixes - Add missing hash verification for `riscv64gc-unknown-linux-musl` ([#18686](https://github.com/astral-sh/uv/pull/18686)) - Fallback to direct download when direct URL streaming is unsupported ([#18688](https://github.com/astral-sh/uv/pull/18688)) - Revert treating 'Dynamic' values as case-insensitive ([#18692](https://github.com/astral-sh/uv/pull/18692)) - Remove torchdata from list of packages to source from the PyTorch index ([#18703](https://github.com/astral-sh/uv/pull/18703)) - Special-case `==` Python version request ranges ([#9697](https://github.com/astral-sh/uv/pull/9697)) ### Documentation - Cover `--python ` in "Using arbitrary Python environments" ([#6457](https://github.com/astral-sh/uv/pull/6457)) - Fix version annotations for `PS_MODULE_PATH` and `UV_WORKING_DIR` ([#18691](https://github.com/astral-sh/uv/pull/18691)) ## 0.11.0 Released on 2026-03-23. ### Breaking changes This release includes changes to the networking stack used by uv. While we think that breakage will be rare, it is possible that these changes will result in the rejection of certificates previously trusted by uv so we have marked the change as breaking out of an abundance of caution. The changes are largely driven by the upgrade of reqwest, which powers uv's HTTP clients, to [v0.13](https://seanmonstar.com/blog/reqwest-v013-rustls-default/) which included some breaking changes to TLS certificate verification. The following changes are included: - [`rustls-platform-verifier`](https://github.com/rustls/rustls-platform-verifier) is used instead of [`rustls-native-certs`](https://github.com/rustls/rustls-native-certs) and [`webpki`](https://github.com/rustls/webpki) for certificate verification **This change should have no effect unless you are using the `native-tls` option to enable reading system certificates.** `rustls-platform-verifier` delegates to the system for certificate validation (e.g., `Security.framework` on macOS) instead of eagerly loading certificates from the system and verifying them via `webpki`. The effects of this change will vary based on the operating system. In general, uv's certificate validation should now be more consistent with browsers and other native applications. However, this is the most likely cause of breaking changes in this release. Some previously failing certificate chains may succeed, and some previously accepted certificate chains may fail. In either case, we expect the validation to be more correct and welcome reports of regressions. In particular, because more responsibility for validating the certificate is transferred to your system's security library, some features like [CA constraints](https://support.apple.com/en-us/103255) or [revocation of certificates](https://en.wikipedia.org/wiki/Certificate_revocation) via OCSP and CRLs may now be used. This change should improve performance when using system certificate on macOS, as uv no longer needs to load all certificates from the keychain at startup. - [`aws-lc`](https://github.com/aws/aws-lc) is used instead of `ring` for a cryptography backend There should not be breaking changes from this change. We expect this to expand support for certificate signature algorithms. - `--native-tls` is deprecated in favor of a new `--system-certs` flag The `--native-tls` flag is still usable and has identical behavior to `--system-certs.` This change was made to reduce confusion about the TLS implementation uv uses. uv always uses `rustls` not `native-tls`. - Building uv on x86-64 and i686 Windows requires NASM NASM is required by `aws-lc`. If not found on the system, a prebuilt blob provided by `aws-lc-sys` will be used. If you are not building uv from source, this change has no effect. See the [CONTRIBUTING](https://github.com/astral-sh/uv/blob/b6854d77bfd0cb78157fecaf8b30126c6f16bc11/CONTRIBUTING.md#setup) guide for details. - Empty `SSL_CERT_FILE` values are ignored (for consistency with `SSL_CERT_DIR`) See [#18550](https://github.com/astral-sh/uv/pull/18550) for details. ### Python - Enable frame pointers for improved profiling on Linux x86-64 and aarch64 See the [python-build-standalone release notes](https://github.com/astral-sh/python-build-standalone/releases/20260320) for details. ### Enhancements - Treat 'Dynamic' values as case-insensitive ([#18669](https://github.com/astral-sh/uv/pull/18669)) - Use a dedicated error for invalid cache control headers ([#18657](https://github.com/astral-sh/uv/pull/18657)) - Enable checksum verification in the generated installer script ([#18625](https://github.com/astral-sh/uv/pull/18625)) ### Preview features - Add `--service-format` and `--service-url` to `uv audit` ([#18571](https://github.com/astral-sh/uv/pull/18571)) ### Performance - Avoid holding flat index lock across indexes ([#18659](https://github.com/astral-sh/uv/pull/18659)) ### Bug fixes - Find the dynamic linker on the file system when sniffing binaries fails ([#18457](https://github.com/astral-sh/uv/pull/18457)) - Fix export of conflicting workspace members with dependencies ([#18666](https://github.com/astral-sh/uv/pull/18666)) - Respect installed settings in `uv tool list --outdated` ([#18586](https://github.com/astral-sh/uv/pull/18586)) - Treat paths originating as PEP 508 URLs which contain expanded variables as relative ([#18680](https://github.com/astral-sh/uv/pull/18680)) - Fix `uv export` for workspace member packages with conflicts ([#18635](https://github.com/astral-sh/uv/pull/18635)) - Continue to alternative authentication providers when the pyx store has no token ([#18425](https://github.com/astral-sh/uv/pull/18425)) - Use redacted URLs for log messages in cached client ([#18599](https://github.com/astral-sh/uv/pull/18599)) ### Documentation - Add details on Linux versions to the platform policy ([#18574](https://github.com/astral-sh/uv/pull/18574)) - Clarify `FLASH_ATTENTION_SKIP_CUDA_BUILD` guidance for `flash-attn` installs ([#18473](https://github.com/astral-sh/uv/pull/18473)) - Split the dependency bots page into two separate pages ([#18597](https://github.com/astral-sh/uv/pull/18597)) - Split the alternative indexes page into separate pages ([#18607](https://github.com/astral-sh/uv/pull/18607)) ## 0.10.12 Released on 2026-03-19. ### Python - Add pypy 3.11.15 ([#18468](https://github.com/astral-sh/uv/pull/18468)) - Add support for using Python 3.6 interpreters ([#18454](https://github.com/astral-sh/uv/pull/18454)) ### Enhancements - Include uv's target triple in version report ([#18520](https://github.com/astral-sh/uv/pull/18520)) - Allow comma separated values in `--no-emit-package` ([#18565](https://github.com/astral-sh/uv/pull/18565)) ### Preview features - Show `uv audit` in the CLI help ([#18540](https://github.com/astral-sh/uv/pull/18540)) ### Bug fixes - Improve reporting of managed interpreter symlinks in `uv python list` ([#18459](https://github.com/astral-sh/uv/pull/18459)) - Preserve end-of-line comments on previous entries when removing dependencies ([#18557](https://github.com/astral-sh/uv/pull/18557)) - Treat abi3 wheel Python version as a lower bound ([#18536](https://github.com/astral-sh/uv/pull/18536)) - Detect hard-float support on aarch64 kernels running armv7 userspace ([#18530](https://github.com/astral-sh/uv/pull/18530)) ### Documentation - Add Python 3.15 to supported versions ([#18552](https://github.com/astral-sh/uv/pull/18552)) - Adjust the PyPy note ([#18548](https://github.com/astral-sh/uv/pull/18548)) - Move Pyodide to Tier 2 in the Python support policy ([#18561](https://github.com/astral-sh/uv/pull/18561)) - Move Rust and Python version support out of the Platform support policy ([#18535](https://github.com/astral-sh/uv/pull/18535)) - Update Docker guide with changes from `uv-docker-example` ([#18558](https://github.com/astral-sh/uv/pull/18558)) - Update the Python version policy ([#18559](https://github.com/astral-sh/uv/pull/18559)) ## 0.10.11 Released on 2026-03-16. ### Enhancements - Fetch Ruff release metadata from an Astral mirror ([#18358](https://github.com/astral-sh/uv/pull/18358)) - Use PEP 639 license metadata for uv itself ([#16477](https://github.com/astral-sh/uv/pull/16477)) ### Performance - Improve distribution id performance ([#18486](https://github.com/astral-sh/uv/pull/18486)) ### Bug fixes - Allow `--project` to refer to a `pyproject.toml` directly and reduce to a warning on other files ([#18513](https://github.com/astral-sh/uv/pull/18513)) - Disable `SYSTEM_VERSION_COMPAT` when querying interpreters on macOS ([#18452](https://github.com/astral-sh/uv/pull/18452)) - Enforce available distributions for supported environments ([#18451](https://github.com/astral-sh/uv/pull/18451)) - Fix `uv sync --active` recreating active environments when `UV_PYTHON_INSTALL_DIR` is relative ([#18398](https://github.com/astral-sh/uv/pull/18398)) ### Documentation - Add missing `-o requirements.txt` in `uv pip compile` example ([#12308](https://github.com/astral-sh/uv/pull/12308)) - Link to organization security policy ([#18449](https://github.com/astral-sh/uv/pull/18449)) - Link to the AI policy in the contributing guide ([#18448](https://github.com/astral-sh/uv/pull/18448)) ## 0.10.10 Released on 2026-03-13. ### Python - Add CPython 3.15.0a7 ([#18403](https://github.com/astral-sh/uv/pull/18403)) ### Enhancements - Add `--outdated` flag to `uv tool list` ([#18318](https://github.com/astral-sh/uv/pull/18318)) - Add riscv64 musl target to build-release-binaries workflow ([#18228](https://github.com/astral-sh/uv/pull/18228)) - Fetch Ruff from an Astral mirror ([#18286](https://github.com/astral-sh/uv/pull/18286)) - Improve error handling for platform detection in Python downloads ([#18453](https://github.com/astral-sh/uv/pull/18453)) - Warn if `--project` directory does not exist ([#17714](https://github.com/astral-sh/uv/pull/17714)) - Warn when workspace member scripts are skipped due to missing build system ([#18389](https://github.com/astral-sh/uv/pull/18389)) - Update build backend versions used in `uv init` ([#18417](https://github.com/astral-sh/uv/pull/18417)) - Log explicit config file path in verbose output ([#18353](https://github.com/astral-sh/uv/pull/18353)) - Make `uv cache clear` an alias of `uv cache clean` ([#18420](https://github.com/astral-sh/uv/pull/18420)) - Reject invalid classifiers, warn on license classifiers in `uv_build` ([#18419](https://github.com/astral-sh/uv/pull/18419)) ### Preview features - Add links to `uv audit` output ([#18392](https://github.com/astral-sh/uv/pull/18392)) - Output/report formatting for `uv audit` ([#18193](https://github.com/astral-sh/uv/pull/18193)) - Switch to batched OSV queries for `uv audit` ([#18394](https://github.com/astral-sh/uv/pull/18394)) ### Bug fixes - Avoid sharing version metadata across indexes ([#18373](https://github.com/astral-sh/uv/pull/18373)) - Bump zlib-rs to 0.6.2 to fix panic on decompression of large wheels on Windows ([#18362](https://github.com/astral-sh/uv/pull/18362)) - Filter out unsupported environment wheels ([#18445](https://github.com/astral-sh/uv/pull/18445)) - Preserve absolute/relative paths in lockfiles ([#18176](https://github.com/astral-sh/uv/pull/18176)) - Recreate Python environments under `uv tool install --force` ([#18399](https://github.com/astral-sh/uv/pull/18399)) - Respect timestamp and other cache keys in cached environments ([#18396](https://github.com/astral-sh/uv/pull/18396)) - Simplify selected extra markers in `uv export` ([#18433](https://github.com/astral-sh/uv/pull/18433)) - Send pyx mint-token requests with a proper `Content-Type` ([#18334](https://github.com/astral-sh/uv/pull/18334)) - Fix Windows operating system and version reporting ([#18383](https://github.com/astral-sh/uv/pull/18383)) ### Documentation - Update the platform support policy with a tier 3 section including freebsd and 32-bit windows ([#18345](https://github.com/astral-sh/uv/pull/18345)) ## 0.10.9 Released on 2026-03-06. ### Enhancements - Add `fbgemm-gpu`, `fbgemm-gpu-genai`, `torchrec`, and `torchtune` to the PyTorch list ([#18338](https://github.com/astral-sh/uv/pull/18338)) - Add torchcodec to PyTorch List ([#18336](https://github.com/astral-sh/uv/pull/18336)) - Log the duration we took before erroring ([#18231](https://github.com/astral-sh/uv/pull/18231)) - Warn when using `uv_build` settings without `uv_build` ([#15750](https://github.com/astral-sh/uv/pull/15750)) - Add fallback to `/usr/lib/os-release` on Linux system lookup failure ([#18349](https://github.com/astral-sh/uv/pull/18349)) - Use `cargo auditable` to include SBOM in uv builds ([#18276](https://github.com/astral-sh/uv/pull/18276)) ### Configuration - Add an environment variable for `UV_VENV_RELOCATABLE` ([#18331](https://github.com/astral-sh/uv/pull/18331)) ### Performance - Avoid toml `Document` overhead ([#18306](https://github.com/astral-sh/uv/pull/18306)) - Use a single global workspace cache ([#18307](https://github.com/astral-sh/uv/pull/18307)) ### Bug fixes - Continue on trampoline job assignment failures ([#18291](https://github.com/astral-sh/uv/pull/18291)) - Handle the hard link limit gracefully instead of failing ([#17699](https://github.com/astral-sh/uv/pull/17699)) - Respect build constraints for workspace members ([#18350](https://github.com/astral-sh/uv/pull/18350)) - Revalidate editables and other dependencies in scripts ([#18328](https://github.com/astral-sh/uv/pull/18328)) - Support Python 3.13+ on Android ([#18301](https://github.com/astral-sh/uv/pull/18301)) - Support `cp3-none-any` ([#17064](https://github.com/astral-sh/uv/pull/17064)) - Skip tool environments with broken links to Python on Windows ([#17176](https://github.com/astral-sh/uv/pull/17176)) ### Documentation - Add documentation for common marker values ([#18327](https://github.com/astral-sh/uv/pull/18327)) - Improve documentation on virtual dependencies ([#18346](https://github.com/astral-sh/uv/pull/18346)) ## 0.10.8 Released on 2026-03-03. ### Python - Add CPython 3.10.20 - Add CPython 3.11.15 - Add CPython 3.12.13 ### Enhancements - Add Docker images based on Docker Hardened Images ([#18247](https://github.com/astral-sh/uv/pull/18247)) - Add resolver hint when `--exclude-newer` filters out all versions of a package ([#18217](https://github.com/astral-sh/uv/pull/18217)) - Configure a real retry minimum delay of 1s ([#18201](https://github.com/astral-sh/uv/pull/18201)) - Expand `uv_build` direct build compatibility ([#17902](https://github.com/astral-sh/uv/pull/17902)) - Fetch CPython from an Astral mirror by default ([#18207](https://github.com/astral-sh/uv/pull/18207)) - Download uv releases from an Astral mirror in installers by default ([#18191](https://github.com/astral-sh/uv/pull/18191)) - Add SBOM attestations to Docker images ([#18252](https://github.com/astral-sh/uv/pull/18252)) - Improve hint for installing meson-python when missing as build backend ([#15826](https://github.com/astral-sh/uv/pull/15826)) ### Configuration - Add `UV_INIT_BARE` environment variable for `uv init` ([#18210](https://github.com/astral-sh/uv/pull/18210)) ### Bug fixes - Prevent `uv tool upgrade` from installing excluded dependencies ([#18022](https://github.com/astral-sh/uv/pull/18022)) - Promote authentication policy when saving tool receipts ([#18246](https://github.com/astral-sh/uv/pull/18246)) - Respect exclusions in scripts ([#18269](https://github.com/astral-sh/uv/pull/18269)) - Retain default-branch Git SHAs in `pylock.toml` files ([#18227](https://github.com/astral-sh/uv/pull/18227)) - Skip installed Python check for URL dependencies ([#18211](https://github.com/astral-sh/uv/pull/18211)) - Respect constraints during `--upgrade` ([#18226](https://github.com/astral-sh/uv/pull/18226)) - Fix `uv tree` orphaned roots and premature deduplication ([#17212](https://github.com/astral-sh/uv/pull/17212)) ### Documentation - Mention cooldown and tweak inline script metadata in dependency bots documentation ([#18230](https://github.com/astral-sh/uv/pull/18230)) - Move cache prune in GitLab to `after_script` ([#18206](https://github.com/astral-sh/uv/pull/18206)) ## 0.10.7 Released on 2026-02-27. ### Bug fixes - Fix handling of junctions in Windows Containers on Windows ([#18192](https://github.com/astral-sh/uv/pull/18192)) ### Enhancements - Activate logging for middleware retries ([#18200](https://github.com/astral-sh/uv/pull/18200)) - Upload uv releases to a mirror ([#18159](https://github.com/astral-sh/uv/pull/18159)) ## 0.10.6 Released on 2026-02-24. ### Bug fixes - Apply lockfile marker normalization for fork markers ([#18116](https://github.com/astral-sh/uv/pull/18116)) - Fix Python version selection for scripts with a `requires-python` conflicting with `.python-version` ([#18097](https://github.com/astral-sh/uv/pull/18097)) - Preserve file permissions when using reflinks on Linux ([#18187](https://github.com/astral-sh/uv/pull/18187)) ### Documentation - Remove verbose documentation from optional dependencies help text ([#18180](https://github.com/astral-sh/uv/pull/18180)) ## 0.10.5 Released on 2026-02-23. ### Enhancements - Add hint when named index is found in a parent config file ([#18087](https://github.com/astral-sh/uv/pull/18087)) - Add warning for `uv lock --frozen` ([#17859](https://github.com/astral-sh/uv/pull/17859)) - Attempt to use reflinks by default on Linux ([#18117](https://github.com/astral-sh/uv/pull/18117)) - Fallback to hardlinks after reflink failure before copying ([#18104](https://github.com/astral-sh/uv/pull/18104)) - Filter `pylock.toml` wheels by tags and `requires-python` ([#18081](https://github.com/astral-sh/uv/pull/18081)) - Validate wheel filenames are normalized during `uv publish` ([#17783](https://github.com/astral-sh/uv/pull/17783)) - Fix message when `exclude-newer` invalidates the lock file ([#18100](https://github.com/astral-sh/uv/pull/18100)) - Change the missing files log level to debug ([#18075](https://github.com/astral-sh/uv/pull/18075)) ### Performance - Improve performance of repeated conflicts with an extra ([#18094](https://github.com/astral-sh/uv/pull/18094)) ### Bug fixes - Fix `--no-emit-workspace` with `--all-packages` on single-member workspaces ([#18098](https://github.com/astral-sh/uv/pull/18098)) - Fix `UV_NO_DEFAULT_GROUPS` rejecting truthy values like `1` ([#18057](https://github.com/astral-sh/uv/pull/18057)) - Fix iOS detection ([#17973](https://github.com/astral-sh/uv/pull/17973)) - Propagate project-level conflicts to package extras ([#18096](https://github.com/astral-sh/uv/pull/18096)) - Use a global build concurrency semaphore ([#18054](https://github.com/astral-sh/uv/pull/18054)) ### Documentation - Update documentation heading for environment variable files ([#18122](https://github.com/astral-sh/uv/pull/18122)) - Fix comment about `uv export` formats ([#17900](https://github.com/astral-sh/uv/pull/17900)) - Make it clear that Windows is supported in user- and system- level configuration docs ([#18106](https://github.com/astral-sh/uv/pull/18106)) ## 0.10.4 Released on 2026-02-17. ### Enhancements - Remove duplicate references to the affected paths when showing `uv python` errors ([#18008](https://github.com/astral-sh/uv/pull/18008)) - Skip discovery of workspace members that contain only git-ignored files, including in sub-directories ([#18051](https://github.com/astral-sh/uv/pull/18051)) ### Bug fixes - Don't panic when initialising a package at the filesystem root (e.g. `uv init / --name foo`) ([#17983](https://github.com/astral-sh/uv/pull/17983)) - Fix permissions on `wheel` and `sdist` files produced by the `uv_build` build backend ([#18020](https://github.com/astral-sh/uv/pull/18020)) - Revert locked file change to fix locked files on NFS mounts ([#18071](https://github.com/astral-sh/uv/pull/18071)) ## 0.10.3 Released on 2026-02-16. ### Python - Add CPython 3.15.0a6 ### Enhancements - Don't open file locks for writing ([#17956](https://github.com/astral-sh/uv/pull/17956)) - Make Windows trampoline error messages consistent with uv proper ([#17969](https://github.com/astral-sh/uv/pull/17969)) - Log which preview features are enabled ([#17968](https://github.com/astral-sh/uv/pull/17968)) ### Preview features - Add support for ruff version constraints and `exclude-newer` in `uv format` ([#17651](https://github.com/astral-sh/uv/pull/17651)) - Fix script path handling when `target-workspace-discovery` is enabled ([#17965](https://github.com/astral-sh/uv/pull/17965)) - Use version constraints to select the default ruff version used by `uv format` ([#17977](https://github.com/astral-sh/uv/pull/17977)) ### Bug fixes - Avoid matching managed Python versions by prefixes, e.g. don't match CPython 3.10 when `cpython-3.1` is specified ([#17972](https://github.com/astral-sh/uv/pull/17972)) - Fix handling of `--allow-existing` with minor version links on Windows ([#17978](https://github.com/astral-sh/uv/pull/17978)) - Fix panic when encountering unmanaged workspace members ([#17974](https://github.com/astral-sh/uv/pull/17974)) - Improve accuracy of request timing ([#18007](https://github.com/astral-sh/uv/pull/18007)) - Reject `u64::MAX` in version segments to prevent overflow ([#17985](https://github.com/astral-sh/uv/pull/17985)) ### Documentation - Reference Debian Trixie instead of Bookworm ([#17991](https://github.com/astral-sh/uv/pull/17991)) ## 0.10.2 Released on 2026-02-10. ### Enhancements - Deprecate unexpected ZIP compression methods ([#17946](https://github.com/astral-sh/uv/pull/17946)) ### Bug fixes - Fix `cargo-install` failing due to missing `uv-test` dependency ([#17954](https://github.com/astral-sh/uv/pull/17954)) ## 0.10.1 Released on 2026-02-10. ### Enhancements - Don't panic on metadata read errors ([#17904](https://github.com/astral-sh/uv/pull/17904)) - Skip empty workspace members instead of failing ([#17901](https://github.com/astral-sh/uv/pull/17901)) - Don't fail creating a read-only `sdist-vX/.git` if it already exists ([#17825](https://github.com/astral-sh/uv/pull/17825)) ### Documentation - Suggest `uv python update-shell` over `uv tool update-shell` in Python docs ([#17941](https://github.com/astral-sh/uv/pull/17941)) ## 0.10.0 Since we released uv [0.9.0](https://github.com/astral-sh/uv/releases/tag/0.9.0) in October of 2025, we've accumulated various changes that improve correctness and user experience, but could break some workflows. This release contains those changes; many have been marked as breaking out of an abundance of caution. We expect most users to be able to upgrade without making changes. This release also includes the stabilization of preview features. Python upgrades are now stable, including the `uv python upgrade` command, `uv python install --upgrade`, and automatically upgrading Python patch versions in virtual environments when a new version is installed. The `add-bounds` and `extra-build-dependencies` settings are now stable. Finally, the `uv workspace dir` and `uv workspace list` utilities for writing scripts against workspace members are now stable. There are no breaking changes to [`uv_build`](https://docs.astral.sh/uv/concepts/build-backend/). If you have an upper bound in your `[build-system]` table, you should update it, e.g., from `<0.10.0` to `<0.11.0`. ### Breaking changes - **Require `--clear` to remove existing virtual environments in `uv venv`** ([#17757](https://github.com/astral-sh/uv/pull/17757)) Previously, `uv venv` would prompt for confirmation before removing an existing virtual environment in interactive contexts, and remove it without confirmation in non-interactive contexts. Now, `uv venv` requires the `--clear` flag to remove an existing virtual environment. A warning for this change was added in [uv 0.8](https://github.com/astral-sh/uv/blob/main/changelogs/0.8.x.md#breaking-changes). You can opt out of this behavior by passing the `--clear` flag or setting `UV_VENV_CLEAR=1`. - **Error if multiple indexes include `default = true`** ([#17011](https://github.com/astral-sh/uv/pull/17011)) Previously, uv would silently accept multiple indexes with `default = true` and use the first one. Now, uv will error if multiple indexes are marked as the default. You cannot opt out of this behavior. Remove `default = true` from all but one index. - **Error when an `explicit` index is unnamed** ([#17777](https://github.com/astral-sh/uv/pull/17777)) Explicit indexes can only be used via the `[tool.uv.sources]` table, which requires referencing the index by name. Previously, uv would silently accept unnamed explicit indexes, which could never be referenced. Now, uv will error if an explicit index does not have a name. You cannot opt out of this behavior. Add a `name` to the explicit index or remove the entry. - **Install alternative Python executables using their implementation name** ([#17756](https://github.com/astral-sh/uv/pull/17756), [#17760](https://github.com/astral-sh/uv/pull/17760)) Previously, `uv python install` would install PyPy, GraalPy, and Pyodide executables with names like `python3.10` into the bin directory. Now, these executables will be named using their implementation name, e.g., `pypy3.10`, `graalpy3.10`, and `pyodide3.12`, to avoid conflicting with CPython installations. You cannot opt out of this behavior. - **Respect global Python version pins in `uv tool run` and `uv tool install`** ([#14112](https://github.com/astral-sh/uv/pull/14112)) Previously, `uv tool run` and `uv tool install` did not respect the global Python version pin (set via `uv python pin --global`). Now, these commands will use the global Python version when no explicit version is requested. For `uv tool install`, if the tool is already installed, the Python version will not change unless `--reinstall` or `--python` is provided. If the tool was previously installed with an explicit `--python` flag, the global pin will not override it. You can opt out of this behavior by providing an explicit `--python` flag. - **Remove Debian Bookworm, Alpine 3.21, and Python 3.8 Docker images** ([#17755](https://github.com/astral-sh/uv/pull/17755)) The Debian Bookworm and Alpine 3.21 images were replaced by Debian Trixie and Alpine 3.22 as defaults in [uv 0.9](https://github.com/astral-sh/uv/pull/15352). These older images are now removed. Python 3.8 images are also removed, as Python 3.8 is no longer supported in the Trixie or Alpine base images. The following image tags are no longer published: - `uv:bookworm`, `uv:bookworm-slim` - `uv:alpine3.21` - `uv:python3.8-*` Use `uv:debian` or `uv:trixie` instead of `uv:bookworm`, `uv:alpine` or `uv:alpine3.22` instead of `uv:alpine3.21`, and a newer Python version instead of `uv:python3.8-*`. - **Drop PPC64 (big endian) builds** ([#17626](https://github.com/astral-sh/uv/pull/17626)) uv no longer provides pre-built binaries for PPC64 (big endian). This platform appears to be largely unused and is only supported on a single manylinux version. PPC64LE (little endian) builds are unaffected. Building uv from source is still supported for this platform. - **Skip generating `activate.csh` for relocatable virtual environments** ([#17759](https://github.com/astral-sh/uv/pull/17759)) Previously, `uv venv --relocatable` would generate an `activate.csh` script that contained hardcoded paths, making it incompatible with relocation. Now, the `activate.csh` script is not generated for relocatable virtual environments. You cannot opt out of this behavior. - **Require username when multiple credentials match a URL** ([#16983](https://github.com/astral-sh/uv/pull/16983)) When using `uv auth login` to store credentials, you can register multiple username and password combinations for the same host. Previously, when uv needed to authenticate and multiple credentials matched the URL (e.g., when retrieving a token with `uv auth token`), uv would pick the first match. Now, uv will error instead. You cannot opt out of this behavior. Include the username in the request, e.g., `uv auth token --username foo example.com`. - **Avoid invalidating the lockfile versions after an `exclude-newer` change** ([#17721](https://github.com/astral-sh/uv/pull/17721)) Previously, changing the `exclude-newer` setting would cause package versions to be upgraded, ignoring the lockfile entirely. Now, uv will only change package versions if they are no longer within the `exclude-newer` range. You can restore the previous behavior by using `--upgrade` or `--upgrade-package` to opt-in to package version changes. - **Upgrade `uv format` to Ruff 0.15.0** ([#17838](https://github.com/astral-sh/uv/pull/17838)) `uv format` now uses [Ruff 0.15.0](https://github.com/astral-sh/ruff/releases/tag/0.15.0), which uses the [2026 style guide](https://astral.sh/blog/ruff-v0.15.0#the-ruff-2026-style-guide). See the blog post for details. The formatting of code is likely to change. You can opt out of this behavior by requesting an older Ruff version, e.g., `uv format --version 0.14.14`. - **Update uv crate test features to use `test-` as a prefix** ([#17860](https://github.com/astral-sh/uv/pull/17860)) This change only affects redistributors of uv. The Cargo features used to gate test dependencies, e.g., `pypi`, have been renamed with a `test-` prefix for clarity, e.g., `test-pypi`. ### Stabilizations - **`uv python upgrade` and `uv python install --upgrade`** ([#17766](https://github.com/astral-sh/uv/pull/17766)) When installing Python versions, an [intermediary directory](https://docs.astral.sh/uv/concepts/python-versions/#minor-version-directories) without the patch version attached will be created, and virtual environments will be transparently upgraded to new patch versions. See the [Python version documentation](https://docs.astral.sh/uv/concepts/python-versions/#upgrading-python-versions) for more details. - **`uv add --bounds` and the `add-bounds` configuration option** ([#17660](https://github.com/astral-sh/uv/pull/17660)) This does not come with any behavior changes. You will no longer see an experimental warning when using `uv add --bounds` or `add-bounds` in configuration. - **`uv workspace list` and `uv workspace dir`** ([#17768](https://github.com/astral-sh/uv/pull/17768)) This does not come with any behavior changes. You will no longer see an experimental warning when using these commands. - **`extra-build-dependencies`** ([#17767](https://github.com/astral-sh/uv/pull/17767)) This does not come with any behavior changes. You will no longer see an experimental warning when using `extra-build-dependencies` in configuration. ### Enhancements - Improve ABI tag error message phrasing ([#17878](https://github.com/astral-sh/uv/pull/17878)) - Introduce a 10s connect timeout ([#17733](https://github.com/astral-sh/uv/pull/17733)) - Allow using `pyx.dev` as a target in `uv auth` commands despite `PYX_API_URL` differing ([#17856](https://github.com/astral-sh/uv/pull/17856)) ### Bug fixes - Support all CPython ABI tag suffixes properly ([#17817](https://github.com/astral-sh/uv/pull/17817)) - Add support for detecting PowerShell on Linux and macOS ([#17870](https://github.com/astral-sh/uv/pull/17870)) - Retry timeout errors for streams ([#17875](https://github.com/astral-sh/uv/pull/17875)) ## 0.9.x See [changelogs/0.9.x](./changelogs/0.9.x.md) ## 0.8.x See [changelogs/0.8.x](./changelogs/0.8.x.md) ## 0.7.x See [changelogs/0.7.x](./changelogs/0.7.x.md) ## 0.6.x See [changelogs/0.6.x](./changelogs/0.6.x.md) ## 0.5.x See [changelogs/0.5.x](./changelogs/0.5.x.md) ## 0.4.x See [changelogs/0.4.x](./changelogs/0.4.x.md) ## 0.3.x See [changelogs/0.3.x](./changelogs/0.3.x.md) ## 0.2.x See [changelogs/0.2.x](./changelogs/0.2.x.md) ## 0.1.x See [changelogs/0.1.x](./changelogs/0.1.x.md) astral-sh-uv-9252ba6/CLAUDE.md000066400000000000000000000000131521221614700156460ustar00rootroot00000000000000@AGENTS.md astral-sh-uv-9252ba6/CONTRIBUTING.md000066400000000000000000000262521521221614700166350ustar00rootroot00000000000000# Contributing ## Finding ways to help We label issues that would be good for a first time contributor as [`good first issue`](https://github.com/astral-sh/uv/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). These usually do not require significant experience with Rust or the uv code base. We label issues that we think are a good opportunity for subsequent contributions as [`help wanted`](https://github.com/astral-sh/uv/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). These require varying levels of experience with Rust and uv. Often, we want to accomplish these tasks but do not have the resources to do so ourselves. You don't need our permission to start on an issue we have labeled as appropriate for community contribution as described above. However, it's a good idea to indicate that you are going to work on an issue to avoid concurrent attempts to solve the same problem. Please check in with us before starting work on an issue that has not been labeled as appropriate for community contribution. We're happy to receive contributions for other issues, but it's important to make sure we have consensus on the solution to the problem first. Outside of issues with the labels above, issues labeled as [`bug`](https://github.com/astral-sh/uv/issues?q=is%3Aopen+is%3Aissue+label%3A%22bug%22) are the best candidates for contribution. In contrast, issues labeled with `needs-decision` or `needs-design` are _not_ good candidates for contribution. Please do not open pull requests for issues with these labels. Please do not open pull requests for new features without prior discussion. While we appreciate exploration of new features, we will almost always close these pull requests immediately. Adding a new feature to uv creates a long-term maintenance burden and requires strong consensus from the uv team before it is appropriate to begin work on an implementation. ## Use of AI We **require all use of AI in contributions to follow our [AI Policy](https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)**. If your contribution does not follow the policy, it will be closed. ## Setup [Rust](https://rustup.rs/) (and a C compiler) are required to build uv. On Ubuntu and other Debian-based distributions, you can install a C compiler with: ```shell sudo apt install build-essential ``` On Fedora-based distributions, you can install a C compiler with: ```shell sudo dnf install gcc ``` On Windows, [NASM](https://www.nasm.us/) is required for building the TLS backend (`aws-lc-sys`). If it is not present, a prebuilt blob provided by `aws-lc-sys` will be used instead. WinGet can be used to install NASM: ```shell winget install NASM.NASM ``` After installation, add `C:\Program Files\NASM` to your `PATH`. While the prebuilt blob will not be used when NASM is found, you can guarantee this behavior by setting `AWS_LC_SYS_PREBUILT_NASM=0`. ## Testing For running tests, we recommend [nextest](https://nexte.st/). To run a specific test by name: ```shell cargo nextest run -E 'test(test_name)' ``` To run all tests and accept snapshot changes: ```shell cargo insta test --accept --test-runner nextest ``` To update snapshots for a specific test: ```shell cargo insta test --accept --test-runner nextest -- ``` ### Python Testing uv requires multiple specific Python versions; they can be installed with: ```shell cargo run python install ``` The storage directory can be configured with `UV_PYTHON_INSTALL_DIR`. (It must be an absolute path.) ### Snapshot testing uv uses [insta](https://insta.rs/) for snapshot testing. It's recommended (but not necessary) to use `cargo-insta` for a better snapshot review experience. See the [installation guide](https://insta.rs/docs/cli/) for more information. In tests, you can use `uv_snapshot!` macro to simplify creating snapshots for uv commands. For example: ```rust #[test] fn test_add() { let context = TestContext::new("3.12"); uv_snapshot!(context.filters(), context.add().arg("requests"), @""); } ``` To run and review a specific snapshot test: ```shell cargo test --package --test -- -- --exact cargo insta review ``` A script is available to update the snapshots based on results in CI. This is useful for updating snapshots without re-running the test suite and for updating platform-specific snapshots. ```shell ./scripts/apply-ci-snapshots.sh ``` ### Git and Git LFS A subset of uv tests require both [Git](https://git-scm.com) and [Git LFS](https://git-lfs.com/) to execute properly. These tests can be disabled by turning off either `git` or `git-lfs` uv features. ### Local testing You can invoke your development version of uv with `cargo run -- `. For example: ```shell cargo run -- venv cargo run -- pip install requests ``` ## Formatting ```shell # Rust cargo fmt --all # Python uvx ruff format . # Markdown, YAML, and other files (requires Node.js) npx prettier --write . # or in Docker docker run --rm -v .:/src/ -w /src/ node:alpine npx prettier --write . ``` ## Linting Linting requires [shellcheck](https://github.com/koalaman/shellcheck) and [cargo-shear](https://github.com/Boshen/cargo-shear) to be installed separately. ```shell # Rust cargo clippy --workspace --all-targets --all-features --locked -- -D warnings # Python uvx ruff check . # Python type checking uvx ty check python/uv # Shell scripts shellcheck {% endblock %} astral-sh-uv-9252ba6/docs/.overrides/partials/000077500000000000000000000000001521221614700212245ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/.overrides/partials/integrations/000077500000000000000000000000001521221614700237325ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/.overrides/partials/integrations/analytics/000077500000000000000000000000001521221614700257215ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/.overrides/partials/integrations/analytics/fathom.html000066400000000000000000000001361521221614700300650ustar00rootroot00000000000000 astral-sh-uv-9252ba6/docs/assets/000077500000000000000000000000001521221614700166275ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/assets/favicon.ico000066400000000000000000000163561521221614700207630ustar00rootroot0000000000000000¨6 ¨Þh†(0` >1JggR’ˆQŽ…MtrA/>U¤•JegLonEFPaéÇQ†N{MvsB7Edÿ×FMUb÷ÑC:GP„}=.\ѵFSY?#6NxtFKSJacW²ž      ( @_ãÃV¯U¤•C:FYÁªHY^>0C;HEFPB7Edÿ×B3BHX]GTZSšDALGW\=.B9FLmlX·¢_çÅZɯHV[A0?                                    ( MtrEGQ?);C8FR”‰C;HB7Edÿ×PŒƒN}xJceQ“ˆEEODAL@'9GW\=.I^aFOVO†~GUZ      astral-sh-uv-9252ba6/docs/assets/github-add-environment.png000066400000000000000000002454251521221614700237230ustar00rootroot00000000000000‰PNG  IHDR'Cž÷uf€IDATx^ìÝw\YØ7ü{Uz{ok[u-[tuÕÕÕQEDP@E±¡bï bÁ† {ņØ6¬¨Ø;âz?Ïç}ïò{æš$fHèêõÇ÷£L93™I&çüræÌ¤|þO0ÆcŒ1ÆcŒ”ÿN`Œ1ÆcŒ1ÆËON0ÆcŒ1Æc¬@q8ÁcŒ1ÆcŒ±ÅácŒ1ÆcŒ1Æ ‡Œ1ÆcŒ1Æ+PN0ÆcŒ1Æc¬@q8ÁcŒ1ÆcŒ±ÅácŒ1ÆcŒ1Æ ‡Œ1ÆcŒ1Æ+PN0ÆcŒ1Æc¬@q8ÁcŒ1ÆcŒ±ÅácŒ1ÆcŒ1Æ ‡Œ1ÆcŒ1Æ+PN0ÆcŒ1Æc¬@N(R¿àÍûD?ƒ)3çÁÕËí;÷Èsº»‹Û¢mÒ¶ih_¤ûÇcŒ1ÆXaPPõfÆËO¹ÕV7(œ  Üø þó‚0möœ8s¯ßÆË–Ë+´-Ú&m›öö%;/:/<}ñÑ7cpêÌyÑõ[1â4érŒ1ÆcìÛ§Yožê?_¬°?yö ïcì›C×7ºÎÑõ.»mu½Ã õ–‚ýáÇdóóííKv^tnˆÿ˜Œõ¡[ЮCg3±ÄÅÌ´¢yÿttĆMÛÄu¤å0ÆcŒ±o‹f½yÏþÃø„„¤$¥|fŒ±o]çèzG×½ì´Õõ'¨k% …!˜P£}¡}¢}“ÎË+oÞÅcð0?˜[•Y±°.‰á~c—ûËcŒ1Æò—ºÞLtª¬Kç3ÆØ·Œ®{tý3´­®w8A]4(ýN/h´O´oÒé¹-Q‘Š€(^²œ,t0TÉ2´t…˜.I·ÃcŒ1ƾnê®ÍL0ƾWtýSßÒ&§‹Þá nAã=H§4Ú'Ú7éôÜtûn,ê7úU2äT£_š!öáSÙöcŒ1ÆØ×‹ê¦GNDʦ3ÆØ÷„®ƒ†´Õõ'hôÍüüR_´O´oÒé¹%t˘YÚÉ‚…Üb]¼4öì;(Û.cŒ1Æû:QÝôÅ«·²éŒ1ö=¡ë !mu½Ã zDˆtZa‘Wû¶|eˆ,LÈ+Ûwí•mŸ1ÆcŒ}}¨njÈ pŒ1ö-¢ë !mu't8qê,Š[ÈB„¼bjYW®Ý”ícŒ1ÆûºäEÝ”1ƾF†\9œÐâþƒ'°+U^ L>{÷‚ϲyúò4 »Ã 0h¹l^Å*?âÅë‚ëxêl&L›-O}в´Ž´ÆcŒ±ïYn×McìkeÈõà -\Ý=eÁyöòMÚ24H¦C—îióhpK'ç^â; #FE·½ðËï-ÒæÿÝÞ×oÅdØõ–ncèðѲýÉk“>aV@ x/Ü[‹çÿµO01@+šGËв´­KeHËeŒ1ÆûåvÝ”1Æ2S˜¯9†ì‡—¯Þ¤t¹Ê²eÉÕè[ˆÿ˜,›®)êr´l©Y·l;äáãg²eó … ÃÇLÆòsñüfãÁÿz#öôCËÒ:´.•ÁEá~ôNŸ»$›ÎcŒ±¼““ºé›w ?v+Ö†büTŒ7 —®Âí{eË~’R>ãlÔUŒ™4»÷–õäÍ ­CëRT–´|ƾ9¹æä5Cö-O‰«7î $t[ŽDßÌØË 3†ì[V4{Ch*[±šlÙœªõÓϲí¾ÞƒdËæêõ@áõ† °áà)ê…zQÐ:´.•AeIËgçÃÇdtv…ïÀŒ1ÆØ·,»uÓý‡£kOY[mÔÄéˆ{ÿQ¶Þ·ŒB÷¾ƒqòÌ…l 2JëкT•%Ï”/¯€‹c_\L•Íc…Ÿ!לäOÿbãÖÝxøô…l^^0dßò$œ˜0mŽìbj¨)³æËÊÕ…–—NËê± 4½Š{/['»>$$ÉÊ×ô.>ï¿xh¼:vÏÿÛ_ cîþýqá.ÆeŽ–¹÷ÿ+×Qö ðËú¦Æ øôó[ÙÀ¤Å"Üþ¤e¾Â¡ŒIxH‘ÍË+„ …‰ þ’ÑGÒÍübFŸ?kü:;IZ–É{ ìp/"Â5 ˆv&Jçg½¬âyú7( ó’Ðwû $ËÖÓΰ×ÿ çÛ£‚M)”o»g“¤óYn3ìüœwñI;i&Þ ßqÒyŒ}O²S7]º]\ϱG¬Ùˆk7cÄ={‰CGNÂwô$q¾—oŽŠN]äuï ºÏEx¼|½‚@½(\N7•AeI§«%><‡Í§Ÿéý½ù­ápâëfÈ5'æþcqù½ûãÁ“¼( Ù·< 'ž¿z+^`¥½!ôµnÓƒ’Cö-3Ú©Tûéç&HHʽ†%¥¸Í[µ•mGmÛÎ=²ur hIãFÄþO<ü_oÜý?ý1lHG4¯Õmý…¿~Ö®MÿÄeFŒpÀýÿ«¼½ƒÊ ²¨LévòÜûKXŽ»9 d¾òp‚s,‘MgÚŽÆŸöÀA;íË&ž›€Z¦ô:,Qux$>ÊÖÓΰ×ÿk:Û)·mÑ Ë_þK3Œaç§à¬Ù°EüNÙ¸U6Oo©‰¸¾{.Üœzaȶ粆‚"ñ9NïX… ÃÃÙÙ »{ ç i˜»ã:^¤h)±`hÝôdäEq^^>¸÷à©l¾ÚÒ•ëÄå|ÇLÎV/µ]{öcÛn²ký}aïèƒñk÷©¦ ¢«¯Wè¶Œœ¼V5*ƒÊ’NWúŒëk}áp^ïïÍo ‡_7C¯9KV(¯%®žñøù+ÙüÜdȾåI8‘ßrkßÚ¶ë( ÔÎ_¼"[>§bî?D#sÙ¶HoÏþ²ås7Ø2ö¿½ÅÛ4.¼ñDóZ­PDz”øõí´£yu,Z eÝÖ¸øÖS\—Ê ²rë\âÃþ([m"’åórä+'^Å}€}WWD]Íéãi“pÐçG3®ŽGògß3¥ˆÆ„Ÿ­ðƒe/lÊ´án¸ÂÑøÓ8h§cYÅc„Œ†÷ðØ«ÿ80†½þ/xµãûblÈe¼J•Îg¹Í°óS0è—\ªø»y ÿ}Ÿ([&KIÏphátqîƒ.Ž®òp"5‘>pp)Á;°ýàq8†¥Ó|Ñ©‹nŒE¢´LÆ €!õ¡I)èÑÛ[ì1ñàÉsÙ|©¹‹–‰åï ;$›g¸/x6 Æ`Cì¿Zæ4)×Ë „›”W¢ÿÓ4š×ÀîO´kÒQo½ÄuÅ2„²rë\è/Õ…qU'4ѯ½û Éù/ ï÷ý¼~($áD™±øÑÄ,o‰; ð[7þ4‡’}±+Óר#œÈ¦ôƯ ~Ÿw_ök5+X_C8±jýf8tw¡©'¤t™L%ÞÁºQ^èØ{6DÂäîZ‰ÏÔs"¯“$×¶OÏ:ÂöC·á÷ž`…€!õ!º “–_¼S[埩ä›êÛ&Fàù§7Ø1Î îÁ7ðäÒL6]œz¡‹ÇhL 9‡Xém©‰¸¾†ùÀ©»›r¹µ‘òå4è,?ãîÖñpôÝŠ[:z}h++ùÙIÌÐÿtÖ¼µÅ “©¼ÿ‚W7aáD?ôtqƒCÏè71[/Åe†&¾¸Œõs'Ãý7:vŽK_ ó_#5æL|Š#ë`Hß¾èäÔ=Í@`x,Þiÿ©É¸wj¦L@?/Oq9çSƒ¸´å>ãÖ†‘èä† qpÉTôîé†N7àšêx||~¡ §¡Ÿ‡:uïﱘ¸å&ÞÓ¾R8ÑÕ Î<ÀáÕ³1À£œú çÐÙX~â9´¼>Vxh{oëCPИ,yP²oN¨Ü{ðX¨Mœ¢ûþ´œš3o‘l{j4.…tùÜDǵ¤'o<2œ Û6êoFeZŠêÙ¶@MÓæ"ú¿æô¿·AÔ;/q]*ƒÊÒÿ\_†Öc´›=×® [XUh„¿oÄeÍ{ãocØhR­Ì̬`jWuÿꃉûž"YyíjÀÔHó¸Y T¿ášŠsÁ¬ÙÜxw«üª%ŠÃ¬Î8œ ãc,ö C§MP¥L ˜X•CÕf½1-üEz¥_ïpâ3 „G›†(ck#ó’(õcsØû„à¼êµˆá„iôÝqëF:¢~ÅR05/…r?wÁðM1ø +3g %Þ¯*nˆ×W6bПU`œáøš¡hyì£JDr$†U·Ä¦ 0꜠[“j°4+>ûRxq2ꈷØ UÐÓôFâ~ô²£Cæ°ó8ˆxÕôø»û1ÙýoÔªP &¦Ö0/U ;ø`êæëx•š„;&¡]µâbc\sL»nÁ[UŠ7—±jtOüöcEXZ‡u¥Æhë„#O%£s'=ÄÎ ÎhPIØ–ETk= «"ÑÆ‚Ê̺ñ—ÖP4o‹€;°{‚#j—±ƒ‰ueÔu-wiËvÒ‡¢åbã©åpû½¬„×b[­ÜÏãEZ%D{8¡½›Š‡GÁ«]”·+#S¡¼ÊѺ÷T¬ˆ|-î“f8ÑjÁlß]<>¦¥Q¡IŒÝ›v®´oû ž®êSáõ˜þ³wïìÅØnÍPIØ—:íG`ÕÕ„Œ¿d%=ÀÚ}¬*¢v;?¬½z“›XëuDŠ—8ºdì›ÔB +[˜•Þ7ü|ñ½¸-E\ú ïSñ˜VèƒÐçªFíÇ(Œmh£œ^ÂA÷è=’Œ˜ðåîú×ýQ(Ϧū¢ö_}1ãàcŠYÆ×zçævŒìÔemla*l¿q·©Ø›‚WWû]C”¶¦—¬ßz-@Ä uå_]†Œ~šŒÓONc¦Ëï(km³ÒõÑzÐzD}Ho€k?¯D˜ „sÛåKØ Ç±ª5uŘͷñNã8eþùÒr\³@ƒiE^¸‚a±|õzqœ¨NÝݱ|Õzq>ýK½'h:ͧåÎ^¼"®'-+Mj<ÎnÙŒ#O>Añá&tÓNh•òk‡¹ÂaÜA<ÉÆëa,·é_úOøÏ—¿pùºlž.>¾cÄuèÉÒy†ÑNÐô3ѹ«/VÜÊøúñÊjôrêb H¥p¢ìû‡Wßɘ¿å09‚àìè ×yçð2í3™‚[[&ÂÑiü–ïÃÁ“g¶iú9»¢çœÓx®ã³«ï±T¼9©î>˜{.Qk¯ ]e)^"úò!L÷è ‡ ;pþêM±çéÝ·t<¾àí¥µè×Ý=F-dž}'pðÀNðŽÙ`Ì:ùVç¶R±X3Ô ÞX½ÿ Ž?Ž›×bÚøå­º¾§<Çž)ý`ßk¶ÑǺhœ=1jïsk}NÅðy°iÿi;y«§ AGÇÂëMRíƒ2œp舠Y>è6x‚Bw û|C¼Î+^žÁtOWtp‡Yk÷bûî]X¹d(¯³ÊpÂÃkÚl ?ƒÃ‡vaޝ'þq.¼ôïÉòŸ¶÷¶¾4Š—oroŒE5Cö-O‰WoãÅuN¼~/+WCöM—3ç.fhðhZ¾R¿$;;¶lß%Ûžš!O,ÉÍp‚nÍP‡tëõ’ ¢‡C;Ìê&rîÐNœFóêÿ3-œoë08œøW¹¢þß0vþ¬ß¶ÃÛ¡¬‰júU5‚°`MÙþ‚žÓC°aË,[4ý;wÀ°}Âû#õ-¢OÁÔv%Q¬œ æ…ŸÄá£'qâ5J”á„iõÞäTvõ1hÊlLYzBYqM<ƒQ4G×as°h턆Ìsý’(jçˆàGª/g=ɤ˜%hic íÆ`þºíØŒ~}ÐÚy)¢U¿6(É2¨Q»6ju‹¹«·`ÝÊ9poREÍÂ/2½1›S±ž‰çáöÝœ=rìÀÀª(ªå}) 'Œlðkoo4´RÝ¢dò|O2¬QƒjX©‚sñv'åÿ-PÆãâ×0¾•l_ˆ:œP¼= ¿Æ%•ëYÀÔVhð+·cZ{0ö¼TU„ÐÁ! `’!t1G± 5PÖ”–—6þäÒŠFÅQ£~=I@fã‡",N¹=ƒŽƒf8aû#*–²ÈøzK£U`Œê—möFl⥙hh©:?tlÕ·“•†}ÈK±R“¾ž%ÊU¯)yMÂ6ÌÁï´úW$mÛNolµm€ÚU­%A’pŒ«xc×uƒ;áôœ‡òuPÙZ¿ó’úÛ<R•!¬kU–æÊ÷Nëfw†*í_ðlg_T 7FV¨1ä„ð~IEô‚¶°¤õ„cÐr‘ú˜~ÄÁAµQ,Ã~«_ 9¢nh¾Öú¨UEþZÍê´Bã²’ó'¼ŸK8mÂc±ž^Æ&5Q§n)IÂ{ßy ª®=ÚΫX]Ý]¸n*ÏgÓ’°¶V}ŽŒË¡õ¢[Ê@%«Ï—ô¸êáà‘“âu†Ðmã§ÎQúþ§ùô/ýMAì%ÌW/KG”–¥âý ŒÏ,œHMÁÛ¸÷xùê%îFG"tÎpté5«¯én”0–Ÿô¯)Lj¢åï?Êú–µé³Šëܸs_6Ï0ºÂ úžÁ4WW¸/¿©q«C Î-ˆ® q„TU8ÑÞÑK®iÔeR“pnétè:kï+à ū£×ÃýÖÆh„½ÿ"vçD8tŽ•w´?æS¿c©À¥¾pšpt„™–¥¸¥ýzÊoëøôë|]ÑqÈFDköîP<Ö±½ÑÁk.èèõ¡xy~Nns@–Kç“w‘AèÑÕç5®]©ïp`º':xg>àtÂ9Ìpí ×e7TßaÊp¾³ ºŽà ë*p~Ù tè>ëïjï +†]\Ð™Ž¡F½WñBxÝ]áµö^æßɬ@é|oë‰WLexxKû.Ï-†ì[ž„“g¤UD²‹.ºÒru¡å¥Ó µkï~I%2ÝÒàÕ²åsËŽÝa²í©í?"[>7ÑqÓN”UöŽpíÜ‹Wt¹tj'NËpB‹Ôçjg ã†3EÝÏјØÐV®»3éY l$“ÝÖ¡ 'Œ„Ê·Í_ • ]O.þØT6)…n[T =É›]afö;¦DkÿR%b8!4”-Ú.Ãmî‘ɱËÐÒÒ µÆ\̲k ¾¨+5õœN7”âS*^®ë¦l<W‡wx’øä¤”•_ êpBõ~-bSö ¿ÉËpðÉ¿5ÊiÙÚâ²Öh<å*Þ)ñäÎ9l^0+/)¿D“S±Ç«¢20±tņøTåþ|¢Æîg\šö»ØH-bZî›å~A\Ô|´²ˆFÖøyêuñK5ùA0Z«‚”b•:aÊŽ38ud3F¶ª  c²n§7…í Ÿ‰¡›Îáò•“r­§ìibT\xÍ•¿Dp4ý?Y¢r·ÅØu §·ŒÆ¯ÅUû\u(~̸læáÄ¿ˆYØ&T¦ÉOþ‰ß!æâNƱ÷Òõ„òÌêÀiaÎE_ÂV¿–°ÿÂþ =¥ª´iÛ¶Fc»õRpÀä]Q¸rùfØWV[£2è±]9Â|òÃUh+žsUuŒݑ8!œ‡–úŸ‡Âg¶ª:Ø¡¡o פW'áׄzØŸ·¿Wà}v?½ÀúÊ}(bÙ ÃÖ S ÌaÙb®h\;’îíÀäiëqðÚS¼JLÆãc“𫸟°ë³_u-Ò|­ºtƒÿÁ«¸tr5œ«§‡&5û 0⢎¡seåg¥ˆu¬{+/ôž7–»ŽËg6Á»q e¦M0á²òÞ_ùy¥_ w¡‡€XÀ¶µ?N½Éϰ½o= ç¬hiw„¾ù¢×çËPôÙ7Å_Û†ò“Î×Dói9Z^ß§eN(Þ œnª:ƒP™vˆm7$=s+@†Ô‡¨‡-OOçÎÓeôÄéâ:4ê¾tžat‡Ô›,2pì=–ã¬úÖÁÄ+˜×Ç=D)¯‡ªp¢Cßµ¸,¹•"ñæzx8ö¨}Ô»@øN>2ºÇЉø \_ÕÞÞE¿®½àfà8’ïÃp—á¾™ù5MgY:‰ä'aêäŠA[4¾¿E_ðúðltr„ k:êI7±˜nˆWã´Œeñ gû¡CŸå8ý>ýxØSб«BÄ^}ÒõTRîaå@Wtž{AU¶*œpŠ¥Ñ’^Š;XîÝ“"4z²d¤ 'ÔçKs]:6®B½à’–×À ïm.[#–ãå3<óžŽ2dßò$œ¸zãüÆOÅÈqÙ3jÂ4œ9IV®.†ì›.{ö+ŽÚÌðŸ'[>·¯Z+ÛžÚÞý¹1Бn™…në0k.Ò¼­#O‰Ï)óª£j#q„ ©ï°Éµ"ŠoŠþk¢ðDë˜Y„&u1ô„~½’ï-B3s[´ ~¥¼(KÉOñ$ö!bÒ<Ç«$áb~eZX£bÇÙØ£½r¬ 'lÐz‰äUbß Å=ht™Ï>c‚Æš 1'¤ó²#nC÷´pB6æ„F8QĪ%¦DeüÎFyrìRüi¡œfRå [y ÷e’ðþè§'$cN¤ÜÁŒßèV37™«iP68+‘6i³±ÂyŒÛÜKÙØ„'iû•xi:ê©pÊÆ_*žß¹‚È —Òœ~&Þ—™ÞP´@)áÜÑ4*Cñ,íT n«;Ƈ á„YKÌJûIˆ!ª_ôMÁD±Áª- ÐÞˆ}ÚÖâk¶D©fƒ°àà]ÄI·ôõÌan¢úu_ð~;ºÙ*÷Ó¬ûvÕkÕ¶mÍÆvÆ`!ሯ²ç‚p ZªŽùÛ­niç¡õ’ôã’xeèu>áøð:Êcbá€%Ï(¤¢2þÅý vbSĦ6~P–›ü`-ìK+ cseO‡"VÍ1åR×õ ¬Âò¦íV©~‘Ó|­6hš6FG*ÎOl #šnTÿ¬y­º|Â1_Õ¾šÿ…¹÷”]…ÓÊ0*!é•ò·;=QÂXõžQ]3´×÷»-GËKçë’U8‘¢ˆÃKWqöÜ9 ÛŠ¹cÂÁi &ì{•gV(R¢hù-;÷ÉæiCºmªƒcOq0Mé|ÃdNü'>F¯CŸ®}1ë´òVãç–¢G×X ¾vªÂ‰Ž4Îä{Eñ&c„Ïq¿õ±Â5ë3‚=ŠZi ”²<–©ñ8à —€³x­£áeY:‰Ä+«áêèŽi'åÇ9ñú:¸;ºaÒQ]·aÁ‡»˜3Ì „FßXy8/Õ!NjvMè¥åX¨8Æõ_©Éˆ9¾³'GOtéî‡n½`O=ÒöYNt›ýïÕ߉*I‘˜ÖÝ=—]×ùc˜r@LOÌ9' 6b<ÀŽó£øúZˆé|o@ý4 ÎÎ}÷!g+ÖdȾåI8‘ßrcßNEž+ŸÚø ![>·Lš2S¶=µÈóQ²ås7]áõœhXZù/ I¨Ç„zZÎÉñôlFº´C½j•`kcSK[›˜g/Oc¦SX £*­à:u.¾Ö¬àfNXtÁŠW’ 4I¸‡þ>øç÷(_º4Ì-‹ÃÔÂElÑf¹²›»4œP¼^Ä1 TLaŒ8¢±×C}Ñ´¼ Š˜–Em?~”¡·‡r@Ì2pÛ#i%ŸÇÈZV°vß—+áÝE瀞Ö!—ú†&­—*¡Ö˜¯»Q¾OÞ(O}ð1Í`-6ȈЀ,÷zÍ=†Ø´óšI8‘¯rÒ.ô‰=r¨AU/‚ºâí'ie$ìDw1XP5þ„JC°½mÆ2~š„Húu^£¡ØxÆ­ô_÷“OcH5Õ1i$>ÞÖ ã Ùè·éƒ­i¯ñ ¯pP‹Êè{ˆÎ…¶€@{#6%á üÛTD1õ-F–(^ßvÝK{Ÿ¦¯g…:®¤W^’ŽÁ»’òئï¡mÛmaû…§¿_/«ƒ4_ôP8šç¡6†œÈÎyHÀº®ò1H20ÿêÛ´„JÛÍÅíU½@èØ þxÕ/jB÷⺠èþgC”+acñ¶ ôòLÚ«*ßš¯µŠÆk^×"U/Óð;§î%ðWf4…1M7k ÿº†iÞÖQ+Ã1Hº>%çB~^5zÄèbd‡n[’ôü|e¢Óç(»—'$f¼FÄ'&‹ÓgÌ]dðà¼Y†RŸâ°F_tp™‹Co ÛcyAÿú=½í±¸<=FôÃGõísºmÚ±W\žn›’Î3\æáDJÊC„ u…“ÿi¼È'ç÷…}¿µ¸¬¾v¨Ã‰‘{Ä4×U¼>ŒQNéáÄí~°¶³ôô-\¹~[ân¿”$«c™psúºLÆæ´ë½n:ËÒN¬BÏ.NȃìÄèµb81ùxç,5 ±aÉT_t¡ž^Ã×ã]§„ï·ã{¡ƒ÷R¸&=‚÷ñ(®gŸqoçtuôÀÀ…a8rù.î_Ù²q+œzzʦúu×à§v|ÎF8A]Æ#fÃ!³.ÖŒå#}ëCjê[¢ç.\ši˜G†ŽN½Ä^·ïåll)¥, #lç$8¸àÐó ˜í®{ ­« 'þñX‰ó’°SÝx³ÿx[Çëptr,|Fõ»½K-Óc™ú;'xÀ#øF&·ëQ…ýåá„âé> srÅÀMÊ[5Ó×ù‚W᳄kÎ,½®ï5ç3žŸ_…¾i·‰¤àô/üãˆcâíš:¤ÜÇJWtµ5¯×ŠôÒ3œPÜÂ’¾=á0ñˆÎk>‡_7ïm=h9³NCöà æV%2T€5:s^¶|N]¾%ÛŽš•m)Ùò¹Ž=þSóišý¥BKT/öVlvFÐÚî¨aÔ\œ–áQ¢OëÐÿQ¢ªF¦­V¿ÐüNÄ&—’Z‚†tîl„³Ðø3k¿ZÕ½Úðp"ù~ þ0WŽó ùX¤¤«3Ñ ³pBËþhõ)GÇÿ Óô_„ó#œ {¹{¸÷I&—]qU âjb÷ð ó³ '4n“øÅÿNZc7½¡* 'ÔRñìÂjô¨¥|’B;l¿´é}S)m̉šÝÒSncÚ/êÛ:üqYúØ3 q›\U!–@»•ªs-0äQ¢š·@غìÀkÕôä‡Áh¥ê>oÝs—ø´ÃŽƒÆm¦¿bâu!û¼«+_»Ù¯˜t•*BÚmXÉþŠÇÕСh(:IO›è:×ËV8¡U½My8¡yì„󠺕ê3Ýâ³-̵ì‡Ì'¦¼Õ¥ˆ¥=‚ë®Ì“ç&£¾xn,QªrUå š&5ÑgêÞÚÔ·XÑQÙ£ˆM',S—§¸‚±õ”·uh'4_k6à #Û ·¶¼Úì[1D±Å_Ëž‹û§íü¼ÛÙG¹œq98oÓwð*]Ÿ¯ì›ê?#ÇO»—«cLÿÒß4Ý1¤Ô²Nˆ ,G_Kž,ÀXAЯ>”îåëwèæê%®7pØhñé6Òù –®o“¢ezz Ì…'u¬Â êqãœ=0aѸ8¥p)Js0Diô HMÀÉ…>èà4¡•å*^Æhçžp™uO ¨Wé>–_wv \ÝpPýô‹,è,+%«¹Âa’¤ñþéBGºÁÞg=®hþ(¢xŠM£3S«¤+pwEŸU·‘Hû2ÎŽ}à·ë©î†¿BõèVÿÈ ÌǘÍðîªå¶máÄçdœYäÝ'bã}íOÝàpâë¦ó½u0A?2äE0A Ù7'4üÝÞA¬€jÓ´EkÙò9Õ¶]GÙvÔ:vî&[>·Ñq{þ_‹p_ÕsâÂOüY·5êX¶ÇŸKþ‰ÊÿÑ ‚»aN ªüÇâ4šWÇ¢Z×ÿ ßzŠëÞÿoo±,ýÎE2¶º–B±ŠCp@ó‚þ>ý«Zf¼­C¶î¬ébãßTOÂHÁ¾þ•P¬dlÍp!ÖN$]ŸFf6øsqúxtA¿½èo˜er[‡üuè–x~"j™–F¯ÝÊ_-ó#œ8yA¼GLŸ.¡úŠßí ±iŸ|v òÚU ;…›šOë(¦=œPÜÁÊž5UOCHo”'ÅlÇäékõoèÜ'=F°c9å†%„ƯØxú„ã#ê*×5©ççpùòY„¾…7©ŸqyF3å&Œ+ Õäp܈KARòG<½{[MÇÊ(å—­xÿ½øä sUï…‡£q5*þ]~TíWVb͆"5ŒÁ}YN9„9NµÄi¼¿ƒ•cr2„ô$…¿§!ôÄŒŸÅ0ÄŽpƒÄü‚'û`ÜÒ}8}ï=„ó”ø< âí'æâ¹{Zá„r0Xåq1®é¥§bpûúI,r©«êÝõyøp|$ª©žÂQÑ1± HLIAÜÓ;8ºÓ¶¨ÖO¼†)¿Ù‰û[´¬ BîœÀÚ4p¥ð¨1û¨”ª‡†øšJºa£øT‘T<>8uÍ•Ç>Ï Új=à¿ç,N^ƒÞõT!‰YSLUÝo¬íü(^x;“pëôÅòó/ð>%ñoŸáRø:L¯q]¿ÏWöyð…—/º»õ…CwwÌ X,þK{ ôEßA†ß©3œHMÄ«·Z*ÖI±ÁÏz/ÅÍcD¿úPFŸ¿¢£uIÿÁ#ÅÞ#ÆN{K¨§ÓgK 1|Gã}Ž;Ÿu8‘’úGæxÁÞ±':úInßPˆé1îž0wS8FƺùcàÔŽ£4ÆHÁ­m“…énpŒµ{`ßÁC]»c†.·dé<–É÷±zh ÚüHç­ R:Ë¢ÆûBoüã46AøÑ£8v‡Ñùï£C1й'œG.Ãú}'qðà^Õ£Deú(QÅËÌð›‡ÀÐ}Ø{üOw8‘òñ†T·‚i}oŸ¸[·/a×O4¬]å³ÑsâýÖhhïƒ1sW`Íֽ…~.Üš”F± }°IÕ3$?‰I3Ä{»¥ÓsBñdJ¨º€«©iáDʧ'î¤lQ㩈©p,M„ËÀ®„²A¥ù´å­BcÉØJ{Dl  Î*ýÃÅçsS™o \Ú}óJiççÃELiQFëãO0* ·½êÁ»Þ#làOÝû•ûgZ£!ª‰!BÖâô†¢lJ Û”–U,ލž‚aÈqÐlô«Òud-‹¶KîªöM[@ ­«Ñ`¦í›XÁHݭ߸"º¬×=ࢸÿyNÐyØÛ¿Ž2ÌÑdNP7º¨ÕðjY¶æ–0¶©Šú]¦`çÝ+˜Ô¸¤ÁáDâMر)*—^‹Pñ7+U¿8ǺkéOîÈëp‚À¤GõQï 鼜ùŒû§Âáç*°0³‚iÉZhè0'é—֬ âÕYÌ÷l‹ê¥ŠÃØ¢4*5q‚ï†hœ˜ü Œ4åÉÏŽaFŸ¨W¥,ÌL-PLX¶ìO£çÔ}¸¥ù«nê[è&UKÃDhä[WnŒ¶ã§o;á.vÌ„vk¢„µ5ŠšÚÁ®Z3t´5ÊJ¼‡-£» N9;[–G-û‰Øû+;Ñ“=²n§7mð§8V l‹*v60¶®ŠŸ»Í@؃Œ_ìú‡ôF¿~šxÏΡG“*07³Eqá3âtQ£»©¶€@[#ö Þœ F¿ŽÍPEx›Ð{¾j4ë‰Qo".ÓÆïæ]8Aî`½¯j•-Së*¨×y ¶…ûãWÕ~ü6'óó Jý€‹¡Óá֦ʗ´®'Ö°,û~wƒ¥‘qHŒY†¿Tš6žžþ8¼O°¤ƒ2Ì*bÞ#O}ÞÓ±mŒð¾(+”cZ%kµCÿàËxtØ•Lò0œ0mŒQÛw`dû°³´†YÙFh?r;®k¼÷už¡ì'‘!ðsù 5+”†©p~Ml« VkŒ ½!Vlõþ|eÝ¿qË.ÜŒ‰•Í#4ægv½6:ÉÔwˆÚ»Sý†¡G7tèâŠN½aàäØõš+άÐÈiÝ””½tí¦8Ð5yøôE†ùb@á« (FMœŽz>¦7{”OÚèè< {¥·O¨Ä½Ou»mîËé±Ô”›e1V˜òÞŽ¾}O\žn¿ÿè¹l>yŸ˜vý‘º}Ë  Ù·< 'ò[nî›}'§´€àצbû®½è7`°,< ik7lÆù‹WŸE7ú³.aͺPôõ$+G'ç^²2òʬ€@,ß81RÐ’zAÐmš Ók™NËÒ:´.•AeIËgùƒAºÀиÒy,÷én(²œJ<;?Š='lñ·ú±¾ß¤ŒÇpÍ'Ç0ƾz¹Y7Õ…>¾cÄmÍ –ÍÏ-Š—àå ç¹çÒí4ùNÐÀº†œÚPT–t:cßC®9¯ßÅ‹cMЭdÒyyÁ}ãpBâPÄñ´ÀÒ¦$ú "N;. Ô¶lß%+Gíäés²å³râ”öçÅç…IŸ0|Ìd1\ Þ4°¥ÔƒÿU’N'´­KePYÒòYþ nžD:å 'rAÊ=l›„ Goâþ«H¤q/ÞÞÄ2gåØEÌ~ä+ßòÀ†ºz_0ƾ¹Y7Í 91mö‚\ [”šˆ;ÏâÈÑ}X4º:У:cµ\“ó!œ3iNžÉyÏP*ƒÊ’Ngì[_לì0dß8œÐÂÕÝ3CX0qŠòB° P$Ôoô«l}©v:ËÖÓ…zdH×Ïk*P¯:Ž4n lIOÞ ^ÚÐоÐ>eç™Ç9uåÚM½Æ›P[¸—®\—MgŒ1Æcß–ÂVofŒ±ü”ݶºÞáQ_h)¡Ñ=$ù9Øm‹¶IÛ¦}0ôÅ2ÆcŒ1– ºÞÌcù)7Úê…„6@]3èÞÜ‚Fߤg—æ5z m‹¶IÛ¦}0ôÅ2ÆcŒ1–_ ªÞÌcù)·Úꇌ1ÆcŒ1Æc¹‰Ã ÆcŒ1ÆcŒ('cŒ1ÆcŒ1V 8œ`Œ1ÆcŒ1ÆXâp‚1ÆcŒ1ÆcŠÃ ÆcŒ1ÆcŒ('cŒ1ÆcŒ1V 8œ`Œ1ÆcŒ1ÆXâp‚1ÆcŒ1ÆcŠÃ ÆcŒ1ÆcŒ('cŒ1ÆcŒ1V 8œ`Œ1ÆcŒ1ÆXâp‚1ÆcŒ1ÆcŠÃ ÆcŒ1ÆcŒ('cŒ1ÆcŒ1V 8œ`Œ1ÆcŒ1ÆXâp‚1ÆcŒ1ÆcŠÃ ÆcŒ1ÆcŒ('cŒ1ÆcŒ1V 8œ`Œ1ÆcŒ1ÆXâp‚1ÆcŒ1ÆcŠÃ ÆcŒ1ÆcŒ('cŒ1ÆcŒ1V 8œ`Œ1ÆcŒ1ÆXâp‚1ÆcŒ1ÆcŠÃ ÆcŒ1ÆcŒ('cŒ1ÆcŒ1V 8œ`Œ1ÆcŒ1ÆXâp‚±v÷Á¬ Ý¿ñÓàìÞí;÷È€¦Ñ~þóƒà=ÔŽ=úÀÑņœKqâ ©²õXÎìÚwXüaÕú­²/î¬Ð:´.•!-÷kŸ¤ÀÓ—odÓ+h|=ü~=xò#ÆNIûuwäø©xòâµl9ƘÜ÷\§aŒå.'¾c‘®À½ï`Y—;)ïa¸pùºl}–=Ô•ÑËg.\½)û’Ö­KePYÒò Rò§±aóNŒž8+×mBìãgæS01rÜT„„n“­ËXAâëá÷mʬù²sM¿èJ—cßEêì ǘ‰3Äï&)¿ñS±qË.%3ñ-×icùÉï5Õ0X²—®ÝÄÛøDý?xÍôèÝ?m¹-;÷ÉÊa†¡_è X[WGCQTVaúµaѲÕ*÷Înýð.>Iœ—˜"Vöh:‡¬0áëá÷¨û c77$¥|ÑÿištYöm Ù¨üÕ>+£&L{VI×ÿÞ}ëuÆXþË·pârô-ñWUétª\¹~[6ý«¿Ž–V¨56 ‰Òy…˜fE|]èv|LJ‘-£F ÊUë6¥-¿}ÏÙ2L?tO%Üüº EeQ™…á~ÍøÄdq_ºõòBä…˘8}®ø÷ÃÇÅJ:˜ n^ƒÄnÓf<|úBVV¡òé1æ·²I‹E¸ýI9-!|ʘT€ÇÝŸöuø®‡Š7ûàùcU4ó¿úU}Wå•»±ºu7¶íÞó—¢13 P<Ÿc'ÏJ[f줙â´Y‹qîÒ5qYZ‡Ö•–÷µQ<Cÿea^²únd-Ë|/¨Çgº+Jø>Õfø˜Éâ2PÐ5@ZÆ÷ê[¯Ó0Æ Fž‡ô‹Ä‚ âÅ&âøÙ|ªÜÑ s·_ÃS…tù¼“xnj™Ò5ÒU‡G⣖e¾êÐüñóW²yjH 5Q\ŽþÍq@‘r“[§×…´*üuËo½NÃ+yNPذh™xAïÝo^ƽ—-óìÅq-CËæ8 *ËûüšÃÖØ¦›£«ÏDLž5Fx£C“j(ç¼OSµ¬—_Y8ñác²Øe™Žµ!qµ'Äu½||³yÿe žßÜŠ>?Z¸ñ„Ý‹ÿ*Ž[n _hTjé—pn¡² Ã/ —®ÊP‰§nÑô§yêñ&hºú¶Ž³QWÅ¿é—KiYÙ¡ ',QÎ~2—­D`à|Œ÷醺%-Pĺ&œM”­“%'tR¼ …C‰¦˜rí³l^aWð×C‰„ô«l #S+”ïwÒùúø‹9ÍK¡Íò—ò Nøn•MûÎ|ÎSÞÞèèÔ ÇO‹×+ê!±ç@^Å}-OÓh-¸|5†}WWôô(–%]>3ñQsð{qs-P3ûqŽ&Ê×ÉܼuEq#+4˜r]þ]ªˆÆ„Ÿ­ðƒe/l’–­xŒ°€Ñð¾{c¿Ÿ´Ñ'œ ¹P¤¾Å© K°p±Ò¼ahfg“&ÞðWO[¸«O½*´½Z¾—: c,ÿåY8A!ÃŒ¹Ê®’®žñìeœl5šGËв´NöŠqwµ#J[¢‚ãr\|/-'Uh åb%ú+ 'vï 1ý‚-§:/ýË8yA6_/ª_ LÚãAnõ`ù Ð OôØ,épÏ®bíD4«Uf6°,ß­¬Âñ§Z–• ² Ã@RTaSî)t¼~'ý—HBu‘U‡t«—zYiYÙ¡ '¬Pwâ• ŸÉøkóð‡µ¬7ᙡá$‡:ü‹ç›ÝPÂü÷¯2œ =º®‡4ÆÄí{eÓ5Ñõ°ÿ¿œ]5¼Þé‰R gS˜”ñÀÖ·òe²’»m¬lµ‡ Ñ7cÄóEaƒtõîÚ±÷ ¦úÏí ;¤õVTì—ÊÐìe‘µls+‡¢ÅÌPĬ:ÏÜ‚]{v`eà ôwømgßB’l,¤¾Ä ‡R(RL{8‘pf,~41ÓN°4Ú‰Sg/ŠÓOŸ»”aY1`¯\ž¾ÇrPhJ:ïJ–°pÝxé¼BJ¯:ÊËG‘˜ëò3,ÍÛcîí²ùÚ–: c,ÿåI8! &²J¤ -C¿hä( H<¡5,Q¬šÂâôX?5BÆ c“akU–åêã·YØvGþëjÒ“ã˜áÞ UK‡±UEÔj?.oF[y8{ÓÝÛ FÙ’0±*[ycî‰×ž€Ÿê/ßã§ÏËæékgØA±ŒùAÁ²yzÑN|ÆãcðhÓelm`d^¥~l{ŸœW-Æô@“jå`ffS»ê¨ûWLÜ÷T<®I1 ñ›Ð o¾èaÆãœ‡`{[7ž‰K)šÓßãìêQèШl-maUé78ŒÙŽèe²Ø¦ìµeÂoü4ñ¹ÞÒ/àt ¸²ÒõÿŒÙ;Îâ\ô Ý:W´F…~ûñ@¶|FT6mCºÝ‚@÷èÒ{„½(G¤¥ei érÙ¡+œHI}ŽÅmm„kƒŽ&Ó4v{”C±ò>Ø—”±Œ§Ç º‰ºoSu­Ïf8!.cåˆU¯ÞáT '~­R¦6­1릪! Ç÷Ä/ÕÊÃ\xÏ—®×W]Áë áɼº¸ýÛ6@)+k˜–¬‹ÞÂçâA(:Yil?ù#zv [t@ÐcÍk¢ð™;¾ýþ¦Ïœ-LKÔ¯=gaï_QS? rå´>#6Â15¶*‡Êà1ÿ4ž¥~ÄÑÉ-QÊ4ã/ÁF?MB$uOÏÅÏN^ÉìzHáݶ"ö°ÎWËñõPM¸N­u*ãúSpúü4üdV×Çi RñèØ °ÿK•€©mETiÔ#÷¼Àã=ÃQ¿„¥ÐXÕøUÞ¼ îÿ«3Hÿø 3<þF­r%ajU;bðòsxª‹ï;Tq1ásѳÅO(ae³Ruð»Û|~¦Ñ€Ïô=#}-ù/îýG8twÏÙÖ]éšR01täxqº&j€jÖE6ï§wÊ R¥åëôéüÿ°ωñ/³qUó»Hæ ÞÞØ‰‰îÂy© \3,J¡L¿Ñkf8bTתäç°ÐãwØ™Hzb˜6˜‹ñ¸±cÚU+žñ½ P_’nà3šf_gÇ(ƒ‘äH «n‰Lê`艗8äƒVu+Á¼8l«ý ç™Gñ0Ã~ÁËsËáùçO°êOÖU›£Ç츳µ¬ÌPÄÎ ;Õ¡Hê[œû&µ„e­adQZxMmàì·‡Ö%·i 'Ô×7Iº<ŸDcOÐ|_áý‘ÙõAoºÂ‰T¡^°u<ìÿDªeaFu£ZÃ#è<^hûžhCß¶°(÷3þòYƒ³6¡³æ÷„>u-=e]§QÖkn × û•P¯mK”µÔ?œ(LuÆXþÊõp‚~iP6èL¨ÑsÆitZ—ÊÐö«Ef>FŽE +Ô™ i˜h•ˆÈé-amR \§bÁšX> ìk•@±²tKã 3>£Ú¡¨m8OY… !ðÚU«ÖFE«Œ¾ä';áZÍVõÜ01x 6®[ŒA­«ÀȪ)&œS>µ  ¨»0SãP:P÷{ ¨»âKÑù¤2úŽ–ÍÓ‹–p")f ZÚðvc0Ývl Æ ¿>hí¼Ñbe(ûÖ„‘í/è9=¶lÁ²E3пs Û¯*Ãp" çf´‚µY5´ˆÕ›·bÙT7Ô±¶B¹[Tû•õ6 áìÞ/£Y@øÈŸaZ2޽‘ÎˈʦmH·[² '¤hÙ¼'Þ`yÛü',š¢Ïàö(Uþ8˜)S×ã4Uwd_F¥þ@ÿµØ°ifxþâ&eÑzqLÚ¾¼±­JXÀ¬f7Œ ÅÚ øuùëþŒ2¦Ù '„ŠlÄ(ÔU;ŽÇ¢ Û±néDüSÍ&µ†"ì -÷OB]QRØÆïbåæíY¾£=Ð~ê9¡ý ¯žÄöQBÙ¦õá±æ8=‰ˆó—š»Ÿ¼’ÙõPsLzBÇ¡#'eË_U’¯A;kkü4é*×0ágkX´_-é]öÏ EM ØÔwư‹0{*|\Ý1ól ><¸‚ðµ>¨ejÆ#v#\8‡_EìÇÿÔN$?ÞžÂw•iÕ𙳫„ëîdO œìPwØQ¼T7~Tïã Q«Òp¼+Ö­ÅÜáQÑÌÅ;­SígVïùë.4ð¡úÜÆ>R>ê˜zIÐßô(Yú?õ èåå#N£Ûwh“þ¦qFè–i¹™Jý€õݨ—ƒÐh·l„¾›btÞ¶iZ”´Ä"ƶ°,n‡b4V“‘%Ê;oÂ=áX¿ßÔ¦’à!-œ8wãXÉçéN³D…z?ÃÆX²¾QI4›ÞÃ#éÞ*´+¥ÜOÍeªÔ¨‚bÅ4Éqg©=l4Ç›R)bѳnl¯+máÄÉHeω³¯È–'P¨{ÐÐ-9(t…Ÿã±½Süá63—mƦÍ!שLLª¡÷žø´ð2)f%þ.mÓŽ¸«V,À?¡l¾'²®kéO¯:Í»gØá×½—]Â]á;§º•þáDaªÓ0ÆòW®‡ë7ï/ØÝÝú"ö±ò‹ßTY u© z¶´t¾n_ð"ĦF¥ÑsWÖ!@òƒ`üem‰J^2$ÐÉ7 SK”è±UõKÏlv…™Ù£’¢ˆÆÄ†Ö°¾¸?Hç©N$? A;[kÔsïÓ–ýŒë­`jÚ£/¤êµMCÐ1•~ùfí56¸U„å?+pãtžmCºÝ‚Pà ūp.g©q[G>…B¿h…Xy?ã}ÝÔ…¿´éðÓ¤3õVv.ƒb"LìÁ“€•Q̪-æÜÖKSbq;¡¡cœÍp"å¦ÿf ó ­1_|äxÔ6µÅ c…ÏP ùT‡qÕˆÃm¾àéªN0¥Ï®æm¹üÙÉ+ô¨H]×CÍpBÍoüT­a;«ÒÍÕK6]BãmQ[á8þ‚‰—é<FÔ”_…sÖ÷4zUhaTg$Âßjï¨ltj¹­CN$!|pmá½Õþ74ß›qjL û2á’ê=§zÿÿ`R ý¦7ˆhY ¡ŠY9aõkÚ}Þ3…òUëÅsJAý­þA…žÊ¡^†nÕ¡iþó‹S@EÓ#e¥åé#îØhÔ¶Põt0²E¹?}0/üQÆÏˆp¬ƒè³-,S´bw,ŽNŽwn¬rAY LjÂçˆÐNý 1‹ÐÌœùV¨?ù*â©HLù,~ÿ%§$bWEñ6’,]±!>U%é“ò}“y8Aûg‡zýÖ"âò-œ ‚z–Êý6n2WÄïQ"†ÖCˆŒJ¡ñ õ8t6;öFMÕkL'°®«²GÑ2îØðüÞ>ATx&Í Çã¬ú\Óy5ô©QôTu êu£-äÔ›ÎpB‹w»à\ÒåG‚8í#ÂúWG1ë¿…†Æï‰ ö¥3|OdY×2€¡ušg‡G ª='Ha©Ó0ÆòWž…=ÜûáÁ“ç²ùY¡uh]*ðpâ_Üjãx$«ž¾àYˆ#ÌLjcÈ é²‰Bƒ ŠÚõÁ±›["¶¹•AÑÂß2–“t}6›iTø1ªŽ,×ãч¼Mó «K X¥¡8(iå'ªDSð#®¦ì‰PwEé|5zTd—½eÓõ¢%œH¼2 -¬Q±ãl쉡ʘdÔwØäZÅŠ7Eÿ5Qx¢¥â«8ñ/Ö:ÁÜ´!üN}Ð8G xuzê˜Ú eÐS$ë±MCúENœ˜‚ÆvõÑ?ì¥lž6…勼0„µ†ÆÝ‡Osû"¶,‚Ç/åPÔ¢!|©Ÿb_á„5Ï”ÞO®À^¡áP¬Âì|­yˆGô¼¶01m‚ ÔHM>ƒ¡BcÁ´Ý*Iþ ¯p€i6É介ÐÔÜÜÁÍëÔ›ð¬` çmBÿâîR{XšTD‹Ñ;p9N[eVG8‘ËŸ¼B×0ê©'N´…ÔÑNÐ5•ž8#®·”Û˜ö«µpš…˪5)Ú ͬÑh†Æ/Õ·çáWaZÃé7uŽO w8!¼_FÔÞ/í×ȇI·ç ×RaÛÓUÛV½ÿ‹V†C>+êóÿ'fÝ¡ó¯Ï{¦p†3UáÄUÇšÓ¸#4M=XoøÑSâßË…u¥åéçîî‹?ÊZ¥ßnaT?:/Eä;ågSñ<íÄ À•‡NŠFÒ1¡ñJ½¬PJ´x“ï/ƪpB>æD Âú©Ã ù˜Y…EËöÅNu}çÓ3¶UÞ’R´Òe&å:&6RöÎ(VÕáÔ;G,;¡.e”=DÒ‰PU¹/Æ¥Q¿W¶]{'Ùß‚³bÍFñ¼Ò3†lK=(Ô"¥ÇKçëÍpB¥¬gºîQ.›|¾?jû,Áó5]2|OdY×2€¡u'cúÊõp"§·uhŽ;aØmª ±QôÚ“U»Tñ—)#óöXôPºÏ¸6³ŒÍ[cýjE_Ìml`ôót\”>êëýtÖ¬ð%쀓•öѸÅ_lú`[LE£ÊÓ±¥ûk¥óõt¡Þ¨‹2 é|B#”S}ÍÓ‹–p‚k×C}Ñ´¼ Š˜–Em?Îø‹’âåiÌtj+cs˜UiשÛpñuúëÐ?œHÅÅ)¿ÀHËùQJoLfµMCèÕRÃóAø§r´šs´Ì—*L] >œœS# XÖì‚qa5™—Oá„I¸í•ü¢öé)µQVöµR‡¦Âõ¤›•ÊùSýB¦QöÁ(­¹}‰„ˆ¡(/í²­!­’›ò{ÆwD%ášVÔ¶.Z÷_€·4+´:‰ϹûÙÉ+™]3ÞÖá­³ޝ‡‚Ĩ)¨k*| Þè˜{¸InÀ ºV0ª7gUáNÂá¡(gl§ÍºÇ:Ð;œß[–(ï£þõUCâ^¸ØZ ¤W¸ržú¶Ž¦óq]:æ@HWáü7Ç õ8*Y¾g =qƒÎ=±ãåëwâ´=û#Äi^>ÃqçÞ#ܸsž†‰Ó—¡[iš¦ëý ä¸Ø8Å?¥b2Ý7‹ß‡‰'£¶øˆO],Ò~1ÏËp¤ÕÜMû~NÂFçââô¢êëeR¼Ê)oéÈØ(þŒKSƒq±ŒcN|üìÝ|çãðÿº ®Ã}0lÀ†»/®Ã]‡»×A[¬PÜÝÝm8”ý@âµ<ÿ{/i›^’6eiàù~>Ïg#¹\Ò$mîž¼÷Þ¥ù¨•Ù9º”IfY«öüSÚ#qL“§/‚Ñ©ûŸòë*æšHhA!N+,n;ÖÈÏ;½1TN„<ljÅ#ФүȚ> œœÜ`ë mJW£—Õ|Nèû]ÚÖic}NÅ¿­elºM“Ðr")mÓ0 óß&ÑË SMˆùbOod²qFaç ~³¤Ž´ƒ:¤0¬ì+é-'NËåDLå„zGªàHW–AËQ'ÖŸôoéC"E騶÷v)²ûÀeÜ1áƨ×äèÉ¿u®36ž’×1fât댊ÞrB×AØî=u ¦…•U älµWcm ‡àöɵÝ®2I¿Vé*cÄAõ†¯árâ>fVŽ]N,½öÒU·ûu^£]{áh öaA†ïSçg‹#ÆM%òW¶D©ôYQaÜaè\¯?Iiò(Ó—ŽÈØh:V¬ßŒu[öâÀ…x¦ó{G9!ýù)ÑÊ =˼½…Éå\`™½-æîR¾ÿ¤ì;‹â›Ô ¨+v £‡ïj¯»3ÒYNÈÏ´Ë iƒ5µ+JÛ©{ßRöœ½¯Uâ|Äó[G±`X+K/mÐ:ä„Çì š hÃå„:‰ó»ó­×ßè 1gÌ™çñä_ý÷PzîêžG=<^_lò Ëõû0h«xÝD9aø°EãË ñÞÒ_|½y¡.'RµÝ«œÐ~ÿ«£§œÐÄð{Æ´ÑžsùšÑ—‹/B Mˆ©ý%ÉÒÕê 1Å:ž<7ü:“—ÿìBŸ_5óPHÛ"“®¿ÇË#ƒS.'àœµ$Jü^N‘ ¨éu⛜°­6_ëTåDM9QežÖéÙßáÌè’:å„HðóX4¤~IëSR¸UÄøsúþvü·¯eÇîý4…ñ#(Ä©°Åáab’Óç.é\otô–oqjB¸Ø¤B–“±hÛQ;{gÏ,G«,ZËFz>'^ìì Úsiÿ¶Vü1~›F„–Ii›†a˜ÿ6ߤœQÿÉ©DŸmF‹ô°Êó'öhŸuA'pG íÇoîTî\¼ÀÊæé`™¢%–Ëëx‰eMRÁ"U¬R¬S½q =Tözˆáu•¼qCggÈô‰–*†°*¯36CFyÉëØ¹ïÎuF%Žr":oaç€Rp°ÕwØ:Ï.ù¡AfGØUö‘Ÿëà«âø[”˜t=v9¡~ª}XÇ?>°³É‰N»”¯}ÜQÞ§òú¸bÜi·žãìÊžø%ÍÏðœ{·t®7œ¤rÚ-±‘7rüù="N*¾T.£%+ÖÉËŠ ¼¨!Ö_CsNèFÚxo–i:`½¢œx¼¬)œ¬¾a9!mì¯j‘)šÃÿ©îí¢¼]¤P±£³ñ¯Îý…õa¯}X‡f¨¯mí%xk=o±µs6Xj•ч 7|x€¾¼~rã*KÏYŠX&ùþ€Ûq–1ùšßo•¸þŠ!ýbDååÊ|õßàíø#“ôÿÚ s—­Â­,þ«òÚ9 ]Ûíòä‰êÃcꡌÑåÄëcèKz¿Tœ‹@åaò:œQxôÅX‡u$¤œˆŠî{Æ´9£9•¨Н¼Nì®X»CGMÀˆ±“±zýV½£7Å©gÅ:t*Ñç¸ùÏKE1÷W¦V„ØQ·+†Á§ßáõ?¾¨¨9¬ã§®{ã,t‚¦á7M9‘oˆúP˜ëE9‘1zÎ ?ŶËW—¯Ïb`ÍaÙûbwôßÐ`ll§>„CYND'(kúW€»¾yœ`Sr .hÿÞïA‡Ÿ´–:üOÏçÄã¥Mà¤ý9¡ŒÛZ†bÜ6MLZN$•m†aþû|³rBD” ^SfEï¨ÜôLg™{ŸÊ3d‹eIJÿº˜óG‡—€•r·]ƒ+qÌï &E¬ì戴ÍÖÆš¤2øú|TKé÷Qb¾Gàœp°É„Kjm\„àü䊰5!¦f’1‡bè{0êØö¤qNî&­;ÉÏõ±Sçt®/û—oÛª}wùXKåõFŘrBŠú¤Th²F½ƒ¨ûmë3øÖr‡u1/õ,Ó—¢–³#ÒwÜkƒîÕ… (fo{BÌësäÉPS×õ‹óÛ‚xïSçzùrýê5m£ó“§8êÛ Ù\2£ìÕØ¼û¶FeÏ¿÷ºX·¸åýþשŠ÷HTıø÷=ÕYND|s©½¬ˆ¡ÙÑñåD øV•0ùºö©ŸcUKiãú›–pwis¤°I…ÒãúFù9–7ÍK—*˜|U{N‡{˜W7,´7:ßbBi=‡Ÿ½8„ž¹¥í 1_ÿ-ÿZf뀵÷âø{«S"¼ÇʼneaãP³5·{ä×@3wâ›9庾âwç[%)ü=£¦´IZ‹´?[4y{ Ó+¥€Eê–XöXúwðQ¹P°ÊÓ; ”ZÁÓ¥V=óœ(Ë 1ysÏ|°r*…á§µ;z=}~µ]a 1*'ŒxϘ2â5oØ¢¼S¹]úÛ:y¦·|V†9óüôâ21'XFœÅJ"n+Îô"&DT.o0¯¶£MÆ4ÈQ©=úO÷ÇÊÍ;±ÞärUO™ª–=ù(?×Ó+kFS¸–Dç¥çðÏ‹wxôWŽoÆô¡a§¦äy}o*ɇ°Ùñä`¬=q‡vlÆ^ùïÙ[éõՌȱɉsãäÉCX¿í†$B9ñ.›;fS¯ß: ~°ǯ\ÁÁåýQ<¹æL#ÑåÄ+ìœ>^ËâìÝW–ÞöõG>y„ˆÖý'ˆQRÚ…¡ßëD-&Dô•ÁûÐ9‹#ìë‹9€b–}~l¨ôÜiÖ5q²k5L½¦ý9ñ~2ÆþœÐå¶–±‰›&vZN$•m†aþû|ÓrBD” âðâýÚÛt®ßNˆëÄ2_WLhòòfÖÉk+é;{e4î6 ÃÆNÀ°A}ТvET&zãì¸W%¸I;ùŤy‹ñפ?£O%:í‚Ö‡ÒÓ½è‘ß É‹¡áÐ9ðY0cº{ GŽ‚Èáæ¬8•èj4Íê ÷B¨Ýk"¦Í‡é“F¢c½Ò(Ñÿ`¬áÒ¦ˆøPÏwÝF­ðÏ=ý§ Õ1…ØÑÃOž½ s½qy‹{—V¢UvGXþžËÏÛÓeP°ZGô?¾ËÖa±ïx4-œ –é[`ÉÝò·4#ª–Gƒî£1Á{–­\Š)}j £½ ;£~NCî·NZX8Eë9»pøôYìY;Í eEòÚ#'D^áð˜rpµvEú²0hª/fÏ™…‘ýÚ¢|Áf˜{ÇÈûL`FŒ› ï…Ët>„å<>a% ÌC`•žKŸèÞF±N±nåýý×£&Ä{K‘bær1qœø÷†­»t–‰:ÎWL‚»ÿð ùÿÿýðxuŒ/'¤¼Ãƒ‘ÇÞi«ŽÂÒãıݘ7 2¥N gëoYN|”O%:³†´Ai›EšÁ¸Ùó0sú$üÙ®6 U›½tj"J¸:À>g=ô™º¾¾Óѧ~adΙOúÛ¥½îwø{Ry8X§E‰^þØqò<Žì^…õFêT©a¡s*Ñ~Èçä‡5ÑqÔlÌòöÆ„‘}áYº0Ú®?÷lé]åZôLjé á·r5|&vCÉ´Np©4'úyxuaŠ9:ÀµhŒ›ç¹>;pñeâÿî|«˜ôïaÈ#̯›–;`ÞQ¢Äj†äÒg”ÇÂGx-íÔÝ\Þ™là^¸9úN˜ƒéÓ'¢_ÛèàwW]nFÏœNÒNd%t›¾ó|ü±ëÖ{=å„ô»rgƒô·Xz_ÿTíÇúÂgþ\ m­>•hî®;tN%9aÜ{ÆÔ‰š#@ñøûRÌhˆ³®Èg¥R.'²kßaõÆQN¤Sœv3*ÖéPvÊ…èß‹ ssP%}Ô|Š8ÔÂ\ùÌ(Ò²o.cÌoî±—ÓÚ}¸¾=Ò*æ–‰z ¿¾œvjÏOE)7Å[Òߟ´ÔIy¶ùzi»ÌÒVzjN+šÌµ"¼.èucªh½ Ó)(½˜ÑWN„<ÅÒ&aéV ]—ÃßW/aßò±ðÈ—™ÒÅ^öÅßÒká.}Näj€~3–`á‚Ùèß°2eɭωx·µ”+žÄ¹M£HBʉ¤²MÃ0ŒiòÍˉ¨ˆY°õ}3!&#Óž!;Qòæöy÷G½RÒ†¹« ,­`ëž¹J7ÅÀ ·c†ý‹É†ü¡N±\Hîä »”¹QÌsü/èN8öòÆ kü;2¹»ÂÊ)rUíÿ‹ç1º„{¬ >‘7÷bRçZ(9-ìíœ`—"; Tí€1ÛïÆ>äÀDYuF•æíŒš*ŽÉnâ6«ÖoÖ¹ÞØ<_ßîÚç:·É‡žÞâå¹%èP½82¥J+éµ²K™ EêÀ‚3šãÓCbëØø-ÏOpvp„¥}J¤ÉW-'ìÁ ­oaƒïîÃØfe‘%¥¬¤eÒþ\íþ:‚Íým¬rB¬3'— GCñqs•—O•«ê÷YŠSbæq#ï3!߈çðèéó:Æÿ6b]bIá1,V<ñÍäµwä¡Ñâßb²9å²"z¨Ë ±±ç¿R},·8¬K¹\B’rBltŸ]ÜU d†“½3ìRåÁ¯FbõÉ…ðpJþmË ‘X3¶=ÊåÏiÇÑÚ)2«NÃý胸s`&Z–Î wiÛ”ùP¦³¯éŠtÊc‰ƒobíÐF(œ9•´Ãè —ÌÅQ£÷R]Ù©µË ‘÷¸sd>zÖûYÒ¤€­´|Æ_P¦ù¬”¿}{«k£Vñ¼Héì KéúŸŠ¡rgo쿯½ž`œžß %³H÷iŸ©KŒÂþàÄÿÝù–1ÕßÃ×7}QÉÙ 9zÖ9V<:A»Ð>³´#RÙG3Òì .oôBóòÕ·R!c‘†µ7jbÁxxd&Í G{W8e®…©ßé-'D^ÜØñm«"wzéõs^§üÕÑvÚAÜÒ~ô¼ÿÕQ–ƾgL1jBÌ+"Fo9qV>”C¼žbBĨeþŠdË(ýmr„•cj¤+P­FlÀy­¡A—W K¥Ÿ‘ÂÉIÚ.I¬Å[`ú͈—ÇØíÕ\þ{`cëçL…P¡ÿ6¹tHŒrB¼Þ·vM„g±ìÒï¹\³–CË™1¯¹ælÉÛ`µ\N¼Å©=áQ"R¹¨·ÇìSåB¡š=1ýÐcÝCI † íbâÀ‘:·û×ÑWN¼£cöcLãßÞUú}²—~׋µÀ¨·°¥KùTÍ1ËJŸg¡uÙüÒ¶¬‹ôY–¥ÚÎÅž•Ýå9'Zi>'âÝÖJ`¾÷m†aL“ÿ¬œ`’V¢Nù*2Úkš¼ñ¥]‰ã-Å·ÙÚÃô—­Þ ³&aY½aZwì™ Y® E¬C¬K¬Sy?¦Ê ã¢ß/"5ë7Ãûu–‰š\N;‰ºÁ÷'ÞâƒIPø÷…“˜èR|a""þ_\¦ïKÆ@BžÀ»¦›\NXfé…Iô4ÂÆäùË`tï;Xþ]ï=`˜|ÖQJˆìÞŸH#&¾qž¯kwÛœ žO"!ùÞ·i†ùïÃrâŽØlÔR=©ˆ8µZ‡n}åÓâ‰ÿº¼e»nòqÊÛ3ÿ.b’'ñü5ß6ˆÛŠu$µ £={!Ÿ¥C£-¾yÔ&­Œ8ŒkÁ’•ò²"âxnå2Œþ°œHüðïᨑ^õ›GŸÑƒCË çÑþy1w\¹/Eó×·@gq¨‡Ü­Ôfžs”të3(úw?ÑGL|Ë„<ÁªVÙaé\sÄ¡ªÊë1ßó6 Ã0ÿ}XNüà“z‰³$ˆo Ûvé-G]»aKù´Zb1 ½øIy;æë"¾;Ư©qq[~»ðã†åÄ· ÿþ¸¹qû4ý#zGTœAìÖÝ:Ë1"šÓkÑLfå{'é¿ê[¤¬ ¯³ßÇߦ¯Cä]”WbåõI!A{¡X•vè5z6æ,ZŽE ç``ã"p±vFöλðà?(‰¸MÃ0Lb…åØ(â˜JñÍœ˜•Zœ6Kœ×[ßÐHq™¸N,#–·áñ˜?vXN0 cÊ<;¹]UBÞÌiá`ïK»äpÏ\å[ŽÅŠKºóv1ß.¯®­C߯•/K:õká©óVBÓ1Ûqí?<´†Û4 Ã$FXN0Œ‰#>”ÅPÆÞ†£A³˜oî¢".׉eøÎ0 Ã0LR ·i†ùš°œ`†a†a†aƤa9Á0 Ã0 Ã0 Ã0ŒIÃr‚a†a†a†a“†åÃ0 Ã0 Ã0 Ã0& Ë †a†a†a†aL– Ã0 Ã0 Ã0 Ø4,'†a†a†a†1iXN0 Ã0 Ã0 Ã0 cÒ°œ`†a†a†aƤa9Á0 Ã0 Ã0 Ã0ŒIÃr‚a†a†a†a“†åÃ0 Ã0 Ã0 Ã0& Ë †a†a†a†aL– Ã0 Ã0 Ã0 Ø4,'†a†a†a†1iXN0 Ã0 c¶¹Ö¼.ÎÍÁ|ǯ±òug†a¾¿üßñÓçaL–¯ÙÄ0 Ã0 “¤rÜ£¼ÎÎ,ó}E¼ÆÊ×a†ùþò•=<Á0 Ã0 Ã0 Ã0 cªüˆˆˆˆˆˆˆˆLˆå™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&eÖåÄã'ÏÐwàpTöð”ÓoðH<z¡\Œˆˆˆˆˆˆˆ’0³.'FŸ]LDeìÄiÊňˆˆˆˆˆˆ( 3ÛrB¥R¡–g Ô¨×rÄÿ‹ËˆˆˆÌFÄø _÷*KQuE>*¯'úáE À«$¬-íð?»{-B¹€ñ¾lA3W{üÏÒŽÍ7ã‹òz""2³)'îÝ€Uk7bÃæí8ñ2¼¦Ì”GJ 1.z™AÃÇÊ—M˜2 ç.\’—··¥ø„ãüˆÂ°rôIJ·Ê눈’žÈ +(_n.’ýn\¬ŸÄ©påZL/âÑüVVóëÅá$øé{‚­2ÀBÚá·ÌÜû>)¯Oˆ/8Ú7/,Ey`“½&ö.?Ë "¢Y”GŽŸÔ9|C¤nãV¼ùOôr7å˔ˉ=~Jk ö ÇýF yåbÈœ:llaŸ" r•¬‹¶#Wàük•òfˆå™—樂P½½…†ÕÔÑ¡ó%ÜŠT.AæNõ:5+Î…ÓÈ;IfgXõj †YsçË#$¶ïÚ‹7ot÷¢Åeâ:±Ì_> °ÿÐQT«ÓMZu”וP‘ÏöbÀïé`iå‚4E }ÿ±ç5zt‚gÅBH½+v½WÞʱœ "ó¢]N8w8„I+/bj™¾í ž&É‚o݆÷º›8ñ*I>@úJ!÷À½TR*'Txî_ÎVÒξmA :¦\ Tx³¾RXÛ!™£´½d‹Ô­°.Q¿¸a9ADô#HòåÄõÀ›òÈQ6(‰y&6m݉Ñ^SälÙ¾ ‘‘ºv†Ž–ס=ÊÂ(a×1¥\jX8þŒ–K®ã½žÏÙðÐ0è¹Ø ±œ "óSNx#õÄU.@”$„c—×bXþž„ʉÈÇð­–ɤ}«_ÆâR¿»‰¡ Â’º©¤u9 M“Ψ\Úñ·J…ÚþA‰¸}Är‚ˆèGäˉwïÞ£fýfr¹°~Ó¶èËE1Ñ£ï Ã7ú &O–eí†-òå šáý‡„ 2TáÅÊ&p·vCq¯« øðŠÀ³£sÑ©J!¤us…]Š\(ÞÌ ;h}+ù³Ê»#ÛŸGqoßd´(“)œ\`Ÿ*~k5 ‡ž+ Õœ[Ü…³ÃÍÑÎ™Š£Îàõ¸¡5kZè¾nHëTK^½Æ©¿Ú¢xæÔ°u-É7¤pÕ;\[ï…ve/KZØÛ§@ªÜ•ÑnîG?U,'ˆÈ¼°œ söê6Z×÷–Gø$•r"òÞ”u%‚3ŠM ÄWìê#ò¾/*8‰u¥FÃU·á_Oö°¯ä‹ºßýK,'ˆˆ~I¾œ>]><|ôX¾LŒ’ÿnѶ‹üÿbE³6äËöì?$/#&Âÿ®R«¡|HH‚¨^cyƒ”°pk„e¯ŒíþUxs°?ò;¹!k­¡˜³b=VøGÍl®°ÉÝ»£†8Êå„ ¬sBîL¿£å(oø-[‚éý<ÉÎîuüñ8úýÎ{•ƒ‹]V”ï9þk×bÁØÈçâ„ôMÖD/'—%Ѷg5¤Jÿ;šþ9cÇ.Ź ÁæN%QºÕLš· ëÖ.Æð:y`c“l Ñ|³Ár‚ˆÌË×—_°t€¯t{\ðâË㈷/°Úÿ0<Ú-Gúª¾°)7)¬AÅѧáðI±#3—ÂJÌiQzz_ŠoOL…Ë‹ÖÀZ^Þ­ŽiJë°ûh[C½ój?è”G †œDî2saQqÖV_öùé}L» ¹=æÁº”´¾²ë1õ‘ò³J…—7oaü¤ø­éb¸•÷µôó¤ò\ƒ*cNÃïò‡8Ÿ³˜ûÝ5òd‰á¸sö*ŽÚ„Ÿë/‚S9ØUYŒÜw ÛªûøGóØté>Ï_‚Ãwî”nºîÒ㲫¼9;ì@÷5q_«Ë~Žå~QµÍ2¤­ì+=þHßl#Î ÀÉàøžoIä\>xþ\‡ì5çöŒœj,EáÞû1jÏ ¼4¸ ÝÇœ°÷†ô쇼€ßìmÈQQwþ“è”ZˆÖ'bß2Lú™—.:+¡šú1ÛW^„Ÿš¯Gõ1'0mß3<úª=êÜœR6VvHæPÓî|ŒÀÉ¥Õëró„ÿ‹ù7If_“n*ŸC¾àáA_ômRy2¥ƒƒ½3RåD¡ê]0aÛ?ø(ÝÏ é~Œ)'To¯bõØÎ¨V$7R»¹ÂÚ!5Òæ­€†ãTt»/ÛÑJŒî`9AD”ä˜E9!ø.\" ¢¨ÆOš!ÿ[œ•#ÊñSgäË&N%ÿ{ßÃò¿çû-^Æhác`'X—œŠØŸ­_ðêÁüsû¶&wðø­f^ŒênÎÈ?øŒÖéå¤ ‹iå`k[CΩïH.'¬í`‘¡1ß3âØÑMhšÒºÒlœ²œ "ó’xåÄ\¸Ž¼gP¹–,”;šX”]‚Úƒ¡½ví$r•V?†,3žÄ½£ñ#[ùÈ벬}»£Vdd9‘¬ÜfÌ RáÍ•¿QNZ^ûqŠa­ö‹xs6 µ¸žŸENé(èutôý–]ƒq·ÞbÝÄUpEˆr=š¸´9Ž=¯•kb?Ï/žA©êêŸW'¥¼‘ªçE\ø¢ÂëKgQ®fìŸS;6õvcIL“¯Còú,¡Ç\Êé{ÃñwÊ[ _ÿÞ={éä÷F‰UN¨ðäÄ1­jøgVǪ “%"cŠ8Ë;çö•çá¡á§0~á—0¼ “¼.÷Æk!¾ËQ¯EÔbçß F^ŽÿqF<ÁÖ^%àb-&ÓÔ«”(ÐyNûÔ‚m<åÄû‹sP+³‹|¸ŠÎz¤Çh•±&Û^ÙYN%Af[NxMV—×£—¹|5@¾LœfTØðˆüïy ý£—1ZèAtÉèÛê~x®ýeTØ È->Ô¢>ì\Pfö}¹tˆ¼3¿Û» ô´yÿï£òz'Úgp€cãuêNM9añS/ˆµ5­Â ¿Ú°µ+ƒÉÿˆ^‚—6€ƒm! <ñ6f}RÞœ†|¶.(ïýX¾ou9ጢ®ë|{£Wø9 ÎëÇ[4Ì,'ˆÈ¼$f9aí¹¿Õ‘v Ë,B‘aÇ1yË-lŽÙ‘5¶œ(³ƒ ¶‡·¼ïÚd ZN;‰áóO¢Ç_7µ“©ú‚ý3WÁ^³SnWgZÏ¿ ÿƒw±éÀ ÌYt•ÎSïK;è‡ÝÔ;ü>ú~KÍG‘Îkäb©át\tþîb󡘻p?~‹Þi÷FÆñ÷ðF9€#Öó¼%kKÏs¥å¨1ý<æï»ƒÍ‡obîü½(\M³S^ÊŦžDíZÒ¿K/DÁÁê×cý‘ÛXºæj7™}îoà©ÎýAKiÝÊó<ˆÇŸ³ÿqÌÜsûÏ=ĶݗУ×RÍóãTýp[ççO„÷FØ<}ñž ®fô„ãÀÜ ’.‹Ê‹x­yÏD¾¼:šÒƲê*4ô½ŠÕ'áð…GØy(ÓçDµ‹`[~#¦ëŒ’1^øù‘Òöƒz§¿†ßó¯šâËñþÈf#­Ë:#Zm Ñ\‚Í­3ªOQšs ŽÇñ;!¾”99´8ìÅÄœÒòÉ‹¢áo,ß²»¶­Ç‚É}á‘7,¬Ü=o.X‰å ”‘V ~ÍC¬\‘©bLX¼Qz½wcãro k]iíía‘ægäL-–c9AD”Ô˜E9!θ!JqÆŽà`õ×2Ûwî•/kÛ¹îܽ›·n£MÇîòe;÷ì——yñâ%ªÖn$_–àÃ:ÂΠ.GØ”š{£EõWölºõ±fn;ä²)'BöDCÍ¿”è¢#갎ߦ)Fe¨ðÚ¿>líJa‚˜+á¸4º¬ô¬K˜2B.'lÒ õv=³ª\Y=­ª–@ö iáìì;XYk0³œ "ó’˜å„¼sYn%:ÿ ûM¯*Ç}Öhvf}Ï7Hk®ø­XGit=§»Ó¤‰sóVjéX޾W´>XŒ-'JÍCj°,ë:«Ÿ<$áÃ…£È¡1áÜþ$¿Õ³ûùéf÷SOÒ˜¬ô"ÔÝùA=²OKôýjŠ€”½.á’|xGlaOo A-õã·¨¼Ëu>CÏså¨;ÂïCàüZ!êþÔÏgÃÝ!P.©zûiî¯ÂVÌ¥X@zMn®Þ¨åQj>~_ô:OAäGluHŽšú¬ØIOŒ÷†FØ4«¤^O\sN¼Ü±b=¥—¡ë߆ÞÍ‘úˆÊŸÇh¡8Ñ??,¥mˆdÉaÙ˽"ˆbaWǬê"swìÕšëãž®H/¶‡¬³¡“ö 7¦ã71_…(&ÒÔ¬kzÞ`¯aFµôòýÈÛ>zˉlk§~,¢˜ÈÛm‚”oh1'ØÎÈc¯¾?–DDIO’/'´'Äܰy{ôå⬆&ÄÔ>cǺM[åËÅ:>|0ü©Cõ‹<Üa‘²56èò)}ÌÝž’vZåÄHgíŠòcàØñ:9¤ÞÈÒ”6ef!ö¡žºåÄÅ‘Ea%}÷Ýx\g}ÇŽŸÂÅGêru9‘ív+7hÂpmz%¸Ú¤F¡¶3°úÀY\ DàµuhŸÍ‘å™-íS‰Æ_xîWî6jï€z#ýX}ßú«©Bn¡~õºìz]Ã3íÁ÷Ï£xYõ:2N~=»WÒŸØ n¦aaÝìÎh?cË ñ8KÍCq¿úïCP}ÂÒ ä‘eWcø =´hÚßÔ[·<ƒ Šý=íûµ(·“îxr¤OÀóóWiŠô¼¨¼ÏØÏsö™Oô?~Õgøˆ1Î}´æ_Ò¦Âåë4…Ðbt>«X(ì zyª.Û?þÆeåË®t U5#œþ¼{”d"½7dF•*Ü^µ¶âþ4‡ï|Ÿ£{V1úÓÉ›o4ø3Cõf#šÊ#‘­ß‰Ø?×—£šC'²Õè߬ÇÙ!¿¨¿€±J޲³ïèdQ¯z¡°¦TÐSN¨^®D]7õõ–Ùºcí6ñÓºêQ,'ˆˆ’ž$_NhN%:pØåUÇÆ-Û1jÜdŒ0 [¶ïÖ{*ÑþÿêT¢‘¸?·ì­ÓÀc±úÐ %e9qk*ŠÛ9£ð¸€¸­0ºœPáÅâ:°·É…žG”¥Clˉðó’ÏIwH˜´’™å™¯D-'¤Ü§•×k‰| í4åB«³¸¤ýG>ò-¦wö•wª-ëÂ=C†Eïèë™/ å„U=iýz÷ì5Þÿƒ†šeÛΗŸ1JaØ¡9Åe²2«1òŸØ k߯m—K¸Ǻ>Ý7y¤Â<4<¨|µŸgô¸`hE¢äÐŒ.)å‹j[?<ä ôÔA¤’§ðE½½±ÇV„]9Žòu>(½:Dïç·,òfuU¿¦Õ÷c[¬ÏDzoF•Àé9L!·ô³çŸ|•CFÁ‡Ý]NÑðÚl3´o žû7€‹8̶0†W>?a8=¨ \<È#4^èy%#naÒobî i—úX¬o™(ð*®^V_9ñi{¤G®:"çÀ3:£m´½ßÜV³,Ë "¢¤&É—_¾„¢qËö¨V§19ŽYsç£ß X°x™Þ"B\¶Ào™¼Ìlïò!!â¶[u@hhÜ;øJªýè–Ó ©«`ÔÑ—:…ƒ²œ@øUŒ.â Ë]°#®¡’F—Ò¢棒‹#Òx.ÇåÐb°œ;&{áÐH3ß…Æ—ó£PÀ–‡u‘ùÒ.'\:Á¬õW0Ç`®a×#åg†Öh¹-˜§sx€–Èðî¡Ùm~ +É{°a³fHþb´?¥Ü5ŠÄÉ9ËåC,Ê®…—ö¼‚Ñå„7Òx=ˆ5颒X6fÙœsžÅ¹“&¼Ú¾]}H‚´C\kç—Xe€öýfžñ$Îu…ž;‚Œš² îå’ÚÏó6,Ô;i¦ ="Bú×>ôE!ìâ1dÑÜŸxÜ1Tx¸~³zBi?4Þ„‹·^H¦÷Õ̽¡3Z!±Þ0ºœÀ—çÖ&j.o¸µØ…¡;ŸáqÂ6]â3„ÅOݰ'ƒIuHÛ1s*»Ë§ µþu"”?³$üò8”ç¶HŽÊóô|Éóe'ZËgÚÁºÄêYGŒPìî˜I=âA§œPŸ}D>“‡U´ÜW{'=ô{¡´=Ë "¢¤(É—Âá£'tߩ׸5nÜŠ xãê6n¥³œÈÑã§´Öh¼÷çg¡ZF'$³I‹¼5: ßè)˜:s&Ž®õ‹ÂÍZ«œ€8•è@pv€c®Úè1Á üb¦×4+W·i>PNȧTAºWd¬Ð£þòÃÂ…>˜8¸#*j‰Åš)ƒå„ê 6´ÌK÷2è³æoܼ{§6LD½Ÿó#szŽœ "ó•˜sNXTÙ‡-Ê}jmb´§áPÕËT“ðFZ¯ûÏâ%ì)úiæH°ít·”{iF—>(±üNQ®íË1ÍiÙòÞëî*Ä” >(ºäu¬ukßï¯Kc_§zÞ¸r¢ê>lU^M«œg¹k¸äW–1KFâô\u$ Ʀì:Lz ¿œøÚ÷†Ñå„$<è>u[ë #¶kPÎ5ì{fp$‰1T¯×¡‘\8"Kï£q>ŽøDÜœŠòÜ HY®FŸ€±ÊŒíŠßSª'ž´-5·”¿3/£ºƒº$°k¸6öp\ó«úr" Çûå‘çÑøŸm ¿¨Å¡ði#9óT¢DDI‘Y”‚5á¿b<ïÄù‹—åC9Dé0tÔøèe +_6fÂTy±¬¸(7¾Føó3X8¨%ÊæÏ GXظÀ!Eä(æý¦bc öבxõ÷ôoXÙÒ¦€­+\3F…6c±åžæÃ4A儸ø®®ƒæe ­»+¬ìS"už hEÏ—`é±Û´¾¼ ½t Ù4Ãõ˯{§[$ œ(»VÏíµ|>¸[=‚CÚi¯±]9É£®°«'¢È?ïE¬ÃMbÝïרrB<ÏŠ£0´h—ë0Ñàq—‡¦ù«UIHtî/ß ('d‘¡¸vèo´ì°vZ%…EY?ü:þ2ŽiN5ž0*-õÔ†QNÇõÅ'F‰c²n=±ý£®Ä~b"ù¢‚fƒk›íñb䄘è2""BŽøq™¾Ã=ˆˆèû’¨åD­¸všÿ¨$xßn¸Ë‡vø¡ÕѨ•EàðLõY!,*nÅ<}ÇÕ'b9ñåÄ~$9±Î˜‘‡‘>zäD°Á‘ñݯÑåDœÏsâ”'þZ¦9Qf Fëž#ÔHÆ>fÄÿÞHh9-Ona¼×Fd*SRØ6؃eOøs©žb~H&íÀ[ ñ .ˆÓ—è“Cû”êÆÄ ¹ž‰õ;ª ZŒjš‘öM6èŸ$5Z'þn œñ¾Q#'ŠaÔ•x~¸/ÛÐÂåQRdÖ儘SŒ”¨Õ yô=ÆNœ¦\Œˆˆ¾CI­œÀû;h.— ÞH5æ.äéµÎáÒ?PqFD,'ÂoAÍ<Ùf=sž!hë6õ·ó¥|á±#öH‹„ÜoÒ)'T¸³jcôœmOê{¡ŒaìcFüï]NÄøôÓ†­ÐŒŠñFò!· ¯ç2$ò¾7Ê;ŠrgñRŸ‚üßú°[sšP«4h°28îÑ971±¤‹\ŠXfé‰ýÚ Äçmh™\=ç„M™ÙqN¸* ˆSÔ§@Õ-'"8©”º¸°ÎˆözFjS/…‡¦a9AD”´˜u9ñôÙsx6k=¯DÓÖñ<è…r1""ú%¹rBÚÚ>^}ö Ëšû±UÚ ½|ÙåC:¢Á^‡Y$b9Ï÷ÐZ³.›öçÇ™D!ï–1~š³u¬ÂÅd ¹ß¤SN¨ËOòuÞÈõ׳ñ¾Œ}̈ÿ½¡UN8Žøwå„,ò-fvUO˜iYývÆõ˜b‰À­iå`ce‡döå0å¶ò&€ê-Ö·H¯žT3Mlˆ÷0ÐHÜû« ìÄá$Ö™Ðz«ÖB´ÎÀ‘,eK¬k]ªXXÓM.9tË àãæ?à.îÃÒ…Æ^‹³| ;1ÙlXN%Ef]NÑ+é•ÒNÒ™Cê~©0€IDATôÒKÐùlNÍ]!ŸÓ²Æ^lP¶Q³œž…-cÕ‰E™•èÇ÷È Ô¨ªÞi?ÓYÅ¢ ¹ß¤TNàó#t©§~>-=vay\gÏ2ÈØÇŒøßZ¯¯]¿ëøWG‰c³¢Úƒ ƾá#®cl1QØÃ®¢îÇõBÆC´uäC"¶ýîx&±T‹|â‡j.êÃ(Ü­Á«èŸ? 'ûkFCX§Amÿ§ßc‘ýPÝU=ÊB_9¡z¾5åû°ƒUþ¡8eð”6Ÿp¬ßÏêûd9AD”ä°œ ""³”Ë „>FêC;rκ†!­Äm¼‘fÜ=õaú$j9!­îößøU3GCË#Ø©oÅO/1£fÔDé…¨±Ew~Š„Üo’*'¤®Ú'Í!):ŸÆþ`Ãþãóg8~_ù Œ}̈ÿ½ù cZ«¯·¬¾ ˃×k¼½ýÇž‡ë]#|z†-}ä‘Ö­Îà¢ò~ ¿8ùåSz¦@õEO ¯?^‘xàSöb„‚Mt=÷,ÑT/±¬~jyÔC2—Z˜¯5_Fø/‘ÏúaËŸšbñm=¿Å¡÷°¼YM¡ ¿œ€*ëZhN5*ýœEÃkT…7'G¡¸¦Ä`9AD”ô°œ ""³SNÌ…s‡C˜´ò"¦Æ‘éÛžài¬}ÔDÜ&&dTŸÊÒªÞ2d;÷¥— ãiÃ#»œÏ¦°|#Üåwéñz¬C3ŸKð?x[ܼ%GPµ‘úð€d¥¼‘nàuèéŸûMZå„Xà5þê½(ú¬–UW¢ú¤3˜¶9+öÝ„ÿ–˘àsõ;-ƒk_TÐ9íª±F¼7"qÚ[=‚F”%nmöcò¾ûØwúÖl»Œe×B¥Ç¯Â½µa[f!rõØ‹>K®bå±8|ù)Žž»UëO¡që…°”ç™òkã>¥lŒ0œ¨)ÌÝK2Q…RÄMLúM3DŽ8fô^½ oÖ·B yž W”žu7æ¹V…à@ŸB°•ɰ‡eªâh2ÔË6îÄŽ­ë±pêÔÿ%-,­R¢@ѰËé+'$·¢zjqêRq?nÈR¥'¼­Çæ»°q¹/F¶¯ŒŸía•©8~É &ôd9AD”Ô°œ ""³¤]N«Æ'q*VG˜; 1ÂO!Ÿ¼S¯¹ßGqHÏÂѽœ¨¾àÄòíÈ×ó#íäæ€Ë†À'ä~“\9!Q}| ß‘+á¬u:N½)å‹Ê›>(Öaìc†Qï Õë{èÔØW]ÅŠ¦QáÁÚMê‰<ãˆEé…(:íÄõbhûr=³‰q¸7]¯g4ñÂ/ŽÁÏb†¥ò=ïd«±¼ß‰?Ò‹âÀÖÅ& öÉ6îaM‡Âp” =±N½wâþñAÈ!æŠ0PNÈ%Èéɨ”ÞI=7…N¤ûÎ\³/>À¢Zbþ –DDI Ë ""2KIµœ@ÄKŒ’ç÷ëñMÊø-Ê wîaÆŒ](Ýl ÜÊûÀ²ì<¤¬¿G„ï¹wqž¾1!÷›Ë µ<¾r£&lGÉ&‹ÕÏAiØWñC¶V›PÊY,<ûou~8c3Œ~oD?…Ϭ]ø­±\ÊùÀ±úäj¿ã.ˆ‘’°8»ÿúŒÜ‚¢ý‚ú±ÚUZ„Ÿšo@IcyÀÇ•÷tE1bÁ:#Zm Q^_p¬_>Íé:ÿt:>a7Í¡¶¿`ðYåOñ wvÍFe#}jØÙ:Ã>U.ªÞ·ßÆGˆù.4§5XN¨E¼<ÿíPé—Háâ+ûTH›·\Œ3/Äí¢N=Êr‚ˆ(©a9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2)–DDdV”]2Ì""¢ï Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2©D/'*{x2 Ã0Ltˆˆˆˆˆâ“èåQB°œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë ""2+ʳ0 Ã0 Ã0æ–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDDDDDD&År‚ˆˆˆˆˆˆˆLŠå™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“2£rB…—;ÇÀ£–'*{4„ÇÝR)—!"""""""sc>åDä3¬ëßUjµ@mφ¨Rw0Ö<ŽT.EDDDDDDDfÆlʉÈ{ëЩ®'ª¶‡>ÝPÕ£1:.¿‡í…TŸp÷ÐrŒé× žžMP½~´è5«®}R_ú'WÍFßNíP»^cÔhÜ †/Ãé×ê!‘ÁW±nÖ(´kÑ5ê6GÃΣ0}k ÞFÐ}†cþ“Ñ­mkÔ¬ÛuZvC—‘>ØtýTÒ}ßÙï‡!Ý;¢Ž´n&ЦßDÌÙ¡š›‘.3)'Âqݯªy4D“9—ðáÚb´¬í‰ªüµŒ Ow‡§8ì£Vs4í1={tA]ÏáØðL%]ýǦuB5éú*õÚ¡}¿¡èÒáxt]‰[bŸ±°»™Ñ-ÿ…¹óæà϶ꑽÖ= O6CMqûæ1fÖŒ>MÀŠûˆ¸µíêˆÐgŠ7&{B‡m0pç+ðè"""""""ÃÌ£œ½ŠÙ4BåZí1íÜ ,sÛJÿ®Ý ³.~Q/ñvjŒÊÑnÁuhÆJ üã'yä‚êéVôªÛPºMGL:ùNSDàãÇ/Òÿ«rh2êÔòDµŽ q&è^¾|…§'|м¶tY§å¸KzÉIõ®¾8üðÄA%ª°0ˆn#üœ·\ŒTñ‚çžã³¸ƒÈ0„ÆÚADDDDDDDJfQN|:3ÄŽ‹¹8ýY\†K>QÕ£!<§ÅGéÕ»CVÏS.,æ\ÑmÂÎÎA=¹<˜Ž£š>#F„<2£ª‡˜lS7Ušü…Óa@øÃ]Ü¢ªˆËjµ@ËÁ>Xñ¥ôh$ŸobY¿Öòu•k5B½Îãá½ë^sZ """"""¢8%ýrBõ‡&hvúõ¤Jã)8¢’ˉ¡r9Ñs¯ê+'þR— gà˜Î$XÔ].'ª÷Z„gÎáôY­œ¿‡7šc3"ßÝÃáU³Ñ«uSuIQ»&«‰þûV`lÏv¨.^Òmæ]ÅADD”0a8Þ/,-ñSïcê2œˆˆˆè;”äË Uð ñl(íè7…g‡>hß5*ÝàY_\ÞƒwCq¨‡Gc´_sXÇÛ'xòN…ȇе®zdÅäS!šÃ:"ñþñS«Tx{pj‹ò¢Ù$ì}©5ÜAõoCÔmFèçÏ1p†>Ö‘­PEŒÞ˜ua_>áSÔÍTŸp{å`¹ ¨ÖYu#""úz‘xä] 6–vøŸ”dv…1øŒ<¬.†*óª»J×Ûñùfè ˜3‘¸=£¼ô³:£Ä”[±'&"""úŽ$ñrB…ç[Gh&¡œâøhŸpjz;¹¨1`žª"pgí`ÔŠš³ç0ôí×<»¨GR¨^`ÇÈ6êÑbBÌþ#Ы{'Ôn4ÇÅWQŸobqÏæêõµ„Q3|0uÊDôj×]VÞ—6#puAO4ê2Bºn>æxOG÷æPEº¯~[Ÿ#ôÔ,4lõ'Nœ‹Ù¾¾Ù]Œöhˆú^ÇbÎöADD‰ v9! çÊ>¸£}Y—*¼Ú?#çn‰{!x»¦)¬ÜPwż{ð7¶Ì÷„Í÷åyˆˆˆˆ¾I»œˆ|ŒU}šÈ;ùg]€â{1|9ï‹&¢¸¨;+H›i‘oqe³7þìÔµê6BµmÑnÄRœ|©nTî`—ï8tlÕÕk7†GÓnèû×<Òlá©Bnbû¼‰èÖöxH·¯îùZÿ9+.‰Ã6"ñäÐ|üÙ¥êÔk$Ýgsxv‚ñ«. (ˆ¸³ãû÷@CÏ&¨Z» j·è…~3¶á*› "¢D¦,'¤XgAËMÁ1gG2ër" 'þÌ KñsY¹"Eº´°¶r‚[ú °³?¯R¶ßÃÓTÑw%i—DDD:bÊ «\eQ"½ƒ\BXÿ< §¢Žé3TN„?ÉţкfäË– N®pJ—¿5u7µ†ç…DßìŽøŸMô9úÇft@©œé`ï©óUCGßsxúÛG7ǯÙRÃÎ>Òü\ }VÝPÌ3¤ÂûÀÕº2rg–sH‰4y+£Õ„=¸g°]ǵ•#Ñ«s4mPYœìÕ‡¯8ä@‰ºÍмm7ôYpNž šˆˆˆè{Ár‚ˆˆÌŒV9Qh46ήg1¢À*%*̹£ž—ÁP9v£ ;#™æpdVöÑÿo™é¬ÒŒ½ˆ*',‘1A¸XkÒGjü„b¥~ÖŒdˆI2ûÂr6¦uø|i:ʦå‰tµ+œÜÝa)?VGdh¼bПaxàßi¬àìž ÖéQ}þmùôÕDDDDß–DDdfbÊ Ë\ƒqòÝY . .,2þ âP>CåTÚ5#æíÁ…ûÁøúç¦{ ¹(¬ÜQsñKõ¡!Ñ儺ô(Ús5N\½ˆ­CÊÃ%ª°N‹Òƒ7á̵óXß§$äËoøEuù>ÕRjWCx¼—Öý ·üš ¸?›œèqHyÀbŒ°›Þ¨”ÒÉœ*aê®iøÝÉÉÜ*`âó:H…ˆˆˆÈ,'ˆˆÈÌÄ”?õÄP^mn‡ôrÁàŒ¼ƒNã“*~úÊ ]‘|QÞ^ Î(<.@=òB«œ°Hß;¢Ž¡Y‹.êÃ,,sÆI͹=U/£šƒX‡RvØ+Ï¡ ZŒêŽbYGdîs<æ4 ¡Ñ5“Má„‚£¯ ‰{ëz¢x:$o¼/#ßbkÛ¬°J^­üo¸ ‘ùb9ADDfF«œHß{DyUN%RHæZ³n¿ÃêFîÒ¿åħ[Ø4¶*þ’É]`i¥.ÔqÂ/c®ªwüµÊ ›òsc¿݇ŽéÔ‡iØÕ]Ž7Qëý²ÍäÒÂÉÛî’ï/üüHäµ}ØGì8 C÷CqNl© ¹…kÔ>âI®ˆ˜‰ˆˆˆ¾C,'ˆˆÈÌè)'$ŸÏ C~;Q8 mËUXÐ2uìrBõ[Úf×Ìùà‚ô¿5Cç~C0°G-d±1\NØz,æ¤OêrBž€Óv×ÇL~)Ê WE9ñ÷pä‘Ë ¸dÿ¥ÊTP¤êN;ÏQDDDD`9ADDfG9U06¶Î 1zÂ. ü,æ{Ð*'¾ìDë”ê‘Ö%§ PÓ DÞŸƒ2òa‰[N¨žù¡ªæ°Ž,½Æyh ÑŽå™儸æŽ*¸iª¡]Nì@«êÁ¶Ò<<‡jD¾Á©ÑåਙÌ21Ë 1!¦ouÍ¡&n¿£×ºkxñ9áŸ^ãþ…]ð=GßG­€ˆˆˆèÇÆr‚ˆˆÌŒárø„Sƒ Ã:úŸÚ‡u<ÇÒúiå‘bâÌ9‹!ÖÔ°vÊ‚ô©ÕT&j9!œš5§+UÄ¡.G­˜ˆˆˆèÇÆr‚ˆˆÌL\å zµ-2ª åÙ:"ŸÂøÆ%ÎÅ –i‘¥T[L=úFƒÕ7('ä›<<ˆY½â×™àlï+Ç4ÈðK-´¿ÿÄ5&Ñ„å™Ë """""""2)–DDDDDDDdR,'ˆˆˆˆˆˆˆÈ¤XN‘I±œ """""""“b9ADDfI¥R!äÝ;<| £ÆMFÓ6PÙÓa†a†1ðœ ""³#ЉcâÔÙí5‡ŽÇóçAøøñ#Ã0 Ã0 c†a9ADDf%ª˜3a*¶ï܃OŸ>!44 Ã0 Ã0Œ™†å™q(‡1!Š QJ‘ùc9ADDfEÌ1!å`1ADDDôý`9ADDfEL~yøè åÅDDDDdÆXN‘Ygåxõ*Xy1™1–DDdVÄ©¦Ä¤˜DDDDôý`9ADDfE”DDDDô}a9ADDf…åÑ÷Ç,ˉÇOžaÅêõØwà0=~¢¼šˆˆ¾c,'ˆˆˆˆ¾?fUN|üô CFŽ—7Lµ3qÚlDFF*Oš"bVyØ”™…;š‡º¯ÒÚd@»Ý <-Þ‡µhàè„ÜCÎ!\y™_¢oŠåÑ÷ǬʉCGË¥í»ö†ÿòÕòyî£ ñïo%òõe¬Ý• 倻“3¬R"Uö¨úǬø \}޾üâ¥+òåµ¶@hh(Þ¼y‹: [¢c÷¾ð]¸_¾$p‡_ásÀ"4ÈæŠdÖ©‘«j{ô5^ãG {³*È‘æW ¿¦¼IÜ~ˆr"g„KóÍø¢¼êG`Ôëòƒ?GD_åÑ÷Çlʉ[¶Ë¤;÷ìW^…î}É×Ý»ÿ>|ÄÐQ^Ñ#*†ž \ÜxN ~X¸•DŸO ¬!T¡¡:—Åë”y‡žc'Ø>_ƨbÎpüQw¼)'~ôçˆè+°œ """úþ˜M9±vÃyƒtåš Ê«ÐóÏ!òuÏžE_v÷Þ4ÿ£³|ùµ€@­¥‰Ç jÁÑ:Ê̼ex'SK؃}˜Ð¶2r§K[§´ÈT¤zÍ?ÚÓaYND<9Š9ýš£|‘|Hãê »äÙQ¸Þpl¼«U`È;ÁÎøeÔ˜Ú¿fI ûHós-ôô¿Šwª˜EeoaÈ(‘#=R eöRð² —Þj-(?>wäxînÁ€‘ÂÙ©ÛíÄ'Äÿ¸"î.G³Ü)ÌÒÿ‹Žê®xu:áƒ.ÕŠ ½»›tû,ÈW¥ ¦í„XÕŒøÙœÒáŸðt¿êú Ž©QÇÿTˆÀ³#³Ñ®R!¤us•ô3§ÊY µº-ƃGÙ¨ðêÜR iUEód…«ƒ+œ3FÕžËqUû6òý¦AËÍqrnWTÌ— ö®pÍRõGìÀ}Eñô&¶.‡¬)Ý`í”yªÀªË«ÐÌÍp9ÿs$ý|Gç¢SñóIÏqŠ\(ÞÌ ;ľóø^ YôÏóÇf¶ÇoÙÒÀNz¾ÒþRC¶>@ØÇ›X3¤1ŠJï;‡TH_¤Fí~ëP“Ðû»1¦EäH›Ö¶.ÒóV¿7„Õ·ôýtDßË """¢ïÙ”Wå Ò6{àóç˜Ã:Î]¸$_Þ¢m­¥Õ6mÝ)_·~Ó6åUñS½ÂâÚɑ̭ V¼VîåëŠ|²-²9Ã6k ô˜â‡%þó0¦]Y¤²uGþ>½ cˉÀi¨R¸ºŽ™¿5±|FWüšÒö¿OÃõ¨=G¹œpDªl¹±x;Œò]þ¡_\°µÎ€š ïÆìd†Ý‚GFXº@³±p©?f o‰‚Éáò»Î~Ô,'?>ig¼n´Ï›9ªwðñ0mû=y]ñ=.UÈœ>àƒFaWÙ {Á¡ÃÇp5HÜZ…×ûÿD>''¤.Õc}—aÉ‚©è^9lìr¡ÅÚ'ˆîqäŸÍå»õFÑ”ÙQ¦í0Œ?«Âq{.Ê»:#cÕ˜µl=VKÏõÄmP±±7nœÀ!Oüš¡`•Î6Ã+7¬ÅÜ~U‘ÎÆ ¹œŠ½ yN3åÿé 4ÁÀ™‹±ÔßC€ƒur”œr=æ9ýpƒ ¹Ã­(šŽYˆe+cJodÍ’™œ —ñ=GoöG~'7d­5sV¬Ç ßᨙÍ6¹{b·Ö{1¾×Bõó(Œl¿uR?çÞ#Q'—,œK¢f•\H_¦+ÆÏ_Ž%s‡Á#»+,’ׇßÍ+v#~q…]®†6o5Ö®öÇìñýàY±3–=ÿ÷‚è[`9ADDDôý1›rB;qš¼QÚ¡[_ø¯Xƒ‘ã&E¾qúì9åâ˜6Ë[¾îÀá£Ê«âvr;ºøä˜=ƒ>á`Ïܰt®ˆ)ÚßnÀ©ÁEamW #/ivS,'tEà기¶¯Ž¹57”w<í`‘álx¥µ£z~sƒe¶¾8,ïu«¼®%RÚ¤GÿÇ1€¸|Ggd¶IŽòsî©/×<¾ÿY¹£ð ãxïþ§žÇv}³;ê²q£‹8úàPœŠ*C„Ð@L-—–Y{bÔ嚟-™m>´ß¤õ˜¥g{m3ØÛ•ÀØ€x_˜¸©žÂ§ª+¬ Ãå¨!ê9MÓËŸiýða—¤t'Ø”ž‰ÛòƒQá©_8Y§“žÓ§Z/—Çý+Ãå„Ìàst^%\áPfµnüùôPäµuEéYwc=±éy-¢~žŒí°I«Øøt¸²ØØý?{÷Uŵ¯|½+½ ŠbïÝDSL®iÆ»XAÄ‚‚DÄ^P±WìcG;öcAìØ{4V°ôS¾73ç8T‡ûýÖú¯fæL;Ì}î½÷À¤úÑê9’°¿7ʘƒëFU/ţŸÙÊ—>Ã;ˆþ% 'ˆˆˆˆ £ '^¼|…á£'¦‡úµdyd²ŒÖ\ضÒrg—.Ò$™9–z¾e­aÞh)ž¼«U–vC«ÛÀ¢Y0žém+¿=õ-íðe€ú/îïN¯×¸ÀÒòLÖ4Ê¥†§ wÚÝÑ Üû3Ì-¿Æ„+â¶IØåU&źcë …s?-»eÓþ®>¿BN¶qzÛfÁ༲hxËïÌÁ7½¨7U«÷D‰ØUm`e^ ŽªÃõµY5SŸ—Ùå)øÂÚ圧aÏßo> ÑœŠ}Þe`Zy"4™’ú¸v·@3ÈB%;º9Á´úœ’¶MÄ6(äØ [ôî©üú4Ô³|¿pBq7ßYƳo"îͼÑÔ˽ð.c ·0è…Ú ¾ õõØ ÇÑþQƬDSk”í¡3oŠâá"ü$ÿ§…T!HrúU¶U-Ì;ò}–ˆòà """¢‚Ç(‰¿ïþƒ]¼¥víÑ[šâÔ™ó?zÁ!kѼ­»´nаÑÒ„˜¢ö<¥eâkGŸEÇèí1„F»ŸÐh7ûfÖ[† ¨%mA;”é®;o‚(e':;X£˜÷~Õºl†©Â1o@|÷IU+ê+XXÚà?b«Ú OTzÚ`bΔmÝ`kV>RÅ1'Xب0L?Ÿ MŽtÊ,knÓZ£qV\§>?³ogãV&×­óÊ¢á>eÌ‹Àu“8{…®´ãCQÑÜ­CÕ‰ˆúÚªfrmbPpkÃ@|Wº0þcQµZùaá¡ÒœYS æÌJ soŒ:•ËÁÁ¾,mìafn“JƒqD'œÈì¸)ØíU ¦U†á¸¸B¼§¿ ÷ô³I†÷4~Ú¾kB̬îÑáþ(c¦=…ni`Ùú.4×3L÷z”/V£…µ¾˜|M'(R>Y†_­ £ÁüûêJÄ_XŽn_”‚‰ia8Õë„«Nã©á—Bô¯a8ADDDTðäûpB|¨&˜˜6k^¦¯'Âôê5@Ú&`ú\iYÄñ“²þҲ۪֜ž1tÔ(Y¶Ž‰Mï-ñðÑci™R©ÄŽÝHËÚ¸yàå«WzŸz9nÏns³h¹Rk¢ÆÌÈÎaxM[X4^f0D~s&¾¶´ÃWÓndsX‡/׸ÂÚ¼"zìÕîÀ¯Ä“ÅÍaaÐð´‚­[tó9îÌþf–ÿUëHÆže`"-ºc¤Fòª6°l¾RõùLÎO%ç%6¼«6¼Å! ßZÙáóIW †uD·‚¥yM ’ZþȲQ)ÅsÝ650ð˜~L#JÅ=Ë¢C{„è4¨æ.Îuñá„ðÙ­]PÈÉ;õ²Å? ðƒUfûÐ’Å=’ßž…ÿZöhЕƒï"‹ëÉq8‘. w÷C­L‡çý;N<ù>œC ñ¢.]Ñ_e`ÙÊPi[q؇¶9 I˃W¯ÓYþ.ʘ­è\Ö…JµÄŒs¯ß2·A Žû}SÛ¸¢ÝÔLÀ‰á_À̲^&ÄTâÙ2gXX|‰±´š“ʬq-…B OqòÆNX­=!æULªo“*C¡>×;¼Q¼š/ Õ Tâù®ž(g^?/T7F39?ͶÙ>/Ù_õ©­áò˜ú=LkÃQí6uêuLoP4“ 1 ÕY‘ƒNðØ©3½¤Z2¶v)“²ý v$I½*Ù¼g8¡À“åΰ1/÷°X­ß îÌkëwMˆ™Õ=’©& 5©ê‹=±YýÆåà»ÈâzÞ?œ$ïGRv¨ìwÒ ÇÑ¿áQÁ“ïà qމNž½ÞÚkBãî½ûÒ?Z,Z¦³<öù 4oÓ¦ÌÔYþnJÄzE¬ñËòø¢]øOšiÓ&ÿO74þ®?¶¨{"(£wûšÐ¨¯Ð}f#dõ2LòV½J´öÀƒ9z•¨üê4Ô³¶EÅA8põ.î\:ˆ%}¢Z媰µÒoxÚ |íº(ýEWŒZÕ+çcp3Õ«D[®ø'£á)ÿ+ÛW„©Ý'h9tV„®Æ‚±Y½J4³p"ç…xìòª 4’«Ãyì „†®DØù8án*wl4>/lƒbßø`ââ5Y>[ý*Ñjèlð*QÃFµ(q³/¾hÙ£f-ÅêÍ;±iÕ t«ç“2Ý–éP y;órh4qþúû.®D¬ƒ“:¨T©Ôû ëÅ…_rü/:O\Šá:gnêÕ?Gõ"v™ìC[Ö÷èÕáá¨cg ›ê­Ðoêb, ^ŽÀ)ÃЩa=ôÚ¥ _²ý]dq=Ù '—Ãí[7ô0KÖmǶ+0ε.¬-k£¸î4¬Dÿ†DDDDO¾'<|„¸8ý±Y»}ç.’’ ß)ðèñ“÷{k‡ ñÎLëéŒ/*—‚•…5L¬Q¬Ê·hÞsþÒúC}ÚãpÌñiŽÚeœ`a]¥ê´D¯ “ˆÖnýeÒø×'Ä¿ôßÚ0k•¥…-¬K~†Æƒ6àêßKѨ°^óp]øŸz‚#³<ðU…â0³, §Oœ1 ä âõÛéÉcG€~¨Q¶Ö°¯X­†¬Æ_Úï Íäü2dó¼ †5á˜Ø¾J؆¥c-xlˆV÷.P öô hõ5Ê::ÀÜ®,ª7ôÆÔýuÿ ŸE£Z$»±}¿A§¢05³…UñøÚeÖ^}Ë›;d°gœ+ê–>cU÷âóx¸ÙEÄW®¾O8!=>ˆÉ@…¢ö0µ-‹Z-Fbóík˜ñ]‘Lö¡ëm÷èù¹Uêú*—t„¹…=ìË}Ÿ»OÂŽ4÷8›ßEדÝpo"ñ»wsÔ©P–æÖ0+\ÕtÃø]÷t†£ý›N<ù>œ ""ÒÆp‚ˆˆˆ¨àa8ADDF…áQÁÃp‚ˆˆŒ à """¢‚‡á†DDDDà ""2* 'ˆˆˆˆ †DDdTN< 'ˆˆÈ¨0œ """*xN‘QiÙ®“þ"""""2r 'ˆˆÈ¨¸w÷Áë×qú‹‰ˆˆˆÈˆ1œ ""£2~ò ?yF11†DDdT9†‰Sgé/&""""#Æp‚ˆˆŒJ\|<¦Íš?þ<¤¿ŠˆˆˆˆŒà ""2*J¥>–zO0  """*N‘ÑÑb 1¤ç à$™DDDDÆ‹á%1 ‡xˆsPˆ“dŠoñhÔÒ…Åb±X,‹e„Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòT¾'’’’±|Õ 1CüǾ»„í‚CÖ"9%EWDDDDDDD”åûpbðð1hÔÒ%Çå?f’þ®ˆˆˆˆˆˆˆ(Ê÷áDÓÖnpéä…‹—¯f»ÚwòD³6¥Ï¿z‡KW®!!!QoÏF@ñó~* óópW¡¿2¯ÈqeÖ/(YÝ[b”ú+ ””ã£ðiézn„¿;DDDDDDF$߇b/ˆÎž½ô¿•¸½ø9ѸÉÓ¥ÿnÙ¾"ŽŸÔÛòíRv{£ˆ©%þÏ¢†NÓ_îÕ–np4·«‡q—dú«ß_^‡ñ‘X½`?îë[†‹ß¾œÖ?+ á„2G–#"Z÷z’ Ae‡Úè÷g‚Îr""""""ú¸ |8qõúMø‡&­: Co½-ßN 'Ìa[ØC¦³X(cÚÞ …anY°Â‰¤}¾(Uy0Žä2J( H.!R>_‹ÖŽß`ÒU¹þ*( Ò…åS>œxÛ²w‘ ë†piWfúâÏ$ý-„Æë“•hf_-ÝaW Â‰xì[ f•2 ' žoêG«ú™†DÆ&55£ÆOAøÑãú« >rLÚÖ(‡½QÂpâ-¤pª!B† Šeyxî~£·…ÿ5†u‰î]Ú–™ ëH{pS½¡F)GXØ–D¹/ÛbÀÒÓˆÑRîaÇØŽø²‚,¬Qê‹·û<‚šÙ† ×±q´;¾®\Ö¶NŸ¶Dßà xþG~"Ç~«ïæàvÜ5¬òm„Jްª5'Ä !ùö΀6 ê¡b‰¢0·-…Jßy ààSHÍsÙe6­ qH‹‰¦¬Q¼ç¤ª÷mjã‚Ð×égñ^DŸXߦ_¢tX­ˆÚ}1ûà#á3RôAIÛVXvïV nƒÏÊ®·8JÕm…AënàM$e.„ G«zUá`ëÛRŸâ)ØvG÷“Òqlœ±äïKîÝUŠ9À̶4ª6ôAà‰X¨ngŽŽÿÅ-¬´®Ó¦ŸŒÅ9™zæeÐãí+È…ëL¸ #ÜðUåR°²´öYŸüÒã÷>VŸ'Qö=ñ-ÚuBcgWDËz(Û‘£'¤mÄm£cbõWý«N¼…*œøS.ŸÁˆÚvpìº:íqùmLÿÖ¥~ûOB]  œ-èRÙ•š£ßÌ`¬ Y‚‰=ÄÆp|2è^jÂå ìêYfBC¸~ÏYX¾vOíï+Ö@År6ºá„ì·, ÓâßÁkú*lØ‚i=¾Có’ø%èTGWU<Ðߥ ŠÖé€þ¦aÒâ#¦UH9ÿᅦˠéZ½›B¦Ã½Ž# m‹ÒùÜ8~“›:¤TGŒ@ø‘£8që”™†J¼<è‡Ú¶¶púÞW8N(V-›…¾ªÂܲ:ºlz’ÞÈ–íåP³vTiî‡ËÖ dÉtýÒ …,ëbèÉdÍN3‘ˆs?¢°y |Öeæ¯Zƒåsýá\£(LJ¶Â¢[÷^uœ²¨Q»:jµ…¹Ák<œ«A!û˜|A§íƒÂ ¡Ñ*¿1_Y‡sHŒ:HÃÙ‘ŸÁ¢º?N¤* É´3ZÝÍ‚¡ÿR ùíÙ¨oi‡/®C.ì-z…3,Í«£„öÀAÚq ¬¬Ýs"{½Ë¤L/ì|ñoÞh*7æü óôã«óZt,Ói< (îÍÃ÷Vöh¼,Z}}Ù'äwæàázêM¯G›±«ÚÀʼUíDÕh/ŒŸeô2¤Äà*¶(âµ7ó‰GÅ}…´öUéO‚‘ˆ^eP¨h7lQ¿XCuñzže„8"ås;Iù8,Ýî쇹rÊ—ë"|§E¾A¯Uç­iDàø©3é=$.\¼,…š`B\WP(•J‹•KEDDôoÉ÷áDèºMR¨ )Í?¨SRRá?f’Î:íÚ´e‡Î~>4œ€â.f7°‡u³G> åV±Ç'c.ÍõL‰¤-è`gƒ2}Âuæ"¤ìDgkóÞ/Íá öÊ0µj‚ Gz3 (îcnC­ 1ôkaùt*½á®¬Û X«§GºÄ¿±szo´ø¦Ê89ÁÚÆÖv(djFK5ù쇩áPƼ\7þí?íøPT4w@ëPÕUƒ¿ºíÖ‹ ÒÎ`X [î¶;‹pB†K¿îSSÃû9®OýfV?aö=Õ:ÕqJ¢ûý½¥áÈ€j0±qÃ:©“HöÉܺNeìqLw©;3+XUhˆ®“Âpñ'ç¤ãБ£Òÿö5o뎦­Ý¤ÿ—Ú (…B‘^bpÍb±Þ¯´Ÿ%†DDôoÊ÷á„è\ä ñ+ý£zÿÃHJJÆ㤟û ޵ÓkõºM¸tåšþ.àޭ燅PàÁïMaeÓóï+øg”±þ“¯‰ë2 '7ÃÕÖ¥û6 '’Uá„“j‚IU8Ñ ¥ÔC‹âü¢N¨~6©Ö+#NàØq½:‰»qâ? „Œ}>Īöåab[í'­Å¾SqõÆM\Û?Ÿ[¾g8q¸?J›Ë&Ãñ#iÇü¤F{ÛuñÒÏú þŒ íº„û4¾^æ!ä¸&…¿"ð¾v8áú{KÃá~UPè}‰\½Nb/íÀŒ^MPÁÖ ¦¥›bÊéxÝ^DïéÀ¡#éÁ­øßØXRÑ/cÑsÞ”tý&?9áÿ~pd±XR ¥gI|¦ÄgK|ÆÄgå6£'Dbð þÃzËöÝè;È_úï~CF 1)ã¯ØññoðÏýé??‰zŠ¡#Çc€ßHiûAÃF§¯ËÝpBøÇpÔJ4+logý…­žå`ùC îHmáL Ù9 ¯i ‹ÆËðDX‡8Ç„¥¾švCÖñl™3,ÌkÃï”^Ÿ~Y$F~b«5¬# ;=K¡P1lVµ³` h(þ™¬lQsÄ9ÐDš?ã=à ÅÝ@|ke‡Ï']5î-4ü-ÍkbÐq­á9j´k(ñ|u{X›WC¿#úqOvt/ “bžØ¦3çCa4 z¨;¬Bù Kš9h]—±Ù 'þë¾å;kб’ ¬š­@ÿ-HÉÑã§pìÄiýÅFIl$Éd2l9±Å]k¡PÓ(Ô® L;•‡i× ,ëCKx–ÄgJ|¶ÄgL|ÖÄgŽå&£ 'ÖmÜ¢êšÜ¦cz‰„Ý¿`Ÿ ßÚZ£x·Ð¼ #~]õ\ºçdp®¹t†ÿÔ{-üžYý™¸ÉÑD:4=&6ßËeaÒ¶ŒaÊÅb}´Ÿ1ñYŸ9ö  "¢Üd4áÄÝ{÷Óƒ±ç„~0!'ÍÔŒ©».k ±×Åû0'Äbcg1µÄìÛ`ÅSÍÿƒÎ,œ–Fï†wµÂ0¯Ð}f#dõ2LòV½J´öÀƒZ¯Á6¯j05/‡ïzÍÄÒÐ5X<­Vûµ«Ú¼Jti«ò0±(‹¯=ÆaöÒ•XúûŒîÕõZâ†tª™Éâ\¶°¬ãƒÇnàïÛ‘Øè…/jÖ@žJD­j‡ÂfEQ«ót¬XŠ%»®KÃP ÷­DÜ±Ñø¼° Š}ド‹× dùlõ+6«¡³þ+6³Ùh7”‚K³£ˆ¹ê¸ÇüµX8"ýU¢¿ßÊèâ!Ç¢ÊW®ˆj­†cvp(–ÏŠ&•íQ¨h#̺šq|ù­9øÆÆÿõÅìõX¹ò îÊ3;×\¸NÙeLiñ :ž‚ÀàMغm‚†;£¼•¾˜t œ“(ƒfn‰g/b¤¿æ2˜`±þŸ5ñ™Ÿ=Í\DDD›Ñ„×oÜ’&ÃÌÊ©3礙èÅPbÄØÉ9œÄíD×’6ppÝ€Œ¹&3'DiÃ1ǧ9j—q‚…uq”ªÓ½‚N"Zÿ/âÉw°y¤ >+WLhè:¢än˜xàŽ ÿVÚá„´í=ìžÑ ¿Ö©{[˜Ù•F¥ú®è¿ì^Iç”Y€ ¡Dü…ø­aM8XÙÀ̾"ê¶w/`—ŽZá„@öÛ†9£º“Ì…cÔtIYî[ØÓ+0 Õ×(ë(n_Õzcêþ‡: ìl7Ú³¢ŒÃåucàR¿íìaU¼&êwœ€Í·tÃ*U8Q}vœÅRŸ_QÉÑ^8çÒ¨ú³/‚μÔë­Œk«ûà‡ÊÂwd%ÜûïpF–Õ¹~äëTÆâðŒîhP» [ÛÀÄJøù´zÌ9‚(ýߢÿašIùĮ忖º›ë7 X,Vî•øÌ‰Ïžfx ""úØŒ.œÈŽˆc'Ó{Yˆµÿ`¸þ&TÀiÂoýp€ˆŒ’¦×DZZš4QŸ4ÇD& (‹•;%>sâ³'>ƒì=ADD¹¡@†¢#GOH¯]·i«4F’þ·dÙsˆŒ’f®‰””éMœü’Åú—KxæÄgO|9÷å†NÐÿ6†D‹fHGRR’ôºCƒ†‹ÅÊõŸ=ñä›;ˆˆ(70œ ‰áQÁ¡=ßà +ïJ?œ`@ADDà ""Ê״ÉÄÄD†,V•øì‰Ï à ""Ê 'ˆˆ(_ÓL†)6ˆN°XyTâ³'>ƒâ³ÈI1‰ˆècc8ADDùà +à ""ÊM 'ˆˆ(_c8Ábåb8ADD¹‰áåk6œðø_oÀ±˜×Hyò 9Ö ‹ŸÅàL˜3,õ·geZf½¼[ðžår1œ "¢ÜÄp‚ˆˆòµÜ 'Ì{5G—­[pèa^¦Ê„c$"&æ*þ<6üklÿ1Ëvr0îÊÓp÷Ü,xõƒ×’N¨Ò«V½|ƒËÛÛÀ*“ϰ ËÌ×ë^ÅãÂV:ºU UfG·1ï÷˜áå&†DD”¯åV8aë?ÛbS…ýÇãöÕ-Ü6 £7/Äï§OãïøëSÅà3¯*ãë· K‹€OŸŠºë<*ÂÌ`{Ö[‹÷̰<`èíD þê£Ý†DD”›NQ¾–+á„· f?Iƒ"ù æÍªoÐKÁ̳ªÁ²[UÑôP4ä ahå©¿ŽÅúð²4SSN‘Ñ`8ADDùÚÇ'*¡|p”‰8¶ö§l °èQÇÎàV\"RR_ãуCXÜ¥<´¶óøƒn&ãÞ>7T™>ënßÇóÔ4$%<Ä©SñkßJÒvCúâ÷ë÷ñZ¡Õ°S&`}`5˜zúbsšïi§ŽTD±1#°äú]ÄÍ„¸¿qè ?þ;p v¤¾ÄÊ™UUÛuo‹YOåH>?:ç_µ7^Dšì8úôW÷Òðî‡m©¯2»ÊM›c‘ ‹Ç®¥_ª²uñße+°çÁS¼JKEüËkص«j÷0¼7úe3È£OœÃßñ‰HÎïÎÍMð›ü%Ì5Ûdó>9Îß犗XX[ï•P)ä$’å÷0kBÕµ÷ìÖ®Vêï²2¾Þ~ Iw¦ fÏÆð:|ÿ$¦ )j tW}¾ôıXr墒RœëW7`è´o`­}éŠǯV¯Çá¨çH”¥âõ‹+ؾ½*KûR•õôuxšz>CZ¢çÁ“¸ý&i©/pëjº ¯ ëÝ1îd$þIHAjÊsܸ¼îC«é^Wvîy¶Î©"JÏ ÁåD9t¢Ù9 ¬º·ï[ 'ˆˆ(71œ "¢|í£‡Ÿ£Ç…$(“÷¡k/½!™”yÿÞX+CÊóãX¸Á^KÇ`|Ä_ˆ‘'ãÚQ\PHî4¤EßÁ­——±vÛ8x/¿}ÇðH®ÀË CQ^ØÖÌçG|ÐÃ"_BžŽ‘Ó;â×|Ú»b¦á„…ß(üùFäèC˜:^+¦`þ¥ûˆŠº‹§ò'„ð‘ƒ!ˆLxŠcÇaüæYèä_YX__‡E"Nþ GþœÏ ~ðÙ²×Säˆ:Ó_ºý{”~¯úùb­p¯âïÆÄàþp_<A7b K½‚Iãjäè>™þÖ¡qr?àz dà¿bc<‹p"%f'‚Î?Å‹Çû´uÆ­rGiŠ(>-×S刾³AX6^ÁÓðûµ(¤Êc]PýŒE½ßGOþFÔã}˜¹v8º/‹©çï!Q™„ÓÁB½­NÈŸãúãû¸|z. ßÍÛq%YÔ¨ü‹ 'Ñ_XÞ{«x/Hº3ŸvÓ\W6ïy6ÏÉn@K4Z²7å2Dîë…FS„ß±É-PÁKë>¾G1œ "¢ÜÄp‚ˆˆòµN¨ñi÷fh5³ªhôç#ÈSÏaøpí9(jáû]7‘&¿I£Ä}…ôF7±lƧZ]ék¡Ù¡'Â>Â3= Qëx³IwX‡A8Q -ŸBžr~CÕÇ«Û÷èsåpo>$œî­ü>‚çÔËhwØáø#Y†k»Ú¢°Ö>j­DŠü¦Õ:ª†÷ÝGZÂAôèŸñz3/¬z)ÇëóQRldgû>ÕBËpqèËtðÑ:¿AãqTø]¸¸¥‰*È"œšÏx}=_h÷<èÖÈöh1¾×^îù+†ÝL„üy(šh–«ï‘"~<új…XÝ[`òC9Ro º:4 á÷4îÒpTHo*ãó-×&œÇ³“¾ªk—–WAý·…ßçS0@uŸ²}ÏspNæ㜜Ã:ˆˆÈx0œ "¢|í£‡ž±ä•©×F£Ü[zH%4øg?“#åŠ?Êèmk1t*ÎÈeøkscU#YÝèV¼\Æ:óHTD©åG„Fæø§ Ù 'º·ÇÜèÌŽ_%–Fò…J$]¡w*ÂiÉA$ â™ëÀá·ZéUlâ"\—§áHˆVïíêÞ3¢äHˆôC9­Ï9üV^‘I¿þâµæà>ÙM Å#ù+lœÿ©z›J¨ºæ,Rd—0j˜vƒ=“pBñó&ë°:§ÅÞa½4Ç.)ÜÏ$Å,˜¬¡Ô÷(þLoÑÙ¶ÚžxÙ³¥ø^=´CN6s·!N™€°ùºoÌ(´Iò[˜,[9¸ç98'†DDdlNQ¾öÑÉîí0ç™iw§¡ö»zNôèÍ©r<9ØIw>±<{b}²±ÝUë4ÃþžŠZ:û­ˆâK n­uvÃ éøŠLo=m ¢NÇÙ«=·…jÛ/·Þ€LÿKH—YÃ^]¹nIͺ±ªLÞŽöZáD¶î“g,ˆ•ãåé>(&õºh¿Û©H¹=9½‡@–á„ì0¼}u‡íXOY'Šdl R1Ñ*«I+p_‘‚]‹ÕAH–÷¨Z}YÌrü¨N(^#t¶nb3{ ^ÊŸ (@÷Í/6ów#I~SLjיƒ{žƒsb8ADDƆáåk=œðø ½®¤@™¸ n=3Y¯]=ú n¢v6L½TáDtxWp"Uh8WÓë`ÐèÎQ8‘y8b5%²9çD°Ë™† úŒmëm¿ ™ü*ç¹¢á$½jz öRõ苭½Š=?¿|Î?Nø%´†ud÷>5Ø{²Ä½èÔ«"̇à¤,á!ßÌ aN¤@7­á bYO E”2[ƒjœ¿Õä`)œØñ»zNƒýjÊ0P…Zß…ºTáÄãŒÞšåzáD¶ïyΉá†DD”¯}ôpBh Ö\‰Teö,Ëbˆ‚¦º·ÆL¡ÁŸru”ÁUãO†ó›~ÕÖ‘ÝFw¶Â‰î.˜#Gò¥aºoÊaÁÄë ëh…éQ¶‘u'ìZ<…<[áDE”ZŽ$Åcè6´ßYÝÛcŽ8åêHƒ{¥S9ºO`9v!nÈãô9ª¯=‡”ÔôÒ\‡X×’u8!†§´ç«Ð:véåG¬xˆy“´‡udv ƒ€ 'rpÏspN 'ˆˆÈØ0œ "¢|íã‡`ÖÛë_) ˆ;ÿquÞÒxSMò(K½€1#µ»ì×Bƒ=·&¿i0!föÝÙ 'ºÖF›c±'G¿Á vS/á~ú9Úá„Ç÷,_öp¾ÔzÍ¥©W{ÌŽ’Ù '„üÀÑ8”"dzóƒQé]Ã^tJ=y¨ì¦MÐcA§rtŸ„êÞ “¦!öÄXŒ¼ž‚ø¿† ´öç ®%ëp´ۯu7²¨åøé7­åž0âvR&bfv ƒ€ 'rpÏspNæƒ&à˜ì-ÃpÞ£NQnb8ADDùZn„ÒˆÓ–"2Ih`Écq!rfl™‘›æbÆÁ8p'®êÆ«YoG é'±h£?<—ŒÂ8õ«D¯ïïbð*Ñì5º³NT€•ÿ$D$*øt?¦®ˆ.‹Ç"àô <ˆ}‚×: âʨµö”¯pbï@ü<ª1¾šì‹ÑgïáÙ›8(²NH¯µÜx¯”©x|c&†ú£çб¹+ îB>Y¿~U|•hhŒÐpM¼…m{§¢ïŠáè³v.žû 'vº¨®)G÷Iu]uî %. OÒâ°õ÷ÏuÃ$ƒkyK8!£èäE¸˜"GìÝM¿j0Öߺ‡§I©Â½ODôÓsÛ3u½2Ù^«¬úwÄÀ‡qñù+$ÊåHJˆÂÅK0|êת†Žî“ª¤7£È„߃7º¯•ÊàZÞNˆU %'ŒÀ‚ ×ñ81©©/pû挚ñ­î$“ûÕ”aðááD…ìÝóœ“ÄŒ‡u÷žâ,ñ/£ŸöëhߣNQnúèáD£–.,‹Åb¥×‡ÊÍp˜ËVhø¾¾óõ¾,Vnà ""ÊM=œ ""ú˜NdRŸ¡MDä)‡àý–a,ÖÇ,†DD”›NQ¾ö¿NXÏ\‰Ó—7aÖ¦±ð^ÔƤS7ðZ)Ã?;ÃQgh‹•{Åp‚ˆˆrà ""Ê×þ×à ‹Á=1íÔ)\‰}‰™rYž>9…Ð]QQû­,V.à ""ÊM 'ˆˆ(_û_'X¬üR 'ˆˆ(71œ "¢|M;œHLLÄÿ5`8ÁbåE‰Ïžø 2œ "¢ÜÀp‚ˆˆò5±$–&œ0ùÉ ¦Ê4œX,V.–ð̉Ϟ&œÐ<—DDD à ""Ê÷4áDRRJº~‚BíÊ6žX,V®•øÌ‰Ïžø j ""¢‰áå{Úá„×ì(Ô´„Aã‰Åbå^‰Ïœøì1œ "¢ÜÂp‚ˆˆò=ͼ)))øçñw© “¶ì=Ábý%>kâ3'>{â3Èù&ˆˆ(70œ "¢|ON¤¦¦âÍ›7Xsh3,[”e@Ábår‰Ï˜ø¬‰Ïœøì‰Ï à ""Ê 'ˆˆ(ßÓž399¯^½ÂšƒaÒ_sÅîæÒœ$“Åú8%[â3&>kâ3'>{œ “ˆˆr à ""2 šÞš¹'ÄÆÒ­{wà9kJ¸Ô–Þ$ð?8²X¬©ŽÒ³$>Sâ³%>c⳦™k‚½&ˆˆ(·0œ ""£¡ (ÒÒÒ¤ÆR||<^¾|‰ØØXÄÄÄ ::šÅb}`‰Ï’øL‰Ï–øŒ‰ÏšøÌ1˜ "¢ÜÄp‚ˆˆŒ†¦;¹¦…89ŸØpÇÂÇÅűX¬Tâ3%>[â3¦Ýc‚áå†DDdT´ ¹\.5œÄIúÄRâ¸x‹õ~%>CšçI|¶ÄgŒÁýN‘QÒ4–4A…&¬`±XVšçIû#""Êm 'ˆˆÈ¨i7 X,ÖÇ-""¢ à """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòÔÿ|8¡¸¿¾m\Шíìz¡Ô_?È®a¡g4rî„É'Òô×’ZÊåPøtì ×aÛpG®¿–ˆˆˆˆˆˆò+£ '”I±fûz£U›Žhéî/¿Ú~±)0œÈŒNnCèŽ ˆÉ·Dùçw†aý‰'Ph-N<û;Ü[»¢¹O(®Ê´VQ¾fá„òÍ,Ð[º ±sg¸þÖ]:£©³+ÚNŠÀ«h@+n1ŽpÂë_ '’#1ÛÃMz¬Ê—|Ù­5ðjíŠÖ³ÏA÷n(‘–øIù𜉈ˆˆˆˆ(kFN¤âʲ~hêì‚Æ'cÓí¨"Þ<¾‚+SÕÛ)‘x7‹&ø¡“›;š»xÂÃoVˆBJú¾”H¸½3†ø U»Îpñ‚å{C1È œHÆý#¡?ÈíÚwD ·^è°GÓeH{kçNBé3Í:ü†žã‚ñç?Éê-丸ØMœ{"ðÄuì ŽîhæÒ¿MX‡S1㔯®`ÝT?¸¹¸ÃÙÓ6îÁ¼Þno'dQ8¿þ}à&\³vÝÐyà ¬8ñLÝ€WâÍÁ©hÞÒM}×â–úpÊøpŒj+\ë¾#Gêƒ#˜;¤š ÷»QKu©×IÛ¿¾íAáíÑÍÛv«ÏXÌ »€˜ô@@ë:]ÅÖÙ#йƒ»tûÎÜkÏïã qèæ®¾ö‰p>ý¾+ñúšp­F¢‡§§ÔC¦U—ð›·×âÅm”xv*C=Ý¥ Js~»,Æyáøi'æ ¥pÞMûxÁ¹;Úx ƈß÷áêkMŸ‹ì/DDDDDD”;ò8‘v ®B#Ô ý¶Détã×&¸~nÂvÎnhûÛ Ô­ÅŸ½1þp¬h(_ŸD@Wq_®hÚ±|õ‡‹$ˆ ñôpB‰èƒÓá*|¶qÇ!ŒùSüÐNø¹I÷ œˆË¢wEâÌõöD·!˜4{>Æö÷¼BC¹Ç \”2 u#X8vóvBc½Ë` ò ÷vªói5îžK‡†£=T½DÚzÁkÐ0xˆaCñZÞN(_áДßàê;£g,ÀÔ±ÐF tÚÅšÄvv .ïÀL_á<…ϺÄÈÙA˜1oŽF '—r +ûwÎÍÍ:À€a~po¯ºçÝDBukt¯³e÷¡4Xü.Ä Áή]Тó@ :í[«®Ý-0IêËH»½>}ÑgôLL›3 ý=:JÇsq ñJ9nïYõ9xÇô¹A˜¹$â%„Êxœ ê#}¿ÛýŸ¡£àÝE 6\Ѳÿ:\“r£l~/DDDDDD”kò}8¡ŒÝ?±gC«^Xx%«¿b§!2è7U£Ó7žH›¥áîúaRôIÏÕ¸.SâåþÉRãµqÇìy¦j°¿Œ˜ ípB~Á¾b/….ðßù1±Ï{ýÝ¥!%c¿Q÷Ü0¤«¢¥RYì~ ýmü°æ¾¸\ÓŽßiIcQ”x¶s,š‹çä¾gdâ0“­è#]¯¦x-Kþt†º¼#œ( Õ¹)•P¤ýƒÐþÂu´tǰ½/…åÙ 'T SŽÎB‹–b°¢=¬C‰ø#3¥¡q§™8¨îíti9ºµ–µ‰MOô®³K ŽŠ‰…2Ggxª×IØ-Md‘†k+ú¡©°¬Y¿0ÜKOPßFázäH86G:f“îËqQ:"ª¾kýaúá„2z·î[+_Ì=¯êq#T]Ä{Ù£Š÷7{ß åžüNˆ L©±î›u8¡|ŠM~bCÜ ¾¦÷®Ý\î­Äàa"v¿ á¾R#´ù˜éóT(ãa¤Ø8W‡bc}´ø³Ö°ŒrC¿Í™õÞP"áïCçw71ÄÐúLz£?£Ü|üau/á/.…›Øv Äq¡¥vvÚŠ?»â„f‰â 6 ~û°åëëØ2o¼4Ü¢™Øc$ýœ;bÐŽ˜NÈq#¸ŸêüGÿ‰ô‘i‘˜é®jìHç¦uÂñFÚNÖû¡™°¬™ßªFè¸Cx­ÙQÂŒn§NV5Ö{`Üæ³8}ö¼VEâjTÆ éR¯`4aetš°—náæå0U¨bN¸¢åôÓéjÙ¥ep×'Îh‰ù8™Þ ~†0¿ŽY‡Êx™&öLßTˆ­§¯áÆÍ³XÔO¼'™„½Öà¦&œÐôLÉF8q]îd+œÐ¾Nîo¦ 'Fþ¡~»ŠaX"¿»>ÒðO Yó×ïàÚÎiªûñáDÚÙ Õg³N¼ý{!"""""¢Ü“ÿà åõW4ñšƒ?iMo™úžÅ ÍÜ4¡ÁÚ3ëa¿©†u¼Ø7-ÄgçÙ—Ò %Îýw)ÀР븃½Ä^ nè¹ê64ÓYŠdqqHÈ${wH“j¶ê‡¥êV¿üú¥™„Î3²n+4ŸkÕó/¨fbP¾<‚qß2¬C3¥¥úo}ª gR¯aaZá|t–jX‹û\M7R"îØ´{(h…©B#ßYÜÎc "Ó§Ä›ˆYªy,ÜgààKÕHº´B=¬c6=ÖÖ¡}Ù '’ŽÌB‘ÆîA8-}P‰çÂw&sz8¡&L9é2Ôôà eô^ 玆u$J÷ cXGg½aoÿ^ˆˆˆˆˆˆ(÷äÿpB :ˆ1];¨æ,hÝ}«ÇohÓÆíæFJ Jùã}.ýß í|FÁϯ/Ú¶öÂè?£U ÓßI5ÑaËnÃ0t”?º¸w’æО3æð,¸ ŸmììÏј=o&Ž ×sp4³†jÊ_˜%N´)»ËøÕذq%F÷ì‚&âðІb/-þ]T×êÒýGG/®h!ÎU8¡ŒÃŸ“ÄW­ ×å;+ÃÂ0¤p]Â5h…Š';1@l¬;w„ûðù˜7ozv·Ñ 'äw7 gk±÷ˆ;:¡~£±\lí§ÞÁêAU=4ĉ#G Gi. áºçžU•Éì:³NÈo¯§t\o Y¼ëWÍOGÕðŽŒpB‰è]ãT!S/üæ?.ÁÑ$ÃpBì%¹¨¿jBÌö>ð1>ª 1[ô^ËRö“Ùùfò½Q®1ŠpB”}çMDÏnhÞªš‰¯ 4 WÚ¦kÞ¸yûŽþ&Ù&…æàs@¯ŸpƒkÙ¤ÒD¤é®Ê/N‘±0ªpB4cÎ)x¸xùªþªtç"/HÛÌ]°XUŽdN Û=Šã?v]¦™ò"aÚÛ–‚çž$Dœ‚¶Ÿ—‡µZ‡Ä@©ŒÇÕÍSУeÔ®XVVŽ(^£z,<ƒJÍ>eˆû¬êÏÄ…[Û1ºÃw(ïès»²¨þko,:ÿ雪¥ü³S¼š¡vY'XÙ8¡dµoÑjÊ1ÄANXT@Ïm±vh[Ô-[ VÅQú³6ðÛx ú±NÊý?0¥Û/¨ZÒæ¶¥Qí'Ì9í‘+©Â6»ü,mcfQveêâ;Wl¸-ÓÚŠˆˆˆˆˆˆ(gŒ6œ8x8B (2+ÿ1“¤mÄI4?D–á„â>†i øKÓ.à {üÔg ê«‚^£11`6\7ˆÃvŸoðƒÇHL_²a›VbLëš07¯Ï]qêÐAN˜9ÔA­*UÑÀg~Yƒ¥3úà»R6(T®'v¾Êˆ'7 Cy[˜”øGÌF`àtŒðí¯¥· ‡&œ(ª5k F똳rÖ®˜Žî_•@!ËÏ1ütÆ€EÔVt­l»O»`ü²Øºý®S»o0î¬:ÆHû c?³‡euWŒ^²›6„`~À¸üÒ ¡Qú± Qöm8ñ®òî=ii6æBN”ƒÇ¦GxúôžF=Á½k'°~Ls”´­î[¢2zHá„%þcQÞ»¢uzd*n܋٠Lßp¨¢U8ajj‡º£#‘ñ%ž¯wΣ zìÓ ‰8ЧLlÀÄ ú} T¤pÂÌ 6ᮘV¨)î/ÂO6¶¨1âT¹J ŽùfÅÛcå㌳V¾Þ¯ò6°o¿±Jásãg+{4^úÌ ч0Úp¢E»Nâ?Ö †žˆàÕëœòáSUê¾­#ã­boŸÐºC#¤p VÍV [ dç1¢–-lºìPOª©',¾ÂøKºÃ$ä7gâkK{´ y© ÒNÁ¯š ,š,ÏòXªp¢0~YôD7(I;‰ÁUlQÄk¯ê¸²sðÏ£ÝjD½yƒ7éUíŠÂ¤\Ó“äô«l«Z˜wä‘VxBDDDDDDôaŒ6œØ0\ÕG§z[GI´˜¶ûþøS¨}ؾif l‰Š¶Ž¨é†ûš^ R8aƒªCO࿆2—7L‚G“ú¨R¦$ìì`i]¦bφÎÛuà ë6~®›8Èÿž‹o,íÑ,8VN$n†‹­5J÷9¬îuaH5!f tÛ¥Ò¤Á°¶(Üm·ê¸I[àjk¥ÀdÔì»c›´¡ñ–£Û¥`bZNõ:aĪÓxjp±DDDDDDD9c´áÄÛ&ÄüX²œs2Üœù#,ÌÊÃ{Ÿºÿ„NØ¢ÆÈóêái¸:çWØ›;ás¯¹Øpè,.^»WÃà]ÙÆ0œ°qAèkdN„ DzF™>š!!†²|[‡~8!ìK :еBøñ8¦WÇOÝ‚NV¢ˆÃÍ}‹0йì̬áðµ?öÇdÑ}ƒˆˆˆˆˆˆ(Œ*œ¸~ã–ôzP1œÈêU¢SÖá„Ðøßï‹âÚÃ&² 'd‘YÛæßâoíñiGзÂ{†iÇ1°² ,š®ÀÓ,rl‡Ù"’¹4<Þݵ,íPoêuiN""""""¢÷a4áDLL,Z¶ï¤3é¥P䦬É7ˆ\¦fåàµ÷='ÒŽa@eXw>•!%r<êXd2¬#;á„°§Ý=*ÀÄöG\É|nl‡HÂáþ5`bý_Œ<­}†Ù¼=JÙ¡²ßIá,DDDDDDDÙd4áÄö]{¥@bùª5HNNF§î>hҪÿ‘ãmTsN8áÇ!˱lÅJ¡‚±hÞT r­3kØ7˜…‹šl «pBù [º–ƒI‘´ñnÝ»‰S[¦¡í§Ÿ Bé÷ì9!-[æ%­aZæ'ô˜„EKbÊpo´±_Ú_öà ñU¢ÛЭŠ ýí‡ÎÆïµ. œŒ~®?âûQªàAñp9ܾuCß ó°dÝvlÛ¸ã\ëÂÚ²6ú‡'hˆˆˆˆˆˆ(GŒ&œØ±{ŸNÌ \ˆÈ —àÚÙ ]?Ê[9²bð¶S+˜XC‰š?ÃmÔz\z­5"«pB Œ=Ž™]¾GY;áóN(_¿¦y„CªÁî=à QÂ-Óé'T-é3 ­ú:/½"?'á„´øIæ÷kƒÏ+•‚µ¥-¬ŠUÅç-|1óðSÕ°•7‘øÝ»9êT(Ksk˜.‹j ºaü®{:û!""""""Ê)£ 'ÄaÍÛºë ë?y†þfDDDDDDDddŒ&œ]½vÃGOÄÿ±˜3õ7!"""""""#cTá< 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(O1œ """"""¢<Åp‚ˆˆˆˆˆˆˆòà """""""ÊS 'ˆˆˆˆˆˆˆ(OE8¡T*±c÷>z"†ø5(¿ã°nãÈd2ýQ>gáDÈš hÔÒå5täx¤¥¥éœˆˆˆˆˆˆˆò1£'ÄbøpèÈQ\¼|5Ó4ltz@‘’’ª¿ """""""ʧŒ"œ‡nˆÁÃÓgÑú«Ò‰Ä¿‘Òvâÿý €B~¾´Ãÿ™X¾¥lQcäyp ч)0á„è£Ê8½v!æÎ[ ª9ð}Qk˜×óÁLͲy r" ýÏQŽm8qòôYiù©3çµ¶’SR0dÄ8i{q¨Ç{ÚRÃÑ»œ l:oGŠþ:"""""""ú FNŒ -3qšÖ–*⤘âÜâúÃF#))Y“œÉ*œPÆãêæ)èѲjW, ++G¯Ñ=žÁ ¥ö†€âÙQÌìÞ•Š¥C%Ôm=®_Çœ†Et†‡¤Þÿ»üŒª%afQveêâ;Wl¸Í$DDDDDDT0m8q┪çÄÙóim™A (†š>Äナ¬Â Äa»Ï7øÁc$¦/Y°M+1¦uM˜›W‚ç®8¤çÉabýb(äð%ÜÆ/Áªeðm„r뢚£]F8‘öÆ~fËꮽd6mÁü€!pù¥B£ôÒ""""""¢Â( ?õ0'QOõW½UjjjzŠ~ƒý¥!ï%Ëp"qÛà^Ìeú†C5 D‰Ø5`o^mC£´æ¨HÁ¥€ïaaš1±¦âÑbüleÆKŸeDDDDDDDœQ„Ë‚C¥€aÔø)Ér6¼AìA¡yièºMú«³''á„ì*ûzneNgé{w‹ÚçKªÄÉßQž4õ n]V9^Í¡ ž6»uœ§ÊïUVÓ®C5zÚ|Í›1^+¾!O÷\j¾Ú²š&Ï ‡ ' (lƒ‰-ÛvXÎzF±…aB¯mPÿê(ƒŸœ–¾ÁV©WõBz9¹Ÿ\}3)WñÖšº{Š*ûx+gäȉÛ;5²~qåÉœZrñÍ l…j©Ë¢“OÞ%å°á„áþýûjѺ½P´j×YK~[a†F­ß#&þe¡×ýõ…§ò÷b= À‹Ë¡Ã C@À}Óê3 0*^GLü«BtnRy%sÍ Ú‹-+eðqøp¤ɿÌR÷ÞuèðQk³Ý…ÞøEuÞ-¯¯: Ò(ÿéš>c²†´)¯l>žò,ÐS;˜³x=áD‚p@?·¨¬rgQR9¹ùÉ/óÛúüëQÚpÕvR^<„À®'€]N»"œvE8ìŠpØá°+ `W„À®'€]N»"œvE8ìŠpØá°+ `W„À®'€]N»r˜pbמ}jÔ¬•Š–¬¨OKTxª*YþK]½zÍz)€8D8qüä©8…¶µgß~ëå@âáÄ a£Ì aÙòUÖ¦Gê7h8áÀ!‰Ví:›AÃ¥ËW¬M”`ɳú±¯\ Õ‰k#/‡'Z¶íd—p"`q}%uv×KNFy(±{2%ÍœOÿû²³¦ì½¥Pë Oƒp€hžÛpbÆìùæ9ç/\²6=53œpI¥šÐðGiè~j߸œr%÷TâdE5è` õ”'#œ šç6œ0\¿qÃz(NÌpÂ5³¾Zþ Úñ;›~P.7/½Új³âONø=xð@í»ôÒêu¬M1¬Z³Þì{çÎ]kðŸz®Ã‰êQá„–ªv yTž£{á´¸nZ9¥ûZ¿Yºnj£W]“ªòœ€ð 'îÔŒUU K:yz$Wª×K¨é„ÝúËfîȃÓËÔ­ú'Êš&¹\Ü|å“þ ½_±­¦ zØ À Íe¿(WÍÜáhíúMÖæ(kÖm4û}¯°å2ìì¹ '"ÿEðŸü¥ûQáDÈé*äé¥,-7EŒœø‡áDÐ1ýT"ƒœS¾¯º}'iú õ©÷~Øk§QáGdF»Ô)o¹g¯¨£§kæt ëÙR 7Òä‹Ï´ú€çÔÎÝ{U¬L}Vª’6mÙfm6mF£/`oÏe8±ô÷•f£*×lðÔçY…‡Usæ9]ºtYÏ×®åÕìý´rJUR£ŽGöüGáÄ_óë*•[VÕûõæÃE6C/ib™ÔrÊÐXËî†vî'}â‘DEÇ\~¶…8¼P¶nßiFmÛ±+êxd0a”ÑHž»pbá¯Ë¢‚‰Èªøe]¿pÑÚõ‰¢ïÖQÎ~zµLÍ=rǶç?'´¤~9¥o¤…7nëöíȺ¥CƒŠÈÕ-¿:ï ’î¯U³,^òx­¦†®9§û¶/±Ø°ykÔ‰Ý{ö™!Ed0a´ …C…Æ4 ãgÛjýC]¾rÕì7ѳ_ÙʵԼÕæÏºö6ÿ[®jm]¼tÙråÇ ß­#¾è³DK—ý®%ÓZém/oeo¹AÑ—ûáDÈy(â=±-×j±Þ˜<ª¿wS­|a¯ãì«Tù«©Ý¤-ºç9¼HV®Yg~/[UŸ—®lþl‡ 'U»öì3û 5Nå«Õщ“§5pèH³mϾýš1g¾ù/…‡³\ùñb®9qKËe—“Ï'p$rJ‡ÙóÑáÄúVzù±áÄ9 /ì+§l 5qíF­ß`©;uâ–ÍDŽ[:¼t”Z”Ì#Oùh«ß®2ÑÀ£-_¹&êûÒøHh*œúã3l°­³ç.Dõ ÑÍ¿n™?ò£yÎî½DµÇUÌpÂX s¬Š&õRª*3u9*x ¥õÓË)Mc-µ„·gW—óc ÝÓÂ:i•8EMÍþÛöÌ' ÔùÅÍôš»ò÷>(Û¨¬Ömجõ·X ‚C„m:t3ƒ†ÓgÎY›©d81ªâYÄNH÷µ©Í›rvË£o×E®;¤òÉÙó3 ?k³?hè--¬û²?6œÕY5•Â5•>|P½žÎýßT/­²´ŠÜ5ÇãáÄèñþfÐ0dÄè#'¬>†àß9a½>òHõšÞ«ÖC‹öþ¬²>ÉNîŸÔâ~T$Ï+Jâå-Ÿtz¥`E5»]7¸½S#ëWžÌ©åîê)ß ÊV¨–º,:·Ñ$0N®ß¸¡Ÿ§Í”ÿÔ¬¹ ëïÛ·Íþñ1rüû&œˆ«øXsüûžÛpbó֪ݰiÔî aznà à'€]N»"œvE8ìŠpØá°+ `W„À®'€]N»"œvE8ìŠpØá°+ `W„À®'€]N»"œvåáD`` V®Y§>‡©aÓïTºb •®TS_5k©~ƒ†kõº ²ž ‡ '¶nߥêuëÓ[µ4Õ®=û¬§€ơ ÿ©3¢Â‡Ê5hìÄÉÚûÇݾsÇ,ãç1~VÅêõ¢úÍš»Ðz€8L8aLLž6S<°v‰ð@ü§FõŸ»àWk—$X (¬4Ù¿Òœ«¡ÖFž{NlÙ¶Ã Š—©¢­ÛwZ›iͺú¬T%-YQû¶6ÇÉ_óë*¥‹ÞêuPÁÖÆ§xp®úÏ9d9?H{z¾§$kê—Ë„€O‚'îÝ»oNá0‰¸‘~[±Ú<·n£æÏ¾HfèuM©Z.îÞr~½³vZ;< mk÷†|¿œ¯k“BB.xA%øpbÑ’ßÌp¡uû®Ö&s‰ã'OYGªM¾3¯±aóVkóS ¹0AŸû&SÑuõŠÇkj±>f¼ðD÷÷ªËÛ>òŠ5œ€øaLykߥ—¹kÑ“¬Z³Þì{çÎ]kðŸJðáDû.=Í`aýÆ-Ö&s cÚÆ£Ç›#,eþ¢%æ5 emz !:9¬ˆÜ=?ÕÐÃËÕ8“·Òµ\w¬Ý„Þ>¨jêƒåã•T~óèÝj?iç‘)ª–#¹9¹ë¥¨òS™©ËQ±³S>9{UÐä¿l/vK»ýÛ¨Tþ¬òóö“wÚ×õaÍ^šwÌö!"ü\‚ýµûÈ|u¨ô¾2%÷“«Oe/òµFíøKdëÒÚaªWäM¥ñó•³Gr¥ÌöJ6™¨]±Ý ‡týÆ }Q®š9míúMÖæ(Æ´7£Ñ÷ÊÕkÖfà?•àÉÈ)÷bŽ7°]$ÓØ¡cùÊ5Ö.¦K—¯˜}5oemz²àCêùŽÜ?©Ó!÷µ¢É«rN]GsmƒCà! -šV‰=²¨Pý®ê?lˆº}ßHe[ÌÒÅ¿NhËÊQª”ÁKîŸöÒïkÖjõšõú㊱úDláÄ]mïù‘|]S+oõ®6iŠÆ n«’9’É)M):9=%ü\¿ß×VáÊ#uøYÑ íܽWÅÊT1ÃÛM[¶Y›ÍcF›ÑÇè Ø[‚'Š—­ªJÕë[›lÉÈjÕ®³FX•¬P]åªÔ¶~¢ ]]”Û-© <§°ßï¯n¡Ln©TvÊu›Q ¡º>½š’¹¦Öç?P¬+[nÒw¯zÅ2­#f8rf´Šøz)Sý%ºa³EȹÉ*“ÚKÉ«ÌÔ5óxĹÎ>z£ÃN=;ö~~©ª¤®éUoiø«Ý›YMîÕýIð"0Öè1£¶íØu<2˜0êYÖñþ >œ(U±º*T«k=lŠ-œhýC—XÉòÕê¨tÅÖÃO õß½&gï/4êœM‡Öë›,^ò*6^ç#…=úkaí´Jœ¼–æ35bóÔáD¨®ù—•‡kµXo]yó®ÖM¯ÄÉÂ^ÇœŠq®ÛÛê²7z$|¸¿ ¸'Q ÿ?Í%h_/åóôQÆ’}ôëñÛ6Á €ç•±ÎNä‰Ý{ö™!Ed0ñ¬kðÿ†N»l¡CppÌñ· 'ŒÑÆân± 2ûÔkÜÂÚôxw—ë«L^r}¯‡Ö;®cÇ:¨É5_VbÏÂ|2"9¯…}圷»öÄ:lBq'‚´·[9{|®‘H”`ìýž\<>Ö@óµ#Îõ,£ ×£Ç ÁÇë]÷$*6áZD #Ó[èýt¾Jä–F¯•j¥WžÑ½hgxÞ¬\³Îüþ3F¡}^º²ù³q HH|8ѳï`ó/Ó»öì³6E-ˆ9rÌ„Ç.ˆ¹uû.ó}³6=Ö_óë*¥‹í"–6å죷z”™„œÓp#œx³G¼„{ºä—³Çg±†Ìp¢ˆ†œ¶ 'l¦„DõŒN„ ½{Zk&tT…7ÓÊÙ9¹rÖ›­31sÏc=žÈ ÷Qkóö”àɫ֚¡îÕo°µÉÜJôÔé3ÖÃ1tîÑ×¼ÆÚ ^¹>†ÐëšR!µ'ÿB™«Y³mjæPUÎâ%ç×;k‡9óâ®æÖH¥Ä)jkÎmë…"áDÖ§ 'BuýçòòtͦfkDë)ÝÑ‚Úé䔢޿™›vXÏ}èQáD”ëZס¼Üb›>ày³nÃæXw=‚N9{UÐ俬m Æz Æ´†qÄ„•ñŸkPÀ^"œø}嚨)‘5Áªµ[¼ X\_IÝõ’[}¿%ÐÚåæœZJîbô˯Î{ƒ¬ÍÏŽpø× B¾HkL<‰ñYŸ `Næ.X¬M¾Sz_›áD§î}­]âN¸$—·¯·^n±^Ö†Ðkš\>•Ü|“ËÕppÆcÇŠëÞ 5¨÷¶ª¯¿®PkÛ¿ÈøL={p˜p"R`PªÕþÊ (Ž;amŽWf8áù?U(÷ª\27Õï÷¬=¤Ð U,Ij•¨ZR>„€C0€4¶Ô|¼ mÿžœK¤U¢锸ôËò«YHõ ùc*ãOð Mÿ¹—&Ÿþ—_ÇÂøLXöàpá„aÉo+Ìp¢J͆ê3`¨6mÙfí¢-Ûv¨ÿàú¡sOmÞºÃÚüTÌpÂãêéßR¯ºgRÅ·-=BtjDQy¦®­ÉcªÈ=–ig–«wÝO•#mr¹y§QÆ·Êê›1[tÕ6œÔ‚NUôVæTróH®´ù*©óâQ,IÌpâÎAÍèPU²¤“gXßT¯—PÓ »õWÔ?±ÆNëÒÚaªWäM¥ñó•sØy)³} ’M&j×È>À‹áó>_jÉžUÖÃáá„{ÓîZqñ¬N^<¬-ÛÖ×ßå”[ývZKXéèŒÏÄøl€ÿšC†ƈ ë³æ.ŒjŸ·pI´6cëQc;Ò¸ 'ÞW¯}[Õ.—’ט¯h‚ªï{~JÛ`™.L®#œ¹0GÕ³øÈí•âjÖ‚&ùV·z)¥[Råþv¥þŒ BohQÜrqM¯‚ hÜÔiú©÷×úàåz9£Wôp"è˜~*‘AÎ)ßWݾ“4}†¿úÔ{_I]Ó¨ðˆ# õ˜áDðñõqeø¬†Nž­éaï¥Ï÷µU¸òHޏ6ð‚Hß4ÿSìÐNx´ªƒ6á`ÐÉÊ[&¯ï 4ÿìNèœ]E—׿…M”¿Æ«òlØUŒ?ˆ!W´fnsjK^å²+CóÚj»á¤î‡Uïo_Væ‘-߸»T5¾Ì¢ÿ-<£ÀßU³ò+ª°îa;gç©u×ÂJ_ñeyU}O « ·Â¾D¬VïèÓ%—£¦€좬¥2ê5ÿ=ß ÆÛ§ÿ•{GßüñèõsŒÏÄøl€ÿšC†ƒ†2C‡Õë6èÊÕk1‚ŠÈ:á¢9jÂøÙaWááDAu? =]ß–Kòªšvýá ðà?z*¯g65]}Oú—“[´pâžV5Ï!'ŸÂêÈöaàŽ6·Ë/÷·ÕyOxßàÃýUÀÃK™,{X„¹½¦¥²ººG 'þš_W©Ü²ªÞ¯7ÎE½¤‰eRË)Cc-»kˆNÜ›YMîÆ½DÆÎO{8|v¨Þ.›OMöF†Y”¾I½Ù¹¯~Ù·[ÛOÓíÐÚ5½¸ü|¥‘ÏêÚßµý÷¦ÊZ®Z¸£S ËË«fK-½yÕPý¹®¡’Wª® Æ—€%œ½¹X_ÖÎ¥&.Õþ?ÿÒ•ó«Ô±MN¥è2MgBÿÖ¬>9ä×gaDx¤m“>RÚºo+i³Úcþ‘ÕUuäûå·Zò„™"O÷ÙñË!ÉÁÃ2‡sæ›S:¬¡DdíØµG“§Í4îÙw°õ2ONìVð¡~zÛ#¥Jú_µí‡¼rËÞV„Æ '·ªuv/¹› Ë–í‚TAw½Õó ‚îve|I¹»fWóµ¢w Ü YlGNhIý rJßH oÜÖíÛ‘uK‡‘kÔëÇ '‚öõR>Oe,ÙG¿¿ýŸ.²$4O÷n 'BtåìJõî’Oî ;i,˜áDf97è ÕQAC˜€•ªW=»Ê®úÓ&D<«!m^Vú‘ëðçt•¨ôºªoú;¢íº&wÏ®}†”щŸ[J^{k»Í¬±û;[+m¹òq-X—ÿRÞ5¿× # Þ§ŽM^Sùù£ôY¥bê}ÖH'îé×Áy”´÷Ý|x‰X=ÝgÄ/‡ 'Ž=#ˆøeæÜ¨ö‹—Fkû¬T¥0­#<œPÈ ,”DžÅÆë¼ñ °^-^M¢Üw‡=ÂÄNÜ›£J>^Jßdµ,‘Cع õ¥Ÿ§RÔÿ-¬-È•áìñ™Fœ³,DrZƒÿg³ fÈy(â«—œÜc/×j±Þx:‰NÁÆ‘é-ô~:_%rK£×JµÒ+ÏDV¼ â2­Ã¹dF¹•Í,×RÆ¢˜9”³sw;1üÀ '^V²ÁË£íæ|~„ –É Ïʹ”¢êÃò-›N=çéNèMÍì“KÉzÍ“1+ôÆT«˜Wõ¶G,-œx …³Ë©LV%·¹VаvçÒ«Ãñ …\­Ê}¤öÇ‚|j Þ¬TM㮇}´~UÌ=­ íjÙ «>_vå±Á$Ó:`/N,[¾Ê Œ1[¶í¤_—-·vÑoa}Œ¶ö]zš‹c>‹há„Btfäçòð*ªa§Ct÷÷&Jïù®z˜Ó$b 'îÎVEoO¥k²*f8q?<œHõÕr›p¢˜~4S!ç4¼°m8þ»S¶†š¸v£Öo°ÔÆ:aÌA5œz÷´ÖLè¨ o¦•³srå¬7[g˜éLœÄlÒYKÎב‹týå‹N¼¢t?®‹öç<øì0(›_õ7žÕ¹k£Õù¿ï›Áííß)]åššt3HgWwýNZ9ÌN,]îmGj·åZç®_Õ-ã+'ø€:7Í¢çŸÔþ™ÅäÛÞ_çBƒuhfqù´› “§‡ãÍSõJÄIDATêíò¥4ð¢u%ÞèXöâpáDPPªÖjh†û¶6Ç«èáDØsÈʼn*æ›Dï Ø¥¹u2ÊýÃ!:fþ]?–p"h»Úäô–[ѱº`Öa¬1áû2§u\[Rn®¹Ôj³e¡º ú!··Í´Ž{ZX'­§¨©Ù£Ác÷èp"JÈu­ëPH^n‘£-€G\¶µ®9Í# ÝÿMµªeV‘‹ÔÆâÁ&}S/›Š-Ý®Ám^Õë?ï{Ø7Z8¬Ã³ŠËý˦šwçQã‚´~lA%é>\ÝÛe×Ç‹ÎËxËæâ•««×ôJòi¹þÄ£±•(ìÅa‰i3çšÓ3JWªi]zô³v‰wÖpB¡×ôsÙTrÉñ®ò&M¦Â£Î™±† ІV¹åì]H=ÿ°ð}GÛä“‹{þ‡ bì£üî^ÊÚ|µl3‡û;:+›í‚˜¡º1«¦R¸¦ÒǃFFÝS„2ò“ŽÊá–J5>úJÀóèð…ãJûu>ëa‹N„ýéÜñK1ùTüT –nÔ+—töÂZ¾r”¦ˆüŽÒŽŸÿ§$ß–Ö[Š«ï9›äÀ² fÈ…ªZ3“2t¤yÇNëܵ3:pp‰F.Y£óyÅÝíß*mõ·•©r9 ½ñ†ƒw«]ã\ÊX;‡²ŽÝª'ÅÆgb|6ÀÍ!‰ßW®‰±ÆÄŒÙó­Ýâ]ŒpÂ!f|©¤ÎîJ”¤ŒÆ_ŠüWÌØÂ‰°£W«~6_¹fþ\MúMÿÏcÕ½~øV¢¹Z¬°ÙJôªæÕÍ&g׌z¿Q™ã³ìÅ!‰Í[·G›ÒQ´dEmܼÍÚ âÌXÒx8GP÷lÜ;‹`ÀÞ"œ0;sŒøi¼zô¤}ûZ›à™£Œi _ƒâùbÜ«qÏŒ˜@Bà0áü›ŒõŒ!+Œ-5—ìYõ\M÷0îŸ'ãÞŒ{4î•5&PN€ ãݘæðyŸ/•¾i~stÁóPƽ÷dÜ¡ `W„À®'€]N»"œvE8ìŠpØá°+ `W„À®'€]N»"œvE8ìŠpØá°+ `W„À®>œxðà=¦½P@Àk3Hà6œøûöm :RÅËTѧ%*˜U¢|5-X¼ÌÚ$`NÁDz_›Dƒ&ßiä˜‰êØ­·Š–¬hÛ¸y›õ@9d81|Ô83„5v¢BCC£Žï?xØ<^¹F}Ùœ‘€…œÕÐ}åZh¨N„X-îÌTy/oåøa‡ìrw¯Ÿ»Ónû¼>à¹äáDÕZ õY©Jº`Öä_féì¹óf[—ýÌ€âÐᣖ³ââ–fTI¥Ä)êhÞKSÐ6}ŸÃK‰’Tÿµ‡Áˆ)ôš&”L*§ÌßhåÓ.A8xÁ9d8QªbuU¨V×üùÔ™³fQ¥fC3¨ðŸ:Ãü}מ}–³â"Dç*&7לúnc`´–àý½•ÏÝC‰\Ò¨êì[ÑÚto©ê¤ö’_µ¹²´<áàçáD£f­ÌâÄÉÓæï]{õ7ï?ÕüýÆŸ7m»?“àý½ô¦»ÞéwDÁQGCtjX¹%ÿH¾™D©ëÿ¦»6çmë n~útÌ%YÆT¯WW—±34còp5ÿ$³œ}ÞUçm'œ2d×˯Q£¾ã5eê$õkRXiܼ•©ö|]Ž GÌpÀKiræQ¦|5ÔaÄdM›2V*å•—K2½?ð°Í(»ÚÞó#ùº¦VÞê]5lÒÜV%s$“SšRuäaPraŽª‡½O·WŠ«Yÿ šä?ZÝê}¤”nI•ûÛ•ú3ÚëÛ„Ži\¹Wä’¦¤†ˆHQw©SÞ$rÏ^QFO×ÌéþÖ³¥*n¤ÉŸz< àâá„aÄOãÍ0ÂX Óhޤ0ŽmÞºÃÒûY„êÒ¸’òpË£6[Ãäï-k¬ÔîyõÃŽ°ßÿš­JÉ|”¯Çð@ `¥eðRòÚ‹¾†f€6´Ì-—”å5ñüÃá¡-UÝL^JRþ™ëiF„/¹½®ëlWß¼£õ­òÊÙýuÛ9˜á€»§ùR¿\±yÐÜ«ÎozËõá:ñR!gF«ˆ¯—2Õ_¢6]CÎMV™Ôaï³ÊÌð××=­jžCN>…ÕÿíЋ;ÚÜ.¿\ÜßVç=A†m8rY‹å•{’÷ÔnÝ_QÓXBÎý¤O<’¨è˜ËO?µðBs˜pbͺ:pèˆùó¥ËWÌ)åªÖÖýûç=lÜ¼Í 'Œã_”«¦’ª«U»Îš5oa´-GŸVðáþ*àTˆµ¾eN¹¼ÜB«ÂC¯h\‰dQ Yíé®<îÉôŤkáåAÛÕö5oy•ûYoßÖí¨º¢Iå’É)cs­0¦„DŽœ0¦ˆX¦emï¨n¾*üÓÅðkšá€‡|ªÌÑßÑzhA­TrÎÞN›Ík„êšYy¸æP‹ÈaQîjaÝôJœ¬–æYHàVµÎî%·bŽÐˆ|t  ºû譞؈pâ.[´µÏ'Jâ™Kµæœ·­æþZ53¦Ã¼VSCל“í¬bãáÄèñþfèб[oó÷ž}›¿Ïœ³ÀÒSjÛ±»Ùf­>†Z»>YàfµÌæ-ïò¿èÏÀ}êœ×ÇfdDøâ˜®^%ôÓ… ULnî©ß±ˆGõ{sTÑÛC/9¹ÇZ‰’ÔÖ<#äˆ'\Þ¨#Ñžò¥ÐËãUÔÓ[¹:FL£ˆ˜Öñjë-Š9hqÝ´r~õ{m0‚´·[9{|®笫lë`ï÷äâñ±+p†½ÏJ>^Jßdµbì~°P_úy*EýßÂÛ"ä,^JÙ<<ä’¯›v÷M¨þÞ=Nµò¥•“³¯R實v“¶è’5# B‚'6lÚb† u5×ù uòÔó÷ŠÕëéÁƒÓºyó/ý¶bµîÞ»gŽªØ´e›êÝÂ<Ç}7wµ¨nz9gl®ßý¨ì›S7|•±ñ}›ÓG©üþpCàfµÊæ%·ÏÆé±ë@F„‰#§yØÚúƒ²¹&Q‘hÓ:ž&œÕõŸËËÓ5›š=ÜN$Â-¨NN)êhž±iGØ=´Éé-·¢cuÁ:­ÃœÖ⣷û²™Öá¥,-Öé^èŸZÒ(—œ=ó«ÍÆÛÑOŒ&Pç7Óka×Éß;bz6|8alZ¥fCóg# 0BcáËÀ  Íž·(Æô k>sÎ<×X‡¢tŶ—~*!'†è=zíœr1º!@+›¾*·\ù•3¬OÙ©GUD-4éùŽ~Øò˜‡÷È1]sªñò[6 ·µæÛ×åìþ®º°]ói‰°ËžŸ¨âI½”®Ö‚ˆ…/#ŽŸ¤’)½”¬r䂘ÚÐ*·œ½ ©ç¶s4îhc›|rqÏû‚˜26(Y¥f9}äúÚ·Z~ó1 ÌýßT/­²´Úd™Ž€„•ª×W‰òÕÌÝ8Œ@bÔØ‰ÚàÙÖ«_øÚš·R˶bTÿÁ#Ì©¢B8‹XØò%'o½Ö~§%{^_X_)\ÜõRä4 !ç©Ö«>Jœì-•o=P#ÇOÔ¨!=Ô¬âGú }ăºN$‘Gּʒé#Õë1F“&OTŸÆŸ(µ±•hŠÚ˜#á„qlïÀ¢JêšJyªvÑ0ÿ©?¤]ÔV¢#<Œ B¯,Výl¾rÍü¹šô› ÿŸÇª{ýð­DsµXñè­DÃü½¾r{zëåú¿Ê˜µrvœ*¿WYM»Õèió5oÆxu®ø†<Ýs©ùjÛÝH—àÉ!#F›ÁÂèqþÑŽï?xXEKVT©ŠÕÍàâQBBB4hø(ó†ühm~ ÷µ¬a&%vͪ¦«c¬þ¨Ð?§«¼Ÿ‡œßèª]Öä"Là…µÖ¬ŒÞ|%mغ·Iä‘2§ VéªÙGŒùÑž_­A_W®ô©äæ™Rió”P£›tÅv¤H,ᄱ.Ç–ŽåîšEUg†ÝÓíY¿¸òdNvÌS.¾”­P-uYtR1?= œ8á’@Ⴑ‡ÿÔ6r¬¹M¨qlÊôÙÑú¯Û°Ùì?rÌDs—†M¿3ûS:Œµ'@Â’àà ÃÑã'T«AÓëI 5V¡¡Ñ×:رkDØö«Vû+íýã@´~ apˆpÂ`ì̱yë-^ò»¹ǹó¬]¢ÜÐú[4gþb8yÚÚ ‡ 'Àó‰pØá°+ `W„À®'€]N»"œvE8ìŠpØá°+ `W„À®'€]N»"œvE8ìŠpØá°+ `W„À®*œ Òêu4dÄhµlÛɬ þSuýÆ ]½v]c'NŽ:>ôÇ1Z»a“BBB¬— ˆÃ„ôu‹ïõi‰ 1ªrú*_­NŒãFµhÝ^ÖË€Âa‰ÑãýͰÁ1mæ\íܽW¿­X­]{G»õÑï+טmƨ‰ßw0#*Q蟋U?ÛËú ï™G‚´³S>9{UÐä¿,pPNS3JWª©2•kê~@@´¶ýE…GŸˆÖvÿþ}•®XC•ª×v<®BþÜ«i]ë«È›Y•ÔÛGΞ)”òÕ‚ú¬N7M?pÇÚ=Þ„^Ÿ§êR)_·ñN„^Õš±´öJ¨µ»qˆpÂXOšµlgmÒ®=û¢Â‰=ûö[›õõ7áSAþþû¶µé©Ü?0^å³$Q"—TÊþY}}×¥zõ줦Պ*kêê¸ë_ž2ª‡QÂ? 'B¯OUéäïªûþ`kvãáÄŸ7oFMé°š:}vT8aL÷°ú¦ÕfÛÍ¿nY›žìÎFµÎí«Ä~ïêÛ%d!B<ˆqìßõO‰]ŸY]É= NϱaßKí»ô2~’UkÖ›}ïܹkmþSNDNë(W¥¶nß ŸFaüüø‰SæTÈpÂh?yêLÔyW®^S©ŠÕÍÅ2ã.DçÇ–”—Krr4bZÅãžY®Þu?UŽ´ÉåæFß*«oÆlÑUÛ CBÎjèÇ~ÊÜbµNýÞSåò½,ow_ùd, ÒmçêÈ}›¾wfª¼—·rü°ãñÓ:Boi·•ÊŸU~Þ~òNûº>¬ÙKóŽE>pÜѺ.)¥›‡^rr*çÜ´ýin €Ã0v/ú¢\5-YQk×o²6GY³n£ÙÇèk|Wöäá„¡â—uÍâ³R•ôí÷¢…ÃG5·üÝh3ú}ßÝ<â,ôº&–J¦D~U4õÏ'¯ÑraŽªgñ‘Û+ÅÕ¬ÿMò­nõŒ@ ©r»RQ—0à _9gyS9³|¢½ÆhÒÏãÕ³A!¥põV†šsu)²ïS…wµ½çGòuM­¼Õ»jؤ)7¸­JæH&§4¥4êˆqf .ý±NóÛ|(·×UoÒj­^³Vk¶ŸÒßO¾5ÆX¸X™*æwà¦-Û¬Íæ1£Íècôì-Á‡AAêÚ«¿2£'ÊV®BÔkÜ"Ú¿ ®Z»Au¾j-¤0Ä4~6®‡a[õ}/¹¼ÓOŸ8 âžV5Ï!'ŸÂêÈv¢Çmn—_.îo«óžˆ×Ž'^r}]-ÖÙ,¦zK¿}SNnÔe_Dß§'BÎŒV_/eª¿D7l‚†s“U&µ—’W™©kæñP]PJnîLë^[·ï4£¶íØu<2˜0Êè$ >œ;arTØÐú‡.Öæ'2Ö©ˆ<âä_¬Íö`•gð”ë§ctáI£ ·ªuv/¹› Ë–¾ÁGª »ÞêyPf$N8ei©µ–+7·UVW_uAæL'†¡ºæ_V®9Ôb½uõ‹»ZX7½'«¥9fB8¼h6lÞ5Bb÷ž}fHLm@B‘àà ۑ¶á„1 ¢[ïZòÛŠ¨c‹—ü®î½škTD² '*T«uü‰·¨Uv/¹¼;@‡Ÿô,oŽ*ùx)}“Õz`m X¨/ý<•¢þoámá„ËûƒtÔrÝÐKãTÔÓ[¹:î#žNio·röø\#ÎÙ.laÖÁÞïÉÅãc îÎVEoO¥k²*f8q?<œHõÕòèáD,¡Gè…1*ìá­Üq'ötÉ/gÏb '˜áD 9M8¼È–¯\õhü $4N\º|%ü˜Íö¢‘A„Ñf=çp"ìáþèÀÿÉÕ%µJLŒ˜fñ(AÛÕ&§·ÜŠŽ1$øpp÷ÑÛ}E›Ö‘8cS-ˆÞ7p}+½ìšDŸŽ¾$ó2O 'Buýçòòtͦfk¬±È-¨NN)êhž¹iG¨®N/¬u6kýÆ-ÖÃ@‚àɶ»G ¶Ó:ÕÈúeæÜ¨c‹–ü¦FÍZE[øÒ6œ° 2žFèÕ¹ú2ƒ§§-¡~Ûÿ  b  ­rËÙ»zþa›8ÜÑÆ6ùäâž?–1³ë«ßl†d„ÞÐüº¯ÊÉã}õ:<1œ»Üù‰*žÔKéj-ˆXø2âøÙI*™ÒKÉ*G.ˆ)ý=­RÄúÖ ûIðáÄÍ¿n©QóVf¸`¬a¬4o»¦Ä£}6lÚ¢rUk›ç6øú[ݸñ§µÛ„êÚŠvÊŸÔS‰Ü3)_¹&jÛ½Ÿúôé¡¶Mj©èûÍ5çW«~6_¹fþ\MúMÿÏcÕ½~øV¢¹Z¬ˆ±•¨SÆÊœ©êt­IþcÕµvA%uñÑ+ ~}2 K*_–´òpó”“Gr¥xõ=o8^»lJž_­A_W®ô©äæ™Rió”P£›tÅ6ˆ'Ü>ýQ›wW™73ËËÝW>™ÞQÙ‹t"ÚÀ‹§ 'dnCºoZGU(˜CÉ}’È#eN¬ÒU³˜ó9lÜן›èÃ,aï/ìÒ¼ßS[ã°»*ÿ‡'"#Œ­E«×mm‰ØªDùj9fÂ3Œ–ø—E„®…†éÄ“€ðÜs¨pÂÖ¹ó´`ñ2 5N»õV§î}õãèñš=o‘9õãî½{ÖS†¨pb(áràpÂaN áÄp€h'€]N»"œvE8ìŠpØá°+ `W„À®'€]%˜pâÖí{:åO8{EGO_¢(Š¢@ßÉÆw³ñ ü[ìN<ÔÙK×cü…˜¢(ŠJXe|WßÙ@|³k8aü%—‘EQŽSÆw6â›]à FLPE9^ßÝ@|²[8aÌ_¶þ…—¢(ŠrŒb ħx'öŸþ[…ZmRâb‹Íÿ¿ÇÆX`Íú—]Š¢(Ê1ÊøâK¼‡‘ÁDd¿Ç†µ&(Š¢·Œïp ¾Ä{8aLDVl¬Ñ¥(Š¢«€øB8AQE=Sñ…p‚¢(Šz¦â áEQõLÄ—Nüqø´ú­¯¿m§¯[´}¶ ;wÀ°1úãè™×§(Š¢ž­€ø’àÉÆß´Ñ§%*ÄK5oÕ!Æõ)Š¢¨g+ ¾$øpâ³R•U¶rm-^¾îU™*µôyéÊ1®ŸPkϬVÊ›*©¼_)«žkÎÇh§(вwñ%Á‡ƈ‡Ê5Ä8×2®a\ËzüÑuAëz|"'w½K¹¼Û[kNZω¯º >mç”*3Žé(E%¼â‹C…›wî×媆U5mÝ}0Ú1ë£lû9V8qIGv.RçÆ T³Õ-=³zX‡7öUA/¹|Ø_ëÿÅÿ'EE/ ¾8T8±cïa5lÖZ•k6Ô®ýÇ¢³æy6ýþQ8ášY%û.ÒÌùK¢jöŠ?t Æ9Ô_çõûïÊÅÙp‚¢þãâ‹C…ÿ¤þY8‘Cu瞥OX_¬º™½ÂúdSYû4³Gm½›-½<Ü“È7Ó»*Ñf–6œ0úžÑÔúÙäd\Ï­€Z®´YGâÔ .‘R‰ÂÚù•ÖÀ]´¥ÿgrµ¼ö¡U”ÇÍ]‰SÖÒ¸ÃG5»g ½•)¥\¼J¨ßÞðkùc•ú]Vo¾’NžIä“6·Þ©øƒF¬:nó¾ÏhTãõ|ôz›5Z3µ“JÈ®$^>rO‘SªôÕÌ=£úG¾n¢äUôãÖêY³ˆ²¤L*Wï4ÊT ŠZMÿCw.PÛJ*s ¿°÷“V/¿W[ÝÕ‘hŸ×­þ¹«*}ô†R'õ ;?­2¾UN\¯½ÏòÞŽïÖ¤¶e•Å×Ã2²ÅGowÙ®ÃÆçq`­6 ÿ<<ܽåâ›Nó•RÍŽSõû!Û÷FQT\ ˆ/ >œø¬T¥x[ÓX\ÓzýGWà '/¥É‘[>.–) ÎÉôVûu:Ö÷àÂoõŠ[øÃsÞ6™ÇÌkì§/’Øžò+;Q;N_|l8ñ’[n}QåJñZ‰üªé§£F0±@ÞHn†Öi(‰“}¨–‹NF¼ïÈÀC.™óèeoëÃ}Øûøl¸ÖEŒBˆz]—tÊ’-[¯í÷¦òåJãu2ÔÖ¸ý‘ŸÕy­PJ)]Ã_+‘[2yûx‡Ÿã’ZïvY1åéßÛáÍ}UÐ=úk†WD8qꀆ–Éõ~9{„UÄ{{õÍ<ËÿOŠ¢žº€ø’àÉM[Ř®ñ¬õUóÖ1®ÿèzÜš>Ê×q‹ù/óɰãÎ~Ê^m¨¦,[§YÃë)»gøƒµóô«1zâø*5Ëåv,ìÁ»@w­ˆxøßùS¥ð Á%ƒÊO0F8H~<ì~ªL ïGöNTÉ”žá¯÷^{ÍÜs!ì󨣑UsÊ)ì='NQQC÷¯—÷vQ‡,ÕWÙŒÏÔ].ôÕšãçtèøy>vŸÇfªR*ã5Ã>«újõѳڳm½¦ í¤oGn~QõLÄ—N”®\Óñðu‹¶ÿ¨Š–ª¤rUëĸþ£+îáDâ”_ê§çŸÜ£Îú„OWOþæ¿ÒŸ×Ò¶Ì!‘G!µßö€~ú˜~,ŸÆ|˜wÊPO“ÌÅ/ŸNxÈ9Ç×úÙ|˜x½Ô2_øë9ej¬)G#ïÃv:É»jmnKj¸æT½¨Q!ç´è»|}óªÙÒs1^×óóµÕxð;¾j]¥4Gox*Y¥)QS3öŽ«¢$æ…$*2ähıªáÇœSªÔ˜QïûÐÒ6ÊnŽÊȬ/1ÞGÜÞÛÑã¿«QöˆpºæÄñ•jòZx["ïœ*üÍOš³ã#`(ŠŠsñ%Á‡ƈû¯9a]s™~ݱ½§M8aìà±:êáø”†”LN¤®­ S¯ë©·<<ÂÒ“¨`÷:t`²Êš# ¼”±þBí7Ï}R8á­l߬Œþ/ÿÇfªJjã:îrýxˆ6GƵ¶ø<üZ.TÙÉð0HäUZƒ"óž?¿g·¾¦’£Ür«ÑÂÈ×xR8á£7ÚmŒN„Oa{ÿ¢M¶áDäµ\2©ÚTK8áWM£¢FYØÜs¬áDô×µ 'Þí±û±áĪ΄_×9©Òäþ@o½c­Oôõ4#ð‰Û{{|8~ÿ{ÖLQ«Ê(MTHá!·\-5óˆµ/EQq) ¾8T8±bݶ¨õ#ÖlÜãXlÙ/Á„aË“§1µ#I)Õ®>åÂ)W[-<þ°OœÃ‰›ô}þÈi_irÔƒ÷M©›5b:Ä{j³6ú´ŽÈÅ4cÜs¼†—´wlùÓ:\R«ÄOáS=b¯¸½·£Ç—«qd8ñA¿¨E#ÎáÄ%ÙýÓÿÛ»覫þãGh“6IÓA¡¬²ÊFá¡"**S¢ˆ …A¡ì%† e¶d/EPq0E@d*ÊFÜÈìÊ矤+MKikù§•÷ëœïá$¹¹¿¡§§÷Ó{ïOMMö~K«Xqûw½­ª>t»K؃pâøi}<ü^ù:÷u(¬;ºÎТõŸhÑäÿêç£6M2†Ð‡nbf%¸Þq³NÙ»@-“6§4Vè¨ñëöiß±SÚ¿¯Ö/ž¦F,Õg°½s;zlGÊ^‹4ՠ囵~ÝJÍßðÿ°IúŒÔK?׎ƒ§uô§ŸôáKõetÜ#û}MÝÏ‚¢¨œ[òU8ñOê…”c‰-ŽÁt‰£Çjz«ÄM0Ÿ0ݧ¡Û\÷@ÈI8a¯CŸh@ÝtútžC@]õ}×ñ$GÛì×;nv Gß[g¶W)_÷Gƒ&–÷£ôq‚“£ÇOjų՜OüHíÏOåzoÔ¡ïW¨]Ò>·y›ååcNz ˆcYÇ@­dYEý£rKž'Úuî‘kOëhßµgºþ¯_73œ8§½QUØù¤ “L÷¿á¶!‡á„½ŽئiýÛ«v…Rò3ûËT¤ŠÂ[ÐäG“BGe/¸Þq³N8ê´v¾;U=¹We‹‘Ñh‘Á¿”Âê=¡g§oÑ^ç½ËÞ¹9ëÀ&k¯B ÈË'X…+Þ¯Ž3¿ÖácÿÓ[}Úªv¥Òò3YTÐ'@þ¡5uoû±Z¸ËåûE娀ܒçÉÕï¢f´K·—DvëáÇ:8—¸÷OQE嬀ܒçà Gí;ô£ÖòyŽkç۴ÿðñtýREQ9/ ·ä‹p‚¢(ŠÊ{äÂ Š¢(*GäÂ Š¢(*GäÂ Š¢(*Gä–\'êü"M0áx‘c'ϧûE—¢(ŠÊåøä–\'¾;þwJ@áø×ñ:#§Ïÿ‘î—]Š¢(*”ãg8[r=œÈª ¯¤ûe—¢(ŠÊåøä…'Ïý–î^Š¢(*o—ãg7›<N\‹‰eï Š¢¨|TŽŸÙŽŸÝ@nòh8áàø%—EQy¿?« &p3x<œHæX¿ìØ`™EQy§?“?›Ùc7Sž 'À­‰pxáð( àQ„À£'€GN"œE8<ŠpxT¾ 'l6›¢ßY¦GÛuVãm²T޶‹–®tï ä!ù&œøø³-釬ÖÖm_¸wòˆ|N,Z²Â4|øñgî]×?u~Çñ]7å›p"zñrgаñ“Mî]—£­ã;Žïæ}qÚ="\Þ–6Zô—ûgü{NdAÂû´dtw5ªYAA~Vy› «Hù:júÔ-;pɽyNnM„7põÀ|µ PCˆ*5í®þ£&jÂøz®CU(Z[ÿ‰uÿJNnM„™¹´C/ÞuõÂgäCØbbÒ½—s„€[áÄu%èôÜY Áª?õ¨âÜ?Î@ì‰OôÊÓU¹x°|üŠ©Ô­ôüœ]ú%Á½åE}·xš×(#?“¿üËÞ«Ž“6kó¸z2¸‡¶?õuÔ‹j^^–YKÝ­G†®ÒáË.m.Ò²¡ítWXq™|ýä[(L·7ìªQœ–óÐ 'õæƒ*Óo³~úx¼ /+?_{_µõÈ5:rÕ¥/ÙôÛ× õRç‡U«J9˜íÇ,®¦}ßÑ~·,¶‹µ|DgÝ[¹”¬– –ª®ºfiOLr‹xû|¦žiRSÅä\Iww˜ N¸D:¶¿ôÕüþjn¿¾³Ÿ Öâ*sg„ºMÙ®_m©Íÿ^ù&œXºb3h˜¹P{¿ý.K5{~´ó;Žïf›í7Eµ,¤Ohñ7%'œY­NaVù”k®>¯GjAôlév¿Šøéö>Sj :ý¸Š¬*Ùh€&G/Uô[cÕ±V9• +-¯4áÄížð€ü}ËéÁ¾Ó½b…æŽí¤jþ~*ñÄrv&õñ³åx—:ÒÒ•Ë4wÚ8õz´¹úp!éŽpÂ_Þa5U%¬þ;aŽ,œ¯ñÿ­¯ÂF?…v^£s.çw&²ƒj4é©aS"µdõ ÍÐTÅíí* Ú©kÉÍbéÍ&ÅUЦúÝGëõiS5fPOµê·2©/›þÜô¢n÷ T¹ˆ—5cñ*-ž5\‡ÈX¹¯>rÞ›Î/í Â>%U¯×E­X¥Es¦ê¥nj>vWê±ÿjù&œ8~┚D´u† Ù©¦-×ÉSgÜ»»±Ø/5¨²E†»_ÓÁx÷Ý]Ѧ¾•åem¨×¹.ô¸¤CkÉà{—FîMš{³S*Úû­1L»\f?Ø~]£%̺Í%œH8¥fVÝ>ôK¥6בIÈǧ¦^úÚÞgÜ·UÓ*kÇwígqIáÄmÆ;Ôïs—é¶ ÚølyùÔÖ¨o3™b;«·›ÈPsœö9›ÙôÛ²*d,ª‡fËxVIüAM¨ sý):äÒàê®—UÕ'@÷½ù££Ïz‡ÉP®¿¶äÞú@>“o ‡={¿Õð15`Ȉ,ÕÈq¯jÿCîÝdMÌ&õ 5ËØxŽÎÜhâDì—z±’E>Í"õ³[Ûø£“TÇת;Ç”#ãˆ?ôªjÙ_‡'½NuIkž,ª)á„M¿/l-³OM Þñ—.^¼˜R~1LÕ|üõà[§•`ûC+;…Ê+¨®z.øZç3ä'…^a´ÕíóØCTÁ识oŸI\’¡}ؽ¤¼S¾EëºWÁà.Zý·{ÛD Ǧê“¿î›tX\Îýâ¨{I³,íVê¢ýˆÇg=,?c¨î²Zûÿ¼a øÊWáÄÿ«Ø]XÉ"CÝ7tøFcæ+«õ¸Õ¢’½7+e»…d×Ö©c Y…»ot~³éy•4©Ír×M#âôõ°š.bÆiïè»äíå«Û2,«j½’pØ~Ý®WÛT—Õ`’©ÌzrìJíýÝ夓 Ã=“uÔíZlçæ©‰ÙOÕ†ïIš‘ _¾ŒÒ öMT=¬”‚äk Áh’Wò ‡„ÓšÑÐ_Þÿ«ä !îb6õµ_§û9§VJZ†µPi?“ VUÃg&kÝ‘¿u£<ðï‘çÉŸNœTŸCÓ-×Èn=?ð¥ì-ï°ý¬9ÍU øI­LÚºáº.¯R[?³JôÞ”>œ¸šN„<óIR8á´Òã+ÝaÄiÏÈ;Ó„{FÖ’·oõ_³]Û¶ïp«Úsʵ8ýºï=½Ö³©ÊØúÞ%Ò„]Iƒüäp"ƒ ÅvfŽšüt{R8qiÇpÕð³¨Hý~š¾f«þ÷íA:üfµ ‘wJ8qJÓáDÍq×'>ë£â†=8ö³ Î}‡v8ŸæI'×ÎþO‹Ç>­:%ýUÀ\I­ædÏ ¸Eäùp¢ÃS=õÐ#íÒ-ÙÈn9öžèÚã9÷î3¯£“ÑPT-¢2[ò`÷•Wñ“O“¹é–€Ä~]µ}­ºkâ¡Äe':—uÜýúQ·e×ôQÒ*˜NØôKÔ#2+©ïÖt‘G¦®|ÿŽž(g‘©Ù|uœOR8Q°ÔsúÄmÄ»m ÊÔxö9ûcì窂­ý‹ë…\ÖÊöÁ©3'ì¯×<¢‚…»jõE—f.⾡»ËWÆp»ÎÌÙ.ìÓ¤f%åeï{Íuúü»äùpÂ1ë¡ãS=ÝßÎ6G޾²ÃöËu 5«`ñzí«¿2YjpMÛÞ.o¿ú¿ßuôI;‡Ëà[ËeCÌíz¡‚E>µÇk¯KSÛ_«G˜%톘'樑¿EEÛ¼£™ŒðÓŸ×_ZØ*H†:¯'ΔHÙ³’žÙè2 Äö»Þ}º¼¼L÷hÂ!GëZÓ©ˆ¼Bûè×<äïOÔ³œÅ%œ°éçèÖò7SóyÇ3âökôVyUè¥ ™=4ÝG :6åAÍ-5/³ïþ5òU8qîçójѺƒ"ZwÔ/¿üšæ=÷eŽrm—“pÂ1rþõÓ¡ªdVßÒ ¬·†Œ}M'ŽÓÞ]Ôäž¾)BÚίW÷Šþ2–yH½_‹Tô¹Û=ñQ¢Õú}êò(ÑxýÙJ… þ*Õt &E-Ö‚·Æ©kÝ*ªz{…ô}µ  mÐK£¦GjÞ¼·5qh5®ù¤¢ÎÛœOë˜ðpC=Ñ‚¦F®ÐšµË4cp„J›>v_âÒ‰ä 1KUV™ÒõõÔØÙZ=W£»ÖQÁªrÿÝ Ä A'çFÈj,¥Æc6蛎iÿÖ%Ò´ºÊ•+žº¬Ãáê^¿7D-•Õ¸ÏDMŸ=G“Ç T‡v¯iGR€ñç¦Áªn5ËR©¥ú¼2Ks#çiê„Aêð@-õ|ß‘ÌÄè³A Õè©—4aæB-_»VÑSž×}ÅýðÐËtº àß"_…¿ÿþ‡ú|Ižî¥¿.$ÎH~Ï=˜p”k»œ…‰.¿A{D(<¬¸L>fy™‚U¸|=5ï1_߸Ì~ˆ=½Y“Ÿi®j%Cäc.¢âÕ[¨çŒ/tÞ}jíoí·—’Ù×_þeëëÉi»tf}û¹†‰m÷/£Ž÷WW± y› +¤Jµ¼Lß]u|þ«6½ÖUõ«•‘¿Ùb?7ûqïh¦n“·èlòq“ ŸÆ3µsýX=Z³Œ,öãZKß­VÃÞ×1×Éq§´ad[Õ(YHÞÆ@ªÐPÝg}­“«:+(,í#?m|£Èmu—ý¾˜}ýd.VC ž[¡R®7A¿}µ@/¶½OaÅ‚eô P@©p5è:VïýäœÒ¡ëFè±zÕTÄß*/£ýó2w«yßyúò7fMÀ­"_„º>ãþv¶µïÒ#ÇáD¾—NëOc6 ÏÉóáDò¬ˆ~/¾œn“ˬ–ãIÎ> sïþÖN¼I8Èsò|8qêô™\y”¨#¤8{îg÷îo „€<,χÈ„€<Œpxáð( àQ„À£'€GN"œE8<Špxáð( àQ„À£'€GN"œE8<Špxáð( àQ„À£ò|8qåÊUÍ[ðŽ ©CFä¬ìߌ^¬«×®¹w<,χýWãmr¥† ëÞ=ð°<N<ôH;µéð´ö~û³ƒ#hXýîzçkÇ¿ÉÁCr›Œªu‡§ÔìÑ'Ü»–çà GðÐñ©ž)¯_›<Ýùž#pppüëxíx?3Ž>í²ãÚúî òöÕm^öò6ÉÛ¢¢•îÓ#ÏÏÖ¶ŸãÝ›€ÈáDÛNÝþñÌ G9 ' !ªßo–Þž=GÓ'W¿öõâk’±J_}ô»Íý+ ›òE8‘›•ÎpÂXFÏ|ãònœ~Šj­Â†Ý;õ˜\>Ù—/‰Gï¬èÅËÿQ9úÈpÂîâjµ 4Éúä:]u¾aÓo_/ÔKV­*å`µd¸šö}Gû/¥ýªbOhý¨öº³Lˆ|-!*]§£FpL_¼\S¦úoê˜kÚqé –k¯Úa%d6+äŽz.rþr™°sü#éÔ@ŠËàão?n ÝÓvˆ–Km@–/ ×='r*Ç{NdN\}_O2»„ :ÙA5šôÔ°)‘Z²z…fhªâF?U´S)0µ]ЧýjÈh(¡ÚÝ_Õìè…š>º›î ­ªÊet 'â¾×¬¡ò.rž~u–-ÖÄn÷ØÏ§˜Î8"gôûFü'@¾•ÚjØìeZ±,ZÓÆP›†=µè,KNùáD&®N\ÚÒ_åºwJ&Ë:lgõvÓjŽÓ¾¤I ?N×}‹Êôب?]²ƒ‹[ª¢ošpâ¯wŸVˆOuÛð§RšÚÎ)êÑ¢ò í¥.Ûû;5K Lj2ççÔ6ä3„™H 'BÕiéO:uò„¾ß¿Sï¾5@÷—ô“WÙîZûKf‘@Œ>ì^RÞa´5ÖñÚ¦_¢‘¯±²^Øî|#Uì.½XÉâN\ÓÝCåU²§Öý~Q/&ךÜHFŸZéH<®nUŸ0‹LU;ëÍ-§’fq¿Nd"Í£D“Ë;H¥ÔÒÃW\Z&è—/£4¨}U+¥À€ ùZd0šäU®¿¶8³ˆ8í}—¼MiÆ)·ù §4½¡j8‘pZ3ù§=®k+«ß6G§6ý½gžº„——·¿BjuÐлtÎ-û /#œÈDâ£D‹©ÙøuZ¿a£6nþRß¹˜n)Ç¥ÃUÃÏ¢"õûiúš­úß·uèðwšÕ&DÞ.áÄÞ¤pbæi·lçôv“—p"1¬ðªØCQ[whÛv·Ú±[Ç.¸ÌÚH¸ Ã¾­~Õe5˜X{ˆ6f:«€¼ƒp"×Ûs"­}Ô#T[+:M pY+ۻ̜°éüüù«iànSâökT¸ŸË²Ž+Z÷Tq,ÜY«þNÛ4s±:½¾ªúZU땃Šwÿ€<ˆp"Y '®jM§"ò í£4ÍþþD=ËY\ )þè$Õ1YT¾Ïf¹f1ûÆ)Üdr 'lú}eg6†èÁ)SŸö‘W7ª[q«Â~!Vwò‰Ld-œHÐɹ²K©ñ˜ úæ‡cÚ¿u‰†4­®r劻,ë°³ý¡ ÏT•ÁÞöž^“¹t™æ½ÞO *TUÙR®bÊù(Ñ9-KËË'Tµ;Ô¤9QšóÖd ëÙJµZLÕ¡xû‘OÎS»zíôÜè75{É»Z»|¾F¶­!³o5õÝ|)ùÈÓò|8ÑüÑ'Ô¦ÃÓŠÏù"…¸¸8µî𔳯ìÈZ8awJF¶U’…äm T¡ Õ}Ö×:¹ª³‚Â\ ‡kÇ´vØãªYºˆ|ÌETòÎvþÞN½éÜsbZj8ápõG­­§U/§‹Ÿ Ö*W§­úÎý*ñQ¤wë­îÍU½LQùÍ2ø‡ªbý.õþÙ›m€åùpbÜ«“3r£&NšæÞ}ÞT¯Ö³Ê§Y¤~fKÀ-&χW¯]Ó¼ïhÀ9¯¡#½x¹bbn0ÂCbMÖ=~~ª0èKö‰Ürò|8ñ¯ÿ½¦µm¨6}ÆjòÜÅZº|¹æOꯆeýU°ðÚù}Η®_Nü²]Ðηz«EÝê*V(PÞF?™ŠTÖ] Ò‚½ÄŠÀ­ˆpxáð( àQ„À£'€GN"œE8<Špxáð( àQ„À£'€GN"œE8<Špxáð( àQ„À£'€Gå‹pâ«Ý{´pÉ E/^ž£r|w÷ž}îÝ€< χÓßž§Æ-ÚäJ͉\èÞ=pC¶?Ö«{Ųº÷Õ½Šsÿðåùp¢EëjÙ¶SºÙÙ­ˆ6œýäDÂû´dtw5ªYAA~Vy› «Hù:júÔ-;pɽy.HÐOÌÔ¢ÝÝ?ÀM•ñ}·ý¶VBC>f7áÜy>œpÌxèøTO÷·³Íч£¯ìºz`¾Z‡¨€!D•švWÿQ5aü=ס‰*­­áßĺåŸKøQ“î+¬Æs~–Íý3Ü<™Ýw›-ý{€\A8‘™K;ôâíþ*XW/|pFî1„-&&Ý{¹!îø,5ö ÈxŒ›†ûž‘ï‰å«ÞM·—DFÕã¹þ.½ä$œHÐé¹²‚UêÑL翦õO—W‰gµ1&í'±_ VycÚ­¾–ò^Ìñ4¦SU(,ƒ¿¬%kèž¶C´ìh¬Î¿ÿ‚j[TÀËW·%—©¡¦ü”øeÛ퉬–µ*(Ð/P~ÅïÐ}'hí÷—Súwã“Þ*æ×RsíѼÞ©RH ~¡ªÜä-:tY1'6j쓨\á@ýK©jóAZþ}ê9¦§Ý#Âå[{¢vY«A×T?«|‚+éî¯è£S.w(á¤Þ|0H©K?¾§AÍk(Ø nèJbßñ¶z=t§JÊ·PYUkÒK“>=%×Ûç¼K„fÿ°O‘Ï6Vyû¹üJ¨ÂÏhêŽ_•tGR\³ß× ]:ï«ÑÞ®âƒÏhò¶_\Ú%^ƒéžÉ:zá€ôj¬rÁ2U}IëßÍä¾_Z¡Ö?U~éë4ÿÜøxñ:·ušº5ª©bþò6«HÅ{Ñ;JßÜŒÕ@Oå»pâä©Ó¡ÅÜèïèÙ'b¿ÑˆÿÈ·R[ ›½L+–EkÚøjÓ°§µéÊÉ=Ú´ðYUñ±ªÖÀwµiËVmþ|¯N]u|ù²¾¿üEõŸN£5mÁ;š7eˆ"*’W±–zûHêÐÙ9°÷)¥jÕ«©F{{Ûèw4{\WU4ËX5Bͪ•VxçqšaÖ˜Nªæo¶Ú'é`|Jnö†€;T¥b55y~’fG/ÒŒ‘OªzY>Õ곿’î“3œPÀ£/¨{ÕbªÐ¬·†E“Öÿd®ÛôǧUÍÏO!÷öÒØY‹´`îz®q}+©ÓŠ3)ƒûÄkUåj•Tõ±—5%òEN{Y•‚T0 ¾ÆíqÞ”ÄCž]£'ì²ÞÑI£æ.×òEÓÕ·Ay[ëjäÿ#‘äkð)ßY}Û”W¡ê«ïè‰ösØ¢'2¹ï„Y9^ü3õ`€U¡MëÍE«´,z¶&ꪆíÞÒáëÞg¸õä»p"§²NÄ~©A•-2ÜýZ&ödY'NÍRS€šd²t þðëªí›~yA‰ÙjäoQéîèw—N-Ò£E- ~b…~Mzß9°7˜äß"J§Rþ”§oÇÞ-ƒ—YE;­Mik?K}5,\Þ¦¦š~Ò}>B²Ä½··¿îšpÀe9‹Mç–´WC½}*1Xp†þºÍ;HáC¶ëO׋ˆß¯ÑwZe¨ñ²vºNöˆ9¤7–W¹¾ú4éýäk°6›§.§çô›U¨ýj%æF×´}Àí2i­¨Ó© m}¨§K[ÐziÒµ&]ƒýúNѾ´“M®{ßÓ‡Y;Þ•dò­£±nø?ÜÒò]8qùÊíýö»tuäè.ßJ/ÛáDÌ&õ 5ËØxŽÎ\/EH‘õpBW·ªO˜E¦ªõæ–SJýÛªŒÉ6ýÝJ&ceõÛæ¾ÓÅe­{º¤ ê¢ÕIËöî}ØÏô½§äo(¢v«Ò®+¸¼¢½L>wjÄÞë-`IØûÖÕ8÷Áöå êRÔ,ë«å|ÎER8Q0¤«Ö\HÛ4þûɪëkU­W*m/öë[ð¨ýú*êùϯ/ùšÌu l¿)2"H^¥Ÿ×&ÇýŽûJCªúÉòØB½xQSê¼sÎå›ie;œˆÝ¥•,2Ô}# Sð³N؇¨ï™§.áööÞþ ©ÕACìÒ9—¼!ãArœö©-oÓCš‘:"I¼¾ROÓƒšôcâgν±˜º¬O;¿¶¾»‚|*ªïÖ´ǵÕdò ×Ë»oN˜[)ÊuÚ†CüA­e•ñÁ™:î8|R8a¨7IGÜî]ÌæçUÒ~?Ú®H^j‘*vû‹*k Ô#‹äkèºÁ=HˆÕ–ç+ÊËÒNKiÈ•ÕjëgJÝ+­ tÕZgÉ×ð¨"Kd|ß•>œÈòñ®éȲ~º§„¿ øSÕ–5ó³Iûn’å»pâdùjE/^ž¦6~ºY6[úg²l‡¶Ÿ5§Y  ?©•nýO/“pbÛ@û€;톘N tøÃ·Õ/¢º¬³kÑÆ_Ï?ãArœöŽªå\z‘Q8qÀN4ÒTg:<°/©n¥=¡Äp¢RÊì„”÷³N<¢ù©ëAÅïרp¿tᄱþ›:ævª1›úª„!HmV¸­©Pò½ T«%;_'^Cˆ:¯KNlêS^“ÉË+ÕÆÏ¬Â­ghóöÚæVÛwQb‘t –6Zô—[—ºÞ}Wúp"ËÇKd»|\["‡«MÍâòöV•n«tâ†Ü:ò]8‘SÙ'ìþ£“ÑPT-¢R7iÌXŒ>ì^R^ÅzéC·pââªN²zgN¤ˆÕéõ}TÕe©Cƃd›~[ØZfcEõÙâv]Ò{]KÈ«ðSZëº_ÃÍ'|îÔð=nm.½§Ž…Íòï¸VÎÅ"™„ ǦªžÉªšc¿K·¬ã|dKù«è…í®Ë:üõÀŒ“iï¿ígÍn(¯rýµÅÑ4v§V´È§é<½~>¥\ '²|<7 ¿éóaõeñÉhiܺ'2aûe:†šU°x ½öÕ_™ì§¯JšÝ6”´]к§Ëª`¦á„ÝÕêVܪ°_87šL8>CõMé÷eH8¥æA•èòžËf–Ž,`"ŠXT¨Û†˜7#œð¶ªÆðÝ.{e$èdT+‚ÕlÞÙÄ{”I8¡øCz¥n€¼« ÒçÎ *’ÄÔ«õ e¸!¦¹Á }ïrZ1ßP=?³ŠtY§ÄI-W´©oey™ïÖK»\;u—y8q½ûž.œÈòñÒ‹ûj¸*ûd4n]y>œhÕ®‹Z¶í¤Ý{ö¥Û3«õõ7{Ѧ“³¯ì±é×O‡ªVY|K+ü±Þ2ö5Mœ8NCzwQ“{újuâ ]ûßÝn²¨ÄÃãµzç~}»{³ ‹P™¢ÅåopyZÇÉyjW¯žý¦f/yWk—Ï×ȶ5dö­¦¾›“6©ŒÝ¥+û©`‰&ê?s±E-Õ¶³ŽQþ5í›ÔDAÆUo?JÓ¢kþÔ¡)}ëHjàp³Â ƒ¥‚Ê–­¤Ÿ{C³.Ôô—Û©²ã1¤5‡i{ò8=³pÂ~O/l¦šþ®ûŒÆÌzGÑó&%=J´¢:¦{”hQ•+«Š-kRä"Í›ò¢š†¨`¡Æzã»ÔkK8»V]Ê[íïß©Ö/NÒ[ó£ôöÔqêÓö~Ýûrbès£pâº÷=]8‘µã]^ÕKá-žÕËoÌÑÂUë´bÁkêR+D^%»heÒ@>'-Y‘nã˜֊Õï¹wŸ%—¿ß ‰="VÜ>€7ËˬÂåë©yùú&åàWtxÙ`=üŸ²²š¬2…TU½ãôþ¾…je-”:sâân½Õ½¹ª—)*_£YÿPU¬ßE£ÞÿQ©K·à¿zK)@Ö²jæÑ¤¿åÛ.èÛ%ÃÕ¦Ne[d*REuž­UGÒîáp³Â ok'-Ü»Rý›ÕPa??ùª¤:O¾®OϸÌ5È4œpHЯ»æëù–µ(£5T•è®W6žtyDir8Q^½ßûŸæ<ÓHå‚äm)¡ ziÆ—¤›É{f«¦õyT5Ë—Ù×O¦ÂTóá^z}Ó¹¤ÀãáÄõî{á„ÃŽwh‰ž‹¨«2!…äm°^¤²j·yY‹¿ãÉà*χ_íÞ“nÌìÔÂ%+´oÿ÷n‘-.ûnš;’–în àß%_„È n4ë ÷]oöàß…pYD8¸9'E„€›ƒpxáð( àQ„À£'€GN"œE8<ŠpxÔÿ25…g¼êõÉIEND®B`‚astral-sh-uv-9252ba6/docs/assets/logo-letter.svg000066400000000000000000000010241521221614700216020ustar00rootroot00000000000000 astral-sh-uv-9252ba6/docs/assets/pypi-add-trusted-publisher.png000066400000000000000000003126341521221614700245400ustar00rootroot00000000000000‰PNG  IHDRx¹KÚ‡_ IDATx^ì˜d5ö·.ƒ»»»»» îîÎâîÎâîî-,î,îƒÎÀ×oøÒÿÛ5UÝUMwOUõ›çÙ‡îÜÜäMr;¿œ““!øg˜$  H@€$  H á ¡Àkø>´€$  H@€žA€$  H@@“Pà5IGÚ H@€$  H@ <Ç€$  H@€$ &! Àk’Ž´€$  H@€xŽ H@€$  H@MB@×$i3$  H@€$  (ð€$  H@€š„€¯I:ÒfH@€$  H@¨(ðþûßÿJG€$  H@€ê”Àì³Ï>HÍÚxs]ò^6¥÷UëÛ#–ê}¶Å€$  H@@Yo¾ùf(ðxp(ð¸ó¬º$  H@€º˜€¯‹ötq ¼ž&îû$  H@€$P¿xõÛ7UÕLW&3I@€$  H WPà5x7+ð¼­¾$  H@€º€¯ aŽ¢xƒƒºï”€$  H@@}PàÕg¿T]+^Õ¨Ì( H@€$ ¦' Àkð.Và5xZ} H@€$  t!^ÂE)ðuß) H@€$ ú$ À«Ï~©ºV ¼ªQ™Q€$  H@MO@×à]¬Àkð´ú€$  H@èB ¼.„98ŠRà ê¾S€$  H@õI@WŸýRu­xU£2£$  H@€šž€¯Á»X×àhõ%  H@€$Ð…x]sp¥ÀÔ}§$  H@€ê“€¯>û¥êZ)ðªFeF H@€$  4=^ƒw±¯Á;ÐêK@€$  H  (ðºæà(J78¨ûN H@€$  Ô'^}öKÕµRàUÊŒ€$  H@hz ¼ïb^ƒw Õ—€$  H@@Pàu!ÌÁQ”opP÷€$  H@¨O ¼úì—ªk¥À«•%  H@€$Ðôx ÞÅ ¼ï@«/ H@€$ .$ ÀëB˜ƒ£(Þà î;%  H@€$PŸVàÍ:Á¨qÿN ÇÅO¾»ßüâ`§{þzsÄóO#ìvsüüû=VŸzx/¾øb¬´ÒJ±ÁÄ‘GÙcmÏ/úÇ?þ—^zi|öÙg1üðÃ÷øû{ó ¯¹æšØzë­ã†nˆ¥–Zª7£ètÛþùçXd‘Eb¤‘FŠÿûß1ÄCtº,”€$  H@u'ðFf¨Øq‘)cí9&ŠiÇ)-v^ùä»8ç±wâÂ'ß?ÿŸ-1õØñ¯ŽŸÿ(Ö¼ð?­=yÆZ³Å¢S3ý¯6½;×Ä£E¿=—ˆ=[Äàñ÷¿9HÏóÎÓÖœ5>ù¡xô_Ö<2šQàýøãqî¹çÆM7Ýo½õVüùçŸ1í´ÓÆf›mm´QëBô¡‡ŠUVY%V^yå$´rÚ}÷Ýã±Ç‹'Ÿ|² Ïÿþ÷¿±Øb‹Åá‡;í´SͬKhF÷ÑGÅ©§ž÷Ýw_ðÿ®sÏ=wì¼óαð ÿmf]U@oxo¼qÜrË-i¼2n;“ú÷ïSN9e\rÉ%±êª«¶ñí·ßÆL3Í”úøÕW_¡‡º3Å·ûÌü^xašŸo¼ñF 7Üp1Ûl³ÅV[m}ûöípÎV[!¾›nºiüïÿ‹1dzÚÇÌ' H@@¨+7Éè#Ä=Û/Ó;r<ùîWñèÛ_Æ¿þs´ˆ³¾3ާ<ôVìzã ­ÍŸh´âóïŽ_fÙqå&sÇ,-Ö=Þß%|ðA¬¾úêi1ˆ°˜þùcÄGŒçŸ>î¾ûîØn»íâè£n} "dì±ÇŽa‡¶õg[l±E¼üòË ¼»âÅ?üË.»lÌ8ãŒñûï¿?î¹çâÊ+¯ŒW\±ÆR»'{ox ˆ©¦š*&šh¢Àچź3V66IæœsÎA=óõ×_ÇC£Ž:j·t_|qÌ;ï¼iƒà·ß~K›¯¼òJ›–Js¶ÚJ]tÑE±Ë.»(ðªf> H@@7¨7ôCÄv_æ˜ci<ù9î/¿ü2åÝ{ï½Ó\À[ºÑÁf›$Ü%qiä }³ùæ›'—JW1}öÙ©LÊš~úéÓ¯üñøç?ÿÏ<óLróä]‡vXŒ5ÖXé÷äÍ?Ã’W.u4g;š‹l •sÉDÐÞxãé•Í4&m [_ H@è]êFàݶõ±ü ãÆ¸ûÝ_þØv1Y®KJÞ˜#sO:zœÝ"¼HÛ^óßôß×?û>ÞùêÇVwÔ½¯Çy¿3H‘Ï3IºÂ mD[­oóù&‹ÏZ\F9Ãwÿ_Äcö‰íž"üò{Ìpä}Uµ«Öá×ouÖIî[¸”!:J¥﫯¾ŠgŸ}6-èIìê“psCPT+ðöØc$ ¨–,,gœqF <òH« Aà!ŠÆgœ$êXñî»ï&qÒ§OŸxê©§ªjGGíìîß#J'Ÿ|ò˜d’IRûªM»í¶[\pÁIHp¶+ m‡‚![gX`/·Ür霮xœ§„ .Ÿœ¡Ä²”"±¼Ã;Ä +¬ˆgúÌ3Ïœú•>@d—x¿üòKzæµ×^K‘ö 8Ÿ…E˜þl”ôÍ7ߤ¶tÐAÁ8cããÁL ‹‚øwÞ‰¥—^:¹Õ"˜w.\jÉÿÒK/%ÑWÆ5ãt¨¡†ŠÅ_<¡( çl,"qÊ s™¾¦KSGs¶£¹ˆË*cqG;qyä‘“è›}öÙ£™ÆD£Œ]ë) H@½—@ݼwY.¹6NÛb-Ëi¸¡‡ŒÑG¦Mï|úý/éߥ/gziß¿"ùUrÑ쨫‹AVjxDÑ\éœÇãö—?m}ÍsM—oÂU‹/cŽrÞyðßR«;?«f.–xÍ4&ê}ÌZ? H@êFàÝÑb©[vúÊgðŽh v²KГFxo´lüøÛÀ˜¹än¾®‚Ý!ð8ËÃyšJgð8›…e »"X„²°Ýpà Óu Xª°La=!©=‡õŽëò™¥®`Þ]e 80°žbM«t`ˆŠ,ð8§…Ð('ðöÚk¯déÄ2ˆ¸kOàÐK^x\€»(ÁUr?/±ÄIXO<ñÄ­JeL0ÁÉ•‘X.²xFwñü»åf‹Y¥r°h„¹‚¥9ke¥T‹À£ ,¹XTé[¸"ø>øà4ÞIX²¹+‘s0/Mm,±•–¹Ä•(l0O*Yðª‹å^3‰¿;¦|^€$ÐêFàm6ï¤qásÆ~·½Gß÷ú m¯V཰ÏRÑâÙ%×$ì¾ÄÔqüª3Ç\ÇÝÏ|ðçÏÚ‹¢YjÁcÄaâó£úÆÍ-n¡Å Ù»ªs»CàåsG,& àQšªxˆ‡tæ Þ'Ÿ|’¬YœY*Z)jÕ <¬¸šr‘sñö®bßåd—ËJ¡øKA?Y4)½¶×;,M§Aâ&Çy.Âõ–;‚ßàÖ—#Ò÷XŠŽ‚‹"3ø_i*ç¢ÉÙ=Îg>üp×»$wóJeâI8 WLœEeÃKÞé§ŸžÄVo~†Å³RºõÖ[Ó3ÕœÁ£Œ|Žaˆ{%}XÏd³Å¢^t³¬…s8{‰+/îÌåæl-s1o*”ºá6˘¨…­y%  H@ƒ‹@Ý<ªôÛc‰˜zì‘bÝ‹ÿ·¼øI&§®1kücÑ);´àý«åª„Ù[.FgßÛ£pÿy§®IXgö‰âêÍæI9Ïyì¯È›ã<\<°Ó"é2öb@–|MÂä‡Üï¶DíÌéÄÕf‰]Ÿ*Ö¸àɸñùAÏþýÝŽïG@¢ûa9`ñWz©v^ÄudÁ#ø!Ê!XJNÕDÑÌy¸vKFNˆ=\ÊÊYðà,§ýöÛ/-¸9s´òÊ+ÿ]Ô=ò<¬8 Gˆ{¬7Å+ËÆ@he ^ok .‚9!„9ƒUÈÙ ƒØ¥orÊ3‰Ä¹æšk¦#Œô{)²¡®XIÙ²T,/—•²8zj'^òÙgŸÅtÓM;í´Sºú£4ÁñÌk$gàÙô+sb“!ÄÉ›ùЉb™¥AVø÷rn’¾ À B›³°9aC´c-åŒ)cËq¾âŠ+’0¤_°ÒåÄîDœÅõ™vp>³Üœ­e.2 æƒ pNÍ0&:1Œ|D€$0XÔÀ£õÓ´ˆ»;¶]0¦»O<üVÿx¬åº-‹œyZ®?XfºqãÕO¿Žº/ªdeߥ§£Vš1.ë÷~<ôfÿøøÛŸâ®W?ë”À­%‚燇¯Ðb…ú3Î|ôé¿›Í7i<üvÿX»Eü• ¼ æš$]‰pÅÓïÇ-÷Þ-:õX)buÕóþ kÞÕ©;uÄ=WMvâ±ìàVÇœȚœ9Ê!öÉ[)È .z,Ž HA¸Œqf,/±¬•‡ <ÜÝX¬²xEÔpwcÁ T*ð<ù#ÜÒ}ôÑ–qÊy£FJ,Ö‰rˆÐ¦þX2YŒã ˆ‹*añ‹î`¬9œ¯C˜s^s[ˆDGœ…Âú‰)½&¡ôl"5òöœCãL&!÷q$ziiY`®·ÞzI`">qÉÃÒÈÙ?~ΕÄ ¹n¡Þ®­ldÀ|®¹æ¤º°áÜZŽÜÊF}\‡±†1ΗœY„!üéKîªãYþÍ]‚Är—"Œˆ‰••ó—¥. Xï(ƒò±rW80W°>r^“ºrww""ú±ÖÒÜÉ| ɳ¤rs– >ÕÎE®Ý`ŽÓÏDeܰÉÐ c¢ÞǬõ“€$  du%ð¨¢j‡–»ãÖžc¢˜|Œ>-Á†ˆ7[ÄÒuÏ}§>øV øu`ª{%7â0CÅ™kÏ}g¿ÅUsˆ8ó‘·ã€–ËÓ;sÑ9ïYrš±ãÄÕgI–Å·ûÿ®;xåÓïâåý–DàM3ÎH±Ë /Ä «ÍóM6fºï’§Þ‹#î~-~k‡Ý‘ºKàQW\ÑX"” ˆ-\¸‹4ÒH©I• X²,ìyÑ‚5 ¼J

?üðÃT—^3°8Õ{B "~©¥¬©{v[¤/rؘëÕwß}—ÄÁYU9±9‚e™{Z48VxGú¾A˜3®K›lxàz‹ÄòH"ú%}ê†CSw6CêDáÌwñQn¥9[í\¤ ÎiòÝ`ÜpnS6ú˜¨÷1ký$  H@™@Ý <»¦6Ý)ðj«‰¹›…®·Xl9Ÿ…Ï ñÀy4„:Â1m’€$  H@¨/ ¼úêšk£À«™t@QÜj‹÷ÝåG¸ä÷;,UXýL€$  H@õE@W_ýQsmx5#óàʹ3Î2E•@/ßÿ}Ä÷;Ü òR ê!T H@€$ ú  À«~èt-xFçƒíà ASˆüH´FÄu¸“³œ 4I@€$  Ô^ýõIM5RàÕ„ËÌ€$  H@hj ¼ï^^ƒw Õ—€$  H@@Pàu!ÌÁQ”opP÷€$  H@¨O ¼úì—ªk¥À«•%  H@€$Ðôx ÞÅ ¼ï@«/ H@€$ .$ ÀëB˜ƒ£(Þà î;%  H@€$PŸxõÙ/U×JW5*3J@€$  H é (ð¼‹x ÞV_€$  H@]H@×…0GQ ¼ÁAÝwJ@€$  H > (ðê³_ª®•¯jTf”€$  H@@ÓPà5x+ð¼­¾$  H@€º€¯ aŽ¢xƒƒºï”€$  H@@}PàÕg¿T]+^Õ¨Ì( H@€$ ¦' Àkð.Và5xZ} H@€$  t!^ÂE)ðuß) H@€$ ú$ À«Ï~©ºV ¼ªQ™Q€$  H@MO@×à]¬Àkð´ú€$  H@èB ¼.„98ŠRà ê¾S€$  H@õI@WŸýRu­xU£2£$  H@€šž€¯Á»X×àhõ%  H@€$Ð…x]sp¥ÀÔ}§$  H@€ê“@§^}6ÅZI@€$  H@Àì³Ï>„!øg94ÿýï£Üb”€$  H@€$ ÁK ’^Sà Þ~ñí€$  H@€j& À«™H@€$  H@¨O ¼úìk% H@€$  H f ¼š‘ù€$  H@€$ ú$ À«Ï~±V€$  H@€j& À«™H@€$  H@¨O ¼úìk% H@€$  H f ¼š‘ù€$  H@€$ ú$ À«Ï~±V€$  H@€j& À«™H@€$  H@¨O ¼úìk% H@€$  H f ¼š‘ù€$  H@€$ ú$ À«Ï~±V€$  H@€j& À«™H@€$  H@¨O ¼úìk% H@€$  H f ¼š‘ù€$  H@€$ ú$ À«Ï~±V€$  H@€j& À«™H@€$  H@¨O ¼úìk% H@€$  H f ¼š‘ù€$  H@€$ ú$ À«Ï~±V€$  H@€j& À«™H@€$  H@¨O ¼úìk% H@€$  H f ¼š‘ù€$  H@€$ ú$ À«Ï~±V€$  H@€j& À«™H@€$  H@¨O ¼úìk% H@€$  H f ¼š‘ù€$  H@€$ ú$ À«Ï~±V€$  H@€j& À«™H@€$  H@¨O ¼ûåúë¯ .¸ N9唘fšij|º¹²ñűñÆÇZk­›o¾ys5®¥5wß}wœsÎ9±ûî»ÇB -4ØÛãQF%N>ùäÁ^—Jøßÿþûì³O,µÔR±õÖ[Öz~üñDZÅ[Ä +¬ÿøÇ?k]|¹šÀVWý7.üÏ{ñÃq+Çðà Ùì͵}€êš@] ¼7Þx#vÞyç6‡n¸o¼ñbî¹çN‚oO&ÞÿÑ®·Í6ÛÄ,³Ì;ì°CUCzÓO?ýtðÿSÓO?}¬±Æ1묳¶–qÙe—Å•W^Ûm·]¬¼òÊ­?¯ô¾ŽÆÆ‘G?þxÜqÇUÕ³4SW¼?þø#6Ø`ƒøæ›oâC‰yç·Suyøá‡ã裎«¯¾:FuÔÖ2ž{î¹Øwß}“ Þÿý;UvW=Ô¿ÿØh£bµÕVëPlþòË/qçwÆ¿ÿýï@=ôÐ1ùä“ÇŠ+®‹,²HWU©ËÊ9øàƒãù矛o¾¹µÌ<þòFi¤gœqbþùçO"wŒ1ƨêý?üðCÜpà ñØcÅ'Ÿ|’æók½õÖ‹©¦šªª21S­ß‘Flcgê\-^gèúŒ$ î!ÐoÑEyæ™'þüóÏøî»ïâ•W^I‹‰&š(Î8ãŒf˜aº‡N™R;ZÄ÷XEêàEõ"ð, ,°@UïÙgŸ „ÖÏ?ÿœÆ‹øáÍ7ߌ<0•Eb¼}þùç1î¸ã¶¡]é}zx´zQ) IDATs¿ýö‹ &˜ ¦›nºØsÏ=;5’îºë®8õÔSxƸm´Ñzt^–kį¿þ«¬²J²2#N*¥o¿ý6:è `Ci†fˆ™fš)~ûí·øÏþ“ÄÞ’K.»í¶[ 9dýX%ÚxŒO„ßÊ×_=žxâ‰@ì!¸gžyævû›ö2>>û쳘m¶ÙbÚi§Móƒ2>ýôÓ8餓bê©§îÔ˜©÷‡jùŽÔ{[º²~ÕrQàu%uË’€$ð÷4„ÀÃÍjÍ5×lÓÒk¯½6.ºè¢ä†ì©tã7Æyç§‹æÿ_ȳx^{íµc³Í6ë©.ä=X×–]vÙ‹Öí·ß>úôé‡zhw9!æ°®Í7ß|1ÔPCµÛ–Jïë ‡µŒEvgî¸Qb¹c,_uÕU1ì°ÃÖ\\ž¥¼š êæønl¹å–±ÜrËU|ÂûÑGMã§oß¾­ù°vÂë¾ûî‹­¶Ú*V_}õn®mõÅ·'ðJÝÇßzë­´qÁÇå¸hq-¾Q‹++"1X´îò»‡z(‰Ý!†¢úŠ6PÎj¿# Ô¤.©jµ\²Àûñø•c¸¡ëg3¤K Xˆ$ #аé]vÙ% FN÷Þ{or[úðÓKÒÒK/ë®»në¢W¬{î¹'-VÞÿý`Ç®6‹»±Ç»M÷½÷Þ{qñÅÇ /¼5,þÇü¸üòËÛx÷ßwÜq©ÿú׿âºë®K.q3Î8c:Ï…uãšk®‰Ûo¿=Y æšk®ØqÇcÄGlóþ_|1YH^{íµTÜ7ÜpØtÒIÛäÃu—8ÚzÚi§ÅË/¿œ\[—Yf™”¯#&åÆ,R¶Xµ ÄïÁU+§lÁ[ýõcŠ)¦Hb¦c5VZ(—[Ãþ¶Ûn‹>ø ¹×b-à CN”s饗ƹçžOø`|õÕWi!º°°-cŹóÈ#B+ê$“L’ÚÕ1§/¿ü2qÀ-”1:Ùd“%·í…^¸5u†M%WTæø¶Ûn›žÁrUš˜Ÿˆ;Üq×ÍÞô _|ñ´ÑÄœa„Òx.×ôÕW\‘,j¸<2žžE±E™ô.¥gŸ}v<óÌ31üð纑·Ñœjx<“]jù~`‘)—˜›ÚM6Ù$}3«IÕ|£ò˜`Seä‘GN}†èsÌ1Ó·›Í™ÒoJ5yy&+Æ –X¼:ÖYgAÜjéGÜNɇ%’o0ã¼|ÏkùŽt4îªý~¿1Ì5æ:–qÚÁX¦Ÿð2È)ÏjòægªùÞçÜ-·Ü’81Fóµp) ¼£î}=.|²å<Þ¯¿ÇÂSŽç¬3[Œ7Êðm†Ô#o÷Ãî~=žzï«aØ¡b…Æ‹cVž1Æi¸Ö|Óq_¬1ë±é¼“ƶ×<¿óeœÝRÖf-ÿ&]ÔræïÔ‡ÞŽ7>ÊßtÞIbߥ§¡‡lÎMˆjæ¤y$  @ açe3{íµWZd‘Xñ ¡ƒ`ñ†˜à÷»îºkʃK'ЖXb‰$Z°êÜtÓMia€xÈ‹M݈1ÒJ+­”bœyyê©§ÒNx{AVòLáG}”þPR—x ¹=a1Að±˜{õÕWãÉ'ŸL‹æâ2™)§œ2¬ÀÝŒÀ×_ÿüç?Û,tYr6×UþËâ`ùå—O®…Õ0) ,¬yõƒ#ï†+._çŸ~«Î ·ÑG=->Y¨±€£î´ á† È ¾´ëì³ÏžZôϳ°äYRµ $ؾýöÛé, xÎK‘p³ÃJWLy“0a T“J^GïËÅwÑ:˜ßÅ{Y´E[­!C[Sˆ_„¼ÿôY{©_¿~É×JÜìb7êPLˆ{˜Ò/Œ#„ c·?\9»HpaäY~Ï‚™2Ë <Ḃ2§è#Äc•ÍRa•™#”è[æ2î´·Þzkzç"³ÈB 2gl0æ(ä%±‘€(Îÿ.å“ÇZñåø±Ù@¾bæí¢nÌÏöú"Ï'6xø}ÿý÷i>1Þù†E#í@@°éÄ<æ{C#T~9Õ*ð÷ˆÎ/WÿŒ Æ'm­ælsµß¨<&µ\Æ.m'ˆâùl_-ymŒ+¾­«®ºjšÛ¸`ÛŸó'‘OÆã ¶ŒÆãœoWµßÊëhÜUûý¢¬<ÞÙ\d1>ò·žqpÔQGµž ®%/eWû½Íå²Ù’­´ü=„S-\x—<õ^Ì3é1ôPCÄŠ-‚íÅ¿+žù0Vžiü¸iËÿ;ï{ýsÅ—> N1f¬?çDñÕ¿%¡6ƈÃD¿=oµ"ðfŸh´x¢EØñß™'%¶š²˜tŒc×_ˆÓþ_l2Ï$ID¾üéwqÆ#ÿ‹ æœ8Î[oöj>ñæ‘€$дBà!òâƒÐb¡‡ÕÁÁn#?ãø¦›nš29a%cAÃ"?¢ìŽ"Ⲙ ;ëìâ"28sBB8² (=sÂî3 ùj‹Ô½÷Þ»ÕŽ‹lvŽq…Êb ‚/ï–²ˆgÑ:ᄦ÷ç u'/uǺ•‹M‚!°ãË}NÕ2)ÝÄç´rŠI[xVR^Œ±`Ç=/¢YPa‘dÅn3ý“Ï~• Y,°FH°p"Õ²@‚ Ö†ÒrKÛD]Å÷çÇL‹ôœ`ÇF©À#O{ï+ rQî«AŸÿG]?þø6}øe§aS Sú~ž£™ $Ä?‹lÆ–¢Ìƒ±ÆxÑ„HÈV$æs«ÔE³œÀË 1X X‚EŒy\t³Î̱\ðó<¦²K(VB¬…XC¨v6aíÅRM]´å®»‡~x² ²ÙCbÐlÂ0Os*í ,ÔMæHq‰ò"yƒŠ2YÔã™-7ˆHÚ‰(¦®9Õ*ðx/šr‰z2þùÆu”jùFå1Õ–þËß9EïZòòͦßèÄsN¸Ü↜ÛA>¾ñ¥®þÅñ\íw¤šqWË÷+wïíþ?ÄA-å¼üÿYï|ó‹XòŒÇâÈgˆ}–þ¿hÖg=úNìxýóñÞ!ËÆD£ýµÉc’€$Ð 4„À+×1X€y±ÀyþP±`Ì‹Užc‡ËîWE×­\& v-Y ì±Çiw9/2Xx!>Š©#7¼ââR«@ŽÊÈÎ*–¹œXx½+ ,ÜvEëdΛËü>»«ekî¤Å€3eRÊ‹ÖêƒÛg¶†¶d%/ÈÙ!Çj™Û˜…vñpg§¶¸¢Ö²@ªv‚qÀ™,R9qž’³h9!VKTÅ~¬FàáNÇb¶4Ñ>,Gà•»&!/”Û;ˆn„0Ö&¢‚’²h/ /ÄBßöέÕ"ð* ‹{D)ª½¹ƒeáÃØc "|ØøaÓ†“âfD-rÞ‹€Ãšˆr)o”-Ò•"š–öEžË|øfËs¶ÜW*OæZæ”Ñ?82þÊŒaã±ZÍ5µ|£*}'Xf,m9ØO-yWXêùΖEÆgÞ|Àå¯ \¬+¬ö;R͸«åûUn)·÷iþ@36ù®×’·–ïm.·¸‘ëP-òW ²rÀ¯ÄÑ÷½o°tL1VŸäV¹e‹µïÚMçŽ%§§MßÍvÌý±ÒLãÅikþÍ÷Ó¯ã탖‰a çú6¹ü™¸üéâý!×g¸ÿs]~ÿë[DâqMKÙkÎö/µ|Ì+ H 4„ÀÃõwAþØœp i‚官X¯R*.X9³ÃYxáì–¬j„Ï»Ÿ„‡Æõ§˜þŽÀËÏ– ¼¼Xæ¬VIÎô °°”ž·Ãý·,F©[¥…a-LJ¹q†÷I\ÄØMÎŒ°Àd—¾ö^>ó“]¥Ø©'xC¹3¹ýYüÕ²@ªv’Cæ#ZŠWo`Ñ }$D>êï ¼JÖÝrQ4kuÑ,'ð°j#vŠâ»´?³Š`ù%–ZD!®s\™@ÊcדּÎJ.‚•RµaI†J×&`‘fÑÌ{I•±Y8¯¬ÀÂÎV/αbÅ-žYªæ-nXx*]#ÀYHúŽ ¥ìIPiÎå¾à›…Å,[ü+Õ¥¸‰T©Lú|Ûø&äÔǸgò)—òÆG5¼Z¾Qí}'ˆÜ|¥Fµy3¥ßæÒ6Ñ6Úù™j¿#äïhÜÕòýjO´áºÊQ6q3®%o-ßÛöÊ­…K%w̿ވýn%žß{‰˜iüQbï[_Šãï«âôÄzwÙFs¥ß#ðÆê3l<¾kÛ@jóøPô{ÿëŠeœ»îì±Å|mϪWó=0$ f!ЯèZ“ݺJ-r¸÷¤Ò9$„g°rá6ˆ5ó=¸N½óÎ;i9/àòY¥r‘ó·ÀË‹Mvõ±Æ*- «eR:˜9o…ˆä‹(¬Z,0XܲP¯Fàa‘d9÷–ÜHË <ÄÖKvØ9çUË©Úbë–µJg`бÑ^^c a¾\B!º+%Üöñí-Ü‹Ïv•ÀC°ýôÓOi^’jxäG@Ò.6#°Ž"ô°èU{uJñÜ$gfË¥ /¼0‰¢¢ l¥9WÚlDÁ–ïKÑõ-¿‡ŸqÞ©½yÜNXèí•,tˆ,|ÕœÁkoœ”~£ªm0¨6/Ö_Î$rö±Òý—|Û±Üq¶¬RÜÕ~GrþöÆ]-߯öÄU Åß+6vjÉ[Ë÷¶§Þ^·¼'<ðV\ºáœ1á¨m¯Àw‚QGˆiÆù+èV%7ï ÆÓ|ÿÞaÐQ<7Ãx£Ä8#—·È7ËâÍvH@h@à “$  ´%ÐpêçÅEѲGD?h¥âüXoX˜aáêU ÂïóèlÁ#?9øãF0“|}þbÙbWM•Ò3xÕºhò^ÎûXEY±D±Ä¥s'Ô+Ÿ'©´0¬†I¹ ÁÎ8V;êŸW5°+\ÎEKbŽ8šiÔkB€”ÏP•Z™²¸.ö[vïÌ.³gl`ÅjÏ‚G¾JïëŒÀËnXÅ+!ˆšˆË¢¢ôš0Â; ò 9Ãȶœ•(ó,)’çȹ¿±d0öˆ¶ˆ¥§xõã²4Òi©»q¹z¶š—Zݳ¥ƒà=‹-¶Xêºjõ¦®X–sâœ(¶œku{ã" ÿR‹=gÄøgtŒÑœ˜sô–Ì|`¹¾ Ž\ÆeÏçïR±ÌrþïZð°lbcÌàö] 0Uä‚àfΈ¾*^-%Œ ‚™L3Í4éÛXí7ªÑVKÞl-ŽŸÜž"Û|ΗÙâ• ´—x¾£íÍë"£jÆ]-߯<ÞóÙÒü.¼(Ê÷&oÊÔ’·–ïm{¯Z.Ô»Z×À¯-AVîŽéÆ9Úi‘èÓrEBNÿø3†*\qPIàÝñò§±òyO¦ˆš\›PLÅ2Olnà­SüV´û‡Â_J@h )ðø£ÃB*¸OåHÙ¥…  ¸’X,sþ QÀ†Å‹ÜõX¼"ÖXа“_vÝåXæX€üƒ+°ÒõG a#ˆeaB—K,œŽ9æ˜$žˆrJû8Ixx,·X)¤J ŸJïëŒÀË 1‚„à‡±Š‹tJ¯T Ÿ9ãÅY1ÎnÁ‹:bý, £_l;Ö1&:Ö¶r®‹XŸxâ‰dý¦Ìluâ¬+cŸg¨ !аaL ê‡,žè#òVº&¼Ü‘XzMgk09U+ð°:rf“ "ßÒŸüŒ ¬Ç„̯61Þ8ÓF›¸êïc ¾ œËeLtМÝDÓÙ…®\_ðÝÀâN#0È‹å: ~þ~u•áEû—œ]äG¿"òŠÑ&Ëñ¡^Ìú‘yļæ{É8åwú»–oT-¢­–¼¸i2®p±gî°IÁw«4ãK$‰ù¸cüò-ená.ÎF!ÖÙìê^Íw¤šqWË÷+wæ³ø^2®ø†2Ï¥Q}«ÉK»«ýÞ¶'ðjù¾V+ð(ó²~ÄæW>S5RlÞrV+Q4o|þãxºåš„±FúË*WIàñ»ü¾%¦;Ö˜m‚–qñØÿ¾Œ¿ý)üÇ_wafKfé&dµßóI@hT )ð€Í]\,XX|±ëœáâ±z°˜D ð‡¡ÂÙ»ÙX@ˆªÈP\„¨&¢1Q /b‚E1e‘—ÝTH,rº[àñ~D ï§ÎüQgÁ…ð)½c­=G91)¼ˆ,3,ê±Np6 k)B—¨R‡eˆ…- Ï"…Èuùʉbùä¡òE縭!ºK­ LvÞY”qn.‡ŠÇ2X*ðX¬am‚å§Üåݹ”‰5‚E+ JÚˆf,`IÊ ùJ ŸJïëŒÀ£N,9ƒÈU,>kˆ ¬Ÿ¥<ê‡(Å]zp~ 7Çj¿´/YÜ"|ᥣ\"ê ¢·ˆA«Vo6TX<3ŠA8—ÆÙ7ÕÌ3¬ÛÔr³Ç=—9…5aDP˜ÕW_½ìEç¥ÖÆÒ +XÓYsÖažçz¥q×ÑHyô9ãƒë ˜gl0”‹¾›/GÔ2GY”·×D,…í@<²Á· ùꉮx¹­lJá}ÀüE—³î–ã‚0äž>6¸¸×2§GÅ‹æ«ýFÕ"ÚjÉËûÙ´ƒ?rløáÝÀ&u-Zè™\—€ØÅUŸ;øóíÏn·Õ|GªwÕ~¿ò7+LË=z¤ö¿ç2õs{'^ùôûdù›o²1b×ŦŒe§ÿkƒ·c6|ø›P´Þvô=ð÷€@] ¼F‡ký% æ$Àâkqñnºæl©­êNY\åûÛ{W-y»³Î–- H@õO@Wÿ}d % :#БռΪkuê”@GÖ³bµkÉÛ“Í}îÃoc÷›_ìÉWVõ®V9f›hÔªòšI@³Pà5[Ú H Û (ðºq¯xA-¢­–¼= ~‹ç>ü¦'_YÕ»f›h´m„aªÊk& H@ÍF@×l=j{$ n' ÀëvĽâµˆ¶Zòö x6R€*Pà98$  H@€$  4 ^“t¤Í€$  H@€$ Às H@€$  H@h ¼&éH›! H@€$  H@ç€$  H@€$Ð$xMÒ‘6C€$  H@€Ï1  H@€$  H I(ðš¤#m†$  H@€$ žc@€$  H@@“Pà5IGÚ H@€$  H@ <Ç€$  H@€$ &! Àk’Ž´€$  H@€xŽ H@€$  H@MB@×$i3$  H@€$  (ð€$  H@€š„€¯I:ÒfH@€$  H@Pà9$  H@€$  4 ^“t¤Í€$  H@€$ Às H@€$  H@h ¼&éH›! H@€$  H@ç€$  H@€$Ð$xMÒ‘6C€$  H@€Ï1  H@€$  H I(ðš¤#m†$  H@€$ žc@€$  H@@“Pà5IGÚ H@€$  H@ <Ç€$  H@€$ &! Àk’Ž´€$  H@€xŽ H@€$  H@MB@×$i3$  H@€$  4„Àëß¿Üzë­ñä“OÆçŸÃ;lL<ñıÄKIJË.C=´=) H@€$  H ×¨{÷ÒK/Åá‡ß}÷]ÙΚa†âàƒŽQF¥);ó’K.‰5ÖX#Fi¤¦l_±Qø¡†*æž{î¦o« ”€$  H@@w¨k÷Í7ßĶÛnß~ûmjû¸ãŽóÎ;oüøãñÈ#Ä/¿ü’~¾À ÄØ|º½Ì?þø#†rȲïyá…â‚ .ˆSN9¥ÛëÑÞ ~úé§Øf›mbýõ×å–[®Kë²ûî»ÇÔSOúùÍ7ߌãŽ;.Î>ûìŠLºôå& H@€$ &#P×ëÕÕW_O7ÝtñÏþ3†n¸ôïwß}7vÙe—V‘wê©§&¡pÌ1Çă>˜òyä‘1Çs¤ÿ¿ñÆÇ_|‘„à 7ÜÃ?|üðñæšk¦ß/´ÐB±ÿþûÇõ×_ŸDéÊ+¯Œo¼1•‡Øœh¢‰b»í¶‹Yf™¥Í0xöÙgãºë®‹7Þx#Y °*n´ÑF1å”S¶æÃÊøÔSOÅL3Íûí·_œuÖYéßcŽ9fëûJÇÖ¡‡‹/¾x,²È"ƒuØ!BO>ùäXzé¥cæ™gîÒºï½÷Þ±úê«'!o’€$  H@€j#P×o«­¶Š?ü0µq7묳¶iÝgœ·ß~{úÙZk­›o¾yÜu×]Ø#mºé¦±Î:ëÄ—_~n¸aë³GuTÌ>ûìñÜsÏžûî›~Ž…jÕUWm#ðúôé“D`1á zÙe—¥s€¤›o¾9Î9çœA¨#DO<ñĘbŠ)Òï²À}ôÑc´ÑF‹wÞy'ý|É%—Œ=öØcç±ZÒž+®¸"‰ÑréÏ?ÿŒ!†¢¶¯³Ü´}ª©¦J<ý‰ål’€$  H@€j#P·ñ²ÒJ+ÅÀS‹n»í¶A‚©<ôÐCIø‘^xá$ „CÒ‚ .p@<üðÃqôÑG·’Ù`ƒ ’àÃêvá…¦Ÿg `Ñ‚7Ì0ÃÄ&›l“M6Yœyæ™ññǧ¼¸ƒâŠq‡v,\X÷°Ú‘|_}õUÌ3Ï<Ž”^®ÄòË/Ÿ¬‹L0A«,vÝ£>šË{ì±­?æ,VFD%VÃN8!¦vÚôÿ/¾øâTÊ£ÎóÏ?zŽ´ó–[nI,ù9ÿqÄ[E–P¬’ÊœÖ^{íôïì’É»aXñÞ~ûíØk¯½S,{Ÿ}öYzÖQêŒÕó믿NV×wÜ1Ä)­Ëï¿ÿžêBÉ—Þ|ï5×\SÄáÿ—€$  H@€ª P·svˆ Ö0DMiêׯ_tÐAéÇX÷²ØCÀ!°ÆgœÀÍÁÅóÓL3Mr£Ìy±äq–o„FH–;JQàqæ,‹¶K/½4®ºêªô®lí㬉tÒI'%¡B"ù‰îÉ{qÛ, ¼¾}û&aØ^ºè¢‹’ûi>ä…VÅ-·Ü2æ›o¾@€„K¾9çœ3^~ùåtfëæ„×׬ IDATN˜¬7ÝtSì´ÓNIHÂŒŸ!ÜrÙx7\b)wÒI'Mu¡ÝwÞygìºë®é݈òÇ<Î?ÿüÄ¶šº –qѤÏ8si’€$  H@€ª'P·&¬¼òÊñÛo¿¥Ö ¤²[dnÞý÷ߟ‚r8§–Ý-±,aµ#q†qõúë¯Â—G#çð°ô}òÉ'IqÄ)Qàqno½õÖ«øs¬iXÏÚK¼oŒ1Æh#ð°Êut– ÷N¬‚XÒrBàq-ÄÖ[oÝú³=÷Ü3 7ÎætÈ!‡ÄôÓO«¬²J¬»îºÉU÷ÓœJϽuVàÁ¡IB#ˆ±¾qž‘„XÃu–(¨Ô±šºðÜf›mûì³O²Nš$  H@€$ ê ÔµÀÃÂôÞ{ï¥Ö æšk®6-Ã=ðž{îI?Cˆ!ÈHœã‚E"Ð ‚¡ˆ% ë …ÐÁ…—FÄ©«åau+Zð¥[i/a]D⦚SÑM’ŸQ÷VX!ýzä‘GnÍ÷óÏ?ÇŠ+®˜î ÄÂvî¹ç¶ºI’©«n¡ÙÊöÊ+¯¤rKë2`À€da{챫ª ÏcݤŸ8'i’€$  H@€ª'P×ë×å—_žZC „Q8òÖ[oÅn»íÖjáãŒÜä“Ožò¾ÿþûÉ’„;$‚Û$Ö,ÎŒ­¶ÚjÉu‘T´¨Õ"ðˆ„É™3ÖÃö¢]Ö*ð8_ÇÙµR ^>Ç;x´‹×¢‹.ڦוX'q¥Ä•7ÊœJ.•ˆÉZÎàQnQàáŠõÑŲ˜*CŸTSž£DÓÔ‚WýD6§$  H@€ P×ïûï¿O®‡DÁ$a-"¸ V!\0ó=xˆ\úŠ ‹‘(Çk¬èß¿kÀ•쾉àãÚÎŽ!ê²ûg-{Û°‘Z‚ã\õ{æ™gÒ7Î÷‘jx\Õ@0”¢;f©róy·r‘8q›ÄE’:âÚ™Ï žò<\Má«&‰ú„†ßW ²R*ðˆ6Š•Ÿ/³Ì2ƒÌ®jë‚h¥X`Ç|g©$  H@€$Pºx´ƒ³s¸Xr]¹DôG¢ZŽ4ÒHm~¨äæ+ˆðxÞyçµæqÆãøãoýw-‡Š‘8Kë‡ÈÄý“T«ÀCÀråC1úg9÷ôÓO§@3œ[j©¥’è%'ÂÑŠ+*UyœéÚ‰Õ‘óyYàÐ…w“À0XNqm/Šf©À£Xô(ûí·OA4ý‡µ”T®.äÇ 5×…H¥Ô®~D óЬ€$  H@èu/ðh%1 ²òÄOÄ矞„ ED©,Mˆ\(sÊ—žgWÂüs¬n¸8æT«Àã9­-óµ×^‹Ÿ~ú)Y 4‚°uÔQ;%ðh3A`ˆL™­‹å…sa:—²s·ïã¬"mâ\–³ÓO?=±£‚­<ÿüóéê‡,ª°t⦠¬f;ï¼sk‹-¶XÕ<êõ ·×;î¸#‰»ñÆ/ÌçËՅ눰™ë™Ê'Ÿ|2 b“$  H@€$P†xµ5©yrc™ä¾<ÜR»2•^.Þ•eÿݲpÅ-”{ M€$  H@@mxµñêÑܸ_^{íµm.;ïŠ ”YéŠ2»¢ .='Z*wàá*j’€$  H@€j# À«Wç&Ø×?¯Aø»•¨WÇ¥ó¸k.¸à‚·‰>/ H@€$ ^I@×+»ÝFK@€$  H@ÍH@׌½j›$  H@€$ ^I@×+»ÝFK@€$  H@ÍH@׌½j›$  H@€$ ^I@×+»ÝFK@€$  H@ÍH@׌½j›$  H@€$ ^I@×+»ÝFK@€$  H@ÍH@׌½j›$  H@€$ ^I@×+»ÝFK@€$  H@ÍH@׌½j›$  H@€$ ^I nÞ€â‘G‰wß}·WvŒ–€$ æ 0Ùd“Å"‹,}úôiŽÙ H@¨ku+ðn¹å–˜d’IbÎ9ç¬k€VN€$ЧŸ~:>ùä“XqÅ% H@èvu+ðÎ:ë¬Øa‡º€/€$  t'ßÿ=Î9çœØn»íºó5–- H@Hx H@@78ãŒ3xÝÌØâ%  Hà/ SN9e|ðÁqûí·ÇK/½<ð@D³Ì2Kzt®¹æŠ 'œ°Uàm´ÑFqë­·ÆH#SO=u,½ôÒI " x?¢nÌ1ÇŒ·ß~;€LžSO=µØ¬EàýñÇÉmô­·ÞŠVX!ÆüÔ¾|06Ùd“XmµÕÚ¼E]4ž|òÉT"uÂQ¸þúë§ü& H@è9 ¼žcí›$  ôvM!ð¾ÿþû$`°°ekÝ/¿ü[l±E|óÍ7qà 7$ ןþ™߸ãŽgŸ}vkßÿþûïÁÿf¤»îº+N>ùä².šÙ‚‡E ëÙÎ;ïÜF´Ý{ï½IbÌéðÃO"òØcM¢2§ZÞ›o¾™,„«¬²J‘9 0 ‰Ìœ²oä‘GN‚uæ™gN¿Âª¹é¦›Æ(£ŒW]uUo÷¶_@Pàõ(n_& H Wh W©;î¸ø×¿þ§vZL3Í4Ià­ºêªÉUò¬³ÎŠ>}ú”}´‡µî¢‹.J®¥{î¹'N<ñÄØrË-c­µÖê”À{ýõ×c§vŠ…^8öÛo¿6¢²øþ,ðvÛm·XvÙeÛT îšX³KgGu÷÷€$ð÷ (ðþ>CK€$ ê4•ÀÃÝ·Ä×^{-¸êÆŸ~ú)Ž:ꨘsÎ9¬yçž{n:¿¶ÒJ+%Äy½bªFàñ"ª\úñÇ“EWÏwÞy'ÕKâ:묛o¾yE‡Õñ»ï¾kS$çåòÙ=,r´wO¬¸a– µö.:?è ƒÒÙÀ+®¸"µß$ H@=C@×3œ}‹$  D:6û쳂bˆ–`$–T鮆‰…m‡v¨ªØ~ø!øãyÿý÷'×HܧšjªÀê…Ð:âˆ#Ò™¸œˆ’yíµ×¦Æ=ôбøâ‹ÇV[mÕ*¤:+ð°^}õÕqÍ5פ@-ÓO?}L7Ýtñ믿Æ-·Ü’‚¡à6šS©‹&çéŽ>úè6mF˜"PIœÃ£n”…€Å “³wë®»n 5ÔP)O{ïC‰'žx".¿üòt^Ð$ H@=C@×3œ}‹$  4‰ÀË–-.'G4e·I"H"äJ^îø?ü0GÃsòÉ'3Ï<3¹>vV༅ .\O°í¶Û¶ FD&Q-;xA!(K16ÚhÉbWšžþù¸ð “µK$çóxNi H@õI@WŸýb­$  4#†·àa½#p ÁKbRL ¼œ—óqœ“#Êå´ÓN÷Ýw_üñƒœ™#²RÎE“à'ï¿ÿ~ÜtÓMmÎæU+ðj`Xô8ׇk*çêxµ4¿$ ž! ÀëξE€šÀ‚GÍ5×\3]sÀÐ!†"õ+çÞ=ôÐôßlÁûùçŸÓr‹,²H›¾GþûßÿNgó¸ÛëØ^{í•®>Øc=ÚämOàaµÃuM¢U’ˆry 'Äã?>ˆ÷JÎ×]|ñÅŽE„#gûpù̉;ý8ÓÇ9C,• ¼1šAÀ`! À,Ø}©$ ^I á-xôb QÆÙ»f˜!]0Ž#ræ /¼Ð*ð²e18ï¼ó¦(šÜ+ÇwÜswä‘G¦AÀù9î¹#0 "7IÊæBôögÛ¸ˆ|‚ &ˆ\0‰»Ç{,äÜ_©‹ævÛm—áf›mÖ&ºf¹‘ˆE‘ús–pÆgL®¤\á‚wD\x½rÛh H (𠓬¢$ &!ÐïÛo¿M÷Ú!xHˆ1DÓ×_ûî»o›3x-‚” Œ°ˆq9÷çm°Ám"R" 9“‡å +¿ïÛ·o»—IÞÝwßX§˜bŠQƳ¥ˆ–\åÀ™ÁŽî¦ÃZ‡)"Èœü{¢‰&Jçï¸ø<'ƒ¬4ÉÌ´@SPà5UwÚ H@uM )^]¶r€$Ðë (ðzý€$ # Àë1Ô¾H€z+^oíyÛ- H ç (ðzž¹o”€$ ^F@×Ë:ÜæJ@Œxƒ¾¯–€$ ÞA@×;úÙVJ@¨ ¼zèë  H@MM@×ÔÝkã$  Ô^]u‡•‘€$ f$ ÀkÆ^µM€ê“€¯>ûÅZI@@Pà5QgÚ H@uN@Wçdõ$  H ñ (ð¿m$ F! Àk”ž²ž€$аx ÛuV\@ÃPà5\—Ya H@h4 ¼Fë1ë+ H q (ð·ï¬¹$  4^ƒt”Õ”€$ÐxMЉ6A€ꛀ¯¾ûÇÚI@h& ¼fêMÛ" H@uI@W—Ýb¥$  4%^Sv«’€$ z" À«§Þ°.€š›€¯¹û×ÖI@@PàÕA'X H@½„€¯—t´Í”€$ ÁC`àÀqöÙgÇvÛm7x*à[%  H W¨[wë­·Æ$“LsÌ1G¯ê+ H@ÍE _¿~ñÉ'ŸDß¾}›«a¶F€ê’@Ý ¼ÄC=ï¿ÿ~]‚³R€$ jL>ùä±ð GŸ>}ªÉn H@Àß"P·ïoµÊ‡%  H@€$  ôB ¼^Øé6Y€$  H@hN ¼æìW[% H@€$  ôB ¼^Øé6Y€$  H@hN ¼æìW[% H@€$  ôB ¼^Øé6Y€$  H@hN ¼æìW[% H@€$  ôB ¼^Øé6Y€$  H@hN ¼æìW[% H@€$  ôB ¼^Øé6Y€$  H@hN ¼æìW[% H@€$  ôB ¼^Øé6Y€$  H@hNu+ðÎ;XñÆ+R¿øâ‹cÀ€±ãŽ;ÿÜqÇí²zíµ×âê«¯Ž—^z)~ÿý÷˜jª©bà 7ŒÙf›-½ãú믻ï¾;Î>ûìzè¡ã?þˆW\1.½ôÒ{ì±Sê¶ÖZkÅ‘GsÌ1G›ºmµÕV±ÄKÄzë­Wu×XcØvÛmc饗®ú™zÌøâ‹/ÆÑGGqDL1ūЗ\rI|õÕW±ë®»¶–SÚ7ë>œ<ðÀqË-·Ä;ï¼à 3LÌ>ûì±ùæ›Çøãï|hð1òì³ÏÆé§Ÿ^xakKnºé¦¸ù曃ùÕÙT®ÜΖÕÓÏ•ûžS‡fù÷4Oß' H@=O nÞwß}—Dé‘G‰«®º*Î<óÌVBºwß}·ËÞã?GuT,¸à‚±ì²ËÆH#@b‘{Úi§¥îSO=ýúõ‹vØ!Õçû￵×^[WÅøýøããòË/-·Ü2ÆcŒ*ž¨œå¬³ÎŠŸþ¹À+훿õŽ .¸ -öÙ¬˜sÎ9ãÏ?ÿŒûî»/~ûí·Øk¯½!çCã”G}4‰»®xåÊmJå¾ç ¼Fé=ë) H@¨[Wìžl-c¡YLo¿ýv— ¼Ÿ~ú)6Ùd“Xl±Åbûí·oó.¶C 1DÙQóé§ŸÆf›mÖío»í¶‹¥–ZÊ‘ûÿ üñ1ÔPCµxÂé:/¿ürì±Ç±ß~ûÅ /ì|¨m{ߊ*‹è±l÷Þ{oòV(xXlñŒèl*WngËêéçÊ}ϳÀóÜÓ½áû$  H 3šBàxâ‰qå•Wîã7^ì¾ûî1õÔS·òÀ]ˆÅ ¿ &˜ ‰¸ùçŸ^X&N=õÔ´àéÓ§OEžE¦ûï¿?Ž;î¸6y©ÏÄO\µ‹æ.»ì³Ì2Kr{Ë ‹ ÿ^n¹åÒpÚ`ƒ âÍ7ߌ'žx"FyäXsÍ5c•UV)[OÄêE]?üp²¶à&Š‹ç˜cŽ™ò#ޱÀà.yòÉ'ÇgŸ}×]w]üøãÉe « eä4óÌ3DZÇ›Þ}Ûm·Å믿ž/.£¸œ9ä­õÜwß}ãÁŒ'Ÿ|2†~øØtÓM[…i©(ÇÍ•Åd1a)Â…óÿû_r‡}î¹çâ‡~ˆYg5õí¨£ŽšØ|òÉ'­ågʹ—Q>ÿûòË/Ó¸àÙf˜¡õYضWçRÀå‡÷]wÝ•,]|ðAŒ2Ê(±ÑFµº×ÂáO¬Ò¸>N:餩LHú 6n¼,(óÆÂ/¿ü’,j°8p`,°Àé÷#Ž8bÙ1Ð^»+õîG <á„Rýí¥zŸ¿þúkš'‡vXÌ=÷Ü©)| `O¾ ô}ÞŒ5¬ÌÙ庽oíá…’ÕŸ9‡K÷¡‡šæl{ãjp““N:)b9ápë­·í¹ýöÛc§vŠsÎ9'°ºÏ4ÓLé{ÁX&µ77+•[:†(÷ÜsÏçŸ>Fa„ô`žPW^y%Íu„gvfÓïSž|ŸøþóŸÿ ¾OÕðn¯©_¥ïùôÓOŸÊßzë­ã­·ÞJù¨sñÛÆóˆC< hÞ+­´Rò9ØîÄò—€$ Nh ‡˜ba€¸;ãŒ3’U‡Å) ÑwÀ$qƒÀ*qÊ)§¤|N8al¸`òÇ›ßW» å¼e²øa±8ÖXc¥ ‚¤Ú3xÕ <ÊÝb‹-bÆgLŒÅ×A4ˆXÅ‚ÀÂ’Ån¤ˆ,Î"$o‚,´<,䔿Ï>û¤… AqÈ!‡¤3W,¾&šh¢$JXü²àyÿý÷ÓâjÏ=÷LVO‹ žßm·ÝÒ™EÎQâ’ÉÿXð” <\,y ñzðÁ§wÐFÜri ýÆ Ñ7×\s¥ú²8¦n¸yþãÿH“ó¥÷²håœÞd“Mÿþ÷¿ãÚk¯ ¡SN9eUu. µ‘sŒ%˜Áõ_ÿúWm,þÇgœVˆz±ð⿈:8b ¦¯`ˆP@”ÀqL»ËŒqúÁ㥩£vWêÿb9,fçwÞ4æy>T#ðèD9cl¸á†KBo¡…ŠÑG½Ãocîšk®IÂŽ>có…¹Tïã„Mæ"±Ä£Þ´5í´Ó¦ï*cŽy¾Â +$ACjonV*·8†¾þúëä%ÁÆÓºë®ß~ûmÚ\cŽb1¦ „Ñ6ÛlË,³LrƒgòsD.úO?ýtê/ú®Þíõq®[¥ï9lèO¾1Ô‹o""¡—¿m´6±ñBÞ/¾ø"mþm¼ñÆz^trâc€$Ð9M!ð-y±Î\,,êIˆ‚y°[Ÿ²Î:ë´¡†5‹sF!a!*>·ÿþû§Åv©ˆxõÕWÓ®\¥»·,"ø£Ÿƒ¬T+ðXps.0'Bˆƒ<°M;èT~Çî8â—„"P ï]yå•[E‚j¾ùæk}~µÕVKbh‘EI?Ú‰HÎLJ‡L°rÐ ,zÙòøÍ7ߤvbÕ„y%·Z‚Ù Ü=˜r‰º`CH“<œb•bß p×_ý´H\~ùå[‹¤Îˆ\þ[MKëÒQKóÓß«®ºjªçâ‹/ÞÊ [Èøã¬iÑU‹.ºh⇓ú²˜DtÄqÚ\´¼UÓîÜ¥ý_¬;ì°|åy‚Pe®åD°yPïó¡Ç\!¨Cˆ…bêèBûé?‚’ä¾é̸êéqBx¸À—ºh"X/»ì²Vlˆ`GPU37Ë•[|Žo%ã‰÷"šHÙj‡ ¡‡ÅO 60{ì±$¨x>6O`Ž%ñðïj·×Çź•ûžçþäÊ7’„€ã{š¿m´ ¯6ó7ÿ†n¬¿•¾ŸeaúC H@Àß$ЯE“à'Xµx,Øu&±«žb‡¨—,ü‹‰?̸²“LBt|þùçéÿï¼óÎi1E£–-ÏM7ÝtmÞƒÀD¨Õ*ðJ£h²`añ€›c1±S{b ˜D£6ZjG%¡…õ ‘‹%…1Ï`±C´‘p‹BdÁ™Å;ëD%©4Ò\^\³ÃúVé½ÔëmÁ‚BbÁËÂŽ€ o¼ñF ‡vØd-!u$ðèK„gQHñ‹O‹,Ȫ©sˆU´‘üŒθj}ôÑGÉ:Çf.[åP'¸=6'¶ˆb\Sˆâ8ƪG_áÆZt)®¦ÝÕœ_e¼Ñg¼Ÿ„p¤hs ë, õ>ªxýû÷Oaú‰Í¾¸ø£oH¥¨“Í…Á=Nò\('ðJ£h"ÄØèAèU37;xŒ,ú9Peš "##âpgŒ!ä}lÊ ð°þ17ø®ö¸iV3+õqéünOà¿Á¥ß66`øç(ÖO\€ùÛb’€$  ô¦xvâûöí›\Þ°„ âÒ³K÷ÜsO²P J‡[ ŠZ^5×$`Ùá IGgðJnU¸‡fAšÛ‡ÀCà²_LXú8CƒE¢Ò« ]„ .Pðãß,~¬XDYˆ!\±–²ˆ)ZÃÊ-‚òù§rïE(R.ÖÄdN<Ù„0=š,IÝ-ðŠg¶Š+-Üs~úÖl"pŽ7MvúCEëiqc¢’Àƒ1â—8Æ4®Ÿ¥)ŸË?ï*‡ØçœTé¼|~­3opÌÆ'ÜÛ;ƒ—E n6Æ$ù§™fš¿!µ ¼z'µ <Îæ±ÐÑÜìŒÀ£>|Wp+Gà}øá‡iìóÃÒÌ÷‘M7Äß>æ!õÁ5¶=Wìw6JûWÔbªUàåòÙä¢]ôú \¾l˜$  H@=E ©XOœµÃjÕQÂ…˜!pH©u¯=‡(½°¸`¯å¼R7GžÅ¡T ²RNˆ/ áÝÅÄ.2eb â܉sn”É™šJ"#‹¦%—\2¹–&0 ±‘]9昴èê¬ÀcGž>â, gérBXpæ†ï|_g¤ØÍÏ!Ênyñ Z©‹&ân¥.šXžˆÕ. ‹,:x#x]g ƒ«cg‹R¬çŸ~²´—°´uÔîj÷#° IDAT,xX±äÒÎåÔ‘À«·ù@½áž]dùwž#¥ÖÝÜFÆ›" âºô êÛQ»«x”C[›ˆSKО;î¸#ESå|ã£Þçí ÿó¦gk©;ü²À#°s›3 HBLsÑÑ7¤³¯Æ ®ÖÌg¬çÌ6Ã:xéhn–+·8DD@(ñíÄõO¬Ýù[B~\B 8Ä7![qáÄ;Ëuv ¯F€µ×Çź•ûžóÍìhÞsF‘6á¾gÏ<óLŠ@Ê™B“$  H §4½À$'¹F…®2œ+B(ÏJsÖ€« Z„ sÜíHå@¸DòÜ5,kÕFÑdÁÄn>®}ˆ7Ü^DT,ZðXذØ~íµ×RºB´Ér +‹W„E¾&‘Ýù*-ð ÁŸÝ.$˜ñ.ꃉ³rˆ?Ü_ù/ åÎ <¸R¿b"pJ>“‡{®¡„ gÁMTº,ðX$"89ŸÇY5"ý•ö ‹z~Fÿw.úžEXN-ÜJùv”F°,`ðÆjаd1ÛYGèS,ô),†âEëZ®kGí®VàQg9EÔTú›+‘aÞ(ó¹ŒX@Ü!â|ô VnÄ2}Åœƒ nvDÐÄ”7Úû†tVàÕÃ8aŽÃ…ÍÎÄ1÷zè¡´éR´ê-xô9߀öæf¹r‰x[L¸`rŽË bˆï+WØ7ÞØpcŽ3çòYP,dÔÍ+¾AÕÎãŽú¸X·Òïù<óÌÓ¡ÀãyÚÄ7’¿!lÖ1GhS¶H–ýXûC H@@h×Åm¶¸ B€ˆd§›Ý|B‚EçR°þ™$  H@€$ ú% À«ß¾éñša‰#ê$.¸ã‘°z²ÃÕ#ï ÷xÅ|¡$  H@€$P^U˜zO¦;ï¼3ø¡Èq7Åňv„÷/½Ï¯÷P±¥€$  H@h  ¼Æè'k) H@€$  H C ¼™A€$  H@@cPà5F?YK H@€$  H@PàuˆÈ €$  H@€ƒ€¯1úÉZJ@€$  H@耯CDf€$  H@€$ÐxÑOÖR€$  H@@‡x"2ƒ$  H@€$ Æ  À«±Ÿ^|ñÅ8úè£ãˆ#Žˆ)¦˜"=½ûî»ÇŠ+®K,±D¥™]€$  H@@×PàÕÈòã?ŽË/¿<¶ÜrËcŒ1ÒÓÛm·]¬µÖZ ¼Yš]€$  H@èZ %ðþüóÏbˆ!º–@”¶ñÆǦ›nú·^½¶­ ðX„$  H@€$ÐCêZàvØa1à 3ÄW_}wÞygl¸á†±æškƳÏ>_|q¼ûî»1ÁÄ&›lóÏ?BvÓM7ÅþóŸ˜cŽ9âž{î‰o¾ù&¦vÚØyçcÜqÇmÅúÌ3ÏÄ¥—^ï¼óNú9¸e–Y¦õ÷·Þzk\ýõ1`À€˜jª©7ÓL3ÅÛo¿;î¸czÿC™~^L›m¶Y¬½öÚñÓO?ÅE]?üpüöÛo©>Ûn»mŒ9昭õ|á…bÁLùxÇ¢‹.gœqF\yå•1ÜpÃ¥|üñGj÷V[m‹/¾x _# H@€$  4"ºx¯¾új,µÔR±ÁÄÐCüû€HbiÎ9猗_~9N9å”$Œ&œpÂ$ðÎ;ï¼”1ˆeìøãÏ>û,N;í´ÔGÏ=÷\ì¿ÿþ±Å[Ä ,o¼ñFœtÒIéß}ûö×_=vÛm·8ꨣbòÉ'ÎÝ:ꨃ<„᯿þšÞƒè[l±ÅR±2î»ï¾ñã?Æ;ìÃ?|«˜<ýôÓcÄGLõ¼æšk’°ã]#)J% kÜ +¬þ¶xeñDËm¶Ù¦¬ÀC\­¶ÚjIt-¼ð±ꪫ& àrË-×ÊË –4D¢îÄOLBKÚJ+­“N:iÊ[À»îºë’X$K1aym´Ñ’À,'DÉûÑGÅÖ[o\@©ÇFm”¬ƒóÍ7Ÿ#Y€$  H@@»NàáBÉ97ΫB0»>–Zð¾ÿþût.Kß¼óΫ¬²Jl¿ýö±ì²Ë¶ñÚk¯Å®»îš¬vã?~«˜ã,Þý÷ߟò/¿üòU <ž»ì²ËÚÔWËQF%Y!+ <ÀZ7Ûl³¥ÿñ gòpý4I@€$  H@h@C <‚ã¬V°r©œp"Ð .—.¬bXÇgœØ{ï½[‹à<Ïâ¢9ÔPCµ)ú†nH¿Ã"W΂·úê«'—Ñ¥—^:=GÎøán9ÑD¥ŸýòË/ÉE‹\vÑ,¢ù¥<òHuŽÁ=s§vrK@€$  H@è@à ¼§Ÿ~::è XýõSð„Ó£>š¢`;ì°Iˆþù±îºë&wLÎð|òÉ)ŠevõD€a+ ²‚eñ…ð8p`,´ÐB1Â#Q¾þúë8î¸ãÊ <ÎßqŽ+â7LÎ÷áZ ²‚8<óÌ3+ZsoñnÎêñ_¬y³Î:k‡i H@€$  H@ 'ðè²§žz*Y¸FBt˹æš+¹mr.Çù7¬i\“€ÈBòûa†¦µÇûõë—,z\µ€5o5ÖH.˜$"nb}Îï¿ÿ³Ì2K:GP•r¼—^z)Eáìß¿*qFÍ .¸ ‰Ï|MgÇ{ìôŽö\4ù=ï衇RôM®c0I@€$  H@èˆ@] ¼Ž*_î÷Yत‘çô¸w;þL€$  H@€ª!Д¯ÒÙ¶j€ îçädw¦§ºêVõl}ú‘–€$  4^£Í¨ã‘€$  H@€š–€¯i§ÞK@€$  H@F@×h3êx$  H@€$ ¦% ÀkÚ©wà€$  H@@£Pà5ÚŒ: H@€$  H i (ðšvê¸$  H@€$Ðhx6£ŽG€$  H@hZ ¼¦z. H@€$  4^£Í¨ã‘€$  H@€š–€¯i§ÞK@€$  H@F@×h3êx$  H@€$ ¦% ÀkÚ©wà€$  H@@£Pà5ÚŒ: H@€$  H i (ðšvê¸$  H@€$Ðhx6£ŽG€$  H@hZ ¼¦z. H@€$  4^£Í¨ã‘€$  H@€š–€¯i§ÞK@€$  H@F@×h3êx$  H@€$ ¦% ÀkÚ©wà€$  H@@£Pà5ÚŒ: H@€$  H i (ðšvê¸$  H@€$Ðhx6£ŽG€$  H@hZ ¼¦z. H@€$  4^£Í¨ã‘€$  H@€š–€¯i§ÞK@€$  H@F@×h3êx$  H@€$ ¦% Àëà©ÿ÷ßÃI'ž{î¹°Øb‹…<0Œ>úèÜ O' H@€$  4"ºx7ß|s¸ä’KZpïÒ¥K˜xâ‰ÃœsÎÖ\sͰÀ tªyùæ›oÂV[mUèóå—_¦šjªøûYgî¿ÿþ0Ùd“…«¯¾ºSËÎJ@€$  H@£ž@§xyd{ì±GX}õÕG=É {ðÏ?ÿ„cŽ9& <8,²È"áˆ#Ž(xð;ì°ðâ‹/*ð*dÙž‡½ýöÛá³Ï> +­´R{6[·m]wÝua•UV‰kM“€$  H@hFà-·Ür¡gÏžá÷ß/¿ürxðÁã,Œ7Þxášk® ãŒ3N§šB5Gm´}ÞyçÃ'Ÿ|R3‡¸4´ø29à€Âl]tÑQºŽðZßwß}¡ÿþaŒ1Æh·¾üý÷ß¡wïÞáðÃK.¹d¸å–[§Ÿ~öÚk¯v;‡ I@€$  ŒzFàָ馛ˆtÐAá•W^‰¿ŸsÎ9a¶Ùf‹?><\ýõá‰'žßÿ}˜rÊ)£§‚Í{7ÙÐÏSN9%ÜyçÑs†ò¹ãŽ;†i¦™¦Åì|ñůÇK/½† ¦›nº°òÊ+‡µÖZ+6šì§Ÿ~ŠçöÙgÃwß}&˜`‚°ð ‡-·Ü2L>ùäñ°uÖY'üñDZ?wß}wxä‘GÂùçŸûž·ã?>tïÞ=¾Œ°ecŽ· ±6묳†u×]7,¾øâ…½ûî»aÏ=÷Œ¿Ÿx≱×^{m ÿˆ˜o¼1 2$¾ÅWD>Øo¿ý6ÜpÀ˜{î¹Ãé§Ÿ>R_Ž<òÈðüóχ9æ˜#Àÿ²Ë.‹<Ë|óÍÅÂSLQøâž{î‰Ç|õÕW‘¡µÛl³Ma¾88Û.}¼øâ‹ [pÁÃn»íàJ¸îo¼Æk¬8§Ûm·] ÁŠhæ|üÃ7þøãÇ<Ç­·Þ:L4ÑD#'½ðÁ„“O>9\xá…­ÓQoÀïnß¾}Ûõ”yÇœ±&a=á„¶ë¹lL€$  H`Ôè´ÐF6¹kˆDÀ>ûìÅLÞ–_~ù(p°b¹}Ùã'tÒèAAœaˆ Â'ñæ Axì±Ç†1Ç3Œ1"Š‘?þx¤ã*.,/ðyçw^ÑUÞí·ß Pô˜M6Ù$Š,+ðzôèQ®¼‡ Cx¦vvÚi§(±gžy&†Žb}úô‰Â5oIˆáYB¬!²†÷ë裎/‘k¸í¶ÛFÁ˜·±Ç;\pÁaÚi§o¥vaˆèÍ·‹Ø¦=øfmï½÷ŽBCÜp áÉ'Ÿé|ÿûßÿ"_Î[Ìàѵk×°Å[}¿ž^,æù­¤ÜèÕ«WÁƒÇgà…0'—U“€$  H@h Nàýúë¯Ñë…÷‰M+Þ;Ì>ûì-§]vÙ%z²úõëÞ|óÍxìæ›o7üˆ „Ê·ß~__c5b®ä KǾöÚk™øÃ?Œa™Œ)yë`‚ˆA¼Ñç¼!”ðb"Lù,†À˸¦ˆsÉ8ðⲎƒx0ñüÁnß}÷"’°U„)ŸÅ#˜ÂESÞ[[Þ 7Ü…Þi„c{ô…q¾óÎ;QÄ"ü4 H@€$ Æ Ð©^6ÄÏíRFµM6ì­ ¼ì# ’hcs6ÀΙ áH€$§.^„ ùzIèU+ðð°Ç†šºÄKÄŸß{ï½8 Q…¸ÊŠÌÖD m%¯ ÂoyqâÏS1Ë 1ƉàÅZ{=å-þ‰0Ë9Œä•ú|C^cÊ£KUSñœ‘“ˆ¸Â3†øjͲçËóõ×_G¡~Çw^Nq¥üÆäéB<²6Ä-^6¼·¼¬ÀÍç½µUàá¹Ã£—ÎÛ}¡ÿ¬wòMñj€$  H@A Ó<ªhêwÜqÇȳ¡^vÙeãï•<„•*[xæ`Ëñl:„]µ±A("‚Š˜ÉÒ¹ùý¿¼¬è('ð²yÙåJ ;%ôCøEUÈÃÙZA’j^ªÐHØ"¹uœOXz¾yu—^ziìVkí¶§ÀK8Ù’/I8&óœ, ¼ì3 ˜ˆ¬~ʱpÄóš*_ò?áÂÉÚKàeC6i»=úB;ÉkL¨¦ÕUã ÝQH@€$ N#ðR(eÖûDˆ^ B Ñ$|‘M7BOJ¶ºevª[xÙÇæ™ ÑÌæ|áJ9hÉÛ—_NTù$GŒjž„ÕfÉFº˜ÀÃû” ›à D &ˆh†JõCŒrú„‘bÅB4[xOÈ#b /ÂùˆÕ:)êÒšU#ð²}Æ[È9˜# Â`í)ðh<6òÙ˜s<^Ùjž¥.sD.¢¾˜/+ðnÌß¹çž;Rsœ /"ñ•,/ðÒW›ƒ—xíÑúˆïe ÿõëP€$  H óètä)¯ŠŸ³9tlœ Ä+TXÄãòþûïÇ"©âbVàá­Bp!ÀØ8§GàÕ¡òf¾È Å*ȇBó…¥<@Š¥§ÅòɽClÏFnÏé£8ašÅ^ ›n ±J a´›Š¬[–Ьàm+Ud¥”ÀãO0y•ä2¦œÂbö)Àƒ Æ[ˆÈãÆ7˜¼u´¿T„…u‰7+ðwˆjÚ@|",i§×Ö¾'ˆ75UÑÄŒ0E¸j€$  H@A S <гaOÏ<Ãc– ¬à£"âSO=sÌðxQ±ÏI*MŸxT®ÄÃÐAü­°Â ±Ú&‚-käç!À(pAî|΋÷'…‚²ÙgÓLÞG}?>ýôÓG¯…KJYáØl~ÎAè'^6΃áíA pÓƒÎ).B]²j•4Ó³ù`“žƒ×ÚÒ®FàÑ^NXÿøã±"%9uðy衇Ú]àq>˜Ð>žCòùp]øSä&?§iœTöä!ô)|´˜ÀãXÖyƒ¬;ªoÒ6â: &òùù<ˆ>=F‚ušUÌÞÍT±ÁGÈ-ÕN[{LBÞƒ×Ö¾’ËÚNý%|˜yIÏBlŒ¯4G! H@€š›@] ¼ZMMVàáùb“ÞŒFî¹ra¯äñ5£!ÈÈ­KÏÑk/Å.Þ^mÿ×v¸™@h1zHÕ9ÿk›~^€$  H`Ôhz—ÍáõÓÑ1=xýõ×U. äñ< yˆÍjxÒð–ÖÚ^–/²Ò^í¶G;wÞygxë­·bQM€$  H q(ð¶Ú*4ië„Ç2=—ްQ môèÑ£Y'a' —‚0íeõ,ðÈGe¬<D“€$  H@h ¼&x ¡"'ù‚ݺu‹9Y•Ñ$  H@€$ ÎO )^çŸ6G  H@€$  H`d ½ûî»a÷Ýw«®ºjØgŸ} ÇqÄá­·Þ 'Ÿ|r˜yæ™Ë¶“=`­µÖ “N:i¸üòËË~î•W^ GuTXzé¥Ã¾ûî[öx¨ jæ¬6=°U t ^Çpö,€$Büm¶Ù&Ì4ÓLqÓ>ªí›o¾ [l±EØyçÃzë­W“î4ªÀÛh£ÂO?ý;ì°Ð³gϪØU#î¼óÎpÞyç…gœ1\xá…UǃÛ@5sÖ~gµ% t<^Ç3÷Œ€š•@Ã<„ÁÜsÏ]ïã?;íJL IDAT´“¯ÄUÕšïÓO? _|ñEXtÑEÃh£VÕuYXøûï¿ÃsÏ=f™e–0ÕTSUun?ÕÌYûÕ–$ÐñxÏÜ3J@hV #ðz÷î^xáºx„îµ×^ ¼6¼ÿr!*þ ½QóYçlÔp÷¬O@×ñÌ=£$ f%ÐoĈ¡W¯^a‰%–¨ ÷ÒK/…ƒ>¸¦%®zËÁ;ðÀà +¬Pözj̓Wöƒ%@,L6Ùdá²Ë.û/ÍøÙ$ Àë@Øžj”PàRüž\@SèôïC /¾øâH“¶Æk„=÷Ü3¾Î&r•UV Ûm·]àìÓO?&Ÿ|òpÑEÅ<,ò±Î:ë¬0×\sµhg³Í6 „òeE¿ß~ûíá¾ûî _~ùeo¼ñb8áV[m¦˜bŠ€'ñ¯¿þ©?¾å–[.¾þË/¿„k¯½6 4(æ›M;í´auÖ «¯¾úHŸûöÛoÕW^žþù0lذ0û쳇­·Þ:œqÆá÷߯Xà~øá1ô‘|³[o½5ÜsÏ=\AÂé3çφDVË%Y9ôÐCÃ8ãŒÇ÷Á„ 'œ0,µÔRIX%kMàÑOÆzõÕWGžÉž}öÙpýõ×Â_1Âq7ß|óø2æyê©§ýúõ ×]w]¸ÿþûÃ÷ߦŸ~ú(¶Zh¡ŠÎ?tèÐX0桇 ·ÜrK ltâ‰'Žù”År*ûöÞ{ï…?þøc¤9Dp2Çå¬Üº`]õéÓ'|ýõ×á’K.iÁ礓N >úh8óÌ3 LÞ|óÍ8Ïo¼ñFœëI&™$^p}ôÑGbÁµOøq“b¬±ÆŠsvØa‡XÐç®»îŠ; ÞîÝ»Çñ#D«¬{o²Ä‚ÏÃjši¦‰ë„ãY+›nºiä‰ð†?³–ï\wˆ¯,ÓÙf›-|ôÑGqís2ÿÆ &˜ Þ`É ´Öj¨\3ÌÅÏ?ÿ{챸æ/¸à‚¸¶°r×c1Ö¾&QA@7*¨{N H@ÍI Ó <¦ ÏÔ¶ÛnÛjˆ&›Èþù'zØ ³ÑLVù÷ßãæ˜ jÿþý m"Ê?6ÑØ½÷ÞÅL±*š„0âÕ8á„ B¾Qý“ÍõUW]Kýcûï¿<¡@Hf2íÞ{ï]µÀcƒŽ1D¢·oß¾QüâJž¦j¸Ð¯$ðr„+ÆØø±!l9?VÀãÑ ôOêDMTà€Ç+;—Ì3´•VZ)zoñ@açž{nÞ¼†g·’ó#ؘ“äE|üñÇãœá…ÅC•l“M6‰žÏK/½´À”õÈœá…m%Vͺ@P&aÉMúóöÛoÇ>$áýM¢6ôëÇŒž¸¼0ã8rGyt†Ã[̦F‰êÃA–_~ùLÙxÓ ÞÄ£{ÅW´¡¼^Là½öÚkQ¨"Ð>úè0æ˜cÆöSn+Ÿk¥×c%ü=Fµ& À«5aÛ—€$ D iÞŸþïö#ò²VaCI(#›\ÂØð&³Ö^]vÙ%ztØg-mjñرÁÆÓBh1<ùŠ’Õ>&¡µÐÇìæ;+H«áB¥žƒ‡Gq‡×“ ;VÀC "V`^êÙxlüñ*!’³¼—ðF$#p*9?Þ#Ñ7ÞóÀ( AˆÚŽ;îXS­ ùäB˜b©ù©f]¤ó’ŸÉù0}ËÚ¯¿þóÝCˆÀ”ÉÀBÐwÜqa‘E)É"»nò/yeÓz-Å4õ ï9„7r±bsÆZA<—2®”OYîz,ÙoJ ƒ(ð:´§‘€$ ¨Q²5#’Ñþ¯:Ý¿Åø´ööf‰G¢Òü¥JrðZóàñG÷Ž;îhQ}0¥XÍì8)&ÂÆ—7Æ‘ o¬Và¥qá¥Ä{CN+r!S(å¨xº$ìðŒa<ªaN!”d¥æ§šu‘Ú#/±–òÓ²Åf8Ï!ž; çÀ%Gž%7'F…Àcm!Ô¹ÞRþ_±9#”–0TB5[3Š&孵뱽¿›lOm! Àk 5?# H@m!ÐbT äqäå­T)v뱨^­ÈÚ®$  ä 4„À£‚%9^ä"þ•/HRJà%1–-1W†ÐF6™xÁ’Àãuª9.³Ì2-8&DnE\R)ûl›é„ò\2*Y®½öÚ-Úacž ·¢F¡Êüg½-| …ofûVni'ᔊ¸¤ã %$¿ðIò›’§§.´•ynäZ¥|0rÀ¨ÌøÝwßEo[*rS,l’vŠ ¼ôȉÔ7ŽK¢‘Ü:æïWæè¿X¥ë‚\>D-,ðÚÁ™ùc¹Á€Ç–ü5n¾Ê¦2]xφü_K} ý”gÇÍnPì&[É^ÅÂ^ ¹LÏÒ£êm6÷kÜRt¥×ã™?+ö" Àk/’¶# H@å4„Àcl\ñlà›sÎ9cùüôPêRgªñàgþÇ[†Hd³œ„“ÀK¢‡3!bˆ±÷ß?>óްËã?>òfC‹G‘Â)ˆ<6º´‡ñ«¯¾ŠynüOéy^g£Š‡×(à‘Âד©HÏ­C„áÙC,±É­öAç´Ç£xç&tò™gž‰b¾Ó—dÕpÉ <ÂÈý«ôz¬„¿ÇH Öxµ&lû€$4ŒÀÃÓEHC,l SARxÜð.ñq6¤ã <“E§ç¯%`xÃòAž5†HLÂŒcÙD““G›löyŸ*•<^ƒ Š:6ÄóÌ3OÜàÎ;ï¼-V'í.IÎã"Œ±D˜“W.|45–„^FÄð¢Ì<£fÃ\ìñÕpIO¥ÅKFq7;Õéñäâ¥-ö@n‚ /%ydÅJÕvªÁVÐÙÖ*£VðQ‘@ÃPà5ü;@ H@uC@W7SQÛŽ(ðÚŸïí·ßCeyxö‘œ‰ð]ŠË¶‰ÈwÜqÛ¿uÖ¢¯Î&ÄîÔ^]M‡‘€$ÐÐx =½ÿÿàxí?Ñ乑óF™^½z…¹æš+æa’wI"ïS …|´f0^3̲cl+^[Éù9 H@¨–€¯Zbôx^m&ŽŠ¦º¡ s¨ JQ‘EY$V Í–ø¯Mê§U^ýÌ…=©? ¼ú›{$ H Q (ðuf—$  Ô ^ÝL…‘€$Ððx ?ÅP€F5Þ¨žÏ/ H y(ðšg®©$  Œ" ¼QÞÓJ@hB ¼&œt‡, H@K@×±¼=›$ f& ÀkæÙwì€$Ð!x‚Ù“H@ÀÿP๠$  H@5& À«1`›—€$ ž‹A€$Pc ¼¶y H@Pà¹$  H@E@×Q¤=$  èÁs H@€jL@WcÀ6/ H@zð\€$ Ž" Àë(ÒžG€ôà¹$  H@5$ð÷߇þýû‡>}úÔð,6- H@øêVàÝqÇa†fÝ»ww®$  H@–ÀàÁƒÃСCCïÞ½;íì¸$  tu+ð†}ôÑðÉ'ŸtšöT€$#Э[·Ð³gÏеkWÙH@€jN n#}ôÑkÀH@€jIàŸþ©eó¶- H@hA ®žs% H@€$  H@•PàUÎÊ#%  H@€$  Ô5^]O“€$  H@€$P9^å¬|xØ`ƒ ÂN;í–\rÉ0ù䓇ÑG½pÄ/¿ü6Úh£š ¼§Ÿ~:œp a©¥– «®ºjüñ îÑG çž{nsÌ1ÃóÏ?V¬o¼®Àk¹*¿øâ‹põÕW‡vØ!L:餭r눵ܯ_¿ðûï¿·»À+ÖnGŒ'{޶ôᦛn ãŽ;nèÝ»w»w·OŸ>aà 7lH—ÿ.ø/EguV¼‰3ß|óµû<Ø $  H ž Ô­ÀËBK<Y«ä.q=ïUßÞ{ï½°Ç{„«®º*Š»¼}ùå—aÛm·­©Àã:Þ¶å–[.ìºë®-ºðï¿ÿ†ÑF­èð‹õ­òJ+­Ô¦)(Õÿ65Øj[µM·el§vZèÒ¥ËHoàÀÑ»ÞV+Ön[Ûjëçê¡Ù¾oµÕVa›m¶iHWÍ•óàUÓ–ÇJ@€@C¼3Î8#\{íµ°µ©§ž:†*eC_z饸ÑÄã7í´ÓFá±ÄKŒ4—ÆýöÛ/œ~úé1d¡„§0ÛÞO?ýà Ÿzê©ðí·ß†é§Ÿ>Š©9æ˜#¶GØÑ+¯¼–]vÙèaá˜Å[,Czè¡è¹B`et¼|Oךk®½lńޛ /¼0‹§€v¶ÜrËØî#<N=õÔÂØ¸{}Ê)§~Ï¿Ïðk¬±¢7´½X>øàƒðIÂ3 kͲaZ­õm®¹æªØƒ·×^{…ùçŸ?l·Ýv…S‘ßS¨!š›o¾yœßgžy&†Óáñ\{íµ‹v“ã=ôÐrÈZb}0ߥÖãzî¹çB÷îÝÃý÷ß~üñÇø™=÷ܳE81"ˆß}÷]\³ôsî¹ç.ôãŽ;îˆë o6á­lðçwÞ!µo¼ñF‹9Os ·Rk%­×W_}5zY/»ì²xÖîùçŸ×ëØcû‚G„ÐÚwÜ1,¿üò…þÑ_Bd“õèÑ#†2þ»îº+®ûÄ~Ðï8 L8á„ñð?ü0Žíå—_¿þúkX`âµ6ÑDEÅÚÍOÐ×_.¾øâðâ‹/Æ·]Ä«I¿Ë‘0çœsÆÐá{ï½7pS‚Ï#þ¹îZëC©~Ó‡¼ø(w>ÓÚ<§ñ~óÍ7qî³Æwk›öaF¨î7Þ8Ò8øÌü.¹ä’ðØcŰm<ûŒs¼ñÆiͳÞiÚ;_Wð¦ëãÏ?ÿŒ?sÞEY$~žïVÚã|Ǧ›oGydÀ£Éµ6ÝtÓŰÉžÑÌÿþÕW_EO?ë›ñõìÙ3®Aæ¶’1çC±ùýàƒŽ ž}öÙ0Î8ãD±œý~òÉ'ãuÀ÷1k>aî¬OM€$Ð4„ÀCd!pwlLñ&° Ç}‡vX̹bóÉfáì³ÏŽÇ±áÈZÚ”°Áf£I~ØÙœ§ö'DÜᙚl²É¢Ðb#Ê&c“Â&‡D6š¼Ç&wâ‰'.Üyçò߀ˆ D#^.6]lBØÌ!ÒØÐå½L?üðC<á°É&›ÄÏ"¤fši¦pÈ!‡ÄM c>è ƒ¢¨%| Z2Þ‡mú‰‡÷Ù´"ðÚ‹%³÷ß?².eÙM]k}c³]iˆf¥1o¿ýöažyæ‰YDÈGQTø3'ˆTæsñżÈ),µ®y¤IÄ#ž1¼AiÓ æŸ=¹kÌßÃ?7ègžyf˜e–YâæzŸ}ö‰a®Ýºu‹óÊF7/ðX§Åæ”u[j­¤õzà 7DaǹXýfÞÄç>üðã`gcœì¯¿þ 䜱Îvß}÷ L.%ãgý!j¹69Žki5Öˆ×FØ5\¸.™c„á /¯—ÖÚÍ®%Ö>ë6œƒ6ظoºé¦1dµÜØ CÖymï¾ûn¼îé×Wk}(ÕoúWLà•:O©yÎŽaÅZâ:åûÎðæ||‘/³Ì2ñÆbŠï„ã?>ŽƬUÆÃ÷ ߬w¾¿Šå ²ÞYSéÆ7’˜{Ä7ƪx¬+æ{Ê)§Œk›pûK/½4®—rë‘kõ@Èþ /¼…e%c†[1Ç 1Ö9"“o®AþqS5Îw(ë™÷¹ÉÁäæ×g11ÜþÈÛG H@h> !ðÎ;X!Æð‘{ÁÆÛÿýãm6«ÉøŽwcã7n1ãIॻмùÄOÄyj/¿DÞ|ó͸JálZn¹å–¸iHÆ~†f(„°±Yá܈86ëˆ=6¦ˆÎä±£ Ô6ž\Œ[%k…~â±`#;É$“ú8ý裢¸ÄX[F6ùyC< òEVŽ\éÂO%ª˜!ñürýaÅÚÍ~Žkìž{î‰ýOžÆô~%cG!V¹Ù‘Œ9æ¦ ž±JúÀ1ù~x¥ÎSjžóœÖZk­ÀÏYá8Q²ä±fxHYOðJsŒ$ZùÏ_SÅŠ1&¾›¸>ªxÙïR&ýAð!ÔÊ <„,7Ûò!ÞŒ‘þ”3ïxx “'ŸõÌÍ¢ø{Àw.ÞvnÚacúɉ¬W½èâõE H@@h—­¢‰‡‚ „ÇKž ~¦ wìÙ\g­XN_¶½´Qe3ÍÝ]„Þ6„ !3Î8ãH›–´Iä6w¢1B¤(À€Ð$|“Íb.ÛG6C„:!²ÆØ¸ÛœÝ”¦¢UVYå? ¼öbI¿ñLàIÀƒ#œ#L‘Í%c¯Fàá bƒ›5ÂñÖT+ðòU4ÙÔ1ÌcÞòEx—[WÅënÝu×^Öõì˜óâå ´36Ÿ„zâIÃ;ÃÃ*x•¬•Öª]~þùçQв—xÈX_x0óÖšÀËWÑD¨â%Aèaˆs¼á„Åá=cÃM¨0Â0]7yᘿð¬xà#õ©’±ËãbNÅ#D±µ>”ëw1—¯r™=O©yά5Wª}(>ûýÂæ{‹Ù|u1G„4®j^¾ÂqV´•x¬np3„ïk®ô\ÉÜxÙë>ãØcžcÂL¹IÁz^h¡…âÍnl0"04 H@@g!а¼&6)6käeMM>䦔À£=6Ÿéñlx)ÛX#Ä+yÏŠm˜Ù0”xlø¹ÃŸõpÑWÎGH^~S›x¼Ïf¼\µxmaÉp¼lÖ󜹋׳ZWÉcØ ‘wX.//ðY#Ì1 Ò,ób¯Üº*¶R…P„=Ær/õuÉ ¼Óx3ðøµUàå×J©Çà­Ã;Ê?<7ˆéä5Îò©Fà‘c“N®^ÎØC"r+xô Ñ’½Ù‘úULàåÇÞšHÈæp[¹~W*ðò¹¢Åæù¿¼Ô>!¥\ÄMç-=&¥Ôzç=¾ß¸±ÅõÁÍ'„f%9xyÇws'­œÀã¼Ü!g!Êw$er8+™»J^v\›Ü´ãfÂ’|m8j€$ ÎD ¡‰ ?¹vxŒÊY%a—-ȧƒÜ¬ÿ"ð1ËÈ(¶Îö›;ñlôÙ“Kƒ¥M#A>R¹ÍTe3»ùªdìÕ°DÌ ¬)Sž÷”–xÅúÆ+ÍÁÃʦñ„ᥠ0‚.[d%+ððf ® ÃJ^ÖrÞr,Š 'Â}ñ4¶ˆèEØÐ|ˆ&¡¢‹¼BF»„Úåç«·JÖJ)G¾¢Ž¼+6¼ÅØÐÇbÏ}+Ö.k6^ÂÛ IDAT <  pcO/!Ô…N¸’À+÷<9ŽM×B6Ï”¶*{%"!߇JúÝV—æ;;Ïù5°Þzëŵš}†c¹qàyCðâ…#* œ»¡Á5L8yZ<®”£‰÷›k/_d%ûóùþDsí—úJ‘Äá\ÇåÆÌçªx|oŽŸB„Ëqò} H@@½hx—67ä;‘[ÃF1EU8`Ö*9xP¦™fš¸9EÜ‘çF(âxä%Ñ.ž'úÉŸ¢ l¨È©Ë›$Ž¥07wÕ Ýcƒ…•xˆ>›˜àÍ$G.ÿÐøžš¼—Õ°¤š“O>9nW\qŘ'Exõ‹¢D~“W¬oˆ¡J›~„›HÄ2óóñÇTE*”¹d|<ð@™É[±µr,j áPý¯ž+Æœ [†I8&e„9¹•°!,“Šš„8â!^zé¥ãšÀ#J‘r7ókµ7¼åÖJ)ǹ)ôÃÿxóZ«"Ȇ1ÈœrMQy¶œÀ£@ ëñÍfŸ¢5ˆ2¼CIàk7;7T¨M× Ea0<ÇÜ€áæG¹±W"ò}À‹T®ßÕ ¼Róœ_‹Ü¸ ޱ1Frl˃RÂX¹é‚8DäQ‰ÐPÖf±õŽ÷ 1ÅÿôpÅìõÁšM7Føî@”Òf^àQ8ŠïZúJ4óÎ:f?‰ä3xb³k†›“Âq•<61lÔ?ýôÓXb;Û4á5ÄX[B49÷gŸ}y'’þ¹cÍÆ?oËf o .pl¬åíò6yˆ!rÊ ¼jX¦>3D "˜œÆÃÝÿT$¢¯|ßͬTà±ä±„["ÞÈ÷cIÕÁ¬±"ß~ûíØ'òͨ¨YÌŠ ¼r,ÂO ¢±Å Ö]Ú0²Aæ8Æ‹p§â ï§3Sq“b'\¤Ì¡bôUl­ãVn­”xŒ‘ó?þøã1'0_Œ#±¢ïx&É£cÝR”¢œÀã³ä±âÍd3Ïc¨‰xM¯X»ùùA¼sÝ á c®n&”{%"¡XÊõ»ZWjžóãeœä0"nY—ðJÆ1|øðèõçæ7¹¸!„'–7ÚMaÀ|—plþúàz¦ˆ;Ö%‚ïë%=&›\œ—›XÜÜ@˜¦‚A|áI¾ŸÏ®®  •.áƒÅ{¹Î:ëÄîV2æj=xa<êô^Éèw*~äÖA€$Ðt ×@ÚG d $÷_ô]D©Š'Ѥ5„ž¾ü#Z}ôˆSž…˜ÂP™Ü$àæB”›š$  H 3Pàu†Y²Ž@9ÏX½¯ a—èÀ“/öSïý·m'КǺí-vŽO}ôÑÑK7‘BVx:ñ"æMõÐü£J:Ǩì¥$  4#^3κc®9Î,ðKL§&$”œ'­y4«Àã1.xñÈ÷CÜ!ö?%ç²Xnnó¬G* H@€¯³Í˜ý•€$  H@€$Ð žKC€$  H@@ƒPà5ÈD: H@€$  H@ <×€$  H@€$ ! Àk‰t€$  H@€x® H@€$  H@ B@× é0$  H@€$  (ð\€$  H@€„@Ó ¼>ø ð0çË/¿tíÚ5Üÿý ¼FXýŽA€$  H@ F ©Þ¿ÿþ>üðò¼u×]7ì±Çaùå—ÓÛm·)ðìBp8€$  H@hu+ð®¿þúpÏ=÷„+¯¼2rþå—_ÂFmVZi¥°ï¾ûÆ×Ž;î¸póÍ7‡1Ç3|ñÅá / „TŽ;î¸ñØ-·Ü2¾‡sÌ1aî¹çßÿ}l{‹-¶ -´P Çyvß}÷(æÖXc°ÕV[µ˜ç{ï½·¨À{ñÅc_?úè£ê¹á††UVY%~öÐC OzøóÏ?ÃÚk¯öÙgŸ°ì²Ë†±Æk$÷òË/GGç’K.…Ú™gžïÝ»w wÜqGA¬öë×/<ûì³aþùç/ˆÕ½÷Þ;,¶Øb±ïš$  H@€$ ¶¨[÷Ï?ÿD±³ÓN;EA†("ÿí¦›n _|qô’!º_|ñèÙÃ{öÐCEO {óÍ7£€â³=ƒràÀášk®‰^½l!„èÒK/xà(Vÿý÷°Á„ÓN;-Ì9çœm™G?# H@€$  H Ô­ÀcnN8á„j‰w Q´Ã;D!‡W¬gÏžaýõ×gœqF˜}öÙÃGÆüpÀ¦•;¼oˆ+Â%x·âÉIàõéÓ' AB+·Ûn»K£œÀ[guÂ.»ìV[mµÂçð"@ñ2"F7ÝtÓXsÑEÞ뮺*l¶ÙfQ~þùçÑ ‰xÅk¨I@€$  H@h ºxä»ÞÈ# C «¯¾:†_®°Â QÝxãQxAàõíÛ·¬À#Ooã77ÜpCzÓM7]g9Ç9 «®ºjá3o¿ýv¦8*p’[7Î8ãDǘ¦n¡mrñòªI@€$  H@h+ºx_~ùeØvÛmc.b¡GîZÿþýÃꫯsÝ?üð8vžI÷È#DAÕ¥K—øZ Ñ¼à‚ B·nÝJzððâ<õÔSc>ÞÃdåÁ)§œ2xà…Ï  å$D“þމ°[d‘Eâïx É)6lX|¾Â¡¨I@€$  H@h+ºx !Dèå ,‹– ˆÈ¹›cŽ9¢oÍ5׌c§2&^´=zÄpÈü1Y™qÆc¬TˆfzÐ9í öR%M>—xTàäøK.¹$L0Áᥗ^ŠB3_dqÊg1rÿ¶Ùf›(4·Þzë(ôžyæ™(Hé+ùwôU“€$  H@€$ÐVu/ð(ˆ‚ ¢º%U&1B.y~Ý€ 3ÌPûgŸ}½b¯¾újÌÝC"¦¨|Y©Àã8¸0Îu×]7üþûïaâ‰'×]w]%ã¯ø˜jÞC=N?ýôíï»ï¾aРA ¼Š©{ $  H@€$P ºx·ÜrK¸øâ‹ãXn;í´SüùŽ;îýúõ‹?O5ÕTáòË/?÷Ýwa‹-¶ˆ?wïÞ=üñÕp({lµo¯½ö ï¼óN¡ÿ³Ï>{èÑ£G@¸êÁ+‹»]>|x¸á†¶ÛnÛ.íÕ{#ÜÔXe•UÂd“MVï]µ€$  H@5"P·綾^ ‡zhöb‹-Ž:ê¨øóI'üñ6µxì}<è ƒâëë­·^ØqÇÛ Ù?ÿün½õÖªB4 Ç6lXìÃ]wݺté>òÈ#xí63¥ºúê«ÃüCjG¥½öÚkáÄO Çw\˜yæ™Û­+ÿýwôl~øáaÉ%— ÜùôÓO74 H@€$ æ$P·ï§Ÿ~ ›l²Iœ•é§Ÿ>\xá…ñç­·Þ:|ýõ×…ÙB0-¾øâáî»ïçw^|PÈ•VZ)þüï¿ÿ†ûî»/Ü{ï½á“O> ãŒ3NX`bÛݺu+´“ùäðØcŦñRâ­Ä¶Új«ðÍ7ß„ÑG=nô篿þZÈ\zé¥ ‚9Û¯¬g’1#diÿÇ ÿûßÿBŸ>}ÂüóÏ_øÈ/¿üî¹çžðÌ3Ï„Ï?ÿ<üùçŸq~´Ë.»ìHûì0Æc&>û>áŸTÃ$$nši¦‰Ç²áÎÞ2ÞG@$ÄûÅv¬õT|¨_“€$  H@h\u-ððÐ%±Ãœß)¦²é¦›FdžC"Bð„"H¨ b<+l˜1D`ò„୹袋âëÆ:+H9ˆ¬å=|é=Äð¼U#ð¨ô˜6äÙŠ¡´IÁ˜ß~û-6' ±Ã¯Fn¢‘=ÂïBˆñâéÃãEÈ)>ŠYv\xÙ`‹µö:BŽ~0Ù\ÈÔ6çÇ3×ÚçÏ<óÌ€ÃóÔ³gÏø3Þ5Š‘`„^"¦²óW¬ï²Ti5ÿ>᫬ƒ¹æš+¾•^ÊÙä5ÆÂ<ï·ß~E K>!‚›>«I[ÙðVæqö_‚¡˜ÀË 4ó­ <Þ'ϋѬÕRàeCOñžQa!‹ØI?B“ç‡0ÈT°&¿’«x„#R¡‘yÁð ñþ¤\:<„¯v„ÀCè¤Üü¸ÈMd^ðÔaù0I^#¼4‰ó¬–7Ü<`xî M!›|®½^ Ù¤M„:ÞØl?XÃô– âÿìó‹ <ŽG â5.æ±mØo2& H@€$ ÔµÀÃ#‡G‹ÿ^xᢉ%/bîË/¿Œ¹O„ÎaÙ\¨lØ!‹ˆŒÿ£>.¢Y‰ÀÛn»íb¨Š\4rÏùKÖšÀCŒ=÷Üsñ0¼“„dfC4Š©0!§©Ä6D“?<–yX¬T oùepÃó“Æ› ©ŸÕ¼l?©ì‰°Æðd}üñÇñçöxÉ¡}òÙW$—1VjpÀãENVLàQØ9yùJ›9áÀXÉ…KWDáz)ŽŠ•Ŭ— ™åÑ ôóÕW_œ“µ§ÀCÔ¤ÇcP¬„œ:þ'ì”üÄRÏ·ÃÓˆ¢(X1GÅPæ ±ÅÂóF'k/Þª«®Zx›pá9æ˜# [ ÇkË/¿|ü==ñT‘•¼Àãæ…‚ðÂ2Ƽ=ÿüóÑsÚäý$.³9x„u^Ê}jyÛ–€$  D <‚$  H ÆÎ?ÿ|^Û¼$  ü? {ØqÇÃl³ÍÖbI 2$\vÙeáí·ß¿þúk˜i¦™Â:ë¬V\qÅ0Úh£Žýå—_µ×^ ~úé§0í´ÓÆãV_}õV—ŸÙtÓMÃôÓOúõë7ÒqwÝuW8÷ÜsÃÁÇ‘Þpà á³Ï>‹ ßÿý0餓†Í6Û,žùý÷ßÃW\~øáø3 ÷ÜsÏxL2^'¸êí¢$ÐáxŽÜJ@hZ %ðvÝu×пÿ0×\s…É&›,ôéÓ'Šë®».\~ùåaºé¦ K.¹døùçŸÃc=ºví.¸à‚0É$“ÄК8AÜqÄaðàÁaþùçíòÉ'á™gž .¸`8餓¢ CÌ!¿ýöÛУG0à 3„ï¾û.н±Æ+Š&ú…}ýõ×±]ºt ½zõ ãŒ3NxçwÂóÏ?Î8ãŒ( 1ÚÚwß}Ã7ß|–^zé0å”S†W^y%öuË-·Œ³5;î¸ã¢(¼ð Ì3ÎØâ°Ã;,¼üòËQØ;þúëÄý§ÏO=õTøë¯¿¢X¾þúëÃüæ›o¾(¤?þøã°è¢‹†c=V×´_!\¨„€¯J# H@íA ¡Þ˜cŽ=J+¯¼rÍk¯½öÛo¿(XŽ>úèÀ1e¯½ö k­µVèÛ·oI‡÷ Ïb Q• !¢ ±Ô³gÏøò³Ï>ºuëÖÂ3ˆç!¹ù曇­¶Ú*wË-·Dáuøá‡Gá– ÏÛLPøýÀ ¯¾új8á„¢Ç CpuÔQáÅ_ W]uU ZvQ H鸭·ÞºðÖo¿ý6ÜpðÈ"‹„#<2¾ž9³æškD)¢o§v _|ñEô.&᪯=.KÛ€€¯ÑfÔñH@¨_ %ðÇ<ôÐC[Ð>æ˜c¢ŠN³vÀDrõÕWÇ—‹‰“¿ÿþ; $;Ì9çœñ¸SN9%†âo¼ñâkIàcˆçŽÐP¼•YCðâAÄ Hè+¦ϯ H@#Pà¹*$  H £4¼À£ ž¬RF¸äøã_Tœ$OY©ÏÏ<óÌ…B&äåáÙ"ÇŽ×ç™gžXeÀ€aŽ9æçœsN¡©aÆEaEá9‚Ûl³M¡0Ì5×\®¼òÊ’}§Ø ¹|­â’¼Àt,6Úh£(XSxf9‡oŠ)¦Ià¥ÐU^G]®žGè¬xuæì·$ ÎG ábñD¨fk¶Øb‹Å·ŠyŸ(¦BL*eæÃ?S{äÌÍ=÷ܱH y}TÃ$ü16!”/X^à¥Ï“φG0Òï¿ÿ>†c–yçw†óÎ;/,¼ðÂ1L²˜! ÿ÷¿ÿ•\y„]¦\¹7Þx#æ$fÃ3xïµÇ@ç" Àë\óeo%  tf /ð¨~É#¡D„•²bñÅãþý÷ßXL$ûH„|[xén½õÖpÚi§EY²r/G¥Lræ(~BŽt ³{÷îáÄOló:ã± Tæ$4ó¥—^Š^²á™ ¼6£õƒ€*" À«“I@@;hx‡ áYoxÓxœAö™m<Ë$©p ùr=¡h ^¯däï!îðªí¿ÿþ…Jœ¼ÏóëxÜ–ŠŽdC ‰äñ´‘õà½ð 1„3ÛòôѤh Õ/1ú×­X1–칓H#÷Q˜}¾U3ÉE\mµÕ!§LɆg¶§Àka;¬U›€$Ði (ð:íÔÙq H@Ž@à ×(`2Ë,³ÄÂ*@γ +D!Èsíðúá›fšibíñˆžqGåOÄ'bóÁŒÇzê©…ª”ô“6ù$žAŽÅ»Çk<(öi“çéaùÇ9ð^Eá@Á™|xf{ ¼Övº+ÃK@hG ¼v„iS€$P’@S<ðL¸Ûn»-æÙ!jˆ0Ä]ª$ÉqxÚ(|BUÈì£ ðÄQÕòTÖ$d\ïÞ½c;É øøãGAIΕ')ò‚h$ïêš©È B‹pNáÀ±ˆAž=‡8ˆwÒ'Ž¥O<ÞÈCÑ)Þ³ìÒã 8ްN^N¾a>5‹á<ùðÌöx¥z-J@hV ¼fyÇ- H ã 4„ÀëxlïŒÃ‡ë®»n®ùðÌÎ7{, H sPàu®ù²·€:3^gž½*úþðÃÇ"+G}tX|ñÅ«ø¤‡J@À% Àû¯ý¼$  TJ@W)©N|Þ»¾}û†.]º„‹.º¨ÅÃÔ;ñ°ìº$ NC@×i¦ÊŽJ@èôx~ [y‡/¾øb,&C…Nrôx¦ž& H@K@×±¼=›$ f& ÀkàÙ?묳ÂC=xúvÛmW¨ ÚÀCvh€ê’€¯.§ÅNI@hH ¼†œV% H@õD@WO³a_$  46^cϯ£“€$ :  À«ƒI° €š„€¯I&ÚaJ@À¨# Àuì=³$ f# Àk¶w¼€$ÐáxŽÜJ@hZ ¼¦z. H@E@×Q¤=$  (ð\€$ PàÕ°ÍK@@€ÏÅ  H@¨1^Û¼$  (ð\€$ Ž" Àë(ÒžG€ôà¹$  H@5& À«1`›—€$ =x® H@@GPàuiÏ# H@zð\€$ PàÕ°ÍK@€<×€$  H #üý÷ß¡ÿþ¡OŸ>q:Ï! H@MN n=xwÜqG˜a†B÷îÝ›|о$  tfƒC‡ ½{÷îÌðï€$ÐIÔ­À6lXxüñÇÃ'Ÿ|ÒIPÚM H@ÀȺuëzöìºví* H@@Í Ô­À«ùÈ=$  H@€$ # Àk° u8€$  H@@óPà5ïÜ;r H@€$  H Á(ðlBŽ$  H@€$мxÍ;÷Ž\€$  H@h0 ¼›P‡# H@€$  4/^óν#—€$  H@€Œ€¯Á&ÔáH@€$  H@ÍK@×¼sïÈ%  H@€$ # Àk° u8€$  H@@óPà5ïÜ;r H@€$  H ÁÔ­À»è¢‹Â­·ÞÚ*îË/¿< 6,ì¶ÛnŸ§šjªv™šW^y%œuÖYá»ï¾ ;î¸cxñÅÃLöÝwߨþ1ǺvíZø½]NÚ ùçŸÂé§Ÿž|òÉ0É$“„‹/¾8Œ1Æ…ž_qÅáûï¿{ï½wáµÛn»-Ü~ûí÷êÅ>øàƒ²k&Ìk¯½N<ñÄpÜqÇ…™gž¹äP*óQGÕbMÕ ›júQ ÇüµÄ—ͨ¸vöØcðÞ{ïÞ”SN×g%ãië¼å¯sÏ=7ôíÛ7l¶ÙfaµÕV«èÜÕÌM:¶’1µ¥Ýüg*9Ïßÿz÷î?üð°ä’K¶Çic¿ýö[Øyç ,K5üÒK/…óÎ;/\zé¥E«¦­v€ I@€j@ nÞÏ?ÿ6hРpÝu×… .¸ €A7dȲ›õj™í¹çžaši¦ Ûo¿}ÜŒÞ}÷ÝaÜqÇ›¬YÞóÏ?Ç~Ê)§„)¦˜"þËZ¿~ýÂï¿ÿÞï‹/¾W_}uØa‡¤“NúŸÞM7ÝÔbMU»FëáøJ6õùké´ÓN%ï›o¾ ýõWÄÆ‰áÇDÖ¥K—xs¨’ñ´uÞò×Îd“Mo"­¼òÊa¾ùæ«èÜm™óJÆÔ–vóŸ©ä<µxˆç,ËRãáæâ®5WM[íÁÍ6$  H@µ"P·/;àûî»/ôïß?zƒ²VÉÆ¢Zpë®»nÜü-¿üòE?Ú¬ï–[n ÷ÜsO¸ä’KŠraóÎf9ïÁ8p`ô°Ö‹U²f*9¦µñàÁ«·1·7ûÿý7|øá‡eo®ä¯¥z¸v¸Añ믿†£>ºæß%éå®ÿ²ÞJÍm­Úm‹ÀC<õêÕ«Ý=xÕ¬íx \ýõ­ ¼jÚòX H@@=hwÆg„k¯½6J7õÔSÇðÉÙf›­ÀÐD¿i§6l½õÖa‰%–h1/Üåßj«­Z¼vï½÷Žä±+¶I½ãŽ;b8é?üæœsθñ~úéc[Ïk#FŒ´GÐ +¬úôéFm´xÌ3Ï<î¼óÎðÎ;ïÄ׸³Oxèè£ß_ýõÃA|ðÁ0xðà0ÑDÅ/<‹^xaøôÓOÃ<óÌ8à€0á„ÆPª_ùEI(ê•W^>úè£èÑØpà Ã*«¬;óÌ3›£d›o¾yØb‹- ¿o·ÝvaèС…ß{ôèÃ;wÝuWÌ xÂæwÞ6÷–ôí?þˆ`¹Ì2ËD–cŽ9f<ÿ^{íæŸþ@Ÿ’m´ÑFñ÷l8Ü‘GðȺ7ÝtÓÅ0¯\0~$¿1ÎÿŽ7oòÓO?çkˆÞ=¼š•Œ9¿†˜ßƒ>8<öØcáÙgŸ ãŒ3NØf›mÂJ+­TÞ‡Ë.»,|ùå—Ír²“N:)ž?Ù¡‡&žxâ°ÿþûÇ—žzê©8\©=<ï¾ûnà³X©¹ç}ú;÷ÜsÇ\D>s¿ÐB µx¿üòKØ}÷Ýã‘5ÖX£¢k©Ü¹Û2–üºÎÿ^Nà•ú.ÉÏÛË/¿%ÿüóÏ£×uÖ «®ºj‹S¶ví0ç»ì²K¼Ö‹ 1Ö87SXx¿kd7Þx# ‘ëüæ›o޳Î:kdÏu–Ú-÷ýXnþ+½¦²¡òxIñêsâNïrÈ!EC4[/ßqûí·_ü^Okšï„ÞX®•,KàãÁwgö;Œï ŽË[~^87¡éÜ`仂ïŸüß—rkÎ÷%  H@M !bjË-·Œ›€óÏ??z’ø£Œ!ú;ì°¸™Bx¼ñÆáì³ÏŽÇ±±ÏÚŸþÖ^{í°Ï>û„e—]6Œ5ÖXe¡£lzñ\ÑBM ››B6P„|r›56ÝÝ»wxCØt²IŸk®¹Â'Ÿ|7Þlf–[n¹Ø=6ã?~Üè°ÑflÏ=÷\Žœ—\86aˆ$lÊõ+;n6ªl¦é#IúÈÆ”ßÙ˜±Á¤=6IlháË¿dò“_d“ϸéb‡MÝsÌç‡ãƒUÓOX¾úê«Q0öìÙ3|ûí·Qø.¶Øba§vŠíUºe#LÈÁºñÆc0‡1”xCÄ:s8öØcÇù^zé¥cnb%c.&ðØÈ²î™Ü, $”Ì;k˜1ŒyÿᇎycÌÙL3ÍÔbÓÏFŸ+bc“hDô¦ùäqS–Œ;ä„^sÍ5qÃÎæ( ¼b}fCÊ4¢yà 7„«®ºª°™B8RÀqTm?‹yOTˆDÎæ­R‡wo.ÆFš¹@ð!ðË <¼¦¯¿þzÜäå7‰ô§Ô˜9_1—_6›nºiÀóÂ:%ÄïþûïÞZŒ 1ýd³ÉËZ¾ï»îºk\›llÙ€“'¹ÁBj¹APÉÜÓ_<ÜI–=}{â‰'â6óÉÊ]KåÎ79[D©ÜXŠ^L¹Ë ¼Rß%ÙyÃcÉ÷žWn6”²b×N)—DŸ›Þ#x±¾’gŸ×ñþsÍss‚k¸ØZ(5¦rsÀ¹*½¦’À£¸ß%ÜDKE‰JåàU2^n˜!^_|ñÀ-æ1Y–e)Ìáþ­åàÑ^±yÉ~W°Î¹vŠyÿ*Y# H@è !ð²¡A„îqÄñ0ž¼EaBÉØäâMã.pÞÊmJ³›¼7ß|³pG=Û>¡R¼aÅD &†§²y¯dî‹qOŒ¸qB_¸©‘ ‹¥O宥rç&®š±Tò…YNàµö]‚Ïsà›æO7ùbV­Àãæ ×sö;…yå{¯fVDãIäF×á±k®¹f˜qÆc7Š…~f¿S¹9àÚ¯öšâ†ýäÆK²R¯’ñòÍú⻑ Ùj¶YQVŠG[^©5QÉšó H@@GhXÇæ‹ /„TòÈ%Àl’Šå³”Û”f7y„{ra¾º"ïb›BΟÝ0!¨6Ȇ¯ ^Úë„'Ë瘔x•ô+»Ð8/Â&›?ôöÛoGQ( ¿¨=a–Œ±Ú~)ÇŒ°FÂé3• Ëåàå­AN#á£rvxjñ¤"èåôPÔÖ^3Ÿ­TàqÜ"‹,§ A‰ ï›nòH »,fxöð#ðØh#kem“‰gŒPJ¬’¹/%ðXãx²Ø½lØs¹k©’sW3–J¾<Û"ðø.)®ÍùðFãbÞ¹)Ã÷AÞªx#Ιõ˜þì ¼MUûÇDDˆL•’R*$i¢PBsQѤ‘R’ÒH¥¢D…RJ#JI3Íå¯I)4ÉÐ$™Sü÷w½ïºï¾»sï9çºÃ9çþÖçããÞsö^{­ïZ{ßç·žg=Û×Í^ë?gβ¨E÷2 ¹ <ß§DÆ Ù{Ê{±n¾ä&ðé/!Á,jù0ï°ç:ú|äš±xä§Àóü™s:FD@D@ ›@F <Œ2¿7.–á v<£4lì’ÞŸ$ZgN<Ÿ„ð6DF޽ºõÖ[]R–¼ ¼DÚn'!©ìE0`@ÖÇìjx ÙOàÅzGX<±“l;c±Ä&$Œ.J4¼¯IA¼‡2–ÑKòöFËB@"/̾³XÀùñúÌyÉ <=aa9e0Î9Â1vˆCƱKx',$? {J"cŸ›À#¹ öðáÃÝž+ö‚ùï^JäÚÉô%‘‡g~ <MB(Ñáp>ÿ]²…¢˜Ó>Œ8‘¾q Þ3æ ቼDÆ Ù{Š×B°€ ƒf>ÄÚƒ—H¹·ˆzÀ»·’°SÿÎXÏó ó`a–Û{9ã%¿ñP ¼Dg¤Ž( /ð¼ñ@ˆûÞÈÖFFBBÊ€ÑÏ(e¿ !•*x/ð‘ÕUsC$!¥$@Á ÄD ²úÍ¢Iø,s„øYµj•3i;{Ù'‰ˆõž2BÄÇC¤#R1ÆIpCöÂDúœ¬À#”•ù[æ x c%‰`¬˜ƒì'$éž>„{“XHÀˆXôa|‰Œ}nÏ{BɈ=ŸI“vDï¥è½“ȵí ãGÈ5!Š„¥æTòKà1ñ–ÑG¼j$AT‘Í7OàEçÞa[G$‘·páB·èÝïG4Þ1öYâÁf#´܉¼DÆ Þ=m?íáþ!ÄÔ'‰áù@h6Ï´è‹Îãõ÷•W^qd}â*¼ìDfø=ÌaQ–Ä÷ûy>E“l1g$ðŠÂ Ñ5E@D@ò›@Æ <€±¢ŒáEh{¥Ø3ƒx ïqñ`ã kÏ=÷œMœ8q«QiÞV#Ty ˆ¹õÕWÛ“O>i*T°Ï>ûÌn¾ùf6l˜Õ­[7W ÷Ôï¿ÿn}ûöp>ñÔSOÙK/½dcÆŒ±m¶Ù&éš7oÞl'œp‚=üðÃV­Zµ¬óO=õTëÞ½»µjÕ*é:Óå„뮻ζß~{ëׯŸkr¬gfºô%^;çÛo¿ÝæÌ™c;ì°ƒ]vÙevÛm·è=¯M9}½óZμHiÇrÄ]º ¼×_Ý^yåëÖ­›íºë® Ï‹/¾hü±]sÍ5 Ÿ=a\µjUëÔ©SÂuÜqÇvØa‡¹…U&Ožl¿ýö›õêÕ+î%-ZäÄýÏ?ÿlåË—·ãŽ;ÎvÜqG=ztÖ¹'žx¢yä‘që*Œrâ™·téR›4i’uíÚÕªT©RïJÑq IDAT Í¿ï¾ûlÆ ….ð>øàûðígÏžyê×êÕ«íŒ3Î(–€råÊY»ví»XÏÌÜÚ¶m›/7ž‰6Ö33/*ì1L¤O?ý´Í˜1Ã|ðÁDÏ—còÊ!Ùû7_«JD@D@Dà¿RVàÝy熱âKéÒ¥mÚ´iFˆæóÏ?o½{÷¶ûï¿ß0¤4h`ýû÷·Š+fϱÏ<óŒ­\¹ÒêׯïDï%s˜…5j¸ûàÓO?µæÍ›ÛøñãmÏ=÷´¡C‡ÚÆÌâÂ?ÿücÍš53DÎvÛmç˜ñ=žÀ×^{Íð|5nÜØyÎ`c_š6mêBß(,¨ð9[¯^=»à‚ ²½æPHÂIñ®2o'Ä6Â*ìAæ¾¥ÝQ=vÍïŸ|ò‰sÌ1®­xl÷Ûo¿Ýã´~p­pÁsºÏ>ûíºøâ‹máÂ…î8<]çw^6ÁG{aúÝwßÙN;ídçž{nL6üyÆ&;räH[±b…›’%KºçRNÏÚ€øÄû´`Áã9vúé§žf_¸ï÷ÞzËy¥š4iâBKñÀS¸Ö=÷Üc_|ñ…[Ð8âˆ#ìœsÎq ,zàÍ&D3§g&u@ë·ß~ëæmhݺuV¶v ©ˆqzàܵ(-[¶tgÚÉszìØ±n\æFçÎJ¼1öíì³Ïvã¾'¨&,ý„túrË-·Ø_ýe×^{­›ËeÊ”q_1vS¦LqóÌ·#/÷U¼û7ÛÕ/" " "PÀRVàaœò‡‘‡Ã;1€!€A¶÷Þ{;ƒˆ?Þxn0Þ(Ƭô"0–§OŸnï¼óŽ3>0È|É‹ÀcEwÖ¬Y6{öl[·n]V]ìƒÁûõå—_ºë„ËW\aµjÕrýñÇvà 7ÄVŒ®C=Ôðà}þùçn_ÆÈŸþéD*â ‹/va‰aïßÌ™3íý÷ßw W¹re·§ £ #ƒîàƒþ×µá€8À¸E(PØëÂÏ¢þ?þø£;wíÚµî^hZ‡ñÌÞ- Ç6mÚ8cÜúDÆ|¸ûg`xì5{ì±Ç\[è³ÂÑFcèú¾úïrx9µãšPXD í¦}Ì5® 7æ B‡y†€ÁÈ&Œ‘¾"®`õœÅânTà1'0è¹>†; ƒ¹ ¼'žxÂÑå—_îöa1Öô…¶RÞ8î„€¹Ë÷Œç%—\âÂÜÞ}÷]'¤˜+Ì_Â?/½ôRwŸPí`Üï:uê84L0¸ñ¦RÌ/Œyæ/so ó8,æðXÓGîÛp‰%ð8@x3†9×?ú裳îq>FcB»èá›p€[b®Ò'¿¯°BÐRÏ]wÝåî-žáâ6—Å¥ÝvÛÍ]'Þ³†6ÀþœËœ´skÇ/:íß}÷ÝÝøÂÿ½÷ÞsŸùÌ|C¸²àıD 0ŸðRS¼ðÏiÌé¬yöŽ7ÎÍmDyXàq¿Pó˜… æ*ûJé;×b|xÉÞW‰Ü¿1øúPD@D@ ˆ@Ê <ú‹!Iò…è<þ?òÈ#Y«¿+ q %„¢êðÃwØ0O«ð ¦ó"ðXeg_F!«À¬Jcdxà.ùu®Y³Æ]÷§Ÿ~rm < :Æ B°OŸ>Y‰!0â0H1Œ¹õuÔQö믿:ƒ# -QÇõ¾7[ü‹åÃDchz±{DýÄ;@Ƙ¤NúŒ'c1†‘ˆ Âð|óÍ7QŽ1KAàq I`´ÿþû;/ 5,ðW·ˆ/=/&b#—VÚ %ÍIàáQÈ­xR:è —Œïuc3÷0ì1\áCÿ0Š1ºY±ÿå—_b†ÿÅâ™›ÀCpP? Þ³ÝÃ˃‡§¶oÈSŸÿì›o¾qó‘…7È ùp»;ˆAŸdÅßKŒ'žQ_0˜aÀÿæ%Âo™/Æ‘yÂXbÔs_’H%êI%ð˜ïÞ«Bˆ¼2Þ«n7ó‘+É m8ùä“ÝáŒW—.]Ì{ø®¼òJwááôq€÷ïÌ3ÏÌöÈõü‡ âD,%‘gM¬D/ÜGÌ1„žåãžò\x>ÐnÚÚ¡C'bxn!’¢%,ðø.Ö3“ñGø2ö¾úÎâÑ£>êîíC?‡™“xìÂ…qá™É³Ð'Ñ™?¾»ÇØ÷Éó‰9oÌé›xÔ½'8Ÿ….XRxv „Y„‹<‰¼dï+ž-ñîß œ>$–/šEÃã¡ç ˜áÕðÑ…‹@ò%/aÉy„]Q?!¼Ñòý÷ß»•ê°ÀóÇÄiþ;ž÷Æy#ƒÕq ±D^8É "ƒœð۱Î5YÇÈ gDÐÑD1Þ<Ä'ßc$c(aœñ9Ï‚º_Ã;ây ð0˜N;í´˜¡o^à²E;XñGF ºcíYôÇæ$ðⵓ1ÅC€Qøa®à Àp£'t’‘0ãƒ1‹Ç+§‹gøØ¨QQLû}IDàEïŒv_xÞcäÒ2Ub„rF}¬x„âiÅ3Fø¢/ÚxÃ0Ü)9e«$‚•:˜K\¾ÑKàåvGÏÏMà…³hâ ÄàgNã±cñƒæ…¸Âsލ —¨˜à»Dž5±Ø°°Ãbb-B+,f©›±`ŽñìàÙ†°­Y³¦k[‹-²·xÌ_8”ÈÏŒ{“z·v Nxq¹?£…ïx&àeõÅ‹/Ä+‘±²#‡ŸëœOà±hÆâìðØr¿" Y8e¢/ÙûŠáx÷oŽ }!" " @ cû&„á`ñs4Û äýžXæEàšI˜ž$ê"œ …u ÑÖ¼hÍpV̼<Ú…§ qrÊ)§d5“ð¢o¼Ñ ïõá˰ Â(gOaQèD !ñ’†#ÞUúí9/ð0PÙ{-<„^= pêó—ð±[#ðⵓýjZ®KÈ"‚cƒ’ÿœä·ß~Ûy\ðÞ!ža‚±Ñ̉g¸/Q‡‘[ïãØ¼‡¨Åû‡—Õöz"†Q„Jç$ðCÚ‹P‹8­XïhÞñÜ^xÌã <êd±„E(îC„+¡Þ5rMATÓÎx{ð¢/Þ}•Èýû¯I¯D@D@D  ¤´ÀÃ;Æãð;ïrZéõ†{Äðù•áÜØå$ðX]Æcê,û¬Âo4⣟/íÛ·wz´ä&ð7cå×ïç Ÿ+‹fXày±Cx§ýûÜÌð¼°…€À¨e/“/¬r³òM¦Ïp ¢c4ì"ÄC“½4$zÀ«Hè—ßûÈùìaòû±ø=‡áÅ>) 2úÉ~8ÂäÂ%‡@ÁîuÄk'aŒ„"Öð8±g1JXY,ï+ `„„á© ·5'žá¾D2Brñ¦x­7FsÛƒ5D9‡ñÇCö¸ùkûE¯?Ú.ïÁà '$×ïÑeŒã•X/‘gM´ <[ðòÜ Í^ÈãõõɃ¸W Ñd/ãpá;D ÷‹Q뙉·°æ°w…¸ãÍåÙµµcÈÜeá Á^D£í<¿ýwþ9齟$"2!Þszâ ½o´',ð£„tQ' è+ì$(¡`l²?…ë“” ZØ«„˜á;¿S`e V·1B÷Úk/g¬bÈaÀò;%Çø`ìRØCƒ7 £;:—ˆÀCjI8({À` “xí¤ˆ_1†.£}cxJðÐab€³·O0^TBÃ/OÏ7*ðÈ0ˆÀ&–9J6B¼<$CIFà±à€OûÙW†È#ƒ$s AÎ÷pE˜poÐ_D;‹$‹ÁPÅCM#B—{Ž>r/!†sD/‹à÷Yæ&ðNêÊ5ÙŒU¶VàáqEˆ²Çï=pñžÄþ<ÄY¤~/¤oo,Çwñž5´… ÿ#˜[xþÙsǘ°7Ž9N²Âõ?ŒDzˆÄ~Aîa„5cÄ}x±ž™Ü—aÿ¼%Ã¥¿—¹y6òa±ŒÏO¼—,2x¯u"ÏõxÇß ØðÌãÞg.xÁÉ<¡m$"<VÓƒÄÀb?ÆçS0 3âᒛϋ `^倘ÁðÀcOàq.{ô0N0 7ÃØ¦ÏaG–CŒŒ|Bôé¾ð9+ØE>ùA¸ýˆ † ŒI„¢ÇÀ´o'}à„!ª}IDà…_tΘÞ9Ä“LDà‘†}h$Âs1h½8×N’`¤²˜€!ˆðE”à%¥0îˆÄ4Ì©ŸpR„S¢<ýq±^“€¨õéݦ0GŒ%#ð¨¯^„ ‚#ï¨ß{Jßr\~`tãeà fA´¯$ ü½Ä<å{’³Ð¶ðç&“&2sF÷~z[+ð¨Ç IîI1¬ñçñú(ãÊÂ÷ý /.p\N/Þ³†6ÐoÎÇÃÄxŒ&œi–1cL3ÿšI¸Ÿ¨Ÿ½‹xæx]óŽköÕQ¢/Ö3“s3,Rà­g¬}»ülíRÏ;¼´Üó<ÛxyŽ.Y²Ä=7yNãýåYÉwþ9’¾3—yž„3qà‘•™gsŸö±p…×/7—È}ïþÍö‡A¿ˆ€ˆ€ˆ@HiWÀ}Ozž÷аêí 4 JÂ#1\JéT0l1¶bíyK§~¤J[å™È‹ÎS¥O[Û2{ò øÀÎ8ã kÑ¢…l·ß~»{ì±vñÅ»s¯ºê*[·nõìÙÓÊ–-k?ü°}ûí·6jÔ('ûôéc5rB²qãF;óÌ3mÈ!vÀm@<öíÛ×vÞyg›>}º½óÎ;öÀXÉ’%5“D@D@D@D@D@D e¤´À‹RzüñÇmÖ¬Yöàƒf j{ÖYgÙW\a‡~¸û~óæÍvúé§^¾}÷Ý7eR ”xˆ©·ß~ÛæÌ™c_ýµýñÇV¦L'Þ(±²h¾ûî»îsŽ™9s¦M:Õ&Mš”m¤¯¹æ«\¹²n¿ÿþ»|÷Üsí±ÇvË-·XÅŠí’K.±ùóçgeè$dÓ—5kÖu4kÖL3HD@D@D@D@D@R†@J <„ÚòåË­S§NÖ¸qc'öð´M™2%GGø$Þ5öÌ=ÿüónÿÜ#<’ øàÁƒˆ»òÊ+ÝçtÞ8®Ó±cG'÷Ûo?ûâ‹/œd^•*U²Õ@ôûüRf4ÕbM ežO€2zôh«[·®¤_|Ñ%LÉMàq<¡™ãƳ¹sçÚÕW_í~Þe—]\ì±Ãc×¹sg¢Iy饗\˜æ\`wÝu—Mœ8Ñíù#± ¢¤*­[·.ÖEH})+ðV­Zåö¿!ÆÈŒùùçŸ;áE&̰ÀÃËvþùç[ƒ œ°3fŒË~I¢B<ñα—.œdeÑ¢EnŸIV(«W¯¶³Ï>Û%U!;f×®]³Fn„ ÎHÈfÆ í—_~± ØÉ'Ÿl´±[·nÖ¾}{w¾Šˆ€ˆ€ˆ€ˆ€ˆ€%”x@yõÕW]xåŸþiÍ›7·#<Ò†žMàáÝ[±b…KŽBØ$0?þø,¦dÐ$) ûøükeÕªUËÆ}èСöÞ{ïÙÝwßmõêÕËúŽLœ$byá…œ¸«Y³¦µlÙÒ…r"ð~íÚµsž>(J)-ðŠŒ®-" " " " " éF@/ÝFLíHàijˆ€ˆ€ˆ€ˆ€ˆ€ˆ@†ÀËT7D@D@D@D@D@D@Os@D@D@D@D@D@2„€^† ¤º!" " " " " xš" " " " " "!$ð2d Õ ÀÓ ! —!©nˆ€ˆ€ˆ€ˆ€ˆ€ˆ€žæ€ˆ€ˆ€ˆ€ˆ€ˆ€d ¼ HuCD@D@D@D@D@$ð4D@D@D@D@D@D CHàeÈ@ª" " " " " " §9 " " " " " B@/CRÝ <ÍÈx2ꆈ€ˆ€ˆ€ˆ€ˆ€Hàiˆ€ˆ€ˆ€ˆ€ˆ€ˆ@†ÀËT7D@D@D@D@D@D@Os@D@D@D@D@D@2„€^† ¤º!" " " " " xš" " " " " "!$ð2d Õ ÀÓ ! —!©nˆ€ˆ€ˆ€ˆ€ˆ€ˆ€žæ€ˆ€ˆ€ˆ€ˆ€ˆ€d ¼ HuCD@D@D@D@D@$ð4D@D@D@D@D@D CHàeÈ@ª" " " " " " §9 " " " " " B@/—ܲe‹ÝrË-öþûïÛ!‡b °’%KfÈЫ" " " " " ™F ¥ÞSO=e>ø`ŽÌ>ø`:thÉ/¿üb]ºtɪ„ V£F»^ºWüúë¯Ûm·ÝæºÁ¸í´ÓNéÞ%µ_D@D@D@D@ÒŠ€^.õyóf»þúëíÃ?´ƒ:È®½öZyðrá5eÊ?~|±xëÖ­³'žxÂÎ?ÿü´z䥱ï½÷ž•*UÊÝ*" " " " EO mÞhûï¿6bµjÕ²æÍ›8EB5K”(‘Ðu…Å5ŒsôèÑöüóÏ{7iÒ$Û¸q£]xá… Í™Dš8q¢ýþûïÖ·o߬Ãñp¿ôÒK6fÌÛf›m©&_Ž9õÔS­{÷îÖªU+ûæ›oløðá® ÅuÞç TU"" " " ùD m¡’:uʱÛC† ±>øÀöÞ{o8p ó$Í;×Ú 6´Ë.»ÌªU«fá0ÂN8Ázõê•UçäÉ“íá‡v¿ã­;ì°Ã줓Nru`¼¾ð î;:Šè{öÙgí±Ç³™3gÚªU«Üù\/­Ù³g¡ž;ì°ƒ£;v´Š+f3Ò}*õ<óÌ3öÆoØüa»ì²‹õèÑÃ5jäŽÿú믭OŸ>îg<‹Ë–-³©S§ÚÏ?ÿl;qÆÖºuk{òÉ'íÅ_t‚`÷Ýw·K/½ÔêÕ«—ÝO?ýd>ú¨c´aëS§ŽvÚivøá‡g—(Ó¥K—:á±zõjC ‡ ý=÷Üsÿ5náðÛxýædêž1c†½ûî»FÛÿúë/«]»¶~úévÔQGåÈóñÇ·7ß|Ó}D˜ÀæÔE=M›6µÞ½{[¥J•²ê¡\K–,± *¸}˜ô%|\´cÿý·uîÜÙ»UªTɧÛÔì¾ûîsãxÌw¼Ë={öÌ·ë$RQXàq<{SO9åÇGED@D@D@D h dœÀÓAÈ¢,\ü~½õë×Û™gži›6m2<€=ôPÖaW\q…}ñÅV¶lYbW¦L™\'">þøcW‡ÐëׯŸ"ÑRµjU»ýöÛ³öò……NùòåmíÚµÙNA >òÈ#®-a·ýöÛ;Ñ-ˆžü1ÛÇ줟ÞÃB¯¹æ'¢!ܦM÷±xñ˜r½‹/¾8æLNDàÅë7b«[·nÿê—¿ ãè£v¿†yÆb„Pƒ%â7\Z¶liW^y¥ûˆëÝtÓM6gΜõ q8jÔ(ÛvÛmcö÷wÞ±çž{.k/b̃òðáˆ#ܼ ¼ã8GXQ(Z)𼘭÷Êñ3F{ݺuq{ë­·:ò„/bÀ¿ýöÛÎh¦`#ò(ñõÝu×]Y{ þüóOJŠPÄËCØ'F=¿cH{Á…Á·-,HÎ:ë,gLS0Ò9—‚÷Šv„=xa‘8}úô,ƒè#Ürÿý÷;@Áóƒ¡ÈDàRw„uR>ÿüó,â“úÃ/Sê TpñâÅ®¾xY4“é7õÁ‚(CœþðÃvã7f‰¶iÓ¦9Á”S½aÑM¯ë ·/'^]ÿb—ýtÞ[Ǽ˜7ožÊðˆU.¿ür'þÞ”•+WÚ%—\âÄÂïîÝwßíD¼÷x!ÔÆggŸ}¶;ÉÂÄŠ+ìž{îq¢ëºë®s”ù „|TàѶ«¯¾Ú P¿ pçwºßÛµkçÚƒÀc>ÒN# ¦ü£¿x6¹&‚o·Ýv³W^yÅ…ü²R½zõ¬:±!D“ù¦,³1§…>B#6ý=ɾ`tûPB> ‹’>4hÐÀësÂ41¶ÙÅž3ŒbŒn<ˆ<}x6(ñ‚ aæ ž%¼W”=öØÃyµ|ÁHÇSCñ^¹° ï3ŒõyXàaœß|óÍ®®×^{Í%º `Èû=Yíx½(„Pž|òÉÎËÂër+>ä1¦Ô—W¯ßÔ÷ ¡A_Ùs-ô”O„^@ Âêˆ#Žp?÷ïßß>ûì3÷3c…`?ñĨʩàñ|àb~ˆ‡ß>ûìã¾G¨#’>ŠŸ#xûz±SEà!ÄÙHØ!ÿ¥ô{é|r›Âxˆ@ÂåÇþN< x¿(Ì%9„–/„är!š\?žÀÃûHZDž/ì ¤O9 <<’Ô‹¨g¼UD@D@D@D@ŠŽ@Ú¼Xˆ*W®œ•ˆ$Y1B}$U!¹Š/Ÿ€Ä–G" Ââ|ÌpÛÙ'Æ~'ŸŒ"¡“<ÚBè¼¢¯’à»}÷Ý×eœ¤$Ë”½láPDê ¬¯R´$ÓoÂ0ñEÛ~½B~ <öáÑòû%£mÇÞþž\™g#m§ÿd¾ä3„5û=ñŒ±÷“‚cOûBÙH"ž‘#GºkxAGÒ2³2?ÉZÊü‰ <Þiˆ0‹&Ya\Nâ̇hzû?úè#×6¾ã,LèÇ×ÁžSÆ•öò.Hæ9‰}Ø#Éï*" " " " EG X <ï%ñø1R½Gpkç’o Æ1Y£xu0ŽÃ/ÊNFèä—À£}¼¼ïÑA›x«üþ±dõ’¨†>-_¾Ü`x/}Hexš'ÓoÎûH¦I²gúôÿþ5|ŸŸúyìC#1 ×äw¸à•ä•þÑ[—9Å«|¬gG¯#, }ä5 5/ðH„B½œ‹w™,–3„"=æÉP/ŽåùãÅçxý1Å›‡g-<§ãyð Eà³@h*I„Hƒ@õ1JÖRƃ± Í¼rÁ‡9ÝãLWHi§át#€×‹X^ò¹õ¡†À‹—Ý4UYàí#±‹ÏLšªíT»D@D@D@D 8À+£¬>*³°ß ¡—H‰å‰Kä¼T8O!a£ZÿˆTh—Ú " " " Å•€^qyõ»ÀæHȨa¼ ¥³Àc_ ¡²Í›7×M}/" " " "P$ð ².!" " " " " …A@¯0(ë" " " " " "P$ð ².!" " " " " …A@¯0(ë" " " " " "P$ð ².!" " " " " …A@¯0(ë" " " " " "P$ð ².!" " " " " …A@¯0(ë" " " " " "P$ð ².!" " " " " …A@¯0(ë" " " " " "PRVà­Y³ÆfÏžmß}÷]!`Ð%D@D@D `Ô©SÇŽ<òH+_¾|Á\@µŠ€ˆ€ˆ@ˆ@Ê ¼©S§Ú®»îjM›6Õ€‰€ˆ€ˆ@Úøè£lÙ²ev '¤mÔpô!²ï¾û={¦IµTD@D@bøûï¿íþûï·=zˆˆ€ˆ€8 ¼G¬ ˆ€ˆ€w£G–À+î“@ýB" WH uâK@¯øŽ½z." …M@¯°‰ëz" " ÅŽ€^±ruXD@ŠŒ€^‘¡×…E@D@Š  ¼â2Ò꧈€= ¼¢µ@D@D à Hàeø«{" "B$ðRh0ÔÌ$ —™ãª^‰€ˆ@*ÀKÅQQ›D@D@2Š€^F §:#" )M@/¥‡GÈx™0Šêƒˆ€¤ ¼ô'µRD@D Hà¥ñà©é" "f$ðÒlÀÔ\ô# —~c¦‹€ˆ@ºÀKבS»E@D@Ò†€^Ú •*" iO@/í‡PHux©>BjŸˆ€d ¼ÌKõDD@D E Hà¥èÀ¨Y" "$ð2pPÕ%Ô" —Zã¡Öˆ€ˆ@&ÀËÇÑ]µj•]zé¥V®\9»ûî»mÛm·ÍÇÚU•ˆ€ˆ@ºÀKבS»E@D ýHàåã˜}ÿý÷vñÅ[É’%íÑGµ*UªäcíªJD@D ] Hà¥ëÈ©Ý" "~$ðòyÌ>ýôSç¹Û{ï½ó¹fU'" "®$ðÒuäÔnH?xé7fj±ˆ€ˆ@šÀK³SsE@D Hà¥ñà©é" " éA@/=ÆI­L ïÉ'Ÿ´xÀ&Ožl_~ù¥=ñĶxñb«X±¢~øávÞyçÙvÛmçÆ«W¯^¶hÑ"{üñÇ­R¥JÙÆðÇ´®]»ZÛ¶m­OŸ>öõ×_»¤)W]u•í¶Ûn6~üx#s›m¶±† Ú\`µk×ÎVG‡ÜÞ» &dÂüPD@D@ò€^>@T" "  È(·Ï>ûØÂ… ­yóæ¶Ã;ûî»ïÜ~¸‘#Gºä'Ó§O·Q£F9¡×¾}ûl¦L™b>ø ÝrË-vÀd ¼zõêÙ·ß~k5²:uê¸:çÎkÛo¿½ñG»FYõHà%4ïtˆ€+xÅj¸ÕY(R%ðrW_}u–àÚ¼y³]sÍ5öñÇ[¿~ý¬uëÖ¶zõjëÔ©“íµ×^vÇwdƒß·o_[²d‰ó"½¯råÊ6hÐ Ûÿý³Žì±Çlâĉ֪U+»âŠ+$ðŠtëâ" "Ú$ðR{|Ô:È$%ðú÷ïoÇsL¶ñùüóϸ;ôÐCmèСnºÉÞ|óM{ä‘G¬zõêî3Þawæ™gÚñÇïÂ3)^àµiÓÆ.¿üòlõnÚ´ÉN=õT+]º´"Š ¤Èƒ—I·‡ú"" ùC@/8ªø2^àýõ×_.“½rìÓ£àÑÃ#Ç:DeæÌ™Î£wóÍ7[“&Mâ <èÝ»·-X°ÀyüðòIàÅŸp:BD@Š# ¼â8ê곈€ Œx`Å+WµjU›4i’£Lèf—.]ܺûî»Ï}6dÈ›?~Vx&ŸåæÁã{<†Ÿ|ò‰ Õ¬Y³¦^ÑÌa]UD@Rž€^Ê‘(" C ãÞÊ•+­cÇŽnÏÝ=÷Ü“5pˆ2öÑ7ÎjÕªåÂ- ïôᙉ<2n’ysÚ´iîåæ…hf̽¡Žˆ€ˆ@¾ÀË7”ªHD@D Œx>ôòä“O¶îÝ»gáXºt©þùvÎ9çØ~ûíç^…ÏŒ'ð–-[æ^¿P·nÝ,/ žî7X$ð4/D@D@ ‹@F <’¡°/Ž÷ÔQ~ýõW»ì²Ëì·ß~³1cƸwÙ… Ù/ɪÉ+^}õU÷n¼R¥JeâC4yM°aòöÙ‘`…Nöòáñã½y¾ÈƒWXSW×ô! —>c¥–Š€ˆ@ºÈ(W¶lY«P¡‚ldµœ3gŽ­]»Ö.ºè";í´Óþ5V³fͲ#F¸½xGqD¶ðLöz)x {¿Â ™9¯»î:+Q¢„^ºß j¿ˆ€  ¼„«ªE@D@²È(7`ÀûùçŸí…^p¯=Ø}÷Ý]–ÌfÍšÅö7ºïׯ_ÿ¯ð̰À# 'ïÀcÏ{îxµ‰[Ø·ç_à/ žî0( <Í Â"QoàÀÖ¢E‹¤ØõêÕËV¬Xñ¯ð̰À#3œ|%© è`bO@¯ØOB#Q/Ö‹Îs#‰°;÷Üs,ݺuûסñ^“Ph£¤ ‰€ˆ€¤5 ¼´>5^D@ÒŠ@±x[¶lqa™³g϶‡~تU«&—VSWô! —>c¥–Š€ˆ@º(vä(Ó§Ow T>ÿüs÷Ž<^—«Èƒ—îÓ[íÔ —ã Vˆ€ˆ@q Pì™3ïºë.«T©’{)ùgœ‘- fxÐ%ðŠÃ- >Š€ˆ@ÁÀ+xƺ‚ˆ€ˆÀd„ÀÓ`Š€ˆ€ˆ@*ÀKåÑQÛD@D ³HàeÖxª7" " )H@/EM % —¡«n‰€ˆ€¤ ¼Ô µDD@2€^¦°ú'" "Pä$ðŠ|Ô(6$ðŠÍP«£" " EE@¯¨Èëº" "PüHà¿1WE@D@ ™€^!×åD@D À+ƃ¯®‹€ˆ€ ¼Âᬫˆ€ˆ€è5 š" " "Pà$ð ±. " "ð_òài*ˆ€ˆ€ˆ@À+`Àª^D@D ‹€ž&ƒˆ€ˆ€0 ¼¬êE@D@$ð4D@D@D 0üóÏ?6fÌëÑ£Ga\N×(æRÖƒ7mÚ4Ûu×]­I“&Å|ˆÔ}t&ðá‡Ú²eˬ]»véÜ µ]D@D M¤¬À[·n½þúëöÃ?¤ J5SD@D@þM`÷Ýw·#Ž8ÂÊ—//<" " "PàRVàÑó’%K8]@D@D@ ’ÀæÍ› ²zÕ-" " Ù¤´ÀÓX‰€ˆ€ˆ€ˆ€ˆ€ˆ€$N@/qV:RD@D@D@D@D@Rš€^J'" " " " " ‰ÀKœ•Ž”& —ÒãƉ€ˆ€ˆ€ˆ€ˆ€ˆ@â$ðg¥#E@D@D@D@D@D ¥ Hà¥ôð¨q" " " " " "8 ¼ÄYéHHix)=k“'O¶þùÇn¹å«W¯^V¯7oÞl'œp‚=üðÃV­Z5÷ù¢E‹ „åûßýn7ÍúÚæ,þÍþú{³5©]Ù†WßZîõŸëŽxí{ðÝïíÓÇXéR%ìŸÍ[¬ÌåSíûëÚØ.•Ëeµ¹Ém¯Û ûÕ°NØ7Ûèõ˜2ÏýºÖ^¾¤y£Zeàóvç)ì܃wMøl|ëköÍ/klÅm­Â¶Û$|îàæÛò?7Ú¸Ndsâ¸÷¬Êv¥müÙM®']œ½èWë8áC{±G3k´S¥tivRíÌiž†+™8q¢ýþûïÖ·o߬¹/Ÿ{î9㻼–XõFëêׯŸ»Ç[¶l™õÕõ×_oåË—7¾+Œrê©§Z÷îÝ­U«V…q9]CD@D@Dà¿RVàýùçŸNÀQfÏžíÄʽ÷Þ›5pºï¾û®@DIºÏŽuëÖÙi§f_|±5kÖÌvÜqG+Y²dV·V¯^mgœqF ¼©Ÿ-³3Ç`'7ÚÉ.8t7Û!4¯,øÅ&Ï]bô;ڶݦ¤Íøb¹Í˜¿ÂF¾¿kßïëþ²jƒf¤œÀ[øËZ;øö×m§JåìÚ@ žqÀÎ O“>Ojëþú'›Àþê7¶}Ùm¬{óÝ®']Dp_÷â—6üÄV³bÙtivRíÌiž†+¹ï¾ûlÆ ù.ðbÕm|=ìôÓO—ÀKjTu°ˆ€ˆ€d”xa¼/½ô’3Æ­|‡KAyÒ}h¿ùæëÝ»·=òÈ#NÜEËòåËíüóÏ/P·fãß¶ûЙ֩im»ûÔFÙš°e‹Y‰±)ûÛZÛó†Y.ðF¼.Ixðn ÞȺ;–·~_gOœpÂÓä¼IÞÉ’Ù^Â'ëÀ”$Ó< 7vĈVªT© ¼©S§º¨ƒ¼–XõFëêÒ¥‹wÞyE.ðšÇ{l^»ªóD@D@D@ò@ #ÞwÜa=ö˜}öÙgV³fM‚Iœ;w®3¨ðøí´ÓNvî¹çÚa‡ö/\Æ+®¸Ân¿ýv'(Jx Ãõ­ZµÊžzê){ûí·í×_µÚµk;1µ÷Þ{»úÁúä“Oì¨£Ž²I“&¹c9äw m|å•W¬téÒN`… D+óœ[¡Bkß¾½ó²•ˆ¡„–.]jcÇŽuÇ–+WÎÕÓ¹sgWïk¯½fÇÏê[Æ í¶ÛnËú=ú=_À¯L™2Κ_,'~ðƒuâÿlù°¶V©\é§æ]o.²»ÞXd‹‡´¶G?úѺb(\Þ¾ìH;´NK4D³ÙoÚQ{îh7·ß/«šêGðæûÙ…‘Bˆæà6õíãÿ°iŸ/ <‹e슖õìÒ#ëæØÎÂzÏ ážÀ;åÁ÷ƒ0ͬléÿyEX¹ÎúOýÜ^þêgWÇÙÖ¶ÛÖþAX'-_ÚÔ¯a3ºæê¨xðÂ!š÷¼µØîyk‘-]µÞšÖÞ!èþÖl÷ªYçÒîÉçd“?^bÓÏgù2¥lX²ê…êÆ ¶÷SŸØsç”iÓbÏj® µwø_¨«¯ì•?Û ççÛËþ´=‚>QO‡†µÜ׌Ék_ÿbªmC_úÊ Úæuw´G:7µªåËXçG>²?Öo²éÿïúté*7Fßic¿­ýËšŽxÝ]ÛÚêTÙÎæ-Ye-FͶYAíEÿŸ}Ô÷ËM'ØúMÿ¸6Lù¿%FÛ[ï]Ý…ÎîTé?^?Î;êî·ì­>GZßg?³Xiuª–·‡Îj𩜭­›ì´õK[òÇzk·_MsævCÐöG>üÑyŠol÷?Nœˆ@ëóÌgöÆ7¿Xå`~^rx]pì^Ž[¼ëæ6O=ß .¸À–-[–5vM›6µaƹçÃóÏ?ïž÷ß¿q/7hÐÀú÷ïo+VtÇ/^¼Ø=cæÍ›gk×®µý÷ßß=ƒ*Uªd9Õë/ôË/¿â.\xÖð,!D“:êÖ­kS¦L±õë×;ˆóÏ™džCï¾û®MŸ>Ý,XàÎ'ó¢‹.ÊŠ Dóì³ÏvÏQŽÝ~ûí]dÁ‰'ž˜Õ<Ú0~üx{ë­·lÓ¦MÖ¤IÖYµjUcaï°ÇÜ¶Ùæ?!Ñ„—ŸuÖYÖ­[7kÑ¢…mܸÑ|ðA{ã7\8: ôg»í¶Ëñ^Ö" " "é2Bà!²8ˆ»Ñ£G»UsDÑwÍ5×8£#ë‹/¾°»îºË·óÎÙÃì¼Gpß}÷uûÓØ¿öÇdÕ÷á‡:qwôÑG;#¡…!‡!BÁ€ÃàhÓ¦uíÚÕ}‡1W¹rå¬uŽaÿ£ÑxÉ%—8ã£# ‘†¡]ý^¹r¥;C¨cÇŽîÜ»ï¾ÛêÔ©cƒ r}8p µUªTqÂϾ‡%íÄÃÇ÷•¼übyI°7nn` ¿wùQ¹ÞCal¿³9Áþ­£æØ××´²ƒpHŒsŒîüxÛnSÊn D_ó@@͘¿Ü.Ä3’%rÂF4Ô¹n¦¡µ*–³êW¿`‚½s'þWý²f£5þº5 ö› =¾¾3v§ÂñêÖõmKà®ìì·C´Ü{zc+U²„ÛkxC_üÊFÏ^lûôÔªè„É­¯~mï\v”5Þå?ûØxìýCà´Ü+C¯/t¡?^œ)„}Ž{÷;›Ñ­™mˆ¿ç>]j§6ÞÙjl¿m¶1xká¯ÖöþwìΓY›}ªû#·nðšÛ¿…Õ«VÁ ¼XE¤ÞqrCÛˆ/„s§¦»8!HXí©½ŸM¼Óþ×±ôfï#œ@Š <~?0ecÎllûÖ¬hef«{ß¶?7l²Q§íïúÅ^ÅÏÁùñ-\øª¯‡1¢Ï»BµÓÄíç€÷ì@ôQ|[ FÌ"¦¹ý «ô™¶Ò޹vÆ|ûqèñ.L˜ñ: £'7ªe—·ØÓ~\¹ÞÎ}ôã`ìöqb9Þus›§4båºë®s÷ߥ—^êD"…{Ÿû’!žYÇ3¦mÛ¶îù@!yÃ󊹄0<ðÀÝs$§zÃü×_9!ÅýÌsŠër}ü#<Ò- !¼xvÜxãîy’Ìsˆö±p„øÜgŸ}ì‡~pý¸òÊ+Ý5)<Ëx¶\xá…¶ß~ûÙ|àDíµ×^ëب㪫®2BÊ{öìieË–uÏÅo¿ýÖFåže:urÇtÐA®NµøÑÇñ<ÛaÂóç>õó\.¬}†™n ¨" " éI #ÆÀ{ìáFÕÈ‘#mÚ´iîw V¬YÕõà £äÌ3ÏÌ6j^à!|ðôQXY&$Ê׿ùóç;c‡CbÀ=ýôÓN¼ùB’…]wÝ5+T‹ý…\Çê=FÍ{ï½çD§_I§<_á±xz衬Umß<€½/¿üÒ.¿ür{ôÑG-þûXIVò‹åY!¾2ðò¼Ø½™»üâÀðn|ÛkYM™„8·O g|{_¾„A6ùVÌM¼D>ÊV0¶IØâ“¬$êÁqRC·/З6÷½cÛ"ã© þz‰gí¡÷¾³ÿëÿŸ„ì+,Wº”M8ç? R7cßùÖ¾ÜÚ «hi;æÝ@¬–Í¢x«7üm; ~щ©‹šÕÉ:ý¸ M¡+ oxÿš[ IDATÐnï‰üyõF«œç½œý‘:;HfƒøAçT޾g¶íˆÑ»B¡³$}9,ð”lµ—“Ûƒä7? =.«ŠžO~x½Ö9ïãßôÿÛ^Oe· í—Q7G÷\×C¬}ƒÿx _ <ˆ­ƒþÍt¬í]½‚ûlm°Oq×!/Ùõm÷±ž¡z\ÝÊö¬VÞó伟ìÜÀË»nD÷{¬¶2ö­±}o<ŠÕ¯žao^z„¾GU2ãKç¹{eˬPá;±<+hóÕ ¼Ü®›Ó< 3G| 6¢IVžxâ ÷¼ð /wÞy§ýöÛoNÈÅ*ˆ™Y³f¹J¬z£çuèÐÁ.»ì²…hrÉ<‚Çw\ÖžÜDŸC±ÚyõÕW»E1®KAౘÅ._X„"ê`ðàÁNlò;‹d,,QسxÎ9ç¸Å-ú0tèPçÙô ه͂çáá䚢•( žÃá}Ç1ÁêCÈP!ðÂY4ñ°aÀ ÈX¹feœ‚§ÌŒ2Ìæ.±öô…ëóéÕW_5„Þ6’ÁÒ¹Ûn»9Ã"š%ƒ¬zõênžB(Q»víÜ ?á›)ˆ¹pY•Fd"úÂ…¾‰ÎV {bžuëÖ[%ðò‹å¥A¨à߯´÷ƒd*”Mÿlq¡y”CïxÃyÀÚ5¨™”À;x·¬W$ŒrØÌ¯‚Ä,›’xÑ,šˆ£W‚°ÄOü/ë çÛâž9vD ”IA()‰S?Å×îþwmR—c>&â <8Ttë+_Û˜·¿ua”höÏ ›6[ù+§»ÃO\ û)ð4žøÀ{.D²ëauìâ@pU«Ý{ÇþÇmúþg/kÕ 4Õ—5ýí¾ 2£¢›c®šþ…½µè7'&)xh—¯'™E÷»é•À“x¼óæäÁó!›œ·‘pÔ°ˆäsXU¯PƉçh=|ÿÒ—+ì„€7«­Ô±[•rvß][¤Û™Y§]th‰µ¦!œsáþoüûŸ@DV°¯l‘Ðu·FàEŸ,ÖàuGèQð\%0gNàÉþúk'h¡FR¶FàE³h"ÆðÂÆ™Ìsˆv^‰ðäIÉ›=ôP'¾(±²h"æxÖñ¼|òÉ'ýˆáÅ0ß?"j,°±è„WÁ†×“çöGá¼xd ?7yò<&Ä•¾ªˆ€ˆ€ˆ@q$±ÃA†ÂxaO\¸ðÇ?ºO#7G}Yþõ|°k„ yïY^áFa/#måzì™ —Xïx„9„ÀË Ë‡ÞûÞz>9ÏíUà .ì‰#ä.Y—ÈkðÞˆ±x{ð¢M^å@vÏpñ^2>ó‚€ ŠN ¼=‡Yë`O‚ƒW<Òyëa©ì‡ó…Ä.÷a›ß¯Œ ä$ð^ÞP Í—‚Ó‘V¹}rc_xeûMu{ÒØ·.Ë–vc•“À{3í|§ïî#Â<óŽýì¥#´ôÕ@Ï ^‹@ITà Îãuá˃}~ã¼µÃ;¸=ɼ©]uso-û£ÉŒâÜ„¥¿n~ ¼O?ýÔEP¥d/á‰7vbȾ9J~ ¼F¹½}É<‡ˆBèÓ§‹ŽàÙS¿~}×~¦Š”XJBÄ Eే73\ðîáµ#ú‚=v„¢óÜÃóGv<}<›î¹çžlçó‹ýË¿¾Ð" " "P d´Àà ´‡½v¬Ç+‰<„]8QÀO?ýäöÚmÀ#É«õ?>™@NmeÅš0T >öœP|ˆ&áK»ï¾{\žÏ²öÖ%Ò÷dX® ¼j{Þð²{×^¡pÉMàÍ Ÿì¡ {{87Ñ=xˆŽÚÁûóÆvüÏ;çHBØ‚.œd%,ð=„6Û½J–×Ç·wÜ;ßÙÀÀ{5/Ï ç»é0ö='œØSvu(¯-VhäIWm‡ÀÃN¨ Ѭ5x†‘ÙÏ›/ô…½iO†B4Ãíö¼°À s&ìÁžˆàzô!¦Ñ¹–ˆÀƒYR óÄG¸«yMDà‘ä¡õe¢¹×C4y•cu]°Om"B+/oP0žO²Ô>¿êXç–D®›Ó< ×…‡ïRx?X¬ îe/ðHºÂ‚ž{ÄåÅ_t‰H¼À‹Uo´§œrŠ[0 ¿ƒ.Ö{ððày—ÌshæÌ™îä=k\ÿÖ[oµ¿ÿþ;G‡w6±Ç™h7²ÈmÀcÿ õ’ñ’v²RýñÇ»ýyì© ^œÌ±$``…Ÿð-Và õ+çñöàÂĹž ÞL’ÁD_ïÃS½÷2–´ùÉÿûÉÎ 2.v cd<‰RZկþìë#t”÷õ ýoh©oÿÌ` Ú}×® ’Àt9¸¶{Gß3AB–+[î•£WŒͰºÈúåŠÕ.ã&H`BIDàái<&H¦³6 'YÁë87ÓŒy"B+/oéª A’•×ìÈ€Óà6{[…`^¾ÎÃëVu n¹nNó4œd…ç0‹U>Ââý÷ßwÏW¢¨‹°v ç0À…†"y .t"“N(®2^à1°doãdgc%˜Œt á½—ˆÀÀ éÇtaIì!¡ Ÿ!Æò¢Éµ—,YâV¢Ù‹ƒ$0¼Î\´p,{YXõG ’êœc½`'ð¨Ð(˜` ±"N2ˆx/–¾Í³ƒ¬˜7Ïú&Ø÷{ñ—i²W@Ã'çˆe òwC ÌØ·÷Hª×6Ø7•¨À#C"¯Z˜ì#Üñ~T5" " " " " "PÔ$ðŠzt}È'xùRÕˆ€ˆ€ˆ€ˆ€ˆ€ˆ@Q(öoÑ¢EÖ«W/›0a‚Õ¨Q#æx|òÉ'6räHûí·ß좋.²¿ÿþÛž{î9›8qbQ_¡]ÿ×_µ›nºÉ.\h‡v˜]uÕU…víxzöÙgãŽGô˜§žzÊ^zé%3fŒm³Í6¹^âú믷òåË[¿~ýb·~ýzëÖ­›uÖYvÜqÇÅkîV}ϵN9å”ë8öØc];arÝu×ÙöÛoŸc¿’mh"÷R²uÆ:>‘ëÌ;×®¾új{òÉ'­B… ÿªæÔSOµîÝ»[«V­ò£I^ÇæÍ›íöÛo·9sæØ;ì`÷ÜsõìÙ³Pæ\²û矬]»v6xð`kÖ¬Y²§çùxžÇ|ð=:ÏuèDÈx ¼>}úX­Zµì /t†þÌ™3ã ŠL˜á><øàƒöþû*UJ™.&"f¢Ç`~øá‡ÎHŽWâ <Œo 6ŒWÝV}¿eË[¶lYVW\q…»n›6mÜgÛm·U®\9!‡ø)W®œ3Æó£$"¼ ë:™&ð˜¯ÌÃÛn»ÍªU«fU«V-´9—ì˜Ià%KLÇ‹€ˆ€ˆ@þ(ÖcyñâÅq=x'Ÿ|²õîÝÛZ´háè'"(òw˜Š¾6„Âá²Ë.Ëscà]¢D‰<ŸŸÓ‰ŒÇÔ©S6·còêu'ðò½CITxÎ9çØI'd§vZ¶³a’Äe:4•¶AƒåêÁëÑ£‡áñL‡òôÓOÛŒ3Œ…–T/,xœp …îÁ{øá‡Ý"T^=xõ|JõñRûD@D@2@Ê ¼ÇÜ4üѦ¬^½ÚÎ8ã gùP9<0Æ 3ÂíJ—.mK—.µ±cÇ!•x&8¶sçÎî; †ú¾ûîk¿ÿþ»«ãø€È&ð¸Î¥—^êÄ\Û¶m­K—.ÙFýÅ_Œ)ð>þøc×Öo¿ýÖ…zž~úéÖºukw.¡bˆ£+¯¼ÒýþöÛo»vÓo`>ôÐCöõ×_Û-·Ü’ízôñÚk¯u¦L™2î»iÓ¦Ù”)SlÒ¤Iî÷+V¸­/¾øÂyÖŽ8â×·m·Ý6ëøgžyÆV®\iõë×wý­]»¶ûnÞ¼y6nÜ8ûé§Ÿœ—±à½A¾!\pA6¯mÜÿý-·>çÄ;*D•;ÿüó]hÕ‚ ÜXÁîÄOt—ÿ믿ÜÏŒÝAä>ûî»ï ãcw§vrãñüóÏ»±~ä‘G\(í~ûíçD¹»Šòèï¹1äÚp­[·®ãN˜dË–-]¼` ‡ü!tð¬RGè7ß|ãÚÁx׫WÏõ#xòäÉ6}út[µjÕ¿æX¶rù%7ÜÿýîÞhРõïßß*V¬èjŒ Wæ÷Òš5klÏ=÷tsŸs¢%§ã¼À»ãŽ;ì±Ç³Ï>ûÌjÖ¬™­ßÔ•Û¼Id¼c É~øÁî½÷^ûꫯlçw¶C9ÄñÍ-Dóì³Ïvcóî»ïºPU榟w´sùòåvß}÷¹ç ažíÛ·wÏ X‹7nt÷ãk¯½æžU7vÞaÆù2~üx{ë­·lÓ¦MÖ¤IŠŽÂ\äÇsŒ«ãçŸvó×ÕwÞi/¿ürÖ0ÐnÆ=fúý÷ßÛ]wÝ娹Ž/O»ãÝGpåš7ß|³órOÀº¸×Þxã Ã;GØ%so1eݺuŽÓ;ï¼ã8âf± ÑLt|ãÝ?\—q#,“vò\¯R¥Š[° <<¹´…gÏŠsÏ=×…˜{îŸ~ú©5oÞÜs~èС‰Þz:ND@D@R–@Ê <Œ}¼EüáÅHD!Þ0®¼§†ï0fn¼ñF'^.¹ägÚ¢ŠûxÖ¬Yn1žü Ï9ê§m|Æ B‡61éÂ+Çñ,Bð,Ùm·ÝܽN=Ìs]81g™Ûô}àÀîù˹Ì'ž7ü‹µ/™ñÍíþá~äùÓ©S'lj…5žiôÓ <ÆŸ¶Óî¦M›º00ßó,„ûO<á„sŸsýb`Â7 $²ã±sñÅ;ƒ ãƒ?äˆ þ(zè¡Î¨G<½òÊ+†'Ì‹ˆùóç;#„szêjÞèg<ÂÆ"{ëXeÇàZ¾tèÐÁÕƒÀ‹%XmÆÃÐñ$^·G}ÔáD.ˆ |Œð 68ïÁˆ#œ‡-YçÅõ† F‰?h×£Àð†np^<Ä(^¿Ü †RõêÕáG‰×gÄe,ÞÑkÄJvÁyˆl ÄD B 5þyÍ50æh#Æž·x¼œRí¡`üû‚W“„*Ì=J,ç=Œ|ϼb|ú˜3\—Â\eþ0g’ cÍMàa¼âÑôF+‹x7ñû~y‡ ÁsÃýC»r*¹çï%„Ë{ìáªÀ£…7È÷;Þ¼!Q¢BÄ'FBÌ!(øÝ{¹كdz%ì­f!Ï?ó޹Ä" óÇa’Ô‹°%;ž+ˆ…pñ¡¢,@x¯9÷;ㆠãÙÂܤnï‘ç|²ØâÇ Ï±x¾þðœcÑ{!H´Ä"÷x"÷‘?·x®R¼ §m$w¡p-Æ‘v#¢è»¿Ïø>·=xÉ´#·ûO#‚Œ±ò%šd…ˆ î}¼¾pßí³Ï>Ž íg‘ó|ßrœøúBD@D@Òˆ@Ê <âMÀàb5ñÒµkWgxáC`à†Çã›0*þðûÂê2Æ"F4á’±öRy£#!ˆøÁx—x°FV‰Ãñ"@1 £¬4“óàƒvÞC oÄ5ᑈW„Q¸$âÁÃÈÂÄcÂÞÂK1ê½À¥>Œ!_ÁC°á@P`HxàŽ«÷HEçpTàÅë3mIdïZ,‡€Ç Ixc"a¬=‘ˆ[êÆhg®Äx91„C¬~à]&L•ðRJ,ÎÌêÇ¡Ãذ˜€1ÎGPa ³€g0™’›À‹î9d®ÑO„^´_+î%ö01ðTâÁ‰–ÜŽ‹:íw¼yC˜]²£päpf×D^4‹&"Œó˜wˆ#~ß7x±ó‹îñâ¾…5‹9ÑÂwÜcañÆ1ÜO1/”âU<ç=lxYÇðâyHd@"÷Q¬ñc¡ æÀs•gˆÏDËÿÌe_òKàåvÿ°/š¿<ï| <Úˆ·:úìC\sžÒ⸗:™g‹ŽH_)-ðØï†ñ€1„±„¡Dø† ¢ÃQKà1,‹„FÅxݬêâõ@èÂãK<Ç5 ao{¦Õìm#<Žð,}˜fźÙ…qÏÊy´ tX¥Îmç`¸°G†º¹ÂK Høa8‡ Æ¥÷ìàÕá•<¬ÜsN´D^"}ΫÀƒ?±ƒá £x{ð¢òÚµkWã7žÀˉ!ûÕrxxGüb@¢Ï#ã…À`cÏ- xI“)É @æ5㘹’›ÀcQ†{;^*aü¼cÑÅß3DÀ!ÇÂûyÃ…}­ˆ¼ÆœC¢vÝuWúL¨$^µp’X±ð”“')YcÇ‹9ÌæuûLº´7×XãGßX¸"q ‘ǽÇó‡$®ËXðôIX¸/ æ¹ëEçyiGTà±ßšPcÆÆìƒäºŒ¥x^œ2~ìãFX#Ú Ã•,+]ÿˆ«Ý" " "%òÐ ŒsjG0”ØcÂê:F”/K–,q+ج|caì#¦|â…DuáiCD°êŽÑxŽ=R+ ¼E¤ãÆpĨІ·a±ß£ã £‰°PB¾Â{X¢ƒ„á‰×#C…,BÄÂ>„g‘.œPÌSN9Žî€Â÷ˆ™^xÁ‰;D”ß§C}ìi$´“¶ÀqKPD=x‰ô9Q'ŒKÂ2¹p"óŸ/RŒ9¸ s¼ŒMcÎ^L}†Æ»CŽYXàÅcXB?¹6û£(´ÕïòÉOÿÚƒ'" " "$ðò¢ªÈ;â(ð EH) xØ?FØ!»„Þ²_(üƼ“ՙʼna¾ì·%”‘½€<Â2Ù—J¸¨Šˆ€ˆ€ˆ@ñ! W|ÆZ=Èpx>Àꞈ€ˆ€ˆ€ˆ€ˆ@ñ! W|ÆZ=Èë“,E IDATpx>Àꞈ€ˆ€ˆ€ˆ€ˆ@ñ! W|ÆZ=Èpx>Àꞈ€ˆ€ˆ€ˆ€ˆ@ñ! W|ÆZ=Èpx>Àꞈ€ˆ€ˆ€ˆ€ˆ@ñ! W|ÆZ=Èp)-ðæÎk£F²‡z(kž}öY{î¹çlâĉ;4ëׯ·nݺÙYgeÇwœë'ýýý÷ß­oß¾Þï!C†Ø¶Ûnkƒ *ðkqXã¼yóf;á„ìᇶjÕª¹v|öÙgvóÍ7Û°aìnݺ…Ò¶Xùè£lðàÁn^ÖªUËòÅ_ØW\a °£>Ú}ö÷ßÛI'dýúõ³-ZØ©§žjÝ»w·V­ZÅl{´‹-²^½zÙ„ ¬FEÒßœwÉvð©§ž²—^zÉÆŒcÛl³M²§çxü?ÿücíÚµscܬY³|«7§Š˜õêÕss#/%ÚÞXϼÔëÏ¡}Ü‹-[¶Ìs5…Í4Ï Õ‰" " "RZàÍ™3ÇÑÅMà!nFŽé„@Æ Ý4»ï¾ûlÆ …"ðnºé&+S¦Œ,…QbóêÕ«íŒ3ÎÈ&ð–.]j“&M²®]»Z•*U £i1¯±jÕ*ëØ±c60eÊ'ÂÛ¶mk={ötç-^¼ØýÇ{CåÊ•s†}çξPæÍ›gãÆ³Ÿ~úÉ…úÎצM÷]8œï‚ .°eË–eÕÛ´iS¦HAìòï·ß~sa^»ï¾ûºïh'\kÄ#¸bÅ òzá…ÚðáÃm¿ýö˪óÆotýìÓ§Mž<Ùyðh–ôsÍš5ŽÌÃeÓ¦Mvï½÷Úÿ·wPvÙ® `Á݃Cð@€…àÛÁ] º¸»»[ÐàÎÜmq—üç«=5§Óég3/É›7_3'™÷º»ª¾ºÝsuoU?þøã‘ѼóÎbJ¯$ QþÞ{ï…)§œ2l¾ùæ¡OŸ>¡hœû÷ïÛ—-'tRlS6e‘þáŸxâ‰1Mï­·ÞŠ©Œ)"Âù8–ôç¶Ûn D޲宻îŠLN;í´˜b9þøã‡%–X"l²É&Qà–+GqDèÚµk8ðÀcØÆ.»ìŽ=öØÈjœqƉcK›Ž;î¸Ö1Ýn»íÂ!CÂ<mÇ9 ¾¼ +xåì2ß^Æoÿý÷ƒ O>ùdèÖ­Û0õq|9-gw©®"»îºëöpÁĺ‰ˆ‘ꈸ… å矎÷öÒ£G˜ȱo¼q<ö÷ßk¬±F8üðÃÃB -ÏÁv¸×ņòéÚ¥îåß~û­¦¶ž‰­’¢\MŠæO<íë7Þˆ¶Oä}Ûm·ö‘/ˆNøpÏ’ÂË=@¿xF¥Íz´7ŸüÅ_„óÏ??<÷Üs±I¤[rbç¥ÚÏ3…{=[˜tÁÖëÑÆ²7˜_J@€$Ðf +ðp I{Cä!äpœF8q8_½zõŠb GrÀ€15g™‚CçwÆh×TSM/Iœ¼ÓÅõ®¹æš(\m8›8Ÿ;í´Stîp”¾üòË(8pvpÆ“ã(ä¸ 'œ0:mW^yeŒ6’>ˆ°ä»ùçŸ?¦ó!.N=õÔ0ýôÓGÇçnƒ 6ˆ?+­´Røðã¨Lë\²}<ì°Ãâuô5G¤+"¾è'×%b3DÓL3ÍÔÚN„‚xºé¦‹ ‰6Ñf„gv lf¤GRǸãŽV¸¶9à 3Äup¡¼À£ÿ&„'N#¼ûöíëᜃ:(:°ˆSÄÔ)§œ£[“O>ùpãL¤’c§8óOEíâ|"uØ$í^pÁã83.ˆ: cʸá ÷îÝ;NT ôGÄu%‡ •³ËüS€ú‘ŒLxPWª¯’–²»l=©ÍY£Œ7ç3æŒ'÷0ü'DÎ~ûíïÆ;ã~å'EÍÚ*ðò÷r=ÚRîéJ_ðÜ7³Ï>{øàƒ¢ î½÷Þ­k1³ç_vÙeñ†½°Ž”S># +®×^vÉÖÒzOì†Taî_ì“g)‚£6ŠcS®ýŒÃºë®ljµ¥ðäüz´±Íµóÿüxàì’–‡j¯ÀËî2IDö<„æÀcD—Bñ{÷Ýwc4•‚Ý01@$‘öÐǼ@-º•ˆ>1‘Àu°¡ &˜ òcÍ()¿Œ;b*m°Â5òcšD Ø@%WÉ.óí¬T_56Z­ÀËŽ ˜0ÉÞ?Ø÷`Úù’—Tê%ðòφz´¥Ò£”]+ùØÑ\"àL¨äw¡EäòŒÈÚ×Î ¼zµ7;öŒ3QRvx-*•ÚŸxõjc%®~/ H@@Û4Àé&‚’¶«çÿùqÂóÎ{‘ÀÃ1c½R6â^Ö€!ZJ '„©wDMŠœg®À#] G!òH”’và¦+G¤À£^œk"nD¡ˆ¢!òH³+Uè3¢‰”Bú™¦sޤ8Ò]ÖO‘‰xEL.µÔRÃ\§“hãˆxˆgÆ !I$”TEœñ™tÒI[ÛÂ1¬ÿBt0îˆÁìzÎRLX/F¿pxù—”]Ö[ÒoR aKä7EEK ®´Î¬’À«d—Õ ¼T_56Ú‡mÁŸµùBd+¥EÃ-•¼ÀcBaQë¼¼À«G[Ê=Z™Ð`Ý*‘Hîmì‹gíGgK_Ö‹f'I²¯^íÍÚ¶ˆØÎN6¥vUÓþ¼À«WÛö'˳$  H@¨D ¡éo8ŠÙwÞ 2"xIàýôÓO1r’¢d•]T&ÒY7Tô~­"G‹u]ií mF Ñ6Ö¹PRt‘ÍHX“-iv?Eäòb€èNÎ …Mú‰xɧh"fpÌËíÂÈ;õˆâq=R3YŸXMA°ÀŒ´¯r…ö"Þp*ÓZÈR¯](çÄ#ª$€hOŠà%‡xd#‘|º\QÛY‹ÈÂ%¥5Vê#B‘ˆ)é®L pµ×^;:ûô!»YL{^%»Ì·µR}ÕØhÞîòuÙ"ñˆMõÎÒ[IeÍFŸ“ Ëî\‰°ÀvRŠ+QuDS¹MVò¯^m)eDˆ¹Ç=IȳÑ‘ï¼Àãž%Uœ(^ÚL‰ëÒGÖs/׫½Ù±ÇnÒ³(?ÁUMû±g&»ÒûëÕÆjž7# H@@íZàá¬ã’BÇîˆl˜RIàQ€ÓM¤‰÷ȱ։”Æ´ö)‹©èzDÕÒ¹¤á!˜Ø}ŽEX—”œZÖøáH¥M$˜ g—J"G(®Á¦"1Ö?‘ÎÇÌ=Ž‘Fœ.œX¢¬‘A4!Fyç§ö‘G‰ë¹>‘”J¢q•¶Ùg=!)ŠDØXVTp˜‰®ÐW8Ð?Òó»\²i )¦óÌ3O@dãà/¹ä’qYraÉ&5ˆ 4Î.})g¢mpKçíc'Ñ¢MVŠR4“À#JÄõaW;©³8ãŒ)›¤A!Œa̦$¤´±NèfQIk›¸nV ãÀcCôŸÔT* ®üx±ñiŸ¬—ÄŽ+Ùe­¯’r½"»ËÖSj=*ìHUD òØ9ÑÃzSì‰~1a‘6a¡¢ŸYǘ‘ÚÉ5{&¸N-¨r[ÚÂ}Á}JJwJéeƒ’l5{챸F•çbŸ{”H0;æÇsobØÈÔSOï_~°AXÔ£½Ô“µ5&ŸÒóŒ¨3aÇæ)o¾ùfÅö39ÂZJ®Á³Ž‰Œö2­ýO•gH@€$P-†x8;¬«Ã‰Â)'õ顇 EkðRŽã"Üî¸ãŽ(îX_Ålp/Ec>úè£è´ãè! ؃èB*9µ8©ìЈó‰ã‡”݈„k°Þ†¶!Žh×H©ƒ4Rq„§vÚ¸m9)žyßqÆq$qÈh ¢#õ“ÔI¾g'PRÓËÑ+ <ÖÕ¡a'HœÍ¢ÂkøCÁAGÀ!²˜Ù‚sŽÐ¢NÒ„8¬ô•BÔñгLš+kÎh+N~Ñ8“^K¿8‡z‰²ÉL­íá'X±æBƒõv|ް&‚H?©“hEÚƒ px¤l6â! “tÛ´ù DZa m'bÙ¯_¿ÖSkx!6#al5•ì2߯Jõ¥ë•²ÑRv—­§”­"²…GÐ3±Áî¡i ìûï¿Åõ믿'8xuITQ“2ÜÿÜ_ØŒYØc©×$äŸ \§-mA¼3éA{±mÆÿ#@³Ûå9Á¶ˆ&Kø!Z$ðh “$l^Ľ­1a„CzMB{Û[ôü€7í$-ûg¢…gÿ¯Ô~ÎaŠØ“6õhcáM净$  ´›@C ¼v÷n] ’pAÕÖý²Lœ×ôJºWÐ$R‚SšÖÿ!š˜$ Iúfv#hn§mB£¿ÁÎdBÔ" H@€™€¯ £ÓÑ颤#1!"Ai3¢v¤9"=—hÑ0"K¤?N¯ÁhÆþw¤>å7Yi¤¶IäÕlø2ÅS4RÓl‹$  H@Ž€¯ FÑÑ©¤vÏ,Z—Ø$ { é±W\qEÜh…;Œ>úèÍÐåÂ>œqÆáöÛoGuT˜þù›¶Ÿ£ºcüb÷ZÙ<þøãá¸ãŽ ›l²IXwÝu«:}‡vï¿ÿ~´µÑF-´Ç±/¾øb8ùä“Ã×_¶ÝvÛ°Új«UÕ.’€$  4^Ë(æÞ'Ÿ|.¿üò°Í6Û„‰&š¨]ïï¿ÿŽŽÆrË-æž{îi3×]w]{ì±Ãª«®Ú!Û_M£Ï:ë¬ð믿–xO?ýtxæ™gÂÎ;ï\Íåª:æ¼óÎ 7Þxcüo#”ö8ÚÐþŽÒ†fx<ð@8þøãÃFm6Ûl³ª† ž¯è»Ûn»…)¦˜"l½õÖqnœqÆ©ª]$ H@h ¼WËÀVŠàÕr­ŽzìСCC—.]:jóc»O8á„I(Á;úè£Ã#ûì³€3˜Ê€býÙúËõˆ à0Ršµ.WÑJf¹‰2öêÕ«õ²GqDLËìß¿ìc6 •ßç˜cŽ˜Òxçw¶¦f=÷ÜsáÒK/ ï¾ûn·õÖ[/,¿üò­×ä¼Ùf›-üùçŸá®»î*Éj¿ýö ÷Þ{osØ5#Âuî¹ç†?ü0Ì9çœaŸ}ö ã7^ëµo½õÖ(”¾ùæ›XÇ¿þõ¯0Í4ÓÄï+Õ»ÕV[…O?ý´õZ ,°@LÏÍ–›nº)Ü|óÍTNÊçŸN;í´ðꫯÆ6.±Ä‘ÃXc5Ìy_~ùe´—^z)~¾à‚ FÞŒNq¶ÐþgŸ}6 üþýï‡ûî»/<ñÄh¶+®¸"¼óÎ;¡gÏžQ ¬½öÚ¡k×®ñ2ØØLV^yåþâ‹/" X¥H4í!rÈXeëç~‚aVàõîÝ;Ú9ã|õÕW·ötUìíC }úô©›-¦ ÑîóÏ??¶‘ÂýD;`‹½b¤àapÙtÓMãó€rá…FÖØs*¤›ò=(Üx`ÏçŸ>œxâ‰ñ{úõ /„Ÿ~ú)Þc¤]3¶”ü1œ~úéH.÷x*DåIO¤”³AX%æÂ}E4üâ‹/p@U€}ÜvÛmqóÌ3Ç{Ú" H@^àñG’™Xhœœ…í¶Û®&ÇXDÔ¤“N…N Î N%‡0ÄIÀéÇaÂIëÛ·o˜p £x"“uÈSO=u˜5LEçt=öˆ ŽŽ?88k8~¬}ãûûï¿? œÒé§Ÿ~˜™rœ§ƒ:( BqpˆBsÎ9Q¤uÔ_®>œœ)„Áˆó†C…“_$ðÈ7Þ8®I{å•Wbû¸óÍ7ß ÿùÏâï)µ“ëà´óâ„ch#\Óz/Ú ƒ½öÚ+:T8ßO=õT*Dר Ñ oœ. B“ïqq‚#Dâ§R½°ƒ5¶€Ê9ùuvy‡ƒ‡(À6¿ÿþûèx2ÙÄVºyDâkÒx¿öÚká²Ë.‹6Íd×b&íÆYF¨1A­Ã»¢L*,µÔRñ|~°¹vÚ)V—œfŽYi¥•¢d¢€ÿÓN bùã?n=ñ9ùä“Çzp|ó¼$æ¸ï¨—˜}ðÁáÊ+¯lV|^[üî»ïbg˜a†(ܘ$a„ô¿~ø!¶Lp,÷÷÷ ¥ZÇ„iÑE}@ÀÒ~Ä=ub“ˆñÄþsa—ÜŒ÷cÃDB9äzœÏäÔ½òSm/¥óÒ÷ÙgŸ=<üðÃÑv°q#<¸.kà¨a…í%Çz·™fš)ÞwŒâCäšRîúÜLÜÐGl…gB’ûœÉ„¼Àcâ-M€ñÜÅvy^ðH)Ε"xÜ_ÜSÜ#<ËHŸ^ýõãóû9_²ÏØ4ñÇó›M",Ãsg7Ï‘ìó†ðå9ŒXçy‚mñüåoÏ3V+ð®¹æš(ìhc”&!FÆvë€$ ÎM á^~m þxâ˜WÁK=†›óp|œ_Ià1@À¤™þ¬É$g‡$„Î:uPŠ ÿÓ¦+ß~ûmt`O:é¤è¼Ýpà qö›z“ãL;™ÍN³È©.Ä §§…‚C…# +õ—«ãE8Ç6¢EI å#xD‚ˆ`¥B}OB`â…{/¿.9/ðàCÄ“ã)bîo¢¡Œ!¥’À#rM_ŠØpªƒgGQÚeþ»úê«G»H“Ùqf’…¨+ð`ûALr¯bßLº01’ ÷ ü[­Àƹ7Ós¹¤±ù…$  H Î:œÀã3Ž&§½ZG:R*Y½’Àc¦œ:qù£Ï¬uJÝ*Zƒ‡ãËL0J‘À˦l¦g QÒLqÀD p4q@è‘lÁÑĉȊ"8XD_pÞòõçëÃÄC°!’¤?&ñRÁË ï5×\3:mÙBÌ^ãè0óßVéˆÄäXÒ_"Do’ƒ“,ú?œÓjê­UàáÜå¡_DEp:‹fè$Ìüc«+ÆbÚi§CXNà¥t·ä ×d>?å”SbÚ\ŠåfŽÁÑ…¶ƒð%zJŠ,“Ø ãƒM!>(EkðÒ=öXêDÓžE©Âõ°ED ¶µï¾û÷¨ã;DCv‚€>ð à~FtV+ð²÷Á†>âìYFÈùá9CA`S5îì…{œÉˆJ6Hú/÷bÑ\¯óRé}Ü·DÑ“-`#¤‚Ã$¥0ÙcÈ$¶ƒ ,wýYg5ŠIDq^ÔÓ—"Gš-“F¤À1ÄæàÉ„¥’Àcc‚£›ÕÃõ¬ˆKm-x|‡íÁÇdBÚ"?W´//ð°Ö»bÿ<xÎ`·8ö””"‹HAT²öÀï•la€PDœ¤R‹À§~kQ IDATCØ0ùÃZ0"mÙÂdAš¸âaN]­@$·È.ˆ’zÈu¬å®Oz'iLÞ¤5jÙ6äQeR[‰Žñ|&+FŠ-÷¥’À㢹L*µ&‡ fírQ©Eàq>¶JV–¨6"¾Õ¼tUZƒ§À+*?”€$ ‘@ Ã <„ ô)8ˆˆ”¶ÃŒ1i4ùMV²/¥<áD’ÂSÀËŽ:é:œ_x¨Uàá@〥ô°rv€³ã œõjœŸ¢Íiˆ1ãŽ(%€ H›ÄT#ðpÀp*³‘œZ1NkÛª’ÀcC œòÁ)ê5õVz×_>E3[)hp¢ùwÂ:­J}aÓ½÷Þ;:­D²ÑÉ¢hHÖ!æ=|ÓM7]tÊÙ¨…èP>E3½[,ÝYÇø¤hQ•4 úC v!ˆtú@„¨(½±¶ˆ@cÛ}ÄU>"J?ÓwØ%EÏh7ëö°¹{î¹§õÞAŒÁÛÌn²’xi£¡l %™¶$‡ˆîׯ_az`%dcR>MœgW5kð˜èÁ¾XëG4«(Rœµ3ÚLÛy."zó)šØ+÷9“Gˆ(ì ÜõÙha‰Á(e¤!ó6Ëð¼Më6ÓõË ¼"»£ßÜS<£Ù†¬™¬æW.E“óùÀµ¹IÍLOH·åy³|Š&k0±ƒdsœGZ:Ûd'»ÉŠo$x0V! H@…^à1;NZÑ%Ö›‘ªˆã’vœäÿ8ÉÙeý ä©z¬ËÂ1dÝ k‹p–p¾‰°à°2ËÌ®xyÁ‡à!âB gAÅZvk«F<Ô*ðp pf˜UÇ1%RBÚZ‘cGD§ç ˆ<úSÅÆ4”j"xœOª)¡8’¬}I¥‡°ÆYÍo²B”g‹ÒV•×Å™DÔAdüp ™‰gÝTµõb/8ª0Ç&²é¼\#+ðHÕÅÑeƒÖTÝÁñãÜ´3+çÐÖH± ÑÆG5­+b3œ@v€%‚F$t¼ìŽ„i Rj2‚ŒÉ î ~Š6Y)'ðX[Ä.¤•ÒO~°1ƈ8‘ ¢sØw6ZBÄœu„"‡ô'_êa‹¤C§qÄ)¤²y÷.ß±Ö 'B[¼iMaŠ® øÙà‚qƒ;»M–x< XwEº#b‚È;kç’ÀãúÜ[¬%£D§¸'S)gƒDëx¾™L!ao¤ùr]žMˆ.ì‡(Ï©|áYuÇwD{ãxúŽ“þKÊ ÿ"æˆö#4øŽçv•ÞÔSO^xḻ#ÏlN”r×ϾˆœõŸD‹I»$jͳ'/ð¨Ÿè!må¹É˜vžvj¥>l‹ç9õÓŽRvÇÄ ¶;ÖÌå7?Jœjàq“Œ›¿0ö©`ÿÜCümaÒ€4kD+}ÂîIÕÅ6ü¤ëÃ噫ÀÎtý@€F†xˆ7ÖÝàøâPággV““C$Æef›qFÓkp&H-"*Àl4NŽbš}Åc¶™Y]¯¼À#bˆÇçi-UÚz¼-¢¥’àÂQæú´5»•=íN‹ý³¶Â1ôõC8Š8¼8¢éýZ•êK×"] Îy¾Ç5pt˜ÕÆ™C4Pov¬Úª‡àÈã#ªpr‰’‘jJ©¦^Æžµf¬¿"¥,»=;×È <êc#DÛ®ãu¬“ËŽ#2ʹD‰XO„SHäÑŽ° R‹“‰mS'ìŠ×%„É1Ô‰s‰ðɾ&tºrÁÂQpHS¡Ï8°ðã^à_l>¥½ÒVxâà§hGÑóª¶H¨¡…(`2‡¢ê£>Š4ˆ"*Œ3ߥ()̉!¤¹W™„¡/¤ –xôƒtE¸ Þ0\¶ÒOT\Ò€}éµ%Œ'Â6T²AúĤiˈt&BK„÷)¢! »ì¶þ‰1u2y…=Ñ®A;¦¢ÀôáG¤•±ÂÖ(² °;l“TÍz¢œ¤U“¢IZ9)¿ H@@3Pà5àh²Å8Q<œÄbG’+ÖÇY$ÐV¤•’^Fj)‘aR.YwÆzÅ¢5žDõH[f-Ñ¿¢µwmmKG:/½˜œ—ˆ§ÍiHsd dÚŒ¨=ýAh#øà°Øb‹UÕõ7ß|3ì¶ÛnñšnºéFé3»ª{$  H x5üä“OÂå—_¶Ùf›0ÑDųwÜqǰÞzë)ðjdyÝu×…±Ç;:lÕ–³Î:+üúë¯U <ÑÉ'Ÿ…ÏÜsÏ]m5 sÜÓO?žyæ™°óÎ;Ç6ýðÃaýõ×ï¯hìjÿ†È:7DW½Àûàƒâ$ÏE]&Ÿ|òQúÌ®³x9 H@@!%ð†ºtéÒpC¹Ùf›…-¶Ø¢]¯-}ëè¼¶ ä 'œFm´ª^[êhäs>ûì³°å–[ <&–]vÙFn~»ÛÖÙÇ?DàÝrË-áâ‹/n7Ój.@dmdØW¥^Š`×Áûî»ï†n`Ö­[·ØÝz<³«áæ1€$ QA ¡fŽ9æˆ)ywÞygØd“Mºë®žþùèØ¼÷Þ{aÊ)§ ›o¾yèÓ§OäÇñ§žz*Ì?ÿüaàÀáÛo¿ ½zõŠ):“M6Y+ãçž{.:Éï¾ûnüœÜòË/ßúý­·ÞHûñÇÃÌ3Ï‚¹æš+¼ýöÛá_ÿúW¬¿k×®ñólÁù&ÂBz3Æ?üpøã?b{H¡ëÙ³gk;_z饰øâ‹Çã¨c©¥– gœqF¸òÊ+ÃXcá¡ßÛn»mXz饇© >ã?~L½öÚkc¤‰âˆ%!üÄO„Ûn»-¼ñÆñ3"Y\‹¶SJõ3oŒ8^ûí·_¸÷Þ{cD‰z‰*;÷ÜsÇ~æœsΰÏ>û„ñÆ/žþÎ;ïD†/¼ðBøé§ŸÂ¼óÎÓ19—’¨ü>Ûl³…?ÿü3Üu×]Ãõg«­¶ Ÿ~úikÓX`˜*[©y§‘ß÷ßÿ0hРðä“OF§žG)¢E/¾øbèÞ½{XmµÕâ¸Â Ÿ¤êüüóÏÑHÆó¼óÎ üq˜bŠ)šk®VXa…aPxàa‚ &{ï½wüü±Ç‹}€Kªÿ / ¤• 0 ÚsJÅ{àÂñÇ?ÌõN:é¤0ûì³ú´ÝvÛ…!C†Žc\ò}ÊžøÅ_„óÏ??pP°¢ÒØQjÆ”¾sڵ馛†1Æ£Õvù{%šýÕW_…EY$ìºë®Ñvï»ï¾x,÷BêÌöÚk¯pÜqÇ…3Ï<3Þw¤Ë1<„à÷ý÷ßg¿Ù±+5þÙ~áÌcsp¥]ÓL3MlÏ€TÊõlž ðÇŽ7Ø`ƒÖs¹ç6pÚi§…W_}5Ú÷K,ï[xbÿ#Â.n¿ýöØ·sÎ9'ŽϧìýGü|ýõ×a–Yf 0ä™ZTʵ³ûªôLíß¿˜gžybRážâ÷”>¿Wþùçx>þøã1›h?ÏÞ8 5E³ÒýÌäãÈ3òË/¿,ùÌ.„⇀$ H áÞàÁƒ£“¸ñÆÇ5Hü~ÐAE±„ƒCuÊ)§Da4ÕTSE‡gŠãƒüqgÖ?9`ŒŽ ŽöÖ[oœ¹ÿüç?ñwÄÐ{ìŽ>úè0à 3œ>œ¶¼ÀCþþûï±Dß?þñØFDŽ+Î "‘Ääé§ŸÆgœØÎk®¹& ;êÂy¡ÐnZsÔÍlõÕW_Ý:ûœì AÄ.¹ä’Q€¼õÖ[áÔSO GuT”ô‡a… U Ñ€¸ ­åú™·eœ(„N:â‰'ž…4‚Œõpô޶$®Šm`œ`‚YpÁ£SJ)x8‰ŒëÌ„çÑ„2ëöHÝe—]¢H%šW®ÔS$ð.pŸo¾ùÂ7Þ… ?ô‘°ÓN;EÛà\œB„b[L"‡™¾ TèN$?+­´R¼ˆÄüºLÄ¢{ à¼"2q|ÓZJx"˜RÔ! <Ä>öŽÁ„‰'ž8 )ꦌsïÞ½Î?B/õ)/‚°KláÆù´a£6Š) ôÞÔ ljúé§N5Û¥~Ä+¢Ñ „kŠds }¤~l;1Ký„?ýEÔa‹ˆADcòïÿ;,´ÐB±®ìØ?}Î&w\“É„*íCÌRèO©¾ólac­µÖŠ}cÜëÜ[܇åž;‡rH 8ЧgŸ}6¬±Æq‚fDÙBáʆ{{å•WŽc@=¶†=1~÷ß9<ëfši¦a¸1©R®•ì«Ò3•Êjx<;o¾ù&>_YoÌD?Ùm+w?S7÷bJÏ,zf§ ¯üsÏß%  H@‘@à <œ,fÆSAœ±B¥‚Ó€ÁA)Z›’œKD ç"RpŽqRA@1Ó}ÅWDáBt†H ÇeK6‚—"‚«¯¾zt^’3TœaœK"Ö1£HàxÚIäŽM&œpÂÖ*§8ºˆK €($‚1_HœËTÒl8Ž~QAز9í%òUªŸùsq¢ˆü¥™vXœ¬ìfDf6\³¨À˜ â€R$ðØTˆJ!ªRp²qÜËm²’í#×)xÙ¾Àq“¢aô Áƒ½¤Hè 7Ü#Ljçd‡zhXtÑEcSFØãDô¦TÉÛbªoß¾áž{ ì„ &%Ïy{F„ ‚Š6YÁ¾'”©H}ʶÇ'ûK‘âô=×%‡í'ñôÚk¯E1†-"h<¸N*ŒÇ´ÓNÛ:.ˆx Œ³#Œ=Qw õ?òÈ#±®T˜¸!2Èx]5ãŸíkjûe—]ïår}ÇîŒk¾Tzî @˜ø`<³eDÚDô+EVnDê˜ar‰…X{Æ„C*ÜL0ño-íä*g_•ž©¨ZQbžÑé¹M{‹6YÉ?›ò÷sѽ˜f—¼aýB€$Ð 4¼ÀËîÉŒ.3Ô”ñâÿ8ÅD|pfŠßãø"ºp¾IÑÌgœNAc„‘4fðÓîkÕ<ÒÍ‹Y˜vâœåÀ*µID Î>í`vç1 ‰¬­ÁÉ"bGD„BÚfJ«$e‡“kÁ‚YûRýÌÛr^$%^â´Ù b• Ñ ¢)>úhŒÆáx9æ˜1rI)xù]Aóý)rðËõ‘zŠ^vÇIfô‰¶qÄ1ˆó‹˜ËÚ„ Îf‘ PcŽ€áD‚‰‚å 6ŒxA`.¼ðÂ1b‹“Ž3ŽÐaü‘ØNq-¯\Ÿ²í`BÎûî»ïpíã;¢˜Y‘M›áƒ’Æ\d»ŒË¤“NÚMŽ8“/D#‹˜MÂ6™ÔH!Éd ‘Á¢±«Fàa›D«w]Ò¬ÓŠåúÎs!/ˆhC5Ï"|+¢D<‹xn$á52ì‚vb?´¡Gtž{'+¨9æDwS9kåÚ™¿‡ò÷L¥g*\jxL"ñ& •R¯ZÛO×Qà wëû$  4'ðpœ/Ìòg kJ}ÌožvLÎ&Î*3íÙõQ¯¿þzŒ>à$±~Š‚SJŠ©nÏLxµópܳ…ˆëzˆ”Û(©ƒüpëšò©h\·”ÀKë\ˆ¢°ö¨%}&"Äš1ÄJv¿¨Ÿyo‹À£}¤F!hè b¾8™Eí¯ÔÎÉ;øÕô±ZGý¤"~‰$f£ÄÔ8%U·”Àã"(¼Ö€ñEH#æó…T:®ÀÃEd“Ú‡ÓÉZ*"¸)ŠÔ^—ú”·CÄkVÄ¥ï‹ßa?¤6Ž ‡"€)ù±«$ðà… `b¾R~Sô‘û©Tßé#cž¯Òs‡¶2‘ĺNÆ”ñ% ŸÖ£Žh» ~î-Ör·Eà•³ßR/ÙW5ÏTž¯ìd[í¼”ÆNä?•Z^‘í§ë(ð†{,ù$  4%ðàŽ“ÀZ»"Ǚ6˜Ò´ˆmÈF0ˆ*q.)Öue 3É|GD®È¹_{íµ£c˜ÞAFôEdbê©§Ž—úí·ßbŠŽgJÑ,õ+ÒÖulCÔ+­YËÛ]%AÄ&38}8¼iɱÇ#wù-®íg¾®ZBÇ:›^ÅÆ)DHÛ#ðòïΫ¦µ <ÚHÔ‘Í+Š„u9—¸±’qËGPøžtLDb[Ãá%—HBa‚Ã\dÏéºDx³›UrÀ³ãIÿ˜´À6R”)}O6}—æH .ëöFt¯œÀ«ôîDî/R\?"¢D“À+×wR_Iÿ-º7*=w²|¹×Ùø‰µpy±8¢ì‚ú³M&VˆlåS4Y¯†P.Wòí¬d_ÕÖj‘W®´£VÇ9}ôQ¼.Î$Î(u!Æp”‹l;c¬HÑ#%˜ GˆÍžp}Wkí€i3ì9»æ¨ÈNHÿÅþ±Mœe"~µ:¹8ÖôÛå¾à¡lºB߉(’eó ¾CP—²Ýz®Á+ÁË?é®Ù‚m3¡Ãf?\‡{ŽÈŸ!Î)åúÎvüðçœ~6÷Ië~K=wX³ˆ g7Vî)Î#Bĺ´‘iÙý$µûÁ^àÆŽ½<™¸È—Jí¬Æ¾Ê=S©Iv¼EL"ÂI'ÍŽ§)Ò‰MÒ^ž½|T·Ëú%  H@€$ Æ' Àk 1"еY¤Œ’ÊΓ H@€$  H@Õh:WmÇ=N€$  H@@³Pà5ÛˆÚ H@€$  H ÓPàuÚ¡·ã€$  H@@³Pà5ÛˆÚ H@€$  H ÓPàuÚ¡·ã€$  H@@³Pà5ÛˆÚ H@€$  H ÓPàuÚ¡·ã€$  H@@³Pà5ÛˆÚ H@€$  H Óhhwøá‡‡qÇ7ì¹çž£|€üñpÄG´¶£{÷îaöÙgÿüç?Ãl³ÍVUûvÞyçðÎ;ïÄcG}ô0ù䓇¥–Z*l°ÁaŒ1ƈŸŸ}öÙá­·Þ 'žxbU×lËAÕp]guÂ;ì–[n¹ðË/¿„í·ß>öuÅW,[åÛo¿_Ò~ñŇÉ&›¬ðØë¯¿>Ü}÷ݱ¯p°H@€$  H@õ! À«’cx×\sM<㫯¾ 7ß|sx衇Âgœ¦žzêŠWBàõîÝ;l¸á†Q4 <8œsÎ9aÁ »ï¾{à ¼¿ÿþ;œ|òÉQìÍ=÷ÜíxO?ýtxæ™g<,€$  H@@ý(ðªd™Þí·ßFm´xÂg«­¶ ýúõ ›nºiÅ+!h^xá°ùæ›·{Ï=÷„SO=5ÜtÓM1Š×ˆ¼ŠËPM¯–ëy¬$  H@€$P=†xã?~˜qÆõ×^£^Ë,³LØqÇC—.]b/Ÿxâ‰pÛm·…7Þx#~F”iÛm·ÿGH­¾úêaÝu×m%rË-·„[o½5\pÁñ³çŸ>¦¾÷Þ{aÊ)§ŒçôéÓg8‚EƒöÛo¿xÞ\sÍ#yW^yek¬±Zà&›lÛ³ôÒKLjU^à½üòËaŸ}ö W\qE˜h¢‰Ê øàƒ0`À€°÷Þ{‡üãQÄá¬gÅ"!‚ ể:(®5[`«¯¾EBmª©¦k©Þ[lVZi¥("ˆ$Ä\ |p¸úê«C·nÝ Bú ¬G+ÁC! ¨‡úèÿ 'œ>ÿüópÚi§Å:«x¤I®¿þú±­Ÿ|òI\ó·ì²Ë†í¶Û.^§œÀCLï±Çáè£3Ì0CäˆÏ üðÃðÙgŸÅ ‰r¼Úqïzª$  H@€†#ÐðrÈ!­ '%’>'Eåož@‚ÓIDATÀ “L2Ièß¿Œöì²Ë.1RÃgD¼Ggžyf˜nºé¢$:ˆ(Kå°Ã‹bG=[ò(*ëèØPñõî»ïFáC9묳bûï¿ü=Áû믿Âk¯½ꢋ.Úº­’ÀËF¾¸fŠ˜!¢èKµÁ™"z\ñ‚ðb!ÑÏr¨é1Çëšx≇á”Úsú駇™fš)~÷ÀD‘M‹R$ðòíAh"8g„=8Ú8ÁÄk0ÎóÌ3ÏpãTdpF0^vÙe±½ô›Í{=ôÐá/õ"“MföÚk¯Ð·oßx¢ÁÉæ;Zl†±^b‰%†¹n9^…F쇀$  H@h#†xù]4nDì¶ÜrËØeÒ6ï½÷Þ¸i‘~ø! ¦8 ~P ¢B´†ô?R4QD¿V^yåxL=Zñýúë¯1ŠÄ®‘Ù’é—œûûï¿·ŠC"W¢@ÔGÊ'B‚Q&ÚCAà‘jH¤Ž4Ã1Ç3,¿üòa›m¶©jͼ0âš´—¾Ñ_„Eµ/ÏBê&”ÅrTJR0I%Hä ÁL)ZƒÇØ Òx¤€ ¼rã +¢¬DÃØ †q&ʆ[d‘E MŸ lî¿ÿþ(¢‰Ð‘JʸÓNR'_ì"_J}—DbÞ^¸.ÑÄÅ[,ÚQc6Í!Ý“è#¥¯6Þ·ž& H@€$ BRà¹!’÷ý÷ßÇõgD®ÖXc¸&ŠH‚€…µmt’TFÒ%ç˜cްÑFE‘†ŽPdýY¶ 6X£–-Ià‘îGº!¢0+ Ó±Dpæ›o¾øC}DùÒ«xÔ CèL8á„ý& Ö‚–(‘GÄÎE]^|ñÅŠkðò‚*õTѱÇ»¬ÀK}EÌ!ÚˆÐí´ÓNQ0•x Dmµ/3õÑ.:‚–¶#*âE…ëíC`³æ‘h)éŸDT§Ÿ~úh+Dm‹^ùPê;Òw‰Þa_¬•Ì¢Šiíâ×__AöœSŽ—Ï% H@€$  Ô“@‡x¤G±Â™ïÚµkärì±ÇƈIx¬/c“Žã_›¢PÒZª¬^ n©MVòÇ?òÈ#QÔ±Q 除¦R´ÉJþüZÞÃ?Ó%Sú!8væL›È dIEeC•ì&+yGŠgÚ$„6UÚd%Ûîn¸! šË/¿|„<Æ‹¸Î§„ÂŽ5Šˆ5 ‘U¢¤Ià±~µyÉF²×(õÝO?ý'ˆÈu­TRZ)ãì-“åUé:~/ H@€$ ZthÇFˆÖR!`W잸ÐB ã¼#²ˆºü÷¿ÿ›´¤òì³ÏÆÔAÖV±Þë·ß~ >úh\WE¤)[ªxD‹Ø„‰æ‘NZOdž¤)’ŽIê!%RÓËàé‘C„»["¼H/$4+ðˆH½äs„Â’ôW8P`B”ŒºòkòXHÿX‹F´õvì*yüñÇG]NúÄ{óDXÓn¥yƒ'šÈ.–DíwDqÙè$ <6˜Áfè#¯¸ ê†í¶Éf/¥¾CDóš ®ÏûáÏu‰È“H&»¶²Þ“ÍX¼ˆ}w)^ìâJj,uÒ7‹$  H@€ÚC C <6¹@ð°ÖŠ´IR.ù—ÍU²ÑDÑ¢8…lÁÙÇ gsv‚dýÂ'Ÿ~Y­ÀãÚl¶Â ÐÙÜ%Eù¼<ÒyÑKÒd´7¥±c™|0î܉ØL»Dfi­ì¾‰°!Å0íþ˜ØÀ•ôKúÂ2Ùˆçñ9ÆCtTJÄǨM„ù AƒâšËTØØ„µxô%[„/;Y’¶‹p#2Çg¤hRO"¿¬‰Dü!Lk2K}[léŽ;îˆLÙ!4­ d,oÚHÚì´ÓNí ÑVŽ]aå¤õZ$  H@€$Ð -ðÚÓ±Qy.)‘DƲ/4¯G{<6qéLåÉ'ŸŒÑ06vI‘Uv1E±ãivÔÎÄžJ@€$  H O@WG›H1"C¬ù#"XÏR´‹f=¯ß¨×¢ß¤Fò:Ö³‘îHä‹÷ê‘Všv)mÔöÛ. H@€$  Œ, ¼:‘f}/=g- éŠ#"Ý®³ <^IAÔ’×-°Ù ´L6Ráõ H@€$  HàxZ‚$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€xÚ€$  H@€$ &! Àk’´€$  H@€ZàøÀpäÀ7Â÷¿þB ]»t sNÞ#œ²Î÷ñüïZ6Sé>Öè¡kËK1‚W½Qz¤$  H@€$Ð6Nà½vÀraöÉ{„åÎx4Ü÷Æậ ëÎ7U8äŽ×Â_o¥øjËqs´÷ÂGß…•Îz4|öÃo¡÷Ôã‡[·[,L=ÁØÃ¼õZο¶å:”®ù¿–ÔÏwÃ8cŒ[yö°w¿Yxm³MÏ’€$  H@€j$Щ‚ áFºeÏýn ¿ÿ54l¾ð´1ò¥O¾ ó¸?âCÈ=¿O¿øÿ¥O}8 òU+Ö¢MVRôî¡–ãþÑr|*¾&¡Fkôp H@€$  H ]:•À;tÅÙcTí¦? k_ðd7i÷±ÂgG­S)g=b`xë˟½§Wo¹pü¾Û7‡ßþü»¬À|àra¶Éz„÷¾ö¿íU^»LÒ“%  H@€$ ¶èTïåý— sM1^IV´ˆ³cZDÚ¦ M.Ýô›£Œ³çÍá—?Ê ¼·Y!Ì8ñ¸á¸ûÞ û¶¬ëKÅ^[ÍÒó$  H@€$ ¶è4oÖIº‡7Z^@ù¢e=ݧßÿÚÊkÞ©ÆÿþÃoÃÇ?–œiâðÐnKÆÏúœ4(<ùÞ[-JÑ|`—%ÂÒ³Lþ<¬xÖc ¼¶X¢çH@€$  H@í&ÐiÞË÷ G®:gx®EÄ-Ø"â²eáé& Oí¹tühÆÃˆ¿OŽ\9L4Θᑷ¿ kž÷Dø¶egL^pÅf ‡-»iž2hHèãKñœ½ûÍŽ[cî¸Ãæ†?®ý¿Ã$ÝÇ go0X{Þ)ã1î¢Ùn[õ€$  H@@Fàý_˦)óµlžòﻇÃZ~²…7°oÒc…}ny9ÿ[aûÅghh½ãa-oIhIÓü+Œ;æh1úÇqY×½åóç÷ífi‰R~øõÏ(¿úñ÷0Ѹc¶¼.Aç( H@€$  ŒxBàÍØsÜðö¡+Dš Ÿð`x¦å%äùrIËNš›µì¨ùôûß„EN|0~½É‚Ó„}—ëHïüà›_ÂÑ÷¼ßmwþ?çî=xSŒ×­%Š7WK”oŠ0æè]Ãݯ}v½þÅðÔ^KÇ×*ÁñÆl €$  H@èì:…ÀkæAþëä5›¹{öM€$  H@¨€¯Xx¨¯GÅ6I@€$  H`ÔPàîu«UW7”^H€$  H@ž€¯ƒ¡¯ƒ Í—€$  H@@ (ðêsT\J7*¨[§$  H@€“€¯1Ç¥êV)ðªFå€$  H@hz ¼>Ä ¼>€6_€$  H@u$ À«#ÌQq)Þ¨ n€$  H@hL ¼Æ—ª[¥À«•J@€$  H é (ð:ø+ð:øÚ| H@€$  Ô‘€¯Ž0GÅ¥x£‚ºuJ@€$  H 1 (ðs\ªn•¯jT( H@€$ ¦' ÀëàC¬Àëàhó%  H@€$PG+ðv»9„.uìi³]jèÐ0ÞØc„o¬Úl=³?€$  H@@ 4¬Àë¹ÿáÛ_þhc·šÿ´Ù'릚`ì0pÇÅš¿³öP€$  H@¨Š@à ¼Óz;ô¿é¥ÿubhËOÃyÿc1´E—0^·1Â;ô ‹N?QUíA€$  H@@óhXúS ‡ÝýzøþW"y <˜tmwsO9^8uíyBß™z6¿…ÚC H@€$  H j -ðªî…J@€$  H@@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@Pài€$  H@€š„€¯IÒnH@€$  H@h“À›$  H YôèÑ#üðÃÍÒû! H@½{÷ŽB—¿þúk¨l$  H@€$  H ãPàuü1´€$  H@€"ž†  H@€$  H I(ðšd í†$  H@€$ ž6  H@€$  H Iü?ËnÐÓî¨IEND®B`‚astral-sh-uv-9252ba6/docs/assets/pypi-with-trusted-publisher.png000066400000000000000000000611141521221614700247550ustar00rootroot00000000000000‰PNG  IHDR‘Üéü% IDATx^íœTUÆ_º»én¥E ZJ$”FºC¥AAPPQ éA éîîÎï<‡ïŒw‡™ÝÙ™ÙÝ™Ùç|?,;÷žøŸ3|÷¹Ï{ÞåÉ“'Ï„…H€H€H€H€H€H€\ …"ÒJ¼„H€H€H€H€H€H@ ˆäB      p™E¤Ë¨x! E$× €Ë("]FÅ I€H€H€H€H€H€("¹H€H€H€H€H€H€\&@é2*^H$@$@$@$@$@$@É5@$@$@$@$@$@$à2ŠH—QñB      ŠH®      — PDºŒŠ’ PDr ¸L€"ÒeT¼H€H€H€H€H€H€"’k€H€H€H€H€H€HÀe‘.£â…$@$@$@$@$@$@‘\$@$@$@$@$@$@. ˆt/$      ˆä      p™E¤Ë¨x! E$× €Ë("]FÅ I l |ðÁrîÜ9ÝÈ´iÓ$mÚ´.5xéÒ%iܸ±¾6Y²d2gÎÛ}wîܑʡC‡¤|ùòÒ±cG—ê´¿(¸6ܪ7‘ ø-¯‰È-ZÈéÓ§5ˆ„ Êwß}'Ñ¢Esfòäɲ`ÁÛgŸþ¹äÎÛo!²ã¾Kà“O>‘-[¶Hš4idúôé¾ÛQÕ³°‘kÖ¬‘#FØÆýÍ7ßHÊ”)CÍ"2ÔÈx ,0‘ 5hÐ yå•W^÷ìÙ3yÿý÷åÊ•+‘»¬|g`ü±ìß¿?ÒŠÈcÇŽI»víäéÓ§Ú¥„Ž3f¨'ˆ"2ÔÈx ,0‘ëÞ½û àvîÜ)={ö ò{:‘»¾"|`M›6• .DZ‰ 8yò¤=zT *$‰'vkN("ÝÂÆ›H€H€H€H x]D&H@nݺ%±cÇ–yóæI¬X±‚€7nœ,]ºTÌuø"2 ×–O ªFòàÁ¿‘p ‚ëJ gm#EŠ2kÖ,WºÆkH€H€H€H€€×EäË/¿,þù§FDZ\¹r6l?–† j‘‰P×mÛ¶éωÈ'NÈêÕ«eÇŽÚIºÿ¾¤J•J×W·n]‰#FéÀµ#GŽÔ¿›8q¢¤K—NæÏŸ¯ëÀý­¥K—–fÍšIœ8q^˜Êж‡ "¸råJYµj•vznß¾íp‰`¬ï5×ýöÛo²nÝ:9þ¼îO¶lÙ‚§hÑ¢¡ZfÇ—Å‹ËöíÛúïyòä‘÷Þ{O²gÏn«ë­·ÞÒýEY´hQýªGŽÑ!(Y²d‘ &Øî³rýúë¯bæÌ™ºï7nÜЉZªT©"cÆŒ‘åË—K¢D‰ô˃3gÎèä0ÿüóÜ»w/H¢˜ÐŒß*^Z·n-ÕªUìóûõ×_cǾۂ JóæÍƒ$¢±Ž×Pˆ ŒÅY1ûK–,)}ûö•%K–Èï¿ÿ.§N’øñãKÞ¼y5ãL™2©bîܹ6U¿~}iÒ¤IÏÍMü²_¿~RªT)ÛçÖ=‘èÖ>æãD›ùòåÓßû6ƒ‘! L|7Ðg¬Ÿ«W¯êµ˜5kV©P¡‚þ/zôèºÖzÒ§O/S¦L‘7ê½ÍX?ÎæÁ:ø³gÏÊÏ?ÿ,ýõ—n+I’$R @ý}Î!ƒÓïspë7á;ˆSÝÅ¿1É“'×ߣêÕ« úÊB$@$@$@$à]^‘x(†€€(+Q¢„ 0ÀÖã­[·êL‘(xFò{9zôhý`è¬àazøðá5jTÛ%V±Ó²eK}?„}Aÿððn-î´÷èÑ# ÀqHÅ*"ñ  A‰‡wGBâÝwß ©Jýù?þ(H”òäÉ“®›±cÇÚ„¤7DäàÁƒµ@:pà€­=NO#"Ñ.ú»té"7oÞÔ×AÀÿðÃúçÐŽß*^Þ|óM-Ôׯ_ÿÂx!Ñ®yAà- A‚ÐÇ¿Ð&;Ö@þüùmŸyKDæÌ™3gÓ€£6Ý‘Ę'¼ÔqTðBãC±¶‘1cFkÙ²e!΃¹s†ï9\aû‚1áß „Ûšbý>·î°þðRÄQ°Å¿ü,$@$@$@$@Þ#àu ' ¢.„"D èÖ®]«]<ô£Ø‹H¸p,àT/^\;xˆ]±b…@¼¡tèÐAªV­êð¡Óü2GŽÚU9xð vKL™4iR7ÇöÐ?܇' ÙiÑ_dýꫯ´ëW©R%í†Ä‹OR§N­ÖÛ¶m«Ç‚‚±A`BlÁéÂïíÅŸ³©Æƒ³Õ1DðŽ6àág$•1Å"^`>!œàúàá}6"í™ë0f¸¡xàãÎø­âÅŒÙE .¬Ý,ãzã³6mÚÈ;ï¼£/ƒzñâEÝ/”¤I“jÁd úoïf[Y[]AükN6;œÐË—/ë˱6‘mØ$«ñ–ˆ ®MŒmšPqwEä!CdÆ zp_}õUÁ‘ »víÒNó§Ÿ~ªÝOwç÷îÝ»Wï;ü›ç/ð² Žüǵ{ˆï%ŠUD:[wØë wß=s‚?Q/.ÖŸYŸÖ¹åÏ$@$@$@$@žðºˆÄžy`7bÂû–jÂQ¿ýö[Ý{{ ·iÓ¦MúÁÖú o}°,R¤ˆÀ¡0ÅúD Â3È„ kß¾½Í™„{ûí·m÷ºÓ^Ÿ>}t Â;!&L1çpÅRkS#Vq]Íš5Ž©)xHGH&¨ÑopsVð PJãêàá¹Aƒ¶ËÁmZ³pzCD¢ˆ/<˜C[‹UDâ÷µjÕ’?ü0ˆ[ìÎøíÅ Ø@8˜uÐå… ê®”-[Vz÷îmëœ6Ã8´G|XE$Æ‚¿›#k®]»¦û€p^”^½zi†â-R›ÖPqwE¤#Ä(ÂR£D‰bc‡µeÝÏìÉ<`-ãœJ¬D(˜‚æ{Ü£Gyíµ×ôGÖï³³ug½ÆÞÁÇw/9ÜÉDëô‹ÇH€H€H€H€4¯‹H¼ýGRìÃ'²aÆéÄ¡C‡êFá ÁÍÀBq%±„Eˆ7ûäÖJì›´Ï‹ýy¦=G{Õì×CHíYE$„ƒ5ë%Æ×å—_~Ñã8ÚíÂu„Á¾Aìs³–nݺɞ={ô>.ãr:Z§p-¿üòKýñãÇŽîñ†ˆ„KÁŒ}öÅ*"ЉPZkqwüVñ7Ѽx0uÉìß¿¿þ+p„&›â-‰µ“6mÚ ã™:uªüôÓOúwo¼ñ†Íõõ–ˆ ©M¸Ü;wÖí»+"áîÞ½[×ÁÎÎvuw°ŸÓ¸…ˆ øâ‹/‚p„Ѓ[Ñg}ybý>;[wÖLÏøà;o¿·ÒÑw¿#   ðŒ€×E$ÂÒfÏž-3fÌÐ!­pÄÒŠÐUÇ—^zI‡{âïX(ŽD$.7oÞ¬É}ûöÙB@Íp!ÌÌýøõ¡ÓÑñ"Ø?h›¯]»¶vý¬%´í!‘2Í¢ÀƒóŠ>ÁD Ü@k’„^Â=t¥Ä×&P]oußPg£FB¬Ö"n\7GÅ*"á£=kqwü!%†ÁþÌN:é¦:kæK‰µûL·ku@ÃRD:kÓ]‰¾Ã‰Ä÷š, a­˜k¼ 0‰ŠÜ|GñÒÕR¦L[¤AHßgSÂnñÒ YlñâÆD* „ÉXH€H€H€H€¼K ÌD$º‰‡8dµ„û‡SL„%:‘Øo‡}¸â¡{8ÂÍìeô–ˆt·=ŒÏŒ!tÈwïÞˆh$}Á¾Oë9öNœÉzéh:áD—QÒš‘.¤+§UD"™‰9ºí»zć'"ÒÝñ»+^0®°t"­N4îÀÑC±ŠÈzõêIÓ¦MƒL±«G|8 gµ¶i\žˆHÓ9$¿AýH|WÅ:.wçÁêDaçìŸ0ì+FæWWE¤© ÿ^ #3B½‘pN$^nXºñî?Ÿ¬H€H€H€"'0‘HÖ,’¦XÃþœ‰H«sQ§N½WËdwô¶ˆt·=88ØSûMZgË!ƒæ,I쩃³êN±öÙÞ}sVΠ4‰x°¯BÞ”ÿýW»Å(Á鉈twügpâ\Pû=´!q·&Öq$è¬n0’!¹ŠÕñv” ‰FŠÜ9‘!µ‰#rÞÿ}]7D¤áá7x¹‚‚µ gÜÝy°¾ 0gŒZæq6¡‘¦¬3°Cöddëu5 ¤5ÁÏI€H€H€H€žS‰Q8efÏÇ ã*Š3‰sñàŠbB_ÍdY39zˉt·=1¸ª[E‚WŒ±. œF¸Rp­ަ£Ä5Ökì¹Ús‚“túôiÉœ9³í6ˆqöŽ‚ý›+VÔ?㨠ˆ”°‘¨Ûñ»+^ÐŽƒAÒ8ØfonHl 0«ˆÄ>=ì×3É ´ÌºÆþ^ã Y÷-b_!>3®¯ýK•àD¤}Sû6ñrÆ$‘¹råŠm_¬}ò¡àøÁ}D§„ YƒÖ¤Qø~àHOæaíoGÁË d̵f‚ÅzÅ kÆ_WD$æû!‹+¤ÿ—ÉmM@ä"þ…H€H€H€HÀma*"Ñ+<(ÿõ×_Zl!ÔÏœçLDbá¨Q£ô€ °àæÀåÃÙuæH |æ-én{ƒE(8ï"ÃŒ ‚™Wq܉ÕñCH'ö™—Iˆ ªÆ1N˃@))ö!a‘)pyq¦Æqö²À‚1ÂkQ¬{Á®T©RZXmݺU‡áâgˆ¢°‘îŒßñ‚q#éŽÙ±pS!Ôá [°°ÿÙŸ ¾¹rå6s^©½H±? ¡Ü¸áÜû÷ï·q6ß Ìƒ)ömB!£©}›Öl°¸ÇÂ@œ± ÷ÕdZuÆ¢çˆba Ø×‰õ WÚ¼ÄA¦e¸zXžÌœWSôî9ÂqñýÆ‹ ìïE¸õOWD¤YÓ Љt>§ ½ÌÞië ·ÿ•ä$@$@$@$@A„¹ˆÄùppQʆGSœ‰Hˆ -8iöÅì‹Dˆœ·D¤»íáa›W pAGA"$dÁC¹³‡2¤„ |±H(â¨Àéó фр=ªFü˜{pœ"ÎŽï°‘îŒßñ‚öáÙrí ’A:+VA‡Àö⎡½›5d9‘oÝv´FpdœQû6q¼…5K)¾_x‰áŒÜ:¬CóBþ¯ø~ 4H‹;Oçûq, ¯£A?kÖ,›+’ˆbŽÒqT'¾{ ñ…L°_`~H$@$@$@$ð0‘Θ—¶8ŽgÂ%CØ^•*Ut’¸tÈêê-‰þ¹ÓD$öÅÁ…ÛwË$$±3\„Œš‚{‘ÄŽá¹sç´{ˆ: J0Îܹs»¼TÑþï¿ÿ®].Œ¡‰È@ Ñm-ØÇ‰s‘ì!–pŸàÈÁÁÄÃ6\ɰ‘èOhÆï©xA{p›±G!»8|ë oÀÉY±ŠH¬¹åË— ’Ä`ž¦ 7ûs±í æa ¸Ž0^ @Ì#Ù’¾tèÐAßâLDb  ýï¿ÿ^»tÖ6±ÇϚɴýŸpæ°à.¢nÀàøÝ»wO'¢ÐÆyŽxÁ€—àòî»ï9sÑó€.xéÇýâÅ‹ºëØ\´hQý݆#oJH"Ò\g!RBbâ¼qÔ‡+aæ.Ùx! €&à5ÙxbO\Ë•+§9·_ìßÄC= Ž!ÞXü‡€UDbî¬ÇFÁž’ €÷ PDºÁŽ ’Ù X“Y«²i‡ç^²ø²³úÏHØS    ð.ŠH7x"¼°~ýúúN„6¶mÛVòçϯCoݺ%Ø ÷ ¢¸²ÇÑnð–0$@†pY5 €_ ˆtsú°góÇ r7öÛY‡`’™ã4ÜlŠ·EŠÈ€Î&I€H€H€H€ü‚E¤Ó´mÛ6AÖI!a²\â8$n)T¨>žÄÕs =èo ‘a•U’ ŠÈ€˜F‚H€H€H€H€H€‡Edøpf+$@$@$@$@$@$("b9     ‘áÙ­ @@ ˆ ˆiä H€H€H€H€H€H |PD†g¶B$@$@$@$@$@A€"2 ¦‘ƒ      ð!@>œÙ ŠÈ€˜F‚H€H€H€H€H€‡Edøpf+$@$@$@$@$@$("b9     ‘áÙ­ @@ ˆ ˆiä H€H€H€H€H€H |PD†g¶B$@$@$@$@$@A€"2 ¦‘ƒ      ð!@>œÙ ŠÈ€˜F‚H€H€H€H€H€‡Edøpf+$@$@$@$@$@$("b9     ‘áÙ­ @@ ˆ ˆiä H€H€H€H€H€H |PD†g¶B$@$@$@$@$@A€"2 ¦‘ƒ      ð!@>œÙ ŠÈ€˜F‚H€H€H€H€H€‡Edøpf+$@$@$@$@$@$("b9     ‘áÙ­ @@ ˆ ˆiä H€H€H€H€H€H |PD†g¶B$@$@$@$@$@A€"2 ¦‘ƒ      ð!@>œÙ ŠÈ€˜F‚H€H€H€H€H€‡Edøpf+$@$@$@$@$@$("b9     ‘áÙ­ @@ ˆ ˆiä H€H€H€H€H€H |PD†g¶B$@$@$@$@$@A€"2 ¦‘ƒ      ð!@>œÙ ŠÈ€˜F‚H€H€H€H€H€‡Edøpf+$@$@$@$@$@$("b9     ‘áÙ­ @@ ˆ ˆiä H€H€H€H€H€H |ø•ˆ¬Q£†sçÎéö»té"+Vt©/‘.aŠÐ‹ì×^‚ $EŠR´hQ©R¥Š¤M›Öíþ;V–-[&É’%“9sæ¸U³µšuíVü‰H€H€H€H€¼H ÌE¤}_sçÎ-Æ “˜1c†z¡yØv&ú¾ÿþ{-R¥J¥û‘}4=<wïÞÝmél š5k&uëÖÕ‡FDž={V ‘´Ç¾˜¤<Öú°×BÓZ&L¨ö˜0]ˆQ8±ö¥S§NR¹reýk#"“$I"‰'–cÇŽéß¡XË-lGãLŒÝ¸qC6l(pwáRþøãzŸ.ä!CdãÆ/t /Æ/+W®Ô:*‘2d¬»'Ož¼p ^>|õÕW¶½˜Î^`8ê÷ÚµkeøðáÛ1b„[njØÒgí$@$@$@$@‘…@˜‹H<\# ‰I<¨¹¶lÙRj֬鑈„Ó1zäÈ›k‰‡ö¹sçJœ8qB%"á@B¢4jÔHJ–,)‡Ö®$R¸ªV‰ëàVeÉ’E.\(wîÜÑ÷öë×OJ•*¥Cx!,àRÆŽ[ÿœ.]:ít"´n~Ž#†MDšWµjUín! êgq@pa¡-Z´ÐN#ʤI“´«'.#Ê+¯¼¢“>!éÒäÉ“µÀüðÃåµ×^ÓëÖ¸èxYУG}\F¼P€£p˜ñ"`ÕªU²cÇ}MµjÕ´KÙ³gOÙ¹s§¾oàÀº^Ô —óÝwßu ¯$    /3鬟S/„Ðs7œ¢î×_•èÑ£ëf°ÇòèÑ£¶n¼Ð8‘Ök! úì3yZ¯yë­·¤]»vº=ë°oBƺkuG÷ìÙ£÷ê¡7É*,°Ocaq@p"îï‚°7ë¢É–LÖà^½zÉ?ÿü# Ô{(oß¾mn޲³â¥Ö"œN„0ïÚµËæ^âe³QB#"­×âÅBµ!`YH€H€H€H€H ¢ „›ˆÄC6ö66oÞÜv6£'"ÒzNäçŸ.+V¬Ð,Û·o¯¥ÐˆH„¦Âù1Bõ`ã{ï½gÛçè¬>G¿GΙ3g;·p³àrYÅÃ=û:'"á*Â-DÓˆpUì£Å^Cg/<¦N¢ˆD½pÀ·lÙbs¥M¦ŽÐŠH¸Ÿÿ†€E‰õ‚5‰3NYH€H€H€H€H ¢„™ˆÄÞ3$ü‰?$•ûh-“£A IDATÞ‘_kÖ¬ÑU›³C#"q§,X°@~ûí7Á:S°¯±téÒNE©£væÍ›'ß|óM°sjB_¹Sµ ü¹]g"YZ!¾ŒÞH„‡$"NŒp×àœH$Õˉõƒðä%JèPYs–$ßôéÓuÛ¡q"q=Bk±–°¶ØÅ^Nô GÔ° @D3iŸÕÑà¹Ô³®g+ö›H€H€H€HÀ D¨ˆD*BQQ8‚ ‰g°ÍG"Ÿáa hpÆü¡/Gˆ¢ŽëÖ­ÓûÙPp. ä ´Ð‘hÃ!òp‹àLÁ}ºråŠ 4H'WAÂü Ñýëׯëö±×±X±búgŒ a‰º(‘Þûò8:'rÆ ¶½©=z´äÈ‘C7 ç¡­(Ø‹ãWð'@ìmÄË S0Ÿ—.]ÒmР®Dz`½™ãh°~±rÙ²e¶— ®ˆHkÒŽ Ñ[©R%Ý'ì³5ÉŸÐ/ô•…H€H€H€H€"‚@„ŠH„ "iˆýÑyòä±ïHD::bîŸ9«"û/M©W¯ž>·Ï^´åÍ›Wïvû‚PÜO>ùD ÀЈHԃ̚hËѱ!Ä E¤÷–½‘ŽjDÒ„BÀ›—ó‹ós$>³fð5×bm"„/¬ëá²p]‘ÖplÜ a —ÔÑ~M$×ÁY¢I“&õ8ÖD$@$@$@$@¡ ¡"ýÄ<’—ÀýÁ>/ý‡Ñ¸@ŽD$ŽÁyŒ+„ÛW‡ÃCœY Žp@„V©REgTu$Úð9Â_‘ý€à€Ó„£ΊZ‰{p¤’ìàhô‡×clp‘°o…"2«5„KíEdܸquˆ*^ £®£„4’¿ÿþ»vq þ޹Á9žp!Q è¦M›&8¿/?p®'Ö\ÅŠõ1/X˜cûñÁè:qƤ+"{6qLŽ”ÁÚûâ‹/t{¨Îõµk×tfÖB… é—.ö™ƒ½G5‘ @ȼ."Cn’W ø+ŠH9ö›H€H€H€H€H€"€Ed@g“$@$@$@$@$@$à¯("ýuæØo     ˆ‘M’ €¿ ˆô×™c¿I€H€H€H€H€H PDFt6I$@$@$@$@$@þJ€"Ò_gŽý&      @ÐÙ$ ø+ŠH9ö›H€H€H€H€H€"€Ed@g“$@$@$@$@$@$à¯("ýuæØo     ˆ‘M’ €¿ ˆô×™c¿I€H€H€H€H€H PDFt6I$@$@$@$@$@þJ€"Ò_gŽý&      @ÐÙ$ ø+ŠH9ö›H€H€H€H€H€"€Ed@g“$@$@$@$@$@$à¯("ýuæØo     ˆ‘M’ €¿𪈼víš¿r`¿}€@’$It/¸Ž|`2ØðC1bÄGùaÏÙe ˆ&Àgˆž¶VÌÚövý^‘Þîë#     ð-‘¾5ì ø4ŠHŸžvŽH€H€H€H€H€|‹E¤oÍ{C$@$@$@$@$@>M€"Ò§§‡#     ß"@é[óÁÞ €O ˆôééaçH€H€H€H€H€HÀ·PDúÖ|°7$@$@$@$@$@$àÓ("}zzØ9     ð-‘¾5ì ø4ŠHŸžvŽH€H€H€H€H€|‹E¤oÍ{C$@$@$@$@$@>M€"Ò§§‡#     ß"@é[óÁÞ €O ˆôééaçH€H€H€H€H€HÀ·PDúÖ|°7$@$@$@$@$@$àÓ("}zzØ9     ð-‘¾5ì ø4ŠHŸžvŽH€H€H€H€H€|‹E¤oÍ{C$@$@$@$@$@>M€"Ò§§‡#     ß"@é[óÁÞ €O ˆôééaç‘Àúõëe̘1R°`A8p` Qú-Þ+ço>) {4¾Nߢ£ÖÈ‘þoHæ¤qC]×ëŽÈkÈÑo¸t¶HïJ9¤LÖd^CH {ƒÕã§Ï$Vç_å§^‘Ò†Ô¤GŸ»ÂÔþšQ«É´-'dWÏ×%F´(Á¶Ÿ´ç"S«€4y%£Ãë6¹,õgþ)K[—’iy4–@»Ù•¹ ´1s<$@$@C€"2b¸³U/ؾ}»ôéÓÇVk”(Q$A‚’+W.©W¯žäÉ“ÇË-º_ÝÔ©Så§Ÿ~’¸qãÊüùó}Õîß’%KäêÕ«’9sfiÚ´©)RD ŸM›6MÊ–-+Ñ¢EÔ AV¥Jiܸ±DU_wòäI™>}ºìÞ½[ÿ®X±bºäÉ“ëϯ\¹"_ýµüóÏ?òðáCí„â~8£¦dÉ’Éœ9s´óxëÖ­ T»uë&*TÛ·oË7ß|#7nˆÏœ9sJ“&MlΪ©«Aƒ3fLùù矵XN—.¬X±BkðàÁºî;wJÏž=µ FëÖ­ÓâºD‰Ò½{÷fõÒí‚0Ä»ÎÉéë÷$wª2©n!y9S}íƒÇO¥ÃüòËîsZ –Ï–BF(wèõ n‘)•s¥’%•<à®=tYjL+½~ûWŠdH,ó›½"“7—o¶”ýnI²x1eЛ¹m¡Œ®ˆHgýÈ$ŽnsâÆ£ºß/Ø­ûU&Kr™ý~QÝ–µ4Q·zþ4º9>[áp µ¦m•Ò/%“s7ïé~÷¯’KÚ•Í*ñº-”Å­JJ•Ü©t•{ÎÝ”‚ÃWË>•$[Šxrüê]ióÃNÙtô²$KêJ+«æÖ×8b…:Ư?*cÖ– ·îKñLIe‚rñr©9@¹yÿ±túy—𛳒4nLi]æ%é»h¯Ìkúr°á¬hCÞÉ«Åò¶×$Vô¨ÒíõÒþÕ,ºÞß÷]·¾Þ"wFV“Ø1ž¯õ ŽÊð•åä *úï`ú•úæ Žà™ë÷¥t–dòu½B’éÿ!ÇöbÆþïÎxĉM;‘£jä—§¯Ëœ¿NI‚XÑå³·Ô÷çÿ¢Ò~Màï寭—õ_Õsü÷Ék’9Y<™Þ°ˆUk EilùlÙ~µŽ Öµú«­<[ã…µo~±òÀEé­¸þ«æ3kòxºÕÔY¾ÿ‚T›ü‡üÓ£‚ž¬Á\ƒWÈǯe“å²Ê®³7däªC²úà%¹qÿ‘”ÏžBf4,ªæþùš|V¿Hzôû>ýýz;oj™T¯°|úû~™ýç)=7ƒß:î2_¬ÓõŒTßK¬› ‰ãÈç5óK…)lõÚ‡o»rG:þ¼[}÷.Iâ81¤M™,Ò£bý nœBá$@$@$ PDrG"NdëÖ­åÎ;òÑGIõêÕeìØ±²lÙ2íNBhA BN˜0A2eÊdx€!š2eJ9tèf±‰ÐØÓ§OKÇŽåîÝ»'Nyò䉊ãÇ×÷A¬A´%MšTR¤H¡ë@¨*„¬½ˆìСƒ9rD \\Ÿ8qb- *$;wÖŸAÐÆŽ[ý6l˜äÏŸßVÚ„XF x!q/®ýî»ïtŸ ŽÑvÅŠ¥K—.Ò«W/-rQ~ýõW-B­e© ?„HÁƒnÚD±¥Ë/{ä¨zpÝ×§¢¾ ÉS¶—%­JIܘÑäumíBé$‰zP­6å}ÏWï’h*6ûàðà‰zÀ/¬ÅÏÕ»¥µW_Î yÓ$”YžÔõêWI2&‰+®ˆHgýH• –n³Ï¢å%Â*Á÷ðÉSiøÍ_Ò²tf-L“—yà2=¶øJ´Kd]û²:Ñ“£*X){/Ö¼ðRžû€%ûdážsj­T°ós5w+”à ñÜ<¼°Pù  §îlÝ.W&Ï–{{ʳ{÷IÊÇD‰[âäÉ!ÉÛ4“¸E_üÿaë®ßt‡"Òo¦Š Ž€}bëµHZƒÏU«VɨQ£VSºtiéÛ·¯C÷Ûo¿é°O”E‹IóæÍåüùóR³fM.‹‚Q„œ"ã*\BÐüQ†}5jÔ7Þx¾/ÏU‰ ®Ë—/—lٲɗ_~©ï]¸p¡Àp&ñ3ÂWá0ÂiDˆªµ˜káJÂy|ï½÷$a„ڙÄý®¸n­ÃC7ÄÓµ{d— áƒkxF=ÜWŸú‡àa·yÉÌÒ²TfI¡B5Qœ‰HG™RO^»+SUæÎ5Ê;té¶®ï µ'®MÙ,/<8ãAûÐÅÛº 8tÁõÃÑ1„Ò®?rEßkJ×_vkw©Uéç{T‚+Qìè¶}“¸Î WëÝÛä%fù‘j£ÈFxÛ³2bu'Sn­)×î=TB¯¸v­f|öÓª¶ÏìEd7år}³õ¤íó=½^×.—#Ù]]»lÿEÙ©æÕUi?¯M¢Äß*œntH"28ö}4|—(_Y¹yΜHk_3ˆHpžþÇ í.WNdºDq¤ÝüTÈéMÙÖå5¹¨Ý|CWÙX5)žQFTË'Ñ?þEÿÎ:·>Öó‡Rˆé ÷(§/­|¯B³M¸[°óŒü´ó¬üyòºjã¾v¢/ ~S_âh]bdJG‹R3§ [”·T¨«3Wfq%lñ‚¾Þ*7kÁh^BdSkýoõ"¸y° †? KàîöÝr²ygQaI*T>ŠúŸ þÉ4ü ü?Cª®m$I£Úáß~¶H€“‡dÎúÇèÐÕ1bh· a©+W®ÔîŠt†œ½Ê•+;xÈš ! áA‰°Ô .8‘A…s‰÷×Ã9DÁÞGÜ몈üüóÏõ¾FWD¤Ù_i{ì§„pD¢víÚécEÞ|óMiß¾½KKd¬:a•«æÒ±ž<“œjßÄF>%"Qð°üûÞó2V9+;ÏÜßZ–T¡£I\‘p°°¯• /mT,£äI@^´L‡2¶u "/ß~¨®Ÿê¶£+GÙì1sÖg"ÎãæËÙ8`?Ûª¶e´ÃjŠ3'Ò^Db?\Ü®Áï‰DwT¶ÚyÛOË(åxÆ‹M–µ.­÷eÚ·c\½?TÿÒ(7×Z Ò'm:¦Ã‚Ïö\ Ø‹Hˆº»Jð˜’R…>"¬Ò‘ˆÄÞÊÍÇ®jAµLí‰DxkH{"íEä õr!i¯Åòkóòv¾Ô!ŠÈàx8‘fÏihD¤Ø¶J‡*ãEHqµFGÕȧC¦±v òL‰3ºÞ‡»Ë¯zO"¹­%¡ N¨^$ 4Ta³Ë•G¸´ÕÙÆËìEìûFNåN¦”ŸU¨w¯…ÿÊÅ!¡‘†©3YxÄjö›Rg«µ_ï••ˆŒ§BÍípkóÂÇÙºté ^D$ §Zv•;Û¶‹SåCµ'ξ²´ý–ªÐËBÒT¹böÇe¼£X[ë6BÖ•=‘Ö3ñb6¿WeTbì×Û¢Äd™/ÖÛB–Ñ6αDâ#kb{‰Ì¶8»ñÄÿCCr"­c²ç"‰ààNV{L]-p½³«d:3=ÏÖk_V©9®¯DäÆNå¤äçk•ƒXXêI§ÛAb&Ú‹û¦n9®B¹÷z]DÂ…ÇK—Yê;±kÏáãpC÷ôªâùœöóà*'^G‘ÀÁ2Õäé-DÈP<úÃZˆ+¦ i-(¾æÝõé>RDúôô°s®p”X¡ pßP°§0{öìÚ‰„#‰‚=Žpé°á§Ø³hD$>ÇÞI¸fo%Žá(^¼¸{:uÒ{át"!’ë@Èa$Ä$œ?$ªAöWˆ7´aĬ#'÷-^¼X÷ ‚Bû‘çèÑ£ú÷±bÅ’è~Áí,P €CWÓÊ .(2½¢ ³g϶ÿRb8YTøåpÚwøòm•Er¿ÎèiœH$©¤‡”Ë–\g E’Žº…Óé¬=•ë‚}ipqY; :¸`H2óƒ ¼¯ÄÜO¸6„ˆ< BW±ïlmû2R6ëóÌ·ö%¸~¸""!0p§_å\Aªv4g"‚k GSUÔèÕ‡e»Ê. ¡‡¤(í~Ü©ÇS@íaÜ~êšT´Y±)¥÷Ð9j§¯nH( |ò»íÄuéôZVí:æºR'&£8EWoUQǺ×4Ç*´ÒY@CH-’·¼”,®à¨D„6"Ëèu%`Ó«ý˜p…ácn >V‰X¼`À¾U$¨Á>S¬$¶AAæZd‹Å@Œß:WŽGXˆHìI\¢óiJèãE÷$QüàÎ:+peßž¼E¹‰¹Tš úÜS¬Ínrèû ©ï¸‘°láÚÿ«=R¬Ò©—Ⱦ[O » *tBBßNdµOmbÞ;«}É—Uòœ¿ÕžGdÓµç~VíýÅ÷øUµ†úUÎ)ñ•˺\‰_¼4@"žàæÁ9~B$`%°¿ÐëÊ€üÎçέ"ºˆ' æ*j‚ø’cãˆ÷€"ÒÏ'ÝNÀ‘ˆ„°C˜sçÎÙö*š#> 0/^¼¨…#„!â$I’Ä&Êþ Çn~ã9^}õ¿=t'NœÐîâ®]»´Ð,Z´¨>âŽ&ÚÀ^E„¢ž9sFàŠ–*UJ;£8^ш„ð„Û¸cÇ=$çA’„¤Îœ9SŸ+iŽø@_òæÍ«¯sT—ýšøàƒ4ëN\1Ü8b¡…Š8zGL@Ø•U.Â@Ί̞}ïS{µ®ëì¬uÔ^8d²ÄÃ,’Ž Cæ_긅’™“ÉŠ¶¥ŠHdgmöív]WZˆÐÙYjfe%H º ˜‰ôœz6IIìÇ\?\‘e”_§ JÿÙSq\Çñ+wuˆª9 m9jÏ#cÕõ]Eb™ÌJ„7z9£ôR- ÜÓvêxìYE6R}·1@e  IDBøü£BqožÔ e´ ëĦ Ëh5¯Øÿ×Deð¬–/vj­"Gµ¼œ1±žSôÙj‡ª£Cbþ?Á²øV¿QgW…Ck\°‘áy‡¬RfÏäžr¾Qb«¾b¬ÙßèèßR¸¾Ÿ*q¸[ÙðOW3ä<òõ¦ã:QÎë1#!TqåF¾¬2½bO#Ž&ARfdF­[8½Nôã ‰þÎPÂk a¬ÈÔj°í¹cLÕ‹ì‘]ä²Î&ŒäDx‰€ïppóàˆG$ð"-"YüŽ@®þÛ ïw÷‘SDúÈD°¾AÀˆ2ˆÁY³fùF§<èD,Bmá¦Nš4I‡Ø²üGIUJŽY'HÊ ÜRsæbd3ÆøŠJ>Ó¥|vnŠ‚—ß«ý©•°»<ä-í–úz1{"ªµjÎãôõ>³$PDúç,SDz>o‘ž3d DÀgÏ_†‹0VœO‰Xs6¤«}Ç!ä³T˜c „‰bÿW¸¨H¶¨cÅÆåƒ³Èã5óˆ1¿ªÂ “«c0º¿ž];ÀpÏ+§!Àû•K·E9ëpé|™Æó@q&^xœPßϰì/^2`° „L€"2dF¾xE¤ç³Bé9CÖ@ED^»vMg‚E¨-ÂpqŽ%öTºZ "­GD¸zŸ?]W.[2fyTZ—ɬÃüÖ¾âOÝU_1Öײ§°‰ÈL*6øýzJ IDATÇûŸˆ|>n®OÔžÞÃê¥ÁCµ71ÚY==5‘J…h_æùK'¦MDW¡¬aÙ_sBª¯/ŽÄ("ýsò)"=Ÿ7ŠHϲ    HH€"Ò?'"Òóy£ˆôœ!k    ˆ„("ýsÒ)"=Ÿ7ŠHϲ    HH€"Ò?'"Òóy£ˆôœ!k    ˆ„("ýsÒ)"=Ÿ7ŠHϲ    HH€"Ò?'"Òóy£ˆôœ!k    ˆ„("ýsÒ)"=Ÿ·‘£G–¿þúKÆŒ#©S§ö|DÔpéÒ%iܸ±¼ûî»òÁxPo%   t‘þ9ÑžÏ[˜‰Èýû÷Ë‚ dÏž=róæMI˜0¡-ZT6lD,vïÞ]þý÷_7nœdÍšUèÆR¿~}éÝ»·”-[6È(  ;wî”_~ùÅáèëÖ­«ë:th¨éPD†o    HK ,DdÂ*$ýˆ~A˜>{øH_¼$w¶ý#—gÌ•‡'NGZæÞ8E¤çÃDDþðÃ2cÆ ‰7®”*UJ‹FÉ-[¶ÈíÛ·å믿–)RèÞ?|øPÿ.iÒ¤¶Ñœ>}ZZ´hAéùü²†0"pðàA騱£®}Ö¬Y¶õlßÜ… ä‹/¾½{÷JŒ1¤GòÙgŸÉƒôýUªT £†MµŸ|ò‰Ä‹Oºté6 øP­‘i¬>„]! ¿"Ö"òáñSšGÔñ%z²$úç§·ïȱ&äÁ¡£~ÅÊ—:Kéùlx]DnܸQ, ”>}úH‚ l½¼ÿ¾v‹/lÏáb~üñÇ&"áf6kÖÌsº¬!\9rDÚµk§Û1b„äÏŸ_®_¿. 4п9r¤äË—Ïæpãwp¾³gÏîvÿ\‘pÚwïÞ-Q£FÕBáÛ~ø!E¤ÛäÃïFŠÈðcÍ–H€HÀ_ „©ˆ|òTö~݆&f¦ô’qÂ0‰™1Üü}œîþ‰¿b‹ð~SDz>^‘OŸ>Õâ;wdÊ”)A¤³®b/äòåËå×_•˜1cj‡®µÀÁY¸p¡þ•;á¬o½õ–”)SFzõê¤^ˆŒÌ™3ÛB_M8+Bn³dÉ"sçΕ“'OJòäÉåí·ß–ZµjyNœ5xÀãÇ¥fÍš‚?»·aÃ2dˆn /ðbb¢‚áÐXWî”gϞɡC‡Bt"=z$ÕªUÓMtêÔI*W®¬®Q£†OŠHŒ+J”(Á"‰LÂ*2Õïï!  O ÞÉ?l();¶‡Ë‘Zÿ1Ò§•”m›Iü’Å$JìXrß!¹4q¦ Ýa›¦T]ZK²&uåÜà±òèÜE}}¬¬™åñ•«rå›äêw?Kª$E‹÷µP}xú¬œ1Aîlþ3èsyºÔ’⣦¿Ì+-nypô„\÷‹\ÿõw}]ôÉ$ÇŠ”}UŽ5j+÷vý÷\'nyéÛ¯äéÝ{r \ yöࡾ'Þ+…%yóF'.Aèî?wÈ…±“åÑésa²Ì("=ÇêU‰ë:è‡äV­Z¹Ô;{ rëÖ­2oÞ<½/2Ož<-Z4)R¤ˆ®ÏˆH„Ä:*p¤²eËdOdhEd’$I$zôèú¡NêÚµkeß¾}òþûïë=,¾G mÛ¶rôèQ©S§Žvú°>̾Ù%Jèu³téRí@fÊ”I&Mš¤—Ó§O·¹…ÅŠÓ÷ãÅYoÛ¶m“~ýú \vüW»vm)Y²d™,Y2íœÃiÏ;·®£k×®A@aO0BY‰ÈàúaÄ/*Ã÷"Q¢DºžM›6‰+„ô“'OdòäÉÚõìÙ³§œ?^(¹råzaÒ0D €ÓöíÛµKš3gNýÂæçŸ–k×®éûðÊ!ƒ¾ß‘° îz„°ÿöÛoràÀ-P+Uª¤_4AÈ£àÞùóçëv|o‘Ø ®1 ú>qâDÍ4~üøòÎ;ïè—öBwõêÕ2aÂùî»ï$V¬Xú^¼ÐjÔ¨‘n«|ùòzÎÀcÅŠòçŸj†X3qâÄѼN:%yóæÕ/°ÛÙX}oå³G$@$@I ¼E¤‚÷vï“cïµÑC+›dž6V…¼Æ“'7nÊÓ;÷$FÚT"ŸÈ‰ÖÝåÎÖíú:sïã —%zªärÿa" чr翵˜»·÷ ÄT¢4Zâ„:tö`¥ºªÎ»úšX9²ÊKÓU[ ãË3õì¶pʵù¿É¹O>×?gž1Vâ-(—§~+ÇMµMQʶHòVïËÍ¥«åtOõï×|SÒPÛdÔ³Á£ó%ªúÿòhIÉ“«×äpÍfòäÚ ¯O1E¤çH½*"ñ€öùçŸë}_¯½öš­wØ —È<¸á?{‰ßÉYb<ÔW *䑈D8ä Aƒl}Ä:¤±¿íûï¿·=¨zŽŸ5x‹ÐÊ•+µ¸ëß¿¿~™qñâE]=6ÌÄ’=AT@,`ï-œË»wï깆Ã]ÈñãÇk¡ñ‰õ†Ÿ±ŽS¦L)íÛ·×/¬{"‚xÃÈ«W¯jÁeú€~À͆p±‘!õ{‹!ŽÑ7ÔùòË/Ë{ï½§Û@?06„×Âí„ÂߑЪ[·nzüβ CXacóæÍµ…3 ‘ºdÉNž.]:-7oÞ,S§NÕÂÏ^D­wv=Ä‹Úa ¡gÎ9{ö¬… Ö{%!²j„îEÈ*Ü3¸fƉÄQ3pÆM±ŠH8th.–uß­«"Ò•~|ûí·ºÿpZË•+§û†uˆPN8£põ°·ãÀx\)VÍ15ßS“uÕš ãèÛ·¯¨Va…ïuH×C˜›R„C ÃõKD' rŽ þ½@¸ª»›ŒÖ~@CðCXÚ—3gÎè±Ïœ9S RˆmD -û±"<.(¸šÌÐß|óüý÷ßú% E¤+«ˆ× @ä&Ö"{ceËlƒ|cÑ 9Óo¸¨ð)ý»$uÞ–4ýƒÏ˜~è͆zo¡3™~DIX¥ü "2˼¯%vžryò¹8~š$®QUÒ~Ò]‡±î+úF‰OѪ±¤P{,‘MöpµÆú³L_”xjæù¡ãäêÜ’¤^uIÓ§“ÜX²JÎôüL_“nX_IôæɃìWÂ[½QÏ닌"Òs¤^‘x…ÃàlO$ÂÒç"rÕªUÚerbëù4°O ÀƒP@Aè'öÙA8bÍ!t ¡™.÷DØ'Ü<„(;‘&$ÚˆHkúi‘øqË ŠÌþCWE¤+ýصk—vÓ¤I#¯¼òŠND‘ƒÐ]„wÂAÃ÷¡´9räp §½°Ây­p!ÇŽäÈT–8qbîjV!]‰_$©ª^½ºæ‚º!!MAv]„C#„µjÕªú{†0T„¨Z ’oÁuT à1oø.#öH"L…"Ò¥%Á‹H€H€BI LE¤%;kú‘JèU./‘#uU‘zy’¤Î;JDvÖ?ßýk§ÃÞŸìô<ìÔk"R…Ãî+²ˆÔ{u“Û›þ”“jofÆ C%~ÙÊ©T¡¬k6꾦ÞO'óqÖÿGgÏË™¾ÃB9+!_N2£®ðªˆ„ÓWGy`ö Y‹«"ÒâlOdhHˆ8K)´gÙY…³šD-“CFWß#`=$¨š6mš‘p¨æj2íΞ=[wnÞºuë´»õå—_êpVˆ¸eõêÕÓ"Ó‰õ…µZ¬ „Æ"”3‰õ…ϰ?/U\é„„Æ1¡˜ÛMŸ>½Þ{ ñŒ¶] gµ:‘ˆÀwÞoý?3ÛVÒõË–-Ó¡ªØ7ié tý´ŠHS7ÂU±gŽ+ΙſøÞ!ÒŠÃ^jG¸µeE½)‘!Ñãç$@$@î/‰ì«Ù~£÷("y ’Ø Ä-VH2O£Eå¡**lT9wΊ§"ÒÎz¤Ö*Cì1[SÇ+øj ½û"Q|'çÒªÄð µ%çªùòLõa·eEIѺ©ú¯‰<0 îzdÅõ`ˆïDD.™¬°àý—àæÈ‹½§W®\±õû4ñ9BM‘tB¡®´¨¡É(¨ ‰wð'\I¼82Åi?Vß\õì @D/‰1¦îÙ^’6¬¥²–^—Co7Ò™S‘„&ûâ9‘ywûn}väã‹—5’¸/’ûÿÐ{Q<‘¨Ãì“D&×S÷×Y[T|U2Œ¨ûbuq½ÙoyyÚwúxû}—13¤ÓuJôh*äõ¹0f’<»ÿ@‹åx%ŠÈíÁ'Ótwî)"Ý%÷ß}^‘¨Õpð _ºtiý‰‡B8 H‚$!Ø×…â(±Îƒtâ„®áaGˆ,ZÝ‘Øë„,x¨Ä'ŽAFLÔ‡Ð8“HÈH„Ñ!$»pMðP qÇÉEX|“ÖÖ Žê@2ìçƒAÓ¡aʉ'´ë…pQ8x8†bÉ_P\‘¸ëûõ°nñ²!³Žë © ÂNá–¡~ô5¤~ /p谇߼Œ±eb~‡Ï àÊö"bnàâÅ‹µ€L:µÞ{‰DC(‚I…ÈD‚Ÿà®‡8FDBÁÁ®+þ<|ø°‘%FB$ÊÂ÷¬@ÚM•J¥%WßQÜý®p#‘áÉŽÉÕ$ؘµ&ºB}p—­/ŒP—;"Ò~¬¾¹êÙ+  ˆ$ž"2ZâD’]eR?ž>×ñÂèçÆFœy$ÓäQUÙ×ï¡ ®®Å‘o¿˜¢¯ó†ˆŒ;»:NdŒîÄÞ%d£'Oªë¿¾`‰œ02Èt$z«’ÊÜÚ[Ÿýˆ ¬ö"C§îÑN‹Ðg÷îË£ËW%†Ê%VL9ñ‘:¢ÄîœJoÌ7E¤çÃDD¢[9Å46<,ã.Ä Uˆ9‘¸ýx }ªï]²d‰\½zU2gÎ,M›6µ¹²¦ÞjÕªé1cü\ÕZà²vîÜYºté¢û×}nÕª•*THV¯^-&Lï¾ûN¿<0lÁ¾E‹zN1ßÍš5ÓëëÀzžÞ}÷][ÐÝ™3gJªT©|àÃ. xƒ€‘êű.òòØ\|¶5UQãÇ•›~óÙ.úKÇ("ýe¦ØÏ` „FD¢" ÈË—/ëz§Nª×°aC-¾ÆŒ#¹rå’]»vI=$a„Z>|Ø©ˆ„H¹yó¦¤L™RÚ·o/·oßÖÂ%A‚Z8¡n8˜Ÿ|ò‰v*ˆtÖ§téÒÙÄ®8H:þ¼®BíBìÞºuKÿ<{öl}ÝØ±cµàŒ=º ž“'Oêë!ˆ2eÊôB½è7ö|¢@hgË–M‹®S§NéßA0ƒƒ'Nl›#"Í/Ò§O¯…Æ ! göã?Ö , L=z¤ë›>}º$I’Äíq®Y³FFŒ¡›GÛ˜SÔ!\µjU=o¾(ànD¶é¯x¥K—–6mÚè¾üøãZ4¢oè/„ëÖ­õxP°Ç/ \1ˆHOQ„8C0ãó^½zIÉ’%u80E¤mÉð §Zv•;[w(0C èÄz)£DO•B2LzþÜÀâ>ŠH÷ÙñN"àlO¤1èªlpÈ Ž là2B€uíÚU^ýuéÝ»·ìرC7n, 43fÈ?ü •+WÖÂÊ*˜ìH·‘#GjG ®ß¥K—¤\¹rZˆþý÷ßZX ˜}š®ôÉ8tpA!ád¡W®\Ñ®"öhÂmCÿP&Nœ¨…#\2ˆFü®Ÿ©®+>³¯7Mš4Ú‘Û·oŸ¼ñÆZôA@¶lÙR×ëlO¤• DÓ«¯¾jã–:ujý3 \B8¨YpFáÜáï^+V|¡?®Œã‚ã áhD#„*X 8Ë´óxзo_-ú¬Å‘ÀÃüÕ«WO;½p4áöÂM2dˆñõë×µHDˆ„ \©R%[ÕæEæœ"Ò‡þ±`WH€HÀ‹îþùœlÞEÕøLýZR©I J/öRU˜œ¨Q$jܸ’a¢Š,+ÇKGÞj("#ïÜÔÈ­"2®úÂ8ˆ #ج²~ýúrãÆ ºˆHÒš?~ínÁQ„ûh¬àD$ÂK!BQ !ôL»%J”Ð?C´Â1|ÿý÷µëéJŸŒØƒ‰ä,(IK8/epËP Ñþ¨Q£Î17ˆ)GõB$mذA‡´Bü„VDa°UÞxñâévP º,X Co!(ŸQDúÝ”±ÃŽ„&œÕ*"á6BÜ@ Ô¬YS Jˆ;8x  p±Gî^p"ÒZïÅ‹µC†¡h/"ÓéHDÚ÷)´â ®DäèÑ£uûp­%Ož<ÚYuT/Ž¡X¿~½ÁÄ>BwEäâÅ‹eüøñAÿÓO?éÄ<»è’•)SF‡‹^»vÍæúº3NˆHÃaªE5Î#ö\†Tœ‰HˆGìׄk‹×{$ñ6ú5âHD†« 7”NdH³ÀÏI€H€H€ü‰E¤?Íûꔀ·D$ÀQ!¨ g.\(*Tnݺé¶]‘‡4a–¯½öš¾á¬f(öá¬Á [wÄÄÜU´ Qˆgݸ""!ò ªˆB¨ª}qÄÄ^Dš£Q Î!*±â ¢ÝjwÆ ÇBN,Âtq„öš"¼Ž!Š;ᬸó‡yÃü¡À¡…pK¼x07>³‘•ÿ|ùòéë("ù @  ˆ ¤ÙŒÄc îˆ<Ì#ÁŠ+®šVìq„Øë„ðNWE$®µ&|Á><“åûqè¼5±Ž·E$Ü>8‘+W®ÔýÆÞDˆ$‹ÁY…®ˆHÜ ~HʃŒ²f“µ:~®ˆÈãÇëÄ4(¸Ùn OD$ƹ|ùr ‰…ðßéÓ§õËÌ›«‰uàŽB F‹M¾þúk$ nªÉ¤‹ð[ô•´ƒûÀû`ñç/¿ü"+V¬Ð!½2d ˆŒÄÿ6qè$@$@$ˆ("qV#ᘜ%Ö ˆ¸Š®ŠHˆ„”ÂMÄ‘ØWg„DhD$Ú6G| ñ Ä \MGG|„…ˆ4G|@d!¼±xñâ‚} Öl¨Ö½–öá¬úŽL$–ÁGˆêܹsÛV™+"cŸ$²¤B„!œ{$=‘¨Ǫ@C¬"„!»˜Ã¬Y³ºtÄö7"¤¬Î;§!œÕ¸™f°H°³nÝ:=¸ª¦@DâEG„°"û-êË›7¯¾„Nd$üG‰C&  &@À“Ë¡‘ D*çïIDAT„L 4á­Q5û0­"/+e–…H€H€H€Ed Ï0ÇG$,WDä… ôžH„Æ"®²µ8J¬Cì$@$@$@$¨("uf9. —„$"‘´{S¦L©õ@vVûBéj^D$@$@$ ("d"9      ‘áA™m @€ ˆ ‰ä0H€H€H€H€H€H <PD†e¶A$@$@$@$@$@B€"2@&’à      ð @”Ù ŠÈ™HƒH€H€H€H€H€ƒEdxPf$@$@$@$@$@$ ("d"9      ‘áA™m @€ ˆ ‰ä0H€H€H€H€H€H <PD†e¶A$@$@$@$@$@B€"2@&’à      ð @”Ù ÿÂ4gœ h¹EIEND®B`‚astral-sh-uv-9252ba6/docs/assets/uv_gui_script_hello_world.png000066400000000000000000000076411521221614700246210ustar00rootroot00000000000000‰PNG  IHDR(f÷@›æ pHYs%%IR$ðSIDATxœíÝYPwžÀñ_«µÔ­µdI ‘1GŒøHbÁÆ9&•q<5ãTmÅU™+[ãÔ>%»[S®ÙÊN^ò°©©š²³»qöaY§Æ³Ùd’ñ1Ž“qb‚m Œn1 t [êCÚl0v@:†ßçV÷Ÿ¿ºøZÝdD8N@­, |>ŸÚÓ@h )--Õ¨=„Ö" !`x©ÃCHB*ÀðR†‡ 0<„T€á!¤ª¸Ãõ÷÷·´´|þç?çrPSS{à¯ÿÊét÷[ ´ 'ŸF£§OŸ gí5žD*;08r½{( ’y‘ I’ðjóAG,†Ú“](‚ ÚÚÚ¾s·¶¶¶ezRK OQ”t: N$“ñhtê³?~ì½á7Ø*ˆ‰+õRÅó¼V«-Ö\‘Zò £ö4¡µµuWCãýÛkkkÛÕÐØÚÚºXRx½½½Í-mëj<¶Êz1¿Ñu>¸A„ì­´óO>ù¤ÉdÂÛ<ô=´}ûö _7ݧ½éê.|Ý´}ûöå˜@UäóyI’ºººÎ7·”ÖïÖ—8#ÙD$'g!'çS“*»wï6 Å.BÅrŸö–»:(8~âÔ‘·~ïoïЃã>k˜+ÑžÓé\ÔbÀ´x<ÞÔÔtèСM›7ëõzÇóÎ;ï ‹¢¸è±N€ƒ'§¿è?<½zûë;=x÷0W´4°˜÷j.ëLЍµµ•Ö²­­­Kܧ0N§³Àð$I ƒgÏž=tèÃáxõÕWÛÚÚ’Édaó¸]Ûtqý‡=s»µÃìmh…,êßqµ'»P°¢¦ß!]ôïît: §ÓYð§e³ÙÎÎÎìß¿ÿ­·Þ*l„Öš¥~ZI’Å\GAhÍXRxF„矾®®®XBh-XÒë•F£1›ÍÏ=÷œËå*Ö„Z –t‡*~",BêÀðR†‡ 0<„T€á!¤ !`x©ÃCHB*ÀðR†‡ (øàƒÜn·Ú3AhMðz½0žÛí^üßJA/5R†‡ 0<„T€á!¤ !`x©ÃCHB*ÀðR†‡ 0<„T€á!¤ !`x©ÃCHB*ÀðR†‡ 0<„T€á!¤‚‚?ŠY 4 }7üàI—ÓTª[ÈCsD“£èË=Tey¼n=»°£îe É?Ò;îü‰Ëa­*¹{ä-ë9БbM~moÖñ“­v+Ï,æùÎ7à‚ÎBó(øOh:wö½O.NeøÐáDèZÓ¿øåÙ«×cbZYàQs%FZ½>ø´}ìš_¼µI™Œû¯|ñþoO¼|É/%¥<ä³qÉûEóå–ÿnñ‡’Ò"ŸïÌ©BZ™óèŸ2B«ãRÓî¬,xS×ÐèðxàÖ¦dB ù²·Û?r­7”ÎÈ ËR(`ˆœËfe¨‚_ê*‚Õc²p‚“ „•H4 ¤'‰‰›ºmu`±$ÆB9IHg3QoošR´uëV‹á!5­ÈÏ_^91ŽE"I ,ÅðÖ2³^»¨£@C1œµÌ¤×ν;ãÍZ³Í›¤ãQ¿”wQrl<´xvYFlÔÍñ´X!‚˜Í„»5ä6¾ÆmdµJZÊ$&ýS)I‘€5Zy³ÕÌ¥ålzj,.k…&’Iš·$3¹™ßtÆá )2æD4„fE“jþêØŸþ÷øÅ€ Ô:ܯ¼½o‡èEF‡{ç+oïÙán¬˜½'g× ®zª%›,ï Éë„ß—ì§ i-ëKÈmá¡äfRŒ±²õFAŠˆN^ÿê½ßœè¸š¾B­ýáÏ÷ý;j°êSc]¿y.ÈDâeÌåËvÏþÊ'è ̘pòÛãB‰kï³o‚_†“‡V¥%†O„¯_<þ¯Þ¯ÍŸ³w6N/ñMN‚ÉI„:޵Üp¶göUs@Ź|àê@¸„•îý‡ã#‰pwDZ?v“ôö}OÛA›óeⱑ?j—ͦÙ늤‘fKÊ„›ÞüäXH–L¡h– ek«ÖÕÙ\ÑÑÑpZŒú&ù‰Ñ«e®-ëìnZÔB`àRSÛ™ AëN·›ÝeLCr0ö^ýðÓòŸ>¹ù!Q#†‚Þ¶AKYnÃö†gJ·Õhß>Y"Ì>Ü!HÚœ¯7åïβ´ŠÖˆ%†—Î&Gû/~ÖÃÒì·Ëë9€l"¨gM&”ÉøÄ7_ü®#¶yWùOìÀäoòÝèy¯wÂedjÍ÷øÖQW¢;öoýùû«a|m´óü»¿:=bÕ·ïÜâÔÎ\Ð×Ñ ï(û‰äØXL´Ž…sº€nÓÓ¦*ªªÇ õÇ2áø8Œ U{êËlù,ÈÞž–®ógÆ_þÓs ÕÏ8¢jþðèg'?ÿ}{c½Ù • Dsƒió¾WÿB¨0ø{Z òY¼=íß4}þáWÏz6xŒ£¡æw^L]N¼ÇBh¦%†g1Ùw½ðw¯m©/­7ÞÙ˜è;ÿ~Ë•sq¾]`l?Û=uåÿŽQ@Ê ‰1M>ºí±ZÜ+¼dB ¥ò£‚½²®X ¿ÞP‘zj×±^kx2ò:€·ˆ,ËU×í<Ó«»ÜÞ™„öá´¹Ý^ö MY 0e»C:ðÕKÁÒ“ÅÀƒ,C(KZcZó¶jÎi x<5á+Ã1ž W™ÝUY#èØÙgI–!Š1'«k÷l2o°EÏS/Âxw„[Ú EkÄã(Æ(T¬hÃCµ%w6&²Ã¶žˆk@Qò’F„Êò¶G«ì °F¨¬{üaäÀü+J^RD0h–ÓO¯½êHÖXb”ZŠËÏÏÚ]C3zgד#‡FÆ„¡¼vGÙ7§ÕmV³«,30:ô[ÈÜúm§ƒ|dIQ(Y£ÓéI†h“Ž£M†LNÉ+ ,Ãhy=­™³ô›Ïƒ$)6§³9RÇ$m*átz#DVÃ21Z~Ë¿¸BR­g ¬jû³ûãž2‘ÄíÇý=÷9Šd ®ˆ™d r,@>­d‘§ð,§ƒ¹1P4e6Âk÷_íó˵›«*tZÐÙæŠ Ý×=¾žqšÓ•ÿÈd·é€ÈES¤Då2é”"Š´r"•„X¨Õj˜[×”÷@@ÑT.­I‡bI%-‚™TäD$™NÇìE8ehõ[þ 9žtvª}|ðú¹æPwÝÛ{ YèyBkÊ ¼âY ëyfÿ¶Ïºo^ýŸwÿí"ÇëH`MPùDc½Rõ]G}=ìmÿŸCv`sq9ÊãO¹·Ö×èaNw@Ð@;ö’*·þ“¤›1X×  ¥H«–sU—Å;%k°rƒ‹×ÚHÐí®yls<ûêÜo?m×·eHFâJ¥ó…Ý&“ÉÞçZ Ý5¸ÃÞ?œýßÃ%«Ìè•ñ”¾îû „n+8< €Ž·­³»óOÏ~Çðœ‡JxËÆÆ—9vfèøM£v:¬VGm©Éê.—í&#¥!‰ùº~üãs° [÷þ½góÆÒŠyæCmÕuž†:c}݆ ·qzñ¥„å××6ÖnªÝ µÖ­çÙ ¢êñF=ŸëþÍ‰ŽŽÐ`\O¼´»ñåÆj ˜ dîL HêÛ- ITTmÝ©‘ƒ§þóòµ®Ø€ ¸öîuº¬4ÛܳÐ<§ÓùÑG-þ£˜s™D0™ŒJ”æggþ´ÝõF9 Ç·߃ 8«`¢Jhej,žçxÆbä©L:tŸ£(’Ö›6ž½çO¶’Žf’1ZÇ8‹YG99›™›’Ù e.3³ZJsgïï\!®¤Äh1óÄ­w®ä9ž¹×)‘MEÇ‚IQÎ$ɘÍtF¡ÅÜ]g¡Yzzz^|ñÅ‚ÃCb:<\ýFHB*ÀðR†‡ 0<„T€á!¤ !`x©ÃCHB*ÀðR†‡ 0<„T€á!¤ !PàõzÕžBkÅtn„ÓéT{&­9ÿì­xr›¹ˆIEND®B`‚astral-sh-uv-9252ba6/docs/assets/uv_gui_script_hello_world_pyqt.png000066400000000000000000000043501521221614700256700ustar00rootroot00000000000000‰PNG  IHDRuÉ‘ pHYs%%IR$ðšIDATxœíÝOLéÇñç{1ÄÁ»U»²Wm*㬴=ØQÑ^Z‚X·R8$RoVÕeÃeãKH9¦Bj.°²œÜCÿI+”¸Ç +Y&êaEJìa»QvE–! !±=3{°±ÇÆ63æ±à÷Q0~=~Iüe^¿8‰ðûý|"züøq«§pD©Õs8j3DÀ Q0CTÌ3DÀ Q0CTÌ3ÅΠ'OžÜ½—8Èà þæ×@à g8,lEu÷^¢÷WØ?é7O6ÊŽÜþç¿F?ú£¢Øz8€CÍî³Ü-nÙ´98üÓ7­Ÿ¦uñßÿdt]GTpØ}–wú|?ùQgí1¦i­N‘ÿè»§[uÌ š ø‡dCéŸïëK‘J¥z{{k[ZZêëëkÄåàÒ±¶öø¯ÿ^&É‚„d É‘d É t´ÿ~ø¬®ë;;;¹ÁÇãñ|ôç;$Ì?ýáû¼‹Íg•£üZ+•J8÷ÅýÏktµ´´tvà\*•jÄD%IJêë I6$ÅŠ!)†$ç?p»ßI§Ó²,—ßG˜®Ž ç|öÓÛÛûÅýÏkt•+ªvuálK½Ó«œô*^¯rò„ëä ×I¯r¢Cîh—Ý®ÊçqudÜÞ4Ç<(tµ´´TvS£‹"ûQmï¼""¯Wñz]o´‰—æÎæ«gßnonìhOw´íÌNÅ{¹½iW{&w_€fªØUŠ"§W*O»üôÅÖ7[Ï3ÛºHË.]VtIÑ%©òºÜÕžq·áJ­QÖUsŠ"§Q}µñíó̶ìÖ%·.¹tIÑ%E—]HFÅñî¶´»íeõó­N ññBÅ#  10µºwüÀ”£9Ãqfíª9E‘Ó¨ÒôJr’Ë•Ü/]VtYÉJU£zù†§FTµ _¥ÅØ’âægiôÚ•úNÈB8ÑÂyBÁ¾ûìœE%»ôâ’OɿВ]H&™ÔÖÖæóù|>_gg§ÛíÎf³üüêÛ=uNmøêd?ÍÜ.Vµ:?»Ø?yu¸Îó±0håDaWáUqߢœEe]òÉ–®$É0MóÅ‹›››ß¿¹¹¹ùüùv:yÿí_öý¸Þ¨(¹d­jáFl±ÿR$TïéಮúšÖ•Ã+•eÉ'®TrVÃ4MÃȇ6 Ó0 ]× C7ŒÊ+C;JªZ¸=C£×® )°«ìuTÓºrx¥²,ùdE—å¬,geYR®%#—ÓîÇyõÏ.tåÚ(Í\ŸZÍ7u¡¥K?8L*îL4§+ï¨H§_¾ÿVHSH¦†L!L! !Ì7NÃ4^Kä£ÚïuEèÝ÷ˆ¾üß* ï^€-/½W1|a”ffçhv¦òš[jìõ5aßÂATáðÏÆÞy«ì iš¦a†!I’$G*,í¼R¾0J3± W>&¢Õ©gÊG\ì?={û/ýK?°aßÝóFwe7*íÙöWÿÿ®¾ÇОmW½mø³G“_žþPäZ½óhòúéXɈPäR,¶8zç>šj„£·õß××·ï»Ôs]5è]êÂï÷ïûhšö·¿ÿ#“©ÿ­±¿ûmäÔ©SÞq p´[QQ6›Õu½îGREÁqì.ÿEÁßÛ°ÿš3DÀ Q0CTÌ3DÀ Q0CTÌ3DÀ Q0CTÌ"ÚÚÂÿÊÀW*fˆ €¢`†¨˜!*fˆ €¢`†¨˜!*fˆ €¢`†¨˜!*fˆ €¢`†¨˜!*fˆ €¢`†¨˜!*fˆ €¢`†¨˜!*fˆ €¢`Ö¨1Ÿïüôúž[®æd^«™ÂkéàQU|–5ã©·ç1Ö§ÏW:K4æñ×§Ïûwv8¼ñò¯+¬RòÖÝbCk+IÚ{D wµbvp|⨂C#*%WÖv?OÌÇÕhT-=BêÈP°“@²°G“¢Ê-•|>»ë%ËøêËÈ`¨›hy5ëúê2uG"Ý¥G¨;´ÛT•sæW‘»·Vš^"V¸£ïbÜö ÇT3¢ZŸ>ß3N4MÓæ¢ñ‹û½Ø²Ž×LÐxO•{ F¢ÅåÞÚJ2ì «Ö# Ú8gr¼ç2ÝÔ4MÓ&Ë$ó]ŒGç´¼¹h¿ plðD•ïñ•°~?O|:žT'nމˆ?™P“ãŸÖºZ•Œ§àØÍê÷è €‰ù¸î¢àЈZWÙøòU^‰àЈš»i}u9w§`¨»xd÷}y<©N|RíÉ uS2OZ.;]a5'­Ñ8;§u2‘(Y¶4±âÌÞ7p5gK=8vïÁ6{n…kÿ<¶d|Ï8UÝš#¢ÜÓžJ~ÆQ©ìç²ÎÎi=ý¤6-¼$œ`Kö#ü~ÿÇ[= €#âÌ™3‡ømJ¯'DÀ Q0CTÌ3DÀ Q0CTÌ3DÀ Q0CTÌ3DÀ Q0CTÌ3DÀ Q0CTÌ3DÀ Q0CTÌ3DÀ Q0CTÌ3á÷û[=€#åvS5”»êÆIEND®B`‚astral-sh-uv-9252ba6/docs/concepts/000077500000000000000000000000001521221614700171435ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/concepts/authentication/000077500000000000000000000000001521221614700221625ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/concepts/authentication/certificates.md000066400000000000000000000073051521221614700251560ustar00rootroot00000000000000# TLS certificates uv uses TLS to securely communicate with package indexes and other HTTPS servers. TLS certificates are used to verify the identity of these servers, ensuring that connections are not intercepted. ## TLS backend uv uses [`rustls`](https://github.com/rustls/rustls), a memory-safe TLS implementation written in Rust, with [`aws-lc-rs`](https://github.com/aws/aws-lc-rs) as the cryptography provider. uv supports the following X.509 certificate signature algorithms: - ECDSA (P-256, P-384, P-521) with SHA-256, SHA-384, or SHA-512 - Ed25519 - RSA PKCS#1 v1.5 (2048–8192 bit) with SHA-256, SHA-384, or SHA-512 - RSA-PSS (2048–8192 bit) with SHA-256, SHA-384, or SHA-512 ## System certificates By default, uv uses bundled Mozilla root certificates for TLS verification. In some cases, you may want to use the platform's native certificate store instead — for example, if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's included in your system's certificate store. To use system certificates, pass the [`--system-certs`](../../reference/cli.md#uv) flag, set the [`UV_SYSTEM_CERTS`](../../reference/environment.md#uv_system_certs) environment variable to `true`, or set [`system-certs = true`](../../reference/settings.md#system-certs) in `uv.toml`. When using system certificates, certificate verification is performed by [`rustls-platform-verifier`](https://github.com/rustls/rustls-platform-verifier), which delegates to the operating system's certificate verifier. ## Custom certificates To use custom CA certificates, set the [`SSL_CERT_FILE`](../../reference/environment.md#ssl_cert_file) environment variable to the path of a PEM-encoded certificate bundle (e.g., `certs.pem`, `ca-bundle.crt`), or set [`SSL_CERT_DIR`](../../reference/environment.md#ssl_cert_dir) to one or more directories containing PEM-encoded certificate files. Multiple entries are supported, separated using a platform-specific delimiter (`:` on Unix, `;` on Windows). Certificates are usually stored with `.pem`, `.crt`, or `.cer` extensions, but uv will attempt to read a certificate from any regular file in the provided `SSL_CERT_DIR`. Files that cannot be parsed as PEM certificates are ignored. uv resolves symlinks and ignores dangling symlinks. DER-encoded files are not supported. When set, these environment variables **override** the default certificate source entirely — only the provided certificates will be trusted. `SSL_CERT_FILE` can point to a single certificate or a bundle containing multiple certificates. `SSL_CERT_DIR` can include multiple directory entries; uv will load all valid certificates from each directory. If client certificate authentication (mTLS) is desired, set the [`SSL_CLIENT_CERT`](../../reference/environment.md#ssl_client_cert) environment variable to the path of a PEM formatted file containing the certificate followed by the private key. ## Insecure hosts If you're using a setup in which you want to trust a self-signed certificate or otherwise disable certificate verification, you can instruct uv to allow insecure connections to dedicated hosts via the [`allow-insecure-host`](../../reference/settings.md#allow-insecure-host) configuration option. For example, adding the following to `pyproject.toml` will allow insecure connections to `example.com`: ```toml [tool.uv] allow-insecure-host = ["example.com"] ``` `allow-insecure-host` expects to receive a hostname (e.g., `localhost`) or hostname-port pair (e.g., `localhost:8080`), and is only applicable to HTTPS connections, as HTTP connections are inherently insecure. Use `allow-insecure-host` with caution and only in trusted environments, as it can expose you to security risks due to the lack of certificate verification. astral-sh-uv-9252ba6/docs/concepts/authentication/cli.md000066400000000000000000000057141521221614700232620ustar00rootroot00000000000000# The `uv auth` CLI uv provides a high-level interface for storing and retrieving credentials from services. ## Logging in to a service To add credentials for service, use the `uv auth login` command: ```console $ uv auth login example.com ``` This will prompt for the credentials. The credentials can also be provided using the `--username` and `--password` options, or the `--token` option for services which use a `__token__` or arbitrary username. !!! note We recommend providing the secret via stdin. Use `-` to indicate the value should be read from stdin, e.g., for `--password`: ```console $ echo 'my-password' | uv auth login example.com --password - ``` The same pattern can be used with `--token`. Once credentials are added, uv will use them for packaging operations that require fetching content from the given service. At this time, only HTTPS Basic authentication is supported. The credentials will not yet be used for Git requests. !!! note The credentials will not be validated, i.e., incorrect credentials will not fail. ## Logging out of a service To remove credentials, use the `uv auth logout` command: ```console $ uv auth logout example.com ``` !!! note The credentials will not be invalidated with the remote server, i.e., they will only be removed from local storage not rendered unusable. ## Showing credentials for a service To show the credential stored for a given URL, use the `uv auth token` command: ```console $ uv auth token example.com ``` If a username was used to log in, it will need to be provided as well, e.g.: ```console $ uv auth token --username foo example.com ``` ## Using credentials with external tools `uv auth helper` allows tools that support credential helpers to request HTTP credentials from uv. At this time, uv supports the [Bazel credential helper protocol](https://github.com/bazelbuild/proposals/blob/main/designs/2022-06-07-bazel-credential-helpers.md). The command is intended to be invoked by external tools. It reads a JSON request from stdin and writes a JSON response to stdout. When matching credentials are available, the response includes the `Authorization` header: ```console $ echo '{"uri": "https://example.com/path"}' | uv --preview-features auth-helper auth helper --protocol=bazel get {"headers":{"Authorization":["Basic ..."]}} ``` If no credentials are found, uv will return an empty set of headers: ```json { "headers": {} } ``` !!! note `uv auth helper` is experimental. Use `--preview-features auth-helper` or `UV_PREVIEW_FEATURES=auth-helper` to disable the warning. The [Bazel integration guide](../../guides/integration/bazel.md) explains how to use this command with Bazel. ## Configuring the storage backend Credentials are persisted to the uv [credentials store](./http.md#the-uv-credentials-store). By default, credentials are written to a plaintext file. An encrypted system-native storage backend can be enabled with `UV_PREVIEW_FEATURES=native-auth`. astral-sh-uv-9252ba6/docs/concepts/authentication/git.md000066400000000000000000000056361521221614700233010ustar00rootroot00000000000000# Git credentials uv allows packages to be installed from private Git repositories using SSH or HTTP authentication. ## SSH authentication To authenticate using an SSH key, use the `ssh://` protocol: - `git+ssh://git@/...` (e.g., `git+ssh://git@github.com/astral-sh/uv`) - `git+ssh://git@/...` (e.g., `git+ssh://git@github.com-key-2/astral-sh/uv`) SSH authentication requires using the username `git`. See the [GitHub SSH documentation](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh) for more details on how to configure SSH. ### HTTP authentication To authenticate over HTTP Basic authentication using a password or token: - `git+https://:@/...` (e.g., `git+https://git:github_pat_asdf@github.com/astral-sh/uv`) - `git+https://@/...` (e.g., `git+https://github_pat_asdf@github.com/astral-sh/uv`) - `git+https://@/...` (e.g., `git+https://git@github.com/astral-sh/uv`) !!! note When using a GitHub personal access token, the username is arbitrary. GitHub doesn't allow you to use your account name and password in URLs like this, although other hosts may. If there are no credentials present in the URL and authentication is needed, the [Git credential helper](#git-credential-helpers) will be queried. ## Persistence of credentials When using `uv add`, uv _will not_ persist Git credentials to the `pyproject.toml` or `uv.lock`. These files are often included in source control and distributions, so it is generally unsafe to include credentials in them. If you have a Git credential helper configured, your credentials may be automatically persisted, resulting in successful subsequent fetches of the dependency. However, if you do not have a Git credential helper or the project is used on a machine without credentials seeded, uv will fail to fetch the dependency. You _may_ force uv to persist Git credentials by passing the `--raw` option to `uv add`. However, we strongly recommend setting up a [credential helper](#git-credential-helpers) instead. ## Git credential helpers Git credential helpers are used to store and retrieve Git credentials. See the [Git documentation](https://git-scm.com/doc/credential-helpers) to learn more. If you're using GitHub, the simplest way to set up a credential helper is to [install the `gh` CLI](https://github.com/cli/cli#installation) and use: ```console $ gh auth login ``` See the [`gh auth login`](https://cli.github.com/manual/gh_auth_login) documentation for more details. !!! note When using `gh auth login` interactively, the credential helper will be configured automatically. But when using `gh auth login --with-token`, as in the uv [GitHub Actions guide](../../guides/integration/github.md#private-repos), the [`gh auth setup-git`](https://cli.github.com/manual/gh_auth_setup-git) command will need to be run afterwards to configure the credential helper. astral-sh-uv-9252ba6/docs/concepts/authentication/http.md000066400000000000000000000070611521221614700234670ustar00rootroot00000000000000# HTTP credentials uv supports credentials over HTTP when querying package registries. Authentication can come from the following sources, in order of precedence: - The URL, e.g., `https://:@/...` - A [netrc](#netrc-files) configuration file - The uv credentials store - A [keyring provider](#keyring-providers) (off by default) Authentication may be used for hosts specified in the following contexts: - `[index]` - `index-url` - `extra-index-url` - `find-links` - `package @ https://...` ## netrc files [`.netrc`](https://everything.curl.dev/usingcurl/netrc) files are a long-standing plain text format for storing credentials on a system. Reading credentials from `.netrc` files is always enabled. The target file path will be loaded from the `NETRC` environment variable if defined, falling back to `~/.netrc` if not. ## The uv credentials store uv can read and write credentials from a store using the [`uv auth` commands](./cli.md). Credentials are stored in a plaintext file in uv's state directory, e.g., `~/.local/share/uv/credentials/credentials.toml` on Unix. This file is currently not intended to be edited manually. !!! note A secure, system native storage mechanism is in [preview](../preview.md) — it is still experimental and being actively developed. In the future, this will become the default storage mechanism. When enabled, uv will use the secret storage mechanism native to your operating system. On macOS, it uses the Keychain Services. On Windows, it uses the Windows Credential Manager. On Linux, it uses the DBus-based Secret Service API. Currently, uv only searches the native store for credentials it has added to the secret store — it will not retrieve credentials persisted by other applications. Set `UV_PREVIEW_FEATURES=native-auth` to use this storage mechanism. ## Keyring providers A keyring provider is a concept from `pip` allowing retrieval of credentials from an interface matching the popular [keyring](https://github.com/jaraco/keyring) Python package. The "subprocess" keyring provider invokes the `keyring` command to fetch credentials. uv does not support additional keyring provider types at this time. Set `--keyring-provider subprocess`, `UV_KEYRING_PROVIDER=subprocess`, or `tool.uv.keyring-provider = "subprocess"` to use the provider. ## Persistence of credentials If authentication is found for a single index URL or net location (scheme, host, and port), it will be cached for the duration of the command and used for other queries to that index or net location. Authentication is not cached across invocations of uv. When using `uv add`, uv _will not_ persist index credentials to the `pyproject.toml` or `uv.lock`. These files are often included in source control and distributions, so it is generally unsafe to include credentials in them. However, uv _will_ persist credentials for direct URLs, i.e., `package @ https://username:password:example.com/foo.whl`, as there is not currently a way to otherwise provide those credentials. If credentials were attached to an index URL during `uv add`, uv may fail to fetch dependencies from indexes which require authentication on subsequent operations. See the [index authentication documentation](../indexes.md#authentication) for details on persistent authentication for indexes. ## Learn more See the [index authentication documentation](../indexes.md#authentication) for details on authenticating index URLs. See the [`pip` compatibility guide](../../pip/compatibility.md#registry-authentication) for details on differences from `pip`. astral-sh-uv-9252ba6/docs/concepts/authentication/index.md000066400000000000000000000005161521221614700236150ustar00rootroot00000000000000# Authentication Authentication is required when working with private repositories or package indexes. Learn more about authentication in uv: - [Using the `uv auth` CLI](./cli.md) - [HTTP authentication](./http.md) - [Git authentication](./git.md) - [TLS certificates](./certificates.md) - [Third-party services](./third-party.md) astral-sh-uv-9252ba6/docs/concepts/authentication/third-party.md000066400000000000000000000017771521221614700247670ustar00rootroot00000000000000# Third-party services ## Authentication with alternative package indexes See the dedicated guides for authentication with popular alternative Python package indexes: - [Azure Artifacts](../../guides/integration/azure.md) - [Google Artifact Registry](../../guides/integration/google.md) - [AWS CodeArtifact](../../guides/integration/aws.md) - [JFrog Artifactory](../../guides/integration/jfrog.md) ## Hugging Face support uv supports automatic authentication for the Hugging Face Hub. Specifically, if the `HF_TOKEN` environment variable is set, uv will propagate it to requests to `huggingface.co`. This is particularly useful for accessing private scripts in Hugging Face Datasets. For example, you can run the following command to execute the script `main.py` script from a private dataset: ```console $ HF_TOKEN=hf_... uv run https://huggingface.co/datasets///resolve//main.py ``` You can disable automatic Hugging Face authentication by setting the `UV_NO_HF_TOKEN=1` environment variable. astral-sh-uv-9252ba6/docs/concepts/build-backend.md000066400000000000000000000240561521221614700221600ustar00rootroot00000000000000# The uv build backend A build backend transforms a source tree (i.e., a directory) into a source distribution or a wheel. uv supports all build backends (as specified by [PEP 517](https://peps.python.org/pep-0517/)), but also provides a native build backend (`uv_build`) that integrates tightly with uv to improve performance and user experience. ## Choosing a build backend The uv build backend is a great choice for most Python projects. It has reasonable defaults, with the goal of requiring zero configuration for most users, but provides flexible configuration to accommodate most Python project structures. It integrates tightly with uv, to improve messaging and user experience. It validates project metadata and structures, preventing common mistakes. And, finally, it's very fast. The uv build backend currently **only supports pure Python code**. An alternative backend is required to build a [library with extension modules](../concepts/projects/init.md#projects-with-extension-modules). !!! tip While the backend supports a number of options for configuring your project structure, when build scripts or a more flexible project layout are required, consider using the [hatchling](https://hatch.pypa.io/latest/config/build/#build-system) build backend instead. ## Using the uv build backend To use uv as a build backend in an existing project, add `uv_build` to the [`[build-system]`](../concepts/projects/config.md#build-systems) section in your `pyproject.toml`: ```toml title="pyproject.toml" [build-system] requires = ["uv_build>=0.11.20,<0.12"] build-backend = "uv_build" ``` !!! note The uv build backend follows the same [versioning policy](../reference/policies/versioning.md) as uv. Including an upper bound on the `uv_build` version ensures that your package continues to build correctly as new versions are released. To create a new project that uses the uv build backend, use `uv init`: ```console $ uv init ``` When the project is built, e.g., with [`uv build`](../guides/package.md), the uv build backend will be used to create the source distribution and wheel. ## Bundled build backend The build backend is published as a separate package (`uv_build`) that is optimized for portability and small binary size. However, the `uv` executable also includes a copy of the build backend, which will be used during builds performed by uv, e.g., during `uv build`, if its version is compatible with the `uv_build` requirement. If it's not compatible, a compatible version of the `uv_build` package will be used. Other build frontends, such as `python -m build`, will always use the `uv_build` package, typically choosing the latest compatible version. ## Modules Python packages are expected to contain one or more Python modules, which are directories containing an `__init__.py`. By default, a single root module is expected at `src//__init__.py`. For example, the structure for a project named `foo` would be: ```text pyproject.toml src └── foo └── __init__.py ``` uv normalizes the package name to determine the default module name: the package name is lowercased and dots and dashes are replaced with underscores, e.g., `Foo-Bar` would be converted to `foo_bar`. The `src/` directory is the default directory for module discovery. These defaults can be changed with the `module-name` and `module-root` settings. For example, to use a `FOO` module in the root directory, as in the project structure: ```text pyproject.toml FOO └── __init__.py ``` The correct build configuration would be: ```toml title="pyproject.toml" [tool.uv.build-backend] module-name = "FOO" module-root = "" ``` ## Namespace packages Namespace packages are intended for use-cases where multiple packages write modules into a shared namespace. Namespace package modules are identified by a `.` in the `module-name`. For example, to package the module `bar` in the shared namespace `foo`, the project structure would be: ```text pyproject.toml src └── foo └── bar └── __init__.py ``` And the `module-name` configuration would be: ```toml title="pyproject.toml" [tool.uv.build-backend] module-name = "foo.bar" ``` !!! important The `__init__.py` file is not included in `foo`, since it's the shared namespace module. It's also possible to have a complex namespace package with more than one root module, e.g., with the project structure: ```text pyproject.toml src ├── foo │ └── __init__.py └── bar └── __init__.py ``` While we do not recommend this structure (i.e., you should use a workspace with multiple packages instead), it is supported by setting `module-name` to a list of names: ```toml title="pyproject.toml" [tool.uv.build-backend] module-name = ["foo", "bar"] ``` For packages with many modules or complex namespaces, the `namespace = true` option can be used to avoid explicitly declaring each module name, e.g.: ```toml title="pyproject.toml" [tool.uv.build-backend] namespace = true ``` !!! warning Using `namespace = true` disables safety checks. Using an explicit list of module names is strongly recommended outside of legacy projects. The `namespace` option can also be used with `module-name` to explicitly declare the root, e.g., for the project structure: ```text pyproject.toml src └── foo ├── bar │ └── __init__.py └── baz └── __init__.py ``` The recommended configuration would be: ```toml title="pyproject.toml" [tool.uv.build-backend] module-name = "foo" namespace = true ``` ## Stub packages The build backend also supports building type stub packages, which are identified by the `-stubs` suffix on the package or module name, e.g., `foo-stubs`. The module name for type stub packages must end in `-stubs`, so uv will not normalize the `-` to an underscore. Additionally, uv will search for a `__init__.pyi` file. For example, the project structure would be: ```text pyproject.toml src └── foo-stubs └── __init__.pyi ``` Type stub modules are also supported for [namespace packages](#namespace-packages). ## File inclusion and exclusion The build backend is responsible for determining which files in a source tree should be packaged into the distributions. To determine which files to include in a source distribution, uv first adds the included files and directories, then removes the excluded files and directories. This means that exclusions always take precedence over inclusions. By default, uv excludes `__pycache__`, `*.pyc`, and `*.pyo`. When building a source distribution, the following files and directories are included: - The `pyproject.toml`. If uv detects TOML 1.1-only syntax, it issues a warning and automatically enables the `toml-backwards-compatibility` preview feature: the `pyproject.toml` is reformatted for backwards compatibility, and the original file is preserved as `pyproject.toml.orig`. Pass `--preview-feature toml-backwards-compatibility` to enable the feature explicitly and suppress the warning. - The [module](#modules) under [`tool.uv.build-backend.module-root`](../reference/settings.md#build-backend_module-root). - The files referenced by `project.license-files` and `project.readme`. - All directories under [`tool.uv.build-backend.data`](../reference/settings.md#build-backend_data). - All files matching patterns from [`tool.uv.build-backend.source-include`](../reference/settings.md#build-backend_source-include). From these, items matching [`tool.uv.build-backend.source-exclude`](../reference/settings.md#build-backend_source-exclude) and the [default excludes](../reference/settings.md#build-backend_default-excludes) are removed. When building a wheel, the following files and directories are included: - The [module](#modules) under [`tool.uv.build-backend.module-root`](../reference/settings.md#build-backend_module-root) - The files referenced by `project.license-files`, which are copied into the `.dist-info` directory. - The `project.readme`, which is copied into the project metadata. - All directories under [`tool.uv.build-backend.data`](../reference/settings.md#build-backend_data), which are copied into the `.data` directory. From these, [`tool.uv.build-backend.source-exclude`](../reference/settings.md#build-backend_source-exclude), [`tool.uv.build-backend.wheel-exclude`](../reference/settings.md#build-backend_wheel-exclude) and the default excludes are removed. The source dist excludes are applied to avoid source tree to wheel builds including more files than source tree to source distribution to wheel build. There are no specific wheel includes. There must only be one top level module, and all data files must either be under the module root or in the appropriate [data directory](../reference/settings.md#build-backend_data). Most packages store small data in the module root alongside the source code. !!! tip When using the uv build backend through a frontend that is not uv, such as pip or `python -m build`, debug logging can be enabled through environment variables with `RUST_LOG=uv=debug` or `RUST_LOG=uv=verbose`. When used through uv, the uv build backend shares the verbosity level of uv. ### Include and exclude syntax Includes are anchored, which means that `pyproject.toml` includes only `/pyproject.toml` and not `/bar/pyproject.toml`. To recursively include all files under a directory, use a `/**` suffix, e.g. `src/**`. Recursive inclusions are also anchored, e.g., `assets/**/sample.csv` includes all `sample.csv` files in `/assets` or any of its children. !!! note For performance and reproducibility, avoid patterns without an anchor such as `**/sample.csv`. Excludes are not anchored, which means that `__pycache__` excludes all directories named `__pycache__` regardless of its parent directory. All children of an exclusion are excluded as well. To anchor a directory, use a `/` prefix, e.g., `/dist` will exclude only `/dist`. All fields accepting patterns use the reduced portable glob syntax from [PEP 639](https://peps.python.org/pep-0639/#add-license-FILES-key), with the addition that characters can be escaped with a backslash. astral-sh-uv-9252ba6/docs/concepts/cache.md000066400000000000000000000247141521221614700205400ustar00rootroot00000000000000# Caching ## Dependency caching uv uses aggressive caching to avoid re-downloading (and re-building) dependencies that have already been accessed in prior runs. The specifics of uv's caching semantics vary based on the nature of the dependency: - **For registry dependencies** (like those downloaded from PyPI), uv respects HTTP caching headers. - **For direct URL dependencies**, uv respects HTTP caching headers, and also caches based on the URL itself. - **For Git dependencies**, uv caches based on the fully-resolved Git commit hash. As such, `uv pip compile` will pin Git dependencies to a specific commit hash when writing the resolved dependency set. - **For local dependencies**, uv caches based on the last-modified time of the source archive (i.e., the local `.whl` or `.tar.gz` file). For directories, uv caches based on the last-modified time of the `pyproject.toml`, `setup.py`, or `setup.cfg` file. - **For flat indexes** (i.e., `--find-links` locations), uv assumes the index contents are immutable, caching each file by name. As such, replacing a file with new contents under the same name (e.g., rebuilding a wheel into a `--find-links` directory) will not be picked up until the cache is refreshed. If you're running into caching issues, uv includes a few escape hatches: - To clear the cache entirely, run `uv cache clean`. To clear the cache for a specific package, run `uv cache clean `. For example, `uv cache clean ruff` will clear the cache for the `ruff` package. - To force uv to revalidate cached data for all dependencies, pass `--refresh` to any command (e.g., `uv sync --refresh` or `uv pip install --refresh ...`). - To force uv to revalidate cached data for a specific dependency pass `--refresh-package` to any command (e.g., `uv sync --refresh-package ruff` or `uv pip install --refresh-package ruff ...`). - To force uv to ignore existing installed versions, pass `--reinstall` to any installation command (e.g., `uv sync --reinstall` or `uv pip install --reinstall ...`). (Consider running `uv cache clean ` first, to ensure that the cache is cleared prior to reinstallation.) As a special case, uv will always rebuild and reinstall any local directory dependencies passed explicitly on the command-line (e.g., `uv pip install .`). ## Dynamic metadata By default, uv will _only_ rebuild and reinstall local directory dependencies (e.g., editables) if the `pyproject.toml`, `setup.py`, or `setup.cfg` file in the directory root has changed, or if a `src` directory is added or removed. This is a heuristic and, in some cases, may lead to fewer re-installs than desired. To incorporate additional information into the cache key for a given package, you can add cache key entries under [`tool.uv.cache-keys`](https://docs.astral.sh/uv/reference/settings/#cache-keys), which covers both file paths and Git commit hashes. Setting [`tool.uv.cache-keys`](https://docs.astral.sh/uv/reference/settings/#cache-keys) will replace defaults, so any necessary files (like `pyproject.toml`) should still be included in the user-defined cache keys. For example, if a project specifies dependencies in `pyproject.toml` but uses [`setuptools-scm`](https://pypi.org/project/setuptools-scm/) to manage its version, and should thus be rebuilt whenever the commit hash or dependencies change, you can add the following to the project's `pyproject.toml`: ```toml title="pyproject.toml" [tool.uv] cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true } }] ``` If your dynamic metadata incorporates information from the set of Git tags, you can expand the cache key to include the tags: ```toml title="pyproject.toml" [tool.uv] cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true, tags = true } }] ``` Similarly, if a project reads from a `requirements.txt` to populate its dependencies, you can add the following to the project's `pyproject.toml`: ```toml title="pyproject.toml" [tool.uv] cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }] ``` Globs are supported for `file` keys, following the syntax of the [`glob`](https://docs.rs/glob/0.3.1/glob/struct.Pattern.html) crate. For example, to invalidate the cache whenever a `.toml` file in the project directory or any of its subdirectories is modified, use the following: ```toml title="pyproject.toml" [tool.uv] cache-keys = [{ file = "**/*.toml" }] ``` !!! note The use of globs can be expensive, as uv may need to walk the filesystem to determine whether any files have changed. This may, in turn, requiring traversal of large or deeply nested directories. Similarly, if a project relies on an environment variable, you can add the following to the project's `pyproject.toml` to invalidate the cache whenever the environment variable changes: ```toml title="pyproject.toml" [tool.uv] cache-keys = [{ file = "pyproject.toml" }, { env = "MY_ENV_VAR" }] ``` Finally, to invalidate a project whenever a specific directory (like `src`) is created or removed, add the following to the project's `pyproject.toml`: ```toml title="pyproject.toml" [tool.uv] cache-keys = [{ file = "pyproject.toml" }, { dir = "src" }] ``` Note that the `dir` key will only track changes to the directory itself, and not arbitrary changes within the directory. As an escape hatch, if a project uses `dynamic` metadata that isn't covered by `tool.uv.cache-keys`, you can instruct uv to _always_ rebuild and reinstall it by adding the project to the `tool.uv.reinstall-package` list: ```toml title="pyproject.toml" [tool.uv] reinstall-package = ["my-package"] ``` This will force uv to rebuild and reinstall `my-package` on every run, regardless of whether the package's `pyproject.toml`, `setup.py`, or `setup.cfg` file has changed. ## Cache safety It's safe to run multiple uv commands concurrently, even against the same virtual environment. uv's cache is designed to be thread-safe and append-only, and thus robust to multiple concurrent readers and writers. uv applies a file-based lock to the target virtual environment when installing, to avoid concurrent modifications across processes. Note that it's _never_ safe to modify the cache directly (e.g., by removing a file or directory). ## Clearing the cache uv provides a few different mechanisms for removing entries from the cache: - `uv cache clean` removes _all_ cache entries from the cache directory, clearing it out entirely. - `uv cache clean ruff` removes all cache entries for the `ruff` package, useful for invalidating the cache for a single or finite set of packages. - `uv cache prune` removes all _unused_ cache entries. For example, the cache directory may contain entries created in previous uv versions that are no longer necessary and can be safely removed. `uv cache prune` is safe to run periodically, to keep the cache directory clean. uv blocks cache-modifying operations while other uv commands are running. By default, those `uv cache` commands have a 5 min timeout waiting for other uv processes to terminate to avoid deadlocks. This timeout can be changed with [`UV_LOCK_TIMEOUT`](../reference/environment.md#uv_lock_timeout). In cases where it is known that no other uv processes are reading or writing from the cache, `--force` can be used to ignore the lock. ## Caching in continuous integration It's common to cache package installation artifacts in continuous integration environments (like GitHub Actions or GitLab CI) to speed up subsequent runs. By default, uv caches both the wheels that it builds from source and the pre-built wheels that it downloads directly, to enable high-performance package installation. However, in continuous integration environments, persisting pre-built wheels may be undesirable. With uv, it turns out that it's often faster to _omit_ pre-built wheels from the cache (and instead re-download them from the registry on each run). On the other hand, caching wheels that are built from source tends to be worthwhile, since the wheel building process can be expensive, especially for extension modules. To support this caching strategy, uv provides a `uv cache prune --ci` command, which removes all pre-built wheels and unzipped source distributions from the cache, but retains any wheels that were built from source. We recommend running `uv cache prune --ci` at the end of your continuous integration job to ensure maximum cache efficiency. For an example, see the [GitHub integration guide](../guides/integration/github.md#caching). ## Cache directory uv determines the cache directory according to, in order: 1. A temporary cache directory, if `--no-cache` was requested. 2. The specific cache directory specified via `--cache-dir`, `UV_CACHE_DIR`, or [`tool.uv.cache-dir`](../reference/settings.md#cache-dir). 3. A system-appropriate cache directory, e.g., `$XDG_CACHE_HOME/uv` or `$HOME/.cache/uv` on Unix and `%LOCALAPPDATA%\uv\cache` on Windows !!! note uv _always_ requires a cache directory. When `--no-cache` is requested, uv will still use a temporary cache for sharing data within that single invocation. In most cases, `--refresh` should be used instead of `--no-cache` — as it will update the cache for subsequent operations but not read from the cache. It is important for performance for the cache directory to be located on the same file system as the Python environment uv is operating on. Otherwise, uv will not be able to link files from the cache into the environment and will instead need to fallback to slow copy operations. ## Cache versioning The uv cache is composed of a number of buckets (e.g., a bucket for wheels, a bucket for source distributions, a bucket for Git repositories, and so on). Each bucket is versioned, such that if a release contains a breaking change to the cache format, uv will not attempt to read from or write to an incompatible cache bucket. For example, uv 0.4.13 included a breaking change to the core metadata bucket. As such, the bucket version was increased from v12 to v13. Within a cache version, changes are guaranteed to be both forwards- and backwards-compatible. Since changes in the cache format are accompanied by changes in the cache version, multiple versions of uv can safely read and write to the same cache directory. However, if the cache version changed between a given pair of uv releases, then those releases may not be able to share the same underlying cache entries. For example, it's safe to use a single shared cache for uv 0.4.12 and uv 0.4.13, though the cache itself may contain duplicate entries in the core metadata bucket due to the change in cache version. astral-sh-uv-9252ba6/docs/concepts/configuration-files.md000066400000000000000000000132161521221614700234370ustar00rootroot00000000000000# Configuration files uv supports persistent configuration files at both the project- and user-level. Specifically, uv will search for a `pyproject.toml` or `uv.toml` file in the current directory, or in the nearest parent directory. !!! note For `tool` commands, which operate at the user level, local configuration files will be ignored. Instead, uv will exclusively read from user-level configuration (e.g., `~/.config/uv/uv.toml`) and system-level configuration (e.g., `/etc/uv/uv.toml`). In workspaces, uv will begin its search at the workspace root, ignoring any configuration defined in workspace members. Since the workspace is locked as a single unit, configuration is shared across all members. If a `pyproject.toml` file is found, uv will read configuration from the `[tool.uv]` table. For example, to set a persistent index URL, add the following to a `pyproject.toml`: ```toml title="pyproject.toml" [[tool.uv.index]] url = "https://test.pypi.org/simple" default = true ``` (If there is no such table, the `pyproject.toml` file will be ignored, and uv will continue searching in the directory hierarchy.) uv will also search for `uv.toml` files, which follow an identical structure, but omit the `[tool.uv]` prefix. For example: ```toml title="uv.toml" [[index]] url = "https://test.pypi.org/simple" default = true ``` !!! note `uv.toml` files take precedence over `pyproject.toml` files, so if both `uv.toml` and `pyproject.toml` files are present in a directory, configuration will be read from `uv.toml`, and `[tool.uv]` section in the accompanying `pyproject.toml` will be ignored. uv will also discover `uv.toml` configuration files in the user- and system-level [configuration directories](../reference/storage.md#configuration-directories), e.g., user-level configuration in `~/.config/uv/uv.toml` on macOS and Linux, or `%APPDATA%\uv\uv.toml` on Windows, and system-level configuration at `/etc/uv/uv.toml` on macOS and Linux, or `%PROGRAMDATA%\uv\uv.toml` on Windows. !!! important User- and system-level configuration files cannot use the `pyproject.toml` format. If project-, user-, and system-level configuration files are found, the settings will be merged, with project-level configuration taking precedence over the user-level configuration, and user-level configuration taking precedence over the system-level configuration. (If multiple system-level configuration files are found, e.g., at both `/etc/uv/uv.toml` and `$XDG_CONFIG_DIRS/uv/uv.toml`, only the first-discovered file will be used, with XDG taking priority.) For example, if a string, number, or boolean is present in both the project- and user-level configuration tables, the project-level value will be used, and the user-level value will be ignored. If an array is present in both tables, the arrays will be concatenated, with the project-level settings appearing earlier in the merged array. Settings provided via environment variables take precedence over persistent configuration, and settings provided via the command line take precedence over both. uv accepts a `--no-config` command-line argument which, when provided, disables the discovery of any persistent configuration. uv also accepts a `--config-file` command-line argument, which accepts a path to a `uv.toml` to use as the configuration file. When provided, this file will be used in place of _any_ discovered configuration files (e.g., user-level configuration will be ignored). ## Settings See the [settings reference](../reference/settings.md) for an enumeration of the available settings. ## Environment variable files `uv run` can load environment variables from dotenv files (e.g., `.env`, `.env.local`, `.env.development`), powered by the [`dotenvy`](https://github.com/allan2/dotenvy) crate. To load a `.env` file from a dedicated location, set the `UV_ENV_FILE` environment variable, or pass the `--env-file` flag to `uv run`. For example, to load environment variables from a `.env` file in the current working directory: ```console $ echo "MY_VAR='Hello, world!'" > .env $ uv run --env-file .env -- python -c 'import os; print(os.getenv("MY_VAR"))' Hello, world! ``` The `--env-file` flag can be provided multiple times, with subsequent files overriding values defined in previous files. To provide multiple files via the `UV_ENV_FILE` environment variable, separate the paths with a space (e.g., `UV_ENV_FILE="/path/to/file1 /path/to/file2"`). To disable dotenv loading (e.g., to override `UV_ENV_FILE` or the `--env-file` command-line argument), set the `UV_NO_ENV_FILE` environment variable to `1`, or pass the`--no-env-file` flag to `uv run`. If the same variable is defined in the environment and in a `.env` file, the value from the environment will take precedence. ## Configuring the pip interface A dedicated [`[tool.uv.pip]`](../reference/settings.md#pip) section is provided for configuring _just_ the `uv pip` command line interface. Settings in this section will not apply to `uv` commands outside the `uv pip` namespace. However, many of the settings in this section have corollaries in the top-level namespace which _do_ apply to the `uv pip` interface unless they are overridden by a value in the `uv.pip` section. The `uv.pip` settings are designed to adhere closely to pip's interface and are declared separately to retain compatibility while allowing the global settings to use alternate designs (e.g., `--no-build`). As an example, setting the `index-url` under `[tool.uv.pip]`, as in the following `pyproject.toml`, would only affect the `uv pip` subcommands (e.g., `uv pip install`, but not `uv sync`, `uv lock`, or `uv run`): ```toml title="pyproject.toml" [tool.uv.pip] index-url = "https://test.pypi.org/simple" ``` astral-sh-uv-9252ba6/docs/concepts/index.md000066400000000000000000000010411521221614700205700ustar00rootroot00000000000000# Concepts overview Read the concept documents to learn more about uv's features: - [Projects](./projects/index.md) - [Tools](./tools.md) - [Python versions](./python-versions.md) - [Configuration files](./configuration-files.md) - [Package indexes](./indexes.md) - [Resolution](./resolution.md) - [The uv build backend](./build-backend.md) - [Authentication](./authentication/index.md) - [Caching](./cache.md) - [The pip interface](../pip/index.md) Looking for a quick introduction to features? See the [guides](../guides/index.md) instead. astral-sh-uv-9252ba6/docs/concepts/indexes.md000066400000000000000000000321341521221614700211270ustar00rootroot00000000000000# Package indexes By default, uv uses the [Python Package Index (PyPI)](https://pypi.org) for dependency resolution and package installation. However, uv can be configured to use other package indexes, including private indexes, via the `[[tool.uv.index]]` configuration option (and `--index`, the analogous command-line option). ## Defining an index To include an additional index when resolving dependencies, add a `[[tool.uv.index]]` entry to your `pyproject.toml`: ```toml [[tool.uv.index]] # Optional name for the index. name = "pytorch" # Required URL for the index. url = "https://download.pytorch.org/whl/cpu" ``` Indexes are prioritized in the order in which they’re defined, such that the first index listed in the configuration file is the first index consulted when resolving dependencies, with indexes provided via the command line taking precedence over those in the configuration file. By default, uv includes the Python Package Index (PyPI) as the "default" index, i.e., the index used when a package is not found on any other index. To exclude PyPI from the list of indexes, set `default = true` on another index entry (or use the `--default-index` command-line option): ```toml [[tool.uv.index]] name = "pytorch" url = "https://download.pytorch.org/whl/cpu" default = true ``` The default index is always treated as lowest priority, regardless of its position in the list of indexes. Index names may only contain alphanumeric characters, dashes, underscores, and periods, and must be valid ASCII. When providing an index on the command line (with `--index` or `--default-index`) or through an environment variable (`UV_INDEX` or `UV_DEFAULT_INDEX`), names are optional but can be included using the `=` syntax, as in: ```shell # On the command line. $ uv lock --index pytorch=https://download.pytorch.org/whl/cpu # Via an environment variable. $ UV_INDEX=pytorch=https://download.pytorch.org/whl/cpu uv lock ``` ## Pinning a package to an index A package can be pinned to a specific index by specifying the index in its `tool.uv.sources` entry. For example, to ensure that `torch` is _always_ installed from the `pytorch` index, add the following to your `pyproject.toml`: ```toml [tool.uv.sources] torch = { index = "pytorch" } [[tool.uv.index]] name = "pytorch" url = "https://download.pytorch.org/whl/cpu" ``` Similarly, to pull from a different index based on the platform, you can provide a list of sources disambiguated by environment markers: ```toml title="pyproject.toml" [project] dependencies = ["torch"] [tool.uv.sources] torch = [ { index = "pytorch-cpu", marker = "sys_platform == 'darwin'"}, { index = "pytorch-cu130", marker = "sys_platform != 'darwin'"}, ] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" [[tool.uv.index]] name = "pytorch-cu130" url = "https://download.pytorch.org/whl/cu130" ``` An index can be marked as `explicit = true` to prevent packages from being installed from that index unless explicitly pinned to it. For example, to ensure that `torch` is installed from the `pytorch` index, but all other packages are installed from PyPI, add the following to your `pyproject.toml`: ```toml [tool.uv.sources] torch = { index = "pytorch" } [[tool.uv.index]] name = "pytorch" url = "https://download.pytorch.org/whl/cpu" explicit = true ``` Named indexes referenced via `tool.uv.sources` must be defined within the project's `pyproject.toml` file; indexes provided via the command-line, environment variables, or user-level configuration will not be recognized. If an index is marked as both `default = true` and `explicit = true`, it will be treated as an explicit index (i.e., only usable via `tool.uv.sources`) while also removing PyPI as the default index. ## Searching across multiple indexes By default, uv will stop at the first index on which a given package is available, and limit resolutions to those present on that first index (`first-index`). For example, if an internal index is specified via `[[tool.uv.index]]`, uv's behavior is such that if a package exists on that internal index, it will _always_ be installed from that internal index, and never from PyPI. The intent is to prevent "dependency confusion" attacks, in which an attacker publishes a malicious package on PyPI with the same name as an internal package, thus causing the malicious package to be installed instead of the internal package. See, for example, [the `torchtriton` attack](https://pytorch.org/blog/compromised-nightly-dependency/) from December 2022. To opt in to alternate index behaviors, use the`--index-strategy` command-line option, or the `UV_INDEX_STRATEGY` environment variable, which supports the following values: - `first-index` (default): Search for each package across all indexes, limiting the candidate versions to those present in the first index that contains the package. - `unsafe-first-match`: Search for each package across all indexes, but prefer the first index with a compatible version, even if newer versions are available on other indexes. - `unsafe-best-match`: Search for each package across all indexes, and select the best version from the combined set of candidate versions. While `unsafe-best-match` is the closest to pip's behavior, it exposes users to the risk of "dependency confusion" attacks. ## Authentication Most private package indexes require authentication to access packages, typically via a username and password (or access token). !!! tip See the dedicated guides for authenticating with specific private index providers: [Azure Artifacts](../guides/integration/azure.md), [Google Artifact Registry](../guides/integration/google.md), [AWS CodeArtifact](../guides/integration/aws.md), and [JFrog Artifactory](../guides/integration/jfrog.md). ### Providing credentials directly Credentials can be provided directly via environment variables or by embedding them in the URL. For example, given an index named `internal-proxy` that requires a username (`public`) and password (`koala`), define the index (without credentials) in your `pyproject.toml`: ```toml [[tool.uv.index]] name = "internal-proxy" url = "https://example.com/simple" ``` From there, you can set the `UV_INDEX_INTERNAL_PROXY_USERNAME` and `UV_INDEX_INTERNAL_PROXY_PASSWORD` environment variables, where `INTERNAL_PROXY` is the uppercase version of the index name, with non-alphanumeric characters replaced by underscores: ```sh export UV_INDEX_INTERNAL_PROXY_USERNAME=public export UV_INDEX_INTERNAL_PROXY_PASSWORD=koala ``` By providing credentials via environment variables, you can avoid storing sensitive information in the plaintext `pyproject.toml` file. Alternatively, credentials can be embedded directly in the index definition: ```toml [[tool.uv.index]] name = "internal" url = "https://public:koala@pypi-proxy.corp.dev/simple" ``` For security purposes, credentials are _never_ stored in the `uv.lock` file; as such, uv _must_ have access to the authenticated URL at installation time. ### Using credential providers In addition to providing credentials directly, uv supports discovery of credentials from netrc and keyring. See the [HTTP authentication](./authentication/http.md) documentation for details on setting up specific credential providers. By default, uv will attempt an unauthenticated request before querying providers. If the request fails, uv will search for credentials. If credentials are found, an authenticated request will be attempted. !!! note If a username is set, uv will search for credentials before making an unauthenticated request. Some indexes (e.g., GitLab) will forward unauthenticated requests to a public index, like PyPI — which means that uv will not search for credentials. This behavior can be changed per-index, using the `authenticate` setting. For example, to always search for credentials: ```toml hl_lines="4" [[tool.uv.index]] name = "example" url = "https://example.com/simple" authenticate = "always" ``` When `authenticate` is set to `always`, uv will eagerly search for credentials and error if credentials cannot be found. ### Ignoring error codes when searching across indexes When using the [first-index strategy](#searching-across-multiple-indexes), uv will stop searching across indexes if an HTTP 401 Unauthorized or HTTP 403 Forbidden status code is encountered. The one exception is that uv will ignore 403s when searching the `pytorch` index (since this index returns a 403 when a package is not present). To configure which error codes are ignored for an index, use the `ignored-error-codes` setting. For example, to ignore 403s (but not 401s) for a private index: ```toml [[tool.uv.index]] name = "private-index" url = "https://private-index.com/simple" authenticate = "always" ignore-error-codes = [403] ``` uv will always continue searching across indexes when it encounters a `404 Not Found`. This cannot be overridden. ### Disabling authentication To prevent leaking credentials, authentication can be disabled for an index: ```toml hl_lines="4" [[tool.uv.index]] name = "example" url = "https://example.com/simple" authenticate = "never" ``` When `authenticate` is set to `never`, uv will never search for credentials for the given index and will error if credentials are provided directly. ### Customizing cache control headers By default, uv will respect the cache control headers provided by the index. For example, PyPI serves package metadata with a `max-age=600` header, thereby allowing uv to cache package metadata for 10 minutes; and wheels and source distributions with a `max-age=365000000, immutable` header, thereby allowing uv to cache artifacts indefinitely. To override the cache control headers for an index, use the `cache-control` setting: ```toml [[tool.uv.index]] name = "example" url = "https://example.com/simple" cache-control = { api = "max-age=600", files = "max-age=365000000, immutable" } ``` The `cache-control` setting accepts an object with two optional keys: - `api`: Controls caching for Simple API requests (package metadata). - `files`: Controls caching for artifact downloads (wheels and source distributions). The values for these keys are strings that follow the [HTTP Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) syntax. For example, to force uv to always revalidate package metadata, set `api = "no-cache"`: ```toml [[tool.uv.index]] name = "example" url = "https://example.com/simple" cache-control = { api = "no-cache" } ``` This setting is most commonly used to override the default cache control headers for private indexes that otherwise disable caching, often unintentionally. We typically recommend following PyPI's approach to caching headers, i.e., setting `api = "max-age=600"` and `files = "max-age=365000000, immutable"`. ### Configuring `exclude-newer` for an index If you're using [`exclude-newer`](./resolution.md#reproducible-resolutions), you can configure a different cutoff for a specific index: ```toml [[tool.uv.index]] name = "internal" url = "https://internal.example.com/simple" exclude-newer = "7 days" ``` Index-specific values only affect packages served from that index. Package-specific `exclude-newer-package` overrides still take precedence. If an index does not provide `upload-time` metadata, you can disable the cutoff for that index entirely: ```toml [[tool.uv.index]] name = "internal" url = "https://internal.example.com/simple" exclude-newer = false ``` ## "Flat" indexes By default, `[[tool.uv.index]]` entries are assumed to be PyPI-style registries that implement the [PEP 503](https://peps.python.org/pep-0503/) Simple Repository API. However, uv also supports "flat" indexes, which are local directories or HTML pages that contain flat lists of wheels and source distributions. In pip, such indexes are specified using the `--find-links` option. To define a flat index in your `pyproject.toml`, use the `format = "flat"` option: ```toml [[tool.uv.index]] name = "example" url = "/path/to/directory" format = "flat" ``` Flat indexes support the same feature set as Simple Repository API indexes (e.g., `explicit = true`); you can also pin a package to a flat index using `tool.uv.sources`. ## `--index-url` and `--extra-index-url` In addition to the `[[tool.uv.index]]` configuration option, uv supports pip-style `--index-url` and `--extra-index-url` command-line options for compatibility, where `--index-url` defines the default index and `--extra-index-url` defines additional indexes. These options can be used in conjunction with the `[[tool.uv.index]]` configuration option, and follow the same prioritization rules: - The default index is always treated as lowest priority, whether defined via the legacy `--index-url` argument, the recommended `--default-index` argument, or a `[[tool.uv.index]]` entry with `default = true`. - Indexes are consulted in the order in which they’re defined, either via the legacy `--extra-index-url` argument, the recommended `--index` argument, or `[[tool.uv.index]]` entries. In effect, `--index-url` and `--extra-index-url` can be thought of as unnamed `[[tool.uv.index]]` entries, with `default = true` enabled for the former. In that context, `--index-url` maps to `--default-index`, and `--extra-index-url` maps to `--index`. astral-sh-uv-9252ba6/docs/concepts/preview.md000066400000000000000000000056231521221614700211540ustar00rootroot00000000000000# Preview features uv includes opt-in preview features to provide an opportunity for community feedback and increase confidence that changes are a net-benefit before enabling them for everyone. ## Enabling preview features To enable all preview features, use the `--preview` flag: ```console $ uv run --preview ... ``` Or, set the `UV_PREVIEW` environment variable: ```console $ UV_PREVIEW=1 uv run ... ``` To enable specific preview features, use the `--preview-features` flag: ```console $ uv run --preview-features foo ... ``` The `--preview-features` flag can be repeated to enable multiple features: ```console $ uv run --preview-features foo --preview-features bar ... ``` Or, features can be provided in a comma separated list: ```console $ uv run --preview-features foo,bar ... ``` The `UV_PREVIEW_FEATURES` environment variable can be used similarly, e.g.: ```console $ UV_PREVIEW_FEATURES=foo,bar uv run ... ``` For backwards compatibility, enabling preview features that do not exist will warn, but not error. ## Using preview features Often, preview features can be used without changing any preview settings if the behavior change is gated by some sort of user interaction, For example, while `pylock.toml` support is in preview, you can use `uv pip install` with a `pylock.toml` file without additional configuration because specifying the `pylock.toml` file indicates you want to use the feature. However, a warning will be displayed that the feature is in preview. The preview feature can be enabled to silence the warning. ## Available preview features The following preview features are available: - `add-bounds`: Allows configuring the [default bounds for `uv add`](../reference/settings.md#add-bounds) invocations. - `json-output`: Allows `--output-format json` for various uv commands. - `package-conflicts`: Allows defining workspace conflicts at the package level. - `pylock`: Allows installing from `pylock.toml` files. - `python-install-default`: Allows [installing `python` and `python3` executables](./python-versions.md#installing-python-executables). - `format`: Allows using `uv format`. - `index-exclude-newer`: Allows setting `exclude-newer` on configured package indexes. - `azure-endpoint`: Allows signing requests to Azure Blob Storage endpoints with Azure credentials. - `native-auth`: Enables storage of credentials in a [system-native location](../concepts/authentication/http.md#the-uv-credentials-store). - `auth-helper`: Allows using `uv auth helper` as a credential helper for external tools. - `workspace-metadata`: Allows using `uv workspace metadata`. - `workspace-dir`: Allows using `uv workspace dir`. - `workspace-list`: Allows using `uv workspace list`. - `malware-check`: Allows `uv sync` and other commands to check for malware using [OSV](https://osv.dev) before installing packages. ## Disabling preview features The `--no-preview` option can be used to disable preview features. astral-sh-uv-9252ba6/docs/concepts/projects/000077500000000000000000000000001521221614700207745ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/concepts/projects/build.md000066400000000000000000000057431521221614700224260ustar00rootroot00000000000000# Building distributions To distribute your project to others (e.g., to upload it to an index like PyPI), you'll need to build it into a distributable format. Python projects are typically distributed as both source distributions (sdists) and binary distributions (wheels). The former is typically a `.tar.gz` or `.zip` file containing the project's source code along with some additional metadata, while the latter is a `.whl` file containing pre-built artifacts that can be installed directly. !!! important When using `uv build`, uv acts as a [build frontend](https://peps.python.org/pep-0517/#terminology-and-goals) and only determines the Python version to use and invokes the build backend. The details of the builds, such as the included files and the distribution filenames, are determined by the build backend, as defined in [`[build-system]`](./config.md#build-systems). Information about build configuration can be found in the respective tool's documentation. ## Using `uv build` `uv build` can be used to build both source distributions and binary distributions for your project. By default, `uv build` will build the project in the current directory, and place the built artifacts in a `dist/` subdirectory: ```console $ uv build $ ls dist/ example-0.1.0-py3-none-any.whl example-0.1.0.tar.gz ``` You can build the project in a different directory by providing a path to `uv build`, e.g., `uv build path/to/project`. `uv build` will first build a source distribution, and then build a binary distribution (wheel) from that source distribution. You can limit `uv build` to building a source distribution with `uv build --sdist`, a binary distribution with `uv build --wheel`, or build both distributions from source with `uv build --sdist --wheel`. ## Build constraints `uv build` accepts `--build-constraint`, which can be used to constrain the versions of any build requirements during the build process. When coupled with `--require-hashes`, uv will enforce that the requirement used to build the project match specific, known hashes, for reproducibility. For example, given the following `constraints.txt`: ```text setuptools==68.2.2 --hash=sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a ``` Running the following would build the project with the specified version of `setuptools`, and verify that the downloaded `setuptools` distribution matches the specified hash: ```console $ uv build --build-constraint constraints.txt --require-hashes ``` ## Preventing publish to PyPI If you have internal packages that you do not want to be published, you can mark them as private: ```toml [project] classifiers = ["Private :: Do Not Upload"] ``` This setting makes PyPI reject your uploaded package from publishing. It does not affect security or privacy settings on alternative registries. We also recommend only generating [per-project PyPI API tokens](https://pypi.org/help/#apitoken): Without a PyPI token matching the project, it can't be accidentally published. astral-sh-uv-9252ba6/docs/concepts/projects/config.md000066400000000000000000000557731521221614700226040ustar00rootroot00000000000000# Configuring projects ## Python version requirement Projects may declare the Python versions supported by the project in the `project.requires-python` field of the `pyproject.toml`. It is recommended to set a `requires-python` value: ```toml title="pyproject.toml" hl_lines="4" [project] name = "example" version = "0.1.0" requires-python = ">=3.12" ``` The Python version requirement determines the Python syntax that is allowed in the project and affects selection of dependency versions (they must support the same Python version range). ## Entry points [Entry points](https://packaging.python.org/en/latest/specifications/entry-points/#entry-points) are the official term for an installed package to advertise interfaces. These include: - [Command line interfaces](#command-line-interfaces) - [Graphical user interfaces](#graphical-user-interfaces) - [Plugin entry points](#plugin-entry-points) !!! important Using the entry point tables requires a [build system](#build-systems) to be defined. ### Command-line interfaces Projects may define command line interfaces (CLIs) for the project in the `[project.scripts]` table of the `pyproject.toml`. For example, to declare a command called `hello` that invokes the `hello` function in the `example` module: ```toml title="pyproject.toml" [project.scripts] hello = "example:hello" ``` Then, the command can be run from a console: ```console $ uv run hello ``` ### Graphical user interfaces Projects may define graphical user interfaces (GUIs) for the project in the `[project.gui-scripts]` table of the `pyproject.toml`. !!! important These are only different from [command-line interfaces](#command-line-interfaces) on Windows, where they are wrapped by a GUI executable so they can be started without a console. On other platforms, they behave the same. For example, to declare a command called `hello` that invokes the `app` function in the `example` module: ```toml title="pyproject.toml" [project.gui-scripts] hello = "example:app" ``` ### Plugin entry points Projects may define entry points for plugin discovery in the [`[project.entry-points]`](https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/#using-package-metadata) table of the `pyproject.toml`. For example, to register the `example-plugin-a` package as a plugin for `example`: ```toml title="pyproject.toml" [project.entry-points.'example.plugins'] a = "example_plugin_a" ``` Then, in `example`, plugins would be loaded with: ```python title="example/__init__.py" from importlib.metadata import entry_points for plugin in entry_points(group='example.plugins'): plugin.load() ``` !!! note The `group` key can be an arbitrary value, it does not need to include the package name or "plugins". However, it is recommended to namespace the key by the package name to avoid collisions with other packages. ## Build systems A build system determines how the project should be packaged and installed. Projects may declare and configure a build system in the `[build-system]` table of the `pyproject.toml`. uv uses the presence of a build system to determine if a project contains a package that should be installed in the project virtual environment. If a build system is not defined, uv will not attempt to build or install the project itself, just its dependencies. If a build system is defined, uv will build and install the project into the project environment. The `--build-backend` option can be provided to `uv init` to create a packaged project with an appropriate layout. The `--package` option can be provided to `uv init` to create a packaged project with the default build system. !!! note While uv will not build and install the current project without a build system definition, the presence of a `[build-system]` table is not required in other packages. For legacy reasons, if a build system is not defined, then `setuptools.build_meta:__legacy__` is used to build the package. Packages you depend on may not explicitly declare their build system but are still installable. Similarly, if you [add a dependency on a local project](./dependencies.md#path) or install it with `uv pip`, uv will attempt to build and install it regardless of the presence of a `[build-system]` table. Build systems are used to power the following features: - Including or excluding files from distributions - Editable installation behavior - Dynamic project metadata - Compilation of native code - Vendoring shared libraries To configure these features, refer to the documentation of your chosen build system. ## Project packaging As discussed in [build systems](#build-systems), a Python project must be built to be installed. This process is generally referred to as "packaging". You probably need a package if you want to: - Add commands to the project - Distribute the project to others - Use a `src` and `test` layout - Write a library You probably _do not_ need a package if you are: - Writing scripts - Building a simple application - Using a flat layout While uv usually uses the declaration of a [build system](#build-systems) to determine if a project should be packaged, uv also allows overriding this behavior with the [`tool.uv.package`](../../reference/settings.md#package) setting. Setting `tool.uv.package = true` will force a project to be built and installed into the project environment. If no build system is defined, uv will use the setuptools legacy backend. Setting `tool.uv.package = false` will force a project package _not_ to be built and installed into the project environment. uv will ignore a declared build system when interacting with the project; however, uv will still respect explicit attempts to build the project such as invoking `uv build`. ## Project environment path The `UV_PROJECT_ENVIRONMENT` environment variable can be used to configure the project virtual environment path (`.venv` by default). If a relative path is provided, it will be resolved relative to the workspace root. If an absolute path is provided, it will be used as-is, i.e., a child directory will not be created for the environment. If an environment is not present at the provided path, uv will create it. This option can be used to write to the system Python environment, though it is not recommended. `uv sync` will remove extraneous packages from the environment by default and, as such, may leave the system in a broken state. To target the system environment, set `UV_PROJECT_ENVIRONMENT` to the prefix of the Python installation. For example, on Debian-based systems, this is usually `/usr/local`: ```console $ python -c "import sysconfig; print(sysconfig.get_config_var('prefix'))" /usr/local ``` To target this environment, you'd export `UV_PROJECT_ENVIRONMENT=/usr/local`. !!! important If an absolute path is provided and the setting is used across multiple projects, the environment will be overwritten by invocations in each project. This setting is only recommended for use for a single project in CI or Docker images. !!! note By default, uv does not read the `VIRTUAL_ENV` environment variable during project operations. A warning will be displayed if `VIRTUAL_ENV` is set to a different path than the project's environment. The `--active` flag can be used to opt-in to respecting `VIRTUAL_ENV`. The `--no-active` flag can be used to silence the warning. ## Build isolation By default, uv builds all packages in isolated virtual environments alongside their declared build dependencies, as per [PEP 517](https://peps.python.org/pep-0517/). Some packages are incompatible with this approach to build isolation, be it intentionally or unintentionally. For example, packages like [`flash-attn`](https://pypi.org/project/flash-attn/) and [`deepspeed`](https://pypi.org/project/deepspeed/) need to build against the same version of PyTorch that is installed in the project environment; by building them in an isolated environment, they may inadvertently build against a different version of PyTorch, leading to runtime errors. In other cases, packages may accidentally omit necessary dependencies in their declared build dependency list. For example, [`cchardet`](https://pypi.org/project/cchardet/) requires `cython` to be installed in the project environment prior to installing `cchardet`, but does not declare it as a build dependency. To address these issues, uv supports two separate approaches to modifying the build isolation behavior: 1. **Augmenting the list of build dependencies**: This allows you to install a package in an isolated environment, but with additional build dependencies that are not declared by the package itself via the [`extra-build-dependencies`](../../reference/settings.md#extra-build-dependencies) setting. For packages like `flash-attn`, you can even enforce that those build dependencies (like `torch`) match the version of the package that is or will be installed in the project environment. 1. **Disabling build isolation for specific packages**: This allows you to install a package without building it in an isolated environment. When possible, we recommend augmenting the build dependencies rather than disabling build isolation entirely, as the latter approach requires that the build dependencies are installed in the project environment _prior_ to installing the package itself, which can lead to more complex installation steps, the inclusion of extraneous packages in the project environment, and difficulty in reproducing the project environment in other contexts. ### Augmenting build dependencies To augment the list of build dependencies for a specific package, add it to the [`extra-build-dependencies`](../../reference/settings.md#extra-build-dependencies) list in your `pyproject.toml`. For example, to build `cchardet` with `cython` as an additional build dependency, include the following in your `pyproject.toml`: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["cchardet"] [tool.uv.extra-build-dependencies] cchardet = ["cython"] ``` To ensure that a build dependency matches the version of the package that is or will be installed in the project environment, set `match-runtime = true` in the `extra-build-dependencies` table. For example, to build `deepspeed` with `torch` as an additional build dependency, include the following in your `pyproject.toml`: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["deepspeed", "torch"] [tool.uv.extra-build-dependencies] deepspeed = [{ requirement = "torch", match-runtime = true }] ``` This will ensure that `deepspeed` is built with the same version of `torch` that is installed in the project environment. Similarly, to build `flash-attn` with `torch` as an additional build dependency, include the following in your `pyproject.toml`: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["flash-attn", "torch"] [tool.uv.extra-build-dependencies] flash-attn = [{ requirement = "torch", match-runtime = true }] [tool.uv.extra-build-variables] flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } ``` !!! note The `FLASH_ATTENTION_SKIP_CUDA_BUILD` environment variable enables `flash-attn` to be resolved from a pre-built wheel, rather than attempting to build it from source, which requires access to the CUDA development toolkit. If the CUDA toolkit is available during resolution, we recommend omitting the `FLASH_ATTENTION_SKIP_CUDA_BUILD` variable, as setting `FLASH_ATTENTION_SKIP_CUDA_BUILD` to `TRUE` can lead to an incompatible install if no compatible pre-built wheel is available for the target PyTorch version, GPU version, and platform. Similarly, [`deep_gemm`](https://github.com/deepseek-ai/DeepGEMM) follows the same pattern: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["deep_gemm", "torch"] [tool.uv.sources] deep_gemm = { git = "https://github.com/deepseek-ai/DeepGEMM" } [tool.uv.extra-build-dependencies] deep_gemm = [{ requirement = "torch", match-runtime = true }] ``` The use of `extra-build-dependencies` and `extra-build-variables` are tracked in the uv cache, such that changes to these settings will trigger a reinstall and rebuild of the affected packages. For example, in the case of `flash-attn`, upgrading the version of `torch` used in your project would subsequently trigger a rebuild of `flash-attn` with the new version of `torch`. #### Dynamic metadata The use of `match-runtime = true` is only available for packages like `flash-attn` that declare static metadata. If static metadata is unavailable, uv is required to build the package during the dependency resolution phase; as such, uv cannot determine the version of the build dependency that would ultimately be installed in the project environment. In other words, if `flash-attn` did not declare static metadata, uv would not be able to determine the version of `torch` that would be installed in the project environment, since it would need to build `flash-attn` prior to resolving the `torch` version. As a concrete example, [`axolotl`](https://pypi.org/project/axolotl/) is a popular package that requires augmented build dependencies, but does not declare static metadata, as the package's dependencies vary based on the version of `torch` that is installed in the project environment. In this case, users should instead specify the exact version of `torch` that they intend to use in their project, and then augment the build dependencies with that version. For example, to build `axolotl` against `torch==2.6.0`, include the following in your `pyproject.toml`: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["axolotl[deepspeed, flash-attn]", "torch==2.6.0"] [tool.uv.extra-build-dependencies] axolotl = ["torch==2.6.0"] deepspeed = ["torch==2.6.0"] flash-attn = ["torch==2.6.0"] ``` Similarly, older versions of `flash-attn` did not declare static metadata, and thus would not have supported `match-runtime = true` out of the box. Unlike `axolotl`, though, `flash-attn` did not vary its dependencies based on dynamic properties of the build environment. As such, users could instead provide the `flash-attn` metadata upfront via the [`dependency-metadata`](../../reference/settings.md#dependency-metadata) setting, thereby forgoing the need to build the package during the dependency resolution phase. For example, to provide the `flash-attn` metadata upfront: ```toml title="pyproject.toml" [[tool.uv.dependency-metadata]] name = "flash-attn" version = "2.6.3" requires-dist = ["torch", "einops"] ``` !!! tip To determine the package metadata for a package like `flash-attn`, navigate to the appropriate Git repository, or look it up on [PyPI](https://pypi.org/project/flash-attn) and download the package's source distribution. The package requirements can typically be found in the `setup.py` or `setup.cfg` file. (If the package includes a built distribution, you can unzip it to find the `METADATA` file; however, the presence of a built distribution would negate the need to provide the metadata upfront, since it would already be available to uv.) The `version` field in `tool.uv.dependency-metadata` is optional for registry-based dependencies (when omitted, uv will assume the metadata applies to all versions of the package), but _required_ for direct URL dependencies (like Git dependencies). ### Disabling build isolation Installing packages without build isolation requires that the package's build dependencies are installed in the project environment _prior_ to building the package itself. For example, historically, to install `cchardet` without build isolation, you would first need to install the `cython` and `setuptools` packages in the project environment, followed by a separate invocation to install `cchardet` without build isolation: ```console $ uv venv $ uv pip install cython setuptools $ uv pip install cchardet --no-build-isolation ``` uv simplifies this process by allowing you to specify packages that should not be built in isolation via the `no-build-isolation-package` setting in your `pyproject.toml` and the `--no-build-isolation-package` flag in the command line. Further, when a package is marked for disabling build isolation, uv will perform a two-phase install, first installing any packages that support build isolation, followed by those that do not. As a result, if a project's build dependencies are included as project dependencies, uv will automatically install them before installing the package that requires build isolation to be disabled. For example, to install `cchardet` without build isolation, include the following in your `pyproject.toml`: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["cchardet", "cython", "setuptools"] [tool.uv] no-build-isolation-package = ["cchardet"] ``` When running `uv sync`, uv will first install `cython` and `setuptools` in the project environment, followed by `cchardet` (without build isolation): ```console $ uv sync --extra build + cchardet==2.1.7 + cython==3.1.3 + setuptools==80.9.0 ``` Similarly, to install `flash-attn` without build isolation, include the following in your `pyproject.toml`: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["flash-attn", "torch"] [tool.uv] no-build-isolation-package = ["flash-attn"] ``` When running `uv sync`, uv will first install `torch` in the project environment, followed by `flash-attn` (without build isolation). As `torch` is both a project dependency and a build dependency, the version of `torch` is guaranteed to be consistent between the build and runtime environments. A downside of the above approach is that it requires the build dependencies to be installed in the project environment, which is appropriate for `flash-attn` (which requires `torch` both at build-time and runtime), but not for `cchardet` (which only requires `cython` at build-time). To avoid including build dependencies in the project environment, uv supports a two-step installation process that allows you to separate the build dependencies from the packages that require them. For example, the build dependencies for `cchardet` can be isolated to an optional `build` group, as in: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["cchardet"] [project.optional-dependencies] build = ["setuptools", "cython"] [tool.uv] no-build-isolation-package = ["cchardet"] ``` Given the above, a user would first sync with the `build` optional group, and then without it to remove the build dependencies: ```console $ uv sync --extra build + cchardet==2.1.7 + cython==3.1.3 + setuptools==80.9.0 $ uv sync - cython==3.1.3 - setuptools==80.9.0 ``` Some packages, like `cchardet`, only require build dependencies for the _installation_ phase of `uv sync`. Others require their build dependencies to be present even just to resolve the project's dependencies during the _resolution_ phase. In such cases, the build dependencies can be installed prior to running any `uv lock` or `uv sync` commands, using the lower lower-level `uv pip` API. For example, given: ```toml title="pyproject.toml" [project] name = "project" version = "0.1.0" description = "..." readme = "README.md" requires-python = ">=3.12" dependencies = ["flash-attn"] [tool.uv] no-build-isolation-package = ["flash-attn"] ``` You could run the following sequence of commands to sync `flash-attn`: ```console $ uv venv $ uv pip install torch setuptools $ uv sync ``` Alternatively, users can instead provide the `flash-attn` metadata upfront via the [`dependency-metadata`](../../reference/settings.md#dependency-metadata) setting, thereby forgoing the need to build the package during the dependency resolution phase. For example, to provide the `flash-attn` metadata upfront: ```toml title="pyproject.toml" [[tool.uv.dependency-metadata]] name = "flash-attn" version = "2.6.3" requires-dist = ["torch", "einops"] ``` ## Editable mode By default, the project will be installed in editable mode, such that changes to the source code are immediately reflected in the environment. `uv sync` and `uv run` both accept a `--no-editable` flag, which instructs uv to install the project in non-editable mode. `--no-editable` is intended for deployment use-cases, such as building a Docker container, in which the project should be included in the deployed environment without a dependency on the originating source code. ## Conflicting dependencies uv resolves all project dependencies together, including optional dependencies ("extras") and dependency groups. If dependencies declared in one section are not compatible with those in another section, uv will fail to resolve the requirements of the project with an error. uv supports explicit declaration of conflicting dependency groups. For example, to declare that the `optional-dependency` groups `extra1` and `extra2` are incompatible: ```toml title="pyproject.toml" [tool.uv] conflicts = [ [ { extra = "extra1" }, { extra = "extra2" }, ], ] ``` Or, to declare the development dependency groups `group1` and `group2` incompatible: ```toml title="pyproject.toml" [tool.uv] conflicts = [ [ { group = "group1" }, { group = "group2" }, ], ] ``` See the [resolution documentation](../resolution.md#conflicting-dependencies) for more. ## Limited resolution environments If your project supports a more limited set of platforms or Python versions, you can constrain the set of solved platforms via the `environments` setting, which accepts a list of PEP 508 environment markers. For example, to constrain the lockfile to macOS and Linux, and exclude Windows: ```toml title="pyproject.toml" [tool.uv] environments = [ "sys_platform == 'darwin'", "sys_platform == 'linux'", ] ``` See the [resolution documentation](../resolution.md#limited-resolution-environments) for more. ## Required environments If your project _must_ support a specific platform or Python version, you can mark that platform as required via the `required-environments` setting. For example, to require that the project supports Intel macOS: ```toml title="pyproject.toml" [tool.uv] required-environments = [ "sys_platform == 'darwin' and platform_machine == 'x86_64'", ] ``` The `required-environments` setting is only relevant for packages that do not publish a source distribution (like PyTorch), as such packages can _only_ be installed on environments covered by the set of pre-built binary distributions (wheels) published by that package. See the [resolution documentation](../resolution.md#required-environments) for more. astral-sh-uv-9252ba6/docs/concepts/projects/dependencies.md000066400000000000000000000703111521221614700237460ustar00rootroot00000000000000# Managing dependencies ## Dependency fields Dependencies of the project are defined in several fields: - [`project.dependencies`](#project-dependencies): Published dependencies. - [`project.optional-dependencies`](#optional-dependencies): Published optional dependencies, or "extras". - [`dependency-groups`](#dependency-groups): Local dependencies for development. - [`tool.uv.sources`](#dependency-sources): Alternative sources for dependencies during development. !!! note The `project.dependencies` and `project.optional-dependencies` fields can be used even if project isn't going to be published. `dependency-groups` are a recently standardized feature and may not be supported by all tools yet. uv supports modifying the project's dependencies with `uv add` and `uv remove`, but dependency metadata can also be updated by editing the `pyproject.toml` directly. ## Adding dependencies To add a dependency: ```console $ uv add httpx ``` An entry will be added in the `project.dependencies` field: ```toml title="pyproject.toml" hl_lines="4" [project] name = "example" version = "0.1.0" dependencies = ["httpx>=0.27.2"] ``` The [`--dev`](#development-dependencies), [`--group`](#dependency-groups), or [`--optional`](#optional-dependencies) flags can be used to add dependencies to an alternative field. The dependency will include a constraint, e.g., `>=0.27.2`, for the most recent, compatible version of the package. The kind of bound can be adjusted with [`--bounds`](../../reference/settings.md#add-bounds), or the constraint can be provided directly: ```console $ uv add "httpx>=0.20" ``` When adding a dependency from a source other than a package registry, uv will add an entry in the sources field. For example, when adding `httpx` from GitHub: ```console $ uv add "httpx @ git+https://github.com/encode/httpx" ``` The `pyproject.toml` will include a [Git source entry](#git): ```toml title="pyproject.toml" hl_lines="8-9" [project] name = "example" version = "0.1.0" dependencies = [ "httpx", ] [tool.uv.sources] httpx = { git = "https://github.com/encode/httpx" } ``` If a dependency cannot be used, uv will display an error.: ```console $ uv add "httpx>9999" × No solution found when resolving dependencies: ╰─▶ Because only httpx<=1.0.0b0 is available and your project depends on httpx>9999, we can conclude that your project's requirements are unsatisfiable. ``` ### Importing dependencies from requirements files Dependencies declared in a `requirements.txt` file can be added to the project with the `-r` option: ``` uv add -r requirements.txt ``` See the [pip migration guide](../../guides/migration/pip-to-project.md#importing-requirements-files) for more details. ## Removing dependencies To remove a dependency: ```console $ uv remove httpx ``` The `--dev`, `--group`, or `--optional` flags can be used to remove a dependency from a specific table. If a [source](#dependency-sources) is defined for the removed dependency, and there are no other references to the dependency, it will also be removed. ## Changing dependencies To change an existing dependency, e.g., to use a different constraint for `httpx`: ```console $ uv add "httpx>0.1.0" ``` !!! note In this example, we are changing the constraints for the dependency in the `pyproject.toml`. The locked version of the dependency will only change if necessary to satisfy the new constraints. To force the package version to update to the latest within the constraints, use `--upgrade-package `, e.g.: ```console $ uv add "httpx>0.1.0" --upgrade-package httpx ``` See the [lockfile](./sync.md#upgrading-locked-package-versions) documentation for more details on upgrading packages. Requesting a different dependency source will update the `tool.uv.sources` table, e.g., to use `httpx` from a local path during development: ```console $ uv add "httpx @ ../httpx" ``` ## Platform-specific dependencies To ensure that a dependency is only installed on a specific platform or on specific Python versions, use [environment markers](https://peps.python.org/pep-0508/#environment-markers). For example, to install `jax` on Linux, but not on Windows or macOS: ```console $ uv add "jax; sys_platform == 'linux'" ``` The resulting `pyproject.toml` will then include the environment marker in the dependency definition: ```toml title="pyproject.toml" hl_lines="6" [project] name = "project" version = "0.1.0" requires-python = ">=3.11" dependencies = ["jax; sys_platform == 'linux'"] ``` Similarly, to include `numpy` on Python 3.11 and later: ```console $ uv add "numpy; python_version >= '3.11'" ``` See Python's [environment marker](https://peps.python.org/pep-0508/#environment-markers) documentation for a complete enumeration of the available markers and operators. !!! tip Dependency sources can also be [changed per-platform](#platform-specific-sources). ## Project dependencies The `project.dependencies` table represents the dependencies that are used when uploading to PyPI or building a wheel. Individual dependencies are specified using [dependency specifiers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/) syntax, and the table follows the [PEP 621](https://packaging.python.org/en/latest/specifications/pyproject-toml/) standard. `project.dependencies` defines the list of packages that are required for the project, along with the version constraints that should be used when installing them. Each entry includes a dependency name and version. An entry may include extras or environment markers for platform-specific packages. For example: ```toml title="pyproject.toml" [project] name = "albatross" version = "0.1.0" dependencies = [ # Any version in this range "tqdm >=4.66.2,<5", # Exactly this version of torch "torch ==2.2.2", # Install transformers with the torch extra "transformers[torch] >=4.39.3,<5", # Only install this package on older python versions # See "Environment Markers" for more information "importlib_metadata >=7.1.0,<8; python_version < '3.10'", "mollymawk ==0.1.0" ] ``` ## Dependency sources The `tool.uv.sources` table extends the standard dependency tables with alternative dependency sources, which are used during development. Dependency sources add support for common patterns that are not supported by the `project.dependencies` standard, like editable installations and relative paths. For example, to install `foo` from a directory relative to the project root: ```toml title="pyproject.toml" hl_lines="7" [project] name = "example" version = "0.1.0" dependencies = ["foo"] [tool.uv.sources] foo = { path = "./packages/foo" } ``` The following dependency sources are supported by uv: - [Index](#index): A package resolved from a specific package index. - [Git](#git): A Git repository. - [URL](#url): A remote wheel or source distribution. - [Path](#path): A local wheel, source distribution, or project directory. - [Workspace](#workspace-member): A member of the current workspace. !!! important Sources are only respected by uv. If another tool is used, only the definitions in the standard project tables will be used. If another tool is being used for development, any metadata provided in the source table will need to be re-specified in the other tool's format. ### Index To add Python package from a specific index, use the `--index` option: ```console $ uv add torch --index pytorch=https://download.pytorch.org/whl/cpu ``` uv will store the index in `[[tool.uv.index]]` and add a `[tool.uv.sources]` entry: ```toml title="pyproject.toml" [project] dependencies = ["torch"] [tool.uv.sources] torch = { index = "pytorch" } [[tool.uv.index]] name = "pytorch" url = "https://download.pytorch.org/whl/cpu" ``` !!! tip The above example will only work on x86-64 Linux, due to the specifics of the PyTorch index. See the [PyTorch guide](../../guides/integration/pytorch.md) for more information about setting up PyTorch. Using an `index` source _pins_ a package to the given index — it will not be downloaded from other indexes. When defining an index, an `explicit` flag can be included to indicate that the index should _only_ be used for packages that explicitly specify it in `tool.uv.sources`. If `explicit` is not set, other packages may be resolved from the index, if not found elsewhere. ```toml title="pyproject.toml" hl_lines="4" [[tool.uv.index]] name = "pytorch" url = "https://download.pytorch.org/whl/cpu" explicit = true ``` ### Git To add a Git dependency source, prefix a Git-compatible URL with `git+`. For example: ```console $ # Install over HTTP(S). $ uv add git+https://github.com/encode/httpx $ # Install over SSH. $ uv add git+ssh://git@github.com/encode/httpx ``` ```toml title="pyproject.toml" hl_lines="5" [project] dependencies = ["httpx"] [tool.uv.sources] httpx = { git = "https://github.com/encode/httpx" } ``` Specific Git references can be requested, e.g., a tag: ```console $ uv add git+https://github.com/encode/httpx --tag 0.27.0 ``` ```toml title="pyproject.toml" hl_lines="7" [project] dependencies = ["httpx"] [tool.uv.sources] httpx = { git = "https://github.com/encode/httpx", tag = "0.27.0" } ``` Or, a branch: ```console $ uv add git+https://github.com/encode/httpx --branch main ``` ```toml title="pyproject.toml" hl_lines="7" [project] dependencies = ["httpx"] [tool.uv.sources] httpx = { git = "https://github.com/encode/httpx", branch = "main" } ``` Or, a revision (commit): ```console $ uv add git+https://github.com/encode/httpx --rev 326b9431c761e1ef1e00b9f760d1f654c8db48c6 ``` ```toml title="pyproject.toml" hl_lines="7" [project] dependencies = ["httpx"] [tool.uv.sources] httpx = { git = "https://github.com/encode/httpx", rev = "326b9431c761e1ef1e00b9f760d1f654c8db48c6" } ``` A `subdirectory` may be specified if the package isn't in the repository root: ```console $ uv add git+https://github.com/langchain-ai/langchain#subdirectory=libs/langchain ``` ```toml title="pyproject.toml" [project] dependencies = ["langchain"] [tool.uv.sources] langchain = { git = "https://github.com/langchain-ai/langchain", subdirectory = "libs/langchain" } ``` Support for [Git LFS](https://git-lfs.com) is also configurable per source. By default, Git LFS objects will not be fetched. ```console $ uv add --lfs git+https://github.com/astral-sh/lfs-cowsay ``` ```toml title="pyproject.toml" [project] dependencies = ["lfs-cowsay"] [tool.uv.sources] lfs-cowsay = { git = "https://github.com/astral-sh/lfs-cowsay", lfs = true } ``` - When `lfs = true`, uv will always fetch LFS objects for this Git source. - When `lfs = false`, uv will never fetch LFS objects for this Git source. - When omitted, the `UV_GIT_LFS` environment variable is used for all Git sources without an explicit `lfs` configuration. !!! important Ensure Git LFS is installed and configured on your system before attempting to install sources using Git LFS, otherwise a build failure can occur. ### URL To add a URL source, provide a `https://` URL to either a wheel (ending in `.whl`) or a source distribution (typically ending in `.tar.gz` or `.zip`; see [here](../../concepts/resolution.md#source-distribution) for all supported formats). For example: ```console $ uv add "https://files.pythonhosted.org/packages/5c/2d/3da5bdf4408b8b2800061c339f240c1802f2e82d55e50bd39c5a881f47f0/httpx-0.27.0.tar.gz" ``` Will result in a `pyproject.toml` with: ```toml title="pyproject.toml" hl_lines="5" [project] dependencies = ["httpx"] [tool.uv.sources] httpx = { url = "https://files.pythonhosted.org/packages/5c/2d/3da5bdf4408b8b2800061c339f240c1802f2e82d55e50bd39c5a881f47f0/httpx-0.27.0.tar.gz" } ``` URL dependencies can also be manually added or edited in the `pyproject.toml` with the `{ url = }` syntax. A `subdirectory` may be specified if the source distribution isn't in the archive root. ### Path To add a path source, provide the path of a wheel (ending in `.whl`), a source distribution (typically ending in `.tar.gz` or `.zip`; see [here](../../concepts/resolution.md#source-distribution) for all supported formats), or a directory containing a `pyproject.toml`. For example: ```console $ uv add /example/foo-0.1.0-py3-none-any.whl ``` Will result in a `pyproject.toml` with: ```toml title="pyproject.toml" [project] dependencies = ["foo"] [tool.uv.sources] foo = { path = "/example/foo-0.1.0-py3-none-any.whl" } ``` The path may also be a relative path: ```console $ uv add ./foo-0.1.0-py3-none-any.whl ``` Or, a path to a project directory: ```console $ uv add ~/projects/bar/ ``` !!! important When using a directory as a path dependency, uv will attempt to build and install the target as a package by default. See the [virtual dependency](#virtual-dependencies) documentation for details. An [editable installation](#editable-dependencies) is not used for path dependencies by default. An editable installation may be requested for project directories: ```console $ uv add --editable ../projects/bar/ ``` Which will result in a `pyproject.toml` with: ```toml title="pyproject.toml" [project] dependencies = ["bar"] [tool.uv.sources] bar = { path = "../projects/bar", editable = true } ``` !!! tip For multiple packages in the same repository, [_workspaces_](./workspaces.md) may be a better fit. ### Workspace member To declare a dependency on a workspace member, add the member name with `{ workspace = true }`. All workspace members must be explicitly stated. Workspace members are always [editable](#editable-dependencies) . See the [workspace](./workspaces.md) documentation for more details on workspaces. ```toml title="pyproject.toml" [project] dependencies = ["foo==0.1.0"] [tool.uv.sources] foo = { workspace = true } [tool.uv.workspace] members = [ "packages/foo" ] ``` ### Platform-specific sources You can limit a source to a given platform or Python version by providing [dependency specifiers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/)-compatible environment markers for the source. For example, to pull `httpx` from GitHub, but only on macOS, use the following: ```toml title="pyproject.toml" hl_lines="8" [project] dependencies = ["httpx"] [tool.uv.sources] httpx = { git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" } ``` By specifying the marker on the source, uv will still include `httpx` on all platforms, but will download the source from GitHub on macOS, and fall back to PyPI on all other platforms. ### Multiple sources You can specify multiple sources for a single dependency by providing a list of sources, disambiguated by [PEP 508](https://peps.python.org/pep-0508/#environment-markers)-compatible environment markers. For example, to pull in different `httpx` tags on macOS vs. Linux: ```toml title="pyproject.toml" hl_lines="6-7" [project] dependencies = ["httpx"] [tool.uv.sources] httpx = [ { git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" }, { git = "https://github.com/encode/httpx", tag = "0.24.1", marker = "sys_platform == 'linux'" }, ] ``` This strategy extends to using different indexes based on environment markers. For example, to install `torch` from different PyTorch indexes based on the platform: ```toml title="pyproject.toml" hl_lines="6-7" [project] dependencies = ["torch"] [tool.uv.sources] torch = [ { index = "torch-cpu", marker = "platform_system == 'Darwin'"}, { index = "torch-gpu", marker = "platform_system == 'Linux'"}, ] [[tool.uv.index]] name = "torch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true [[tool.uv.index]] name = "torch-gpu" url = "https://download.pytorch.org/whl/cu130" explicit = true ``` ### Disabling sources To instruct uv to ignore the `tool.uv.sources` table (e.g., to simulate resolving with the package's published metadata), use the `--no-sources` flag: ```console $ uv lock --no-sources ``` The use of `--no-sources` will also prevent uv from discovering any [workspace members](#workspace-member) that could satisfy a given dependency. ## Optional dependencies It is common for projects that are published as libraries to make some features optional to reduce the default dependency tree. For example, Pandas has an [`excel` extra](https://pandas.pydata.org/docs/getting_started/install.html#excel-files) and a [`plot` extra](https://pandas.pydata.org/docs/getting_started/install.html#visualization) to avoid installation of Excel parsers and `matplotlib` unless someone explicitly requires them. Extras are requested with the `package[]` syntax, e.g., `pandas[plot, excel]`. Optional dependencies are specified in `[project.optional-dependencies]`, a TOML table that maps from extra name to its dependencies, following [dependency specifiers](#dependency-specifiers) syntax. Optional dependencies can have entries in `tool.uv.sources` the same as normal dependencies. ```toml title="pyproject.toml" [project] name = "pandas" version = "1.0.0" [project.optional-dependencies] plot = [ "matplotlib>=3.6.3" ] excel = [ "odfpy>=1.4.1", "openpyxl>=3.1.0", "python-calamine>=0.1.7", "pyxlsb>=1.0.10", "xlrd>=2.0.1", "xlsxwriter>=3.0.5" ] ``` To add an optional dependency, use the `--optional ` option: ```console $ uv add httpx --optional network ``` !!! note If you have optional dependencies that conflict with one another, resolution will fail unless you explicitly [declare them as conflicting](./config.md#conflicting-dependencies). Sources can also be declared as applying only to a specific optional dependency. For example, to pull `torch` from different PyTorch indexes based on an optional `cpu` or `gpu` extra: ```toml title="pyproject.toml" [project] dependencies = [] [project.optional-dependencies] cpu = [ "torch", ] gpu = [ "torch", ] [tool.uv.sources] torch = [ { index = "torch-cpu", extra = "cpu" }, { index = "torch-gpu", extra = "gpu" }, ] [[tool.uv.index]] name = "torch-cpu" url = "https://download.pytorch.org/whl/cpu" [[tool.uv.index]] name = "torch-gpu" url = "https://download.pytorch.org/whl/cu130" ``` ## Development dependencies Unlike optional dependencies, development dependencies are local-only and will _not_ be included in the project requirements when published to PyPI or other indexes. As such, development dependencies are not included in the `[project]` table. Development dependencies can have entries in `tool.uv.sources` the same as normal dependencies. To add a development dependency, use the `--dev` flag: ```console $ uv add --dev pytest ``` uv uses the `[dependency-groups]` table (as defined in [PEP 735](https://peps.python.org/pep-0735/)) for declaration of development dependencies. The above command will create a `dev` group: ```toml title="pyproject.toml" [dependency-groups] dev = [ "pytest >=8.1.1,<9" ] ``` The `dev` group is special-cased; there are `--dev`, `--only-dev`, and `--no-dev` flags to toggle inclusion or exclusion of its dependencies. See `--no-default-groups` to disable all default groups instead. Additionally, the `dev` group is [synced by default](#default-groups). ### Dependency groups Development dependencies can be divided into multiple groups, using the `--group` flag. For example, to add a development dependency in the `lint` group: ```console $ uv add --group lint ruff ``` Which results in the following `[dependency-groups]` definition: ```toml title="pyproject.toml" [dependency-groups] dev = [ "pytest" ] lint = [ "ruff" ] ``` Once groups are defined, the `--all-groups`, `--no-default-groups`, `--group`, `--only-group`, and `--no-group` options can be used to include or exclude their dependencies. !!! tip The `--dev`, `--only-dev`, and `--no-dev` flags are equivalent to `--group dev`, `--only-group dev`, and `--no-group dev` respectively. uv requires that all dependency groups are compatible with each other and resolves all groups together when creating the lockfile. If dependencies declared in one group are not compatible with those in another group, uv will fail to resolve the requirements of the project with an error. !!! note If you have dependency groups that conflict with one another, resolution will fail unless you explicitly [declare them as conflicting](./config.md#conflicting-dependencies). ### Nesting groups A dependency group can include other dependency groups, e.g.: ```toml title="pyproject.toml" [dependency-groups] dev = [ {include-group = "lint"}, {include-group = "test"} ] lint = [ "ruff" ] test = [ "pytest" ] ``` An included group's dependencies cannot conflict with the other dependencies declared in a group. ### Default groups By default, uv includes the `dev` dependency group in the environment (e.g., during `uv run` or `uv sync`). The default groups to include can be changed using the `tool.uv.default-groups` setting. ```toml title="pyproject.toml" [tool.uv] default-groups = ["dev", "foo"] ``` To enable all dependencies groups by default, use `"all"` instead of listing group names: ```toml title="pyproject.toml" [tool.uv] default-groups = "all" ``` !!! tip To disable this behaviour during `uv run` or `uv sync`, use `--no-default-groups`. To exclude a specific default group, use `--no-group `. ### Group `requires-python` By default, dependency groups must be compatible with your project's `requires-python` range. If a dependency group requires a different range of Python versions than your project, you can specify a `requires-python` for the group in `[tool.uv.dependency-groups]`, e.g.: ```toml title="pyproject.toml" hl_lines="9-10" [project] name = "example" version = "0.0.0" requires-python = ">=3.10" [dependency-groups] dev = ["pytest"] [tool.uv.dependency-groups] dev = {requires-python = ">=3.12"} ``` ### Legacy `dev-dependencies` Before `[dependency-groups]` was standardized, uv used the `tool.uv.dev-dependencies` field to specify development dependencies, e.g.: ```toml title="pyproject.toml" [tool.uv] dev-dependencies = [ "pytest" ] ``` Dependencies declared in this section will be combined with the contents in the `dependency-groups.dev`. Eventually, the `dev-dependencies` field will be deprecated and removed. !!! note If a `tool.uv.dev-dependencies` field exists, `uv add --dev` will use the existing section instead of adding a new `dependency-groups.dev` section. ## Build dependencies If a project is structured as [Python package](./config.md#build-systems), it may declare dependencies that are required to build the project, but not required to run it. These dependencies are specified in the `[build-system]` table under `build-system.requires`, following [PEP 518](https://peps.python.org/pep-0518/). For example, if a project uses `setuptools` as its build backend, it should declare `setuptools` as a build dependency: ```toml title="pyproject.toml" [project] name = "pandas" version = "0.1.0" [build-system] requires = ["setuptools>=42"] build-backend = "setuptools.build_meta" ``` By default, uv will respect `tool.uv.sources` when resolving build dependencies. For example, to use a local version of `setuptools` for building, add the source to `tool.uv.sources`: ```toml title="pyproject.toml" [project] name = "pandas" version = "0.1.0" [build-system] requires = ["setuptools>=42"] build-backend = "setuptools.build_meta" [tool.uv.sources] setuptools = { path = "./packages/setuptools" } ``` When publishing a package, we recommend running `uv build --no-sources` to ensure that the package builds correctly when `tool.uv.sources` is disabled, as is the case when using other build tools, like [`pypa/build`](https://github.com/pypa/build). ## Editable dependencies A regular installation of a directory with a Python package first builds a wheel and then installs that wheel into your virtual environment, copying all source files. When the package source files are edited, the virtual environment will contain outdated versions. Editable installations solve this problem by adding a link to the project within the virtual environment (a `.pth` file), which instructs the interpreter to include the source files directly. There are some limitations to editables (mainly: the build backend needs to support them, and native modules aren't recompiled before import), but they are useful for development, as the virtual environment will always use the latest changes to the package. uv uses editable installation for workspace packages by default. To add an editable dependency, use the `--editable` flag: ```console $ uv add --editable ./path/foo ``` Or, to opt-out of using an editable dependency in a workspace: ```console $ uv add --no-editable ./path/foo ``` ## Virtual dependencies uv allows dependencies to be "virtual", in which the dependency itself is not installed as a [package](./config.md#project-packaging), but its dependencies are. By default, dependencies are never virtual. A dependency with a [`path` source](#path) can be virtual if it explicitly sets [`tool.uv.package = false`](../../reference/settings.md#package). Without this setting, uv treats the path dependency as a normal package and will attempt to build it, even if the project does not declare a [build system](./config.md#build-systems). To treat a dependency as virtual, set `package = false` on the source: ```toml title="pyproject.toml" [project] dependencies = ["bar"] [tool.uv.sources] bar = { path = "../projects/bar", package = false } ``` If a dependency sets `tool.uv.package = false`, it can be overridden by declaring `package = true` on the source: ```toml title="pyproject.toml" [project] dependencies = ["bar"] [tool.uv.sources] bar = { path = "../projects/bar", package = true } ``` Similarly, a dependency with a [`workspace` source](#workspace-member) can be virtual if it explicitly sets [`tool.uv.package = false`](../../reference/settings.md#package). Without this setting, the workspace member will be built even if a [build system](./config.md#build-systems) is not declared. Workspace members that are _not_ dependencies can be virtual by default, e.g., if the parent `pyproject.toml` is: ```toml title="pyproject.toml" [project] name = "parent" version = "1.0.0" dependencies = [] [tool.uv.workspace] members = ["child"] ``` And the child `pyproject.toml` excluded a build system: ```toml title="pyproject.toml" [project] name = "child" version = "1.0.0" dependencies = ["anyio"] ``` Then the `child` workspace member would not be installed, but the transitive dependency `anyio` would be. In contrast, if the parent declared a dependency on `child`: ```toml title="pyproject.toml" [project] name = "parent" version = "1.0.0" dependencies = ["child"] [tool.uv.sources] child = { workspace = true } [tool.uv.workspace] members = ["child"] ``` Then `child` would be built and installed. ## Dependency specifiers uv uses standard [dependency specifiers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/), originally defined in [PEP 508](https://peps.python.org/pep-0508/). A dependency specifier is composed of, in order: - The dependency name - The extras you want (optional) - The version specifier - An environment marker (optional) The version specifiers are comma separated and added together, e.g., `foo >=1.2.3,<2,!=1.4.0` is interpreted as "a version of `foo` that's at least 1.2.3, but less than 2, and not 1.4.0". Specifiers are padded with trailing zeros if required, so `foo ==2` matches foo 2.0.0, too. A star can be used for the last digit with equals, e.g., `foo ==2.1.*` will accept any release from the 2.1 series. Similarly, `~=` matches where the last digit is equal or higher, e.g., `foo ~=1.2` is equal to `foo >=1.2,<2`, and `foo ~=1.2.3` is equal to `foo >=1.2.3,<1.3`. Extras are comma-separated in square bracket between name and version, e.g., `pandas[excel,plot] ==2.2`. Whitespace between extra names is ignored. Some dependencies are only required in specific environments, e.g., a specific Python version or operating system. For example to install the `importlib-metadata` backport for the `importlib.metadata` module, use `importlib-metadata >=7.1.0,<8; python_version < '3.10'`. To install `colorama` on Windows (but omit it on other platforms), use `colorama >=0.4.6,<5; platform_system == "Windows"`. Markers are combined with `and`, `or`, and parentheses, e.g., `aiohttp >=3.7.4,<4; (sys_platform != 'win32' or implementation_name != 'pypy') and python_version >= '3.10'`. Note that versions within markers must be quoted, while versions _outside_ of markers must _not_ be quoted. astral-sh-uv-9252ba6/docs/concepts/projects/export.md000066400000000000000000000075211521221614700226440ustar00rootroot00000000000000--- title: Exporting a lockfile description: Exporting a lockfile to different formats --- # Exporting a lockfile uv can export a lockfile to different formats for integration with other tools and workflows. The `uv export` command supports multiple output formats, each suited to different use cases. For more details on lockfiles and how they're created, see the [project layout](./layout.md) and [locking and syncing](./sync.md) documentation. ## Overview of export formats uv supports three export formats: - `requirements.txt`: The traditional pip-compatible [requirements file format](https://pip.pypa.io/en/stable/reference/requirements-file-format/). - `pylock.toml`: The standardized Python lockfile format defined in [PEP 751](https://peps.python.org/pep-0751/). - `CycloneDX`: An industry-standard [Software Bill of Materials (SBOM)](https://cyclonedx.org/) format. The format can be specified with the `--format` flag: ```console $ uv export --format requirements.txt $ uv export --format pylock.toml $ uv export --format cyclonedx1.5 ``` !!! tip By default, `uv export` prints to stdout. Use `--output-file` to write to a file for any format: ```console $ uv export --format requirements.txt --output-file requirements.txt $ uv export --format pylock.toml --output-file pylock.toml $ uv export --format cyclonedx1.5 --output-file sbom.json ``` ## `requirements.txt` format The `requirements.txt` format is the most widely supported format for Python dependencies. It can be used with `pip` and other Python package managers. ### Basic usage ```console $ uv export --format requirements.txt ``` The generated `requirements.txt` file can then be installed via `uv pip install`, or with other tools like `pip`. !!! note In general, we recommend against using both a `uv.lock` and a `requirements.txt` file. The `uv.lock` format is more powerful and includes features that cannot be expressed in `requirements.txt`. If you find yourself exporting a `uv.lock` file, consider opening an issue to discuss your use case. ## `pylock.toml` format [PEP 751](https://peps.python.org/pep-0751/) defines a TOML-based lockfile format for Python dependencies. uv can export your project's dependency lockfile to this format. ### Basic usage ```console $ uv export --format pylock.toml ``` ## CycloneDX SBOM format uv can export your project's dependency lockfile as a Software Bill of Materials (SBOM) in CycloneDX format. SBOMs provide a comprehensive inventory of all software components in your application, which is useful for security auditing, compliance, and supply chain transparency. !!! important Support for exporting to CycloneDX is in [preview](../preview.md), and may change in any future release. ### What is CycloneDX? [CycloneDX](https://cyclonedx.org/) is an industry-standard format for creating Software Bill of Materials. CycloneDX is machine readable and widely supported by security scanning tools, vulnerability databases, and Software Composition Analysis (SCA) platforms. ### Basic usage To export your project's lockfile as a CycloneDX SBOM: ```console $ uv export --format cyclonedx1.5 ``` This will generate a JSON-encoded CycloneDX v1.5 document containing your project and all of its dependencies. ### SBOM Structure The generated SBOM follows the [CycloneDX specification](https://cyclonedx.org/specification/overview/). uv also includes the following custom properties on components: - `uv:package:marker`: Environment markers (e.g., `python_version >= "3.8"`) - `uv:workspace:path`: Relative path for workspace members ## Next steps To learn more about lockfiles and exporting, see the [locking and syncing](./sync.md) documentation and the [command reference](../../reference/cli.md#uv-export). Or, read on to learn how to [build and publish your project to a package index](../../guides/package.md). astral-sh-uv-9252ba6/docs/concepts/projects/index.md000066400000000000000000000014571521221614700224340ustar00rootroot00000000000000# Projects Projects help manage Python code spanning multiple files. !!! tip Looking for an introduction to creating a project with uv? See the [projects guide](../../guides/projects.md) first. Working on projects is a core part of the uv experience. Learn more about using projects: - [Understanding project structure and files](./layout.md) - [Creating new projects](./init.md) - [Managing project dependencies](./dependencies.md) - [Running commands and scripts in a project](./run.md) - [Using lockfiles and syncing the environment](./sync.md) - [Configuring the project for advanced use cases](./config.md) - [Building distributions to publish a project](./build.md) - [Exporting a lockfile to different formats](./export.md) - [Using workspaces to work on multiple projects at once](./workspaces.md) astral-sh-uv-9252ba6/docs/concepts/projects/init.md000066400000000000000000000215041521221614700222630ustar00rootroot00000000000000# Creating projects uv supports creating a project with `uv init`. When creating projects, uv supports two basic templates: [**applications**](#applications) and [**libraries**](#libraries). By default, uv will create a project for an application. The `--lib` flag can be used to create a project for a library instead. ## Target directory uv will create a project in the working directory, or, in a target directory by providing a name, e.g., `uv init foo`. The working directory can be modified with the `--directory` option, which will cause the target directory path to be interpreted relative to the specified working directory. If there's already a project in the target directory, i.e., if there's a `pyproject.toml`, uv will exit with an error. ## Applications Application projects are suitable for web servers, scripts, and command-line interfaces. Applications are the default target for `uv init`, but can also be specified with the `--app` flag. ```console $ uv init example-app ``` The project includes a `pyproject.toml`, a sample file (`main.py`), a readme, and a Python version pin file (`.python-version`). ```console $ tree example-app example-app/ ├── .python-version ├── README.md ├── main.py └── pyproject.toml ``` !!! note Prior to v0.6.0, uv created a file named `hello.py` instead of `main.py`. The `pyproject.toml` includes basic metadata. It does not include a build system, it is not a [package](./config.md#project-packaging) and will not be installed into the environment: ```toml title="pyproject.toml" [project] name = "example-app" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [] ``` The sample file defines a `main` function with some standard boilerplate: ```python title="main.py" def main(): print("Hello from example-app!") if __name__ == "__main__": main() ``` Python files can be executed with `uv run`: ```console $ cd example-app $ uv run main.py Hello from example-project! ``` ## Packaged applications Many use-cases require a [package](./config.md#project-packaging). For example, if you are creating a command-line interface that will be published to PyPI or if you want to define tests in a dedicated directory. The `--package` flag can be used to create a packaged application: ```console $ uv init --package example-pkg ``` The source code is moved into a `src` directory with a module directory and an `__init__.py` file: ```console $ tree example-pkg example-pkg/ ├── .python-version ├── README.md ├── pyproject.toml └── src └── example_pkg └── __init__.py ``` A [build system](./config.md#build-systems) is defined, so the project will be installed into the environment: ```toml title="pyproject.toml" hl_lines="12-14" [project] name = "example-pkg" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [] [project.scripts] example-pkg = "example_pkg:main" [build-system] requires = ["uv_build>=0.11.20,<0.12"] build-backend = "uv_build" ``` !!! tip The `--build-backend` option can be used to request an alternative build system. A [command](./config.md#entry-points) definition is included: ```toml title="pyproject.toml" hl_lines="9 10" [project] name = "example-pkg" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [] [project.scripts] example-pkg = "example_pkg:main" [build-system] requires = ["uv_build>=0.11.20,<0.12"] build-backend = "uv_build" ``` The command can be executed with `uv run`: ```console $ cd example-pkg $ uv run example-pkg Hello from example-pkg! ``` ## Libraries A library provides functions and objects for other projects to consume. Libraries are intended to be built and distributed, e.g., by uploading them to PyPI. Libraries can be created by using the `--lib` flag: ```console $ uv init --lib example-lib ``` !!! note Using `--lib` implies `--package`. Libraries always require a packaged project. As with a [packaged application](#packaged-applications), a `src` layout is used. A `py.typed` marker is included to indicate to consumers that types can be read from the library: ```console $ tree example-lib example-lib/ ├── .python-version ├── README.md ├── pyproject.toml └── src └── example_lib ├── py.typed └── __init__.py ``` !!! note A `src` layout is particularly valuable when developing libraries. It ensures that the library is isolated from any `python` invocations in the project root and that distributed library code is well separated from the rest of the project source. A [build system](./config.md#build-systems) is defined, so the project will be installed into the environment: ```toml title="pyproject.toml" hl_lines="12-14" [project] name = "example-lib" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [] [build-system] requires = ["uv_build>=0.11.20,<0.12"] build-backend = "uv_build" ``` !!! tip You can select a different build backend template by using `--build-backend` with `hatchling`, `uv_build`, `flit-core`, `pdm-backend`, `setuptools`, `maturin`, or `scikit-build-core`. An alternative backend is required if you want to create a [library with extension modules](#projects-with-extension-modules). The created module defines a simple API function: ```python title="__init__.py" def hello() -> str: return "Hello from example-lib!" ``` And you can import and execute it using `uv run`: ```console $ cd example-lib $ uv run python -c "import example_lib; print(example_lib.hello())" Hello from example-lib! ``` ## Projects with extension modules Most Python projects are "pure Python", meaning they do not define modules in other languages like C, C++, FORTRAN, or Rust. However, projects with extension modules are often used for performance sensitive code. Creating a project with an extension module requires choosing an alternative build system. uv supports creating projects with the following build systems that support building extension modules: - [`maturin`](https://www.maturin.rs) for projects with Rust - [`scikit-build-core`](https://github.com/scikit-build/scikit-build-core) for projects with C, C++, FORTRAN, Cython Specify the build system with the `--build-backend` flag: ```console $ uv init --build-backend maturin example-ext ``` !!! note Using `--build-backend` implies `--package`. The project contains a `Cargo.toml` and a `lib.rs` file in addition to the typical Python project files: ```console $ tree example-ext example-ext/ ├── .python-version ├── Cargo.toml ├── README.md ├── pyproject.toml └── src ├── lib.rs └── example_ext ├── __init__.py └── _core.pyi ``` !!! note If using `scikit-build-core`, you'll see CMake configuration and a `main.cpp` file instead. The Rust library defines a simple function: ```rust title="src/lib.rs" use pyo3::prelude::*; #[pymodule] mod _core { use pyo3::prelude::*; #[pyfunction] fn hello_from_bin() -> String { "Hello from example-ext!".to_string() } } ``` And the Python module imports it: ```python title="src/example_ext/__init__.py" from example_ext._core import hello_from_bin def main() -> None: print(hello_from_bin()) ``` The command can be executed with `uv run`: ```console $ cd example-ext $ uv run example-ext Hello from example-ext! ``` !!! important When creating a project with maturin or scikit-build-core, uv configures [`tool.uv.cache-keys`](https://docs.astral.sh/uv/reference/settings/#cache-keys) to include common source file types. To force a rebuild, e.g. when changing files outside `cache-keys` or when not using `cache-keys`, use `--reinstall`. ## Creating a minimal project If you only want to create a `pyproject.toml`, use the `--bare` option: ```console $ uv init example-bare --bare ``` uv will skip creating a Python version pin file, a README, and any source directories or files. Additionally, uv will not initialize a version control system (i.e., `git`). ```console $ tree example-bare example-bare └── pyproject.toml ``` uv will also not add extra metadata to the `pyproject.toml`, such as the `description` or `authors`. ```toml [project] name = "example-bare" version = "0.1.0" requires-python = ">=3.12" dependencies = [] ``` The `--bare` option can be used with other options like `--lib` or `--build-backend` — in these cases uv will still configure a build system but will not create the expected file structure. When `--bare` is used, additional features can still be used opt-in: ```console $ uv init example-bare --bare --description "Hello world" --author-from git --vcs git --python-pin ``` astral-sh-uv-9252ba6/docs/concepts/projects/layout.md000066400000000000000000000111671521221614700226410ustar00rootroot00000000000000# Project structure and files ## The `pyproject.toml` Python project metadata is defined in a [`pyproject.toml`](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/) file. uv requires this file to identify the root directory of a project. !!! tip `uv init` can be used to create a new project. See [Creating projects](./init.md) for details. A minimal project definition includes a name and version: ```toml title="pyproject.toml" [project] name = "example" version = "0.1.0" ``` Additional project metadata and configuration includes: - [Python version requirement](./config.md#python-version-requirement) - [Dependencies](./dependencies.md) - [Build system](./config.md#build-systems) - [Entry points (commands)](./config.md#entry-points) ## The project environment When working on a project with uv, uv will create a virtual environment as needed. While some uv commands will create a temporary environment (e.g., `uv run --isolated`), uv also manages a persistent environment with the project and its dependencies in a `.venv` directory next to the `pyproject.toml`. It is stored inside the project to make it easy for editors to find — they need the environment to give code completions and type hints. It is not recommended to include the `.venv` directory in version control; it is automatically excluded from `git` with an internal `.gitignore` file. To run a command in the project environment, use `uv run`. Alternatively the project environment can be activated as normal for a virtual environment. When `uv run` is invoked, it will create the project environment if it does not exist yet or ensure it is up-to-date if it exists. The project environment can also be explicitly created with `uv sync`. See the [locking and syncing](./sync.md) documentation for details. It is _not_ recommended to modify the project environment manually, e.g., with `uv pip install`. For project dependencies, use `uv add` to add a package to the environment. For one-off requirements, use [`uvx`](../../guides/tools.md) or [`uv run --with`](./run.md#requesting-additional-dependencies). !!! tip If you don't want uv to manage the project environment, set [`managed = false`](../../reference/settings.md#managed) to disable automatic locking and syncing of the project. For example: ```toml title="pyproject.toml" [tool.uv] managed = false ``` ## The lockfile uv creates a `uv.lock` file next to the `pyproject.toml`. `uv.lock` is a _universal_ or _cross-platform_ lockfile that captures the packages that would be installed across all possible Python markers such as operating system, architecture, and Python version. Unlike the `pyproject.toml`, which is used to specify the broad requirements of your project, the lockfile contains the exact resolved versions that are installed in the project environment. This file should be checked into version control, allowing for consistent and reproducible installations across machines. A lockfile ensures that developers working on the project are using a consistent set of package versions. Additionally, it ensures when deploying the project as an application that the exact set of used package versions is known. The lockfile is [automatically created and updated](./sync.md#automatic-lock-and-sync) during uv invocations that use the project environment, i.e., `uv sync` and `uv run`. The lockfile may also be explicitly updated using `uv lock`. `uv.lock` is a human-readable TOML file but is managed by uv and should not be edited manually. The `uv.lock` format is specific to uv and not usable by other tools. ### Relationship to `pylock.toml` In [PEP 751](https://peps.python.org/pep-0751/), Python standardized a new resolution file format, `pylock.toml`. `pylock.toml` is a resolution output format intended to replace `requirements.txt` (e.g., in the context of `uv pip compile`, whereby a "locked" `requirements.txt` file is generated from a set of input requirements). `pylock.toml` is standardized and tool-agnostic, such that in the future, `pylock.toml` files generated by uv could be installed by other tools, and vice versa. Some of uv's functionality cannot be expressed in the `pylock.toml` format; as such, uv will continue to use the `uv.lock` format within the project interface. However, uv supports `pylock.toml` as an export target and in the `uv pip` CLI. For example: - To export a `uv.lock` to the `pylock.toml` format, run: `uv export -o pylock.toml` - To generate a `pylock.toml` file from a set of requirements, run: `uv pip compile requirements.in -o pylock.toml` - To install from a `pylock.toml` file, run: `uv pip sync pylock.toml` or `uv pip install -r pylock.toml` astral-sh-uv-9252ba6/docs/concepts/projects/run.md000066400000000000000000000066051521221614700221310ustar00rootroot00000000000000# Running commands in projects When working on a project, it is installed into the virtual environment at `.venv`. This environment is isolated from the current shell by default, so invocations that require the project, e.g., `python -c "import example"`, will fail. Instead, use `uv run` to run commands in the project environment: ```console $ uv run python -c "import example" ``` When using `run`, uv will ensure that the project environment is up-to-date before running the given command. The given command can be provided by the project environment or exist outside of it, e.g.: ```console $ # Presuming the project provides `example-cli` $ uv run example-cli foo $ # Running a `bash` script that requires the project to be available $ uv run bash scripts/foo.sh ``` ## Requesting additional dependencies Additional dependencies or different versions of dependencies can be requested per invocation. The `--with` option is used to include a dependency for the invocation, e.g., to request a different version of `httpx`: ```console $ uv run --with httpx==0.26.0 python -c "import httpx; print(httpx.__version__)" 0.26.0 $ uv run --with httpx==0.25.0 python -c "import httpx; print(httpx.__version__)" 0.25.0 ``` The requested version will be respected regardless of the project's requirements. For example, even if the project requires `httpx==0.24.0`, the output above would be the same. ## Running scripts Scripts that declare inline metadata are automatically executed in environments isolated from the project. See the [scripts guide](../../guides/scripts.md#declaring-script-dependencies) for more details. For example, given a script: ```python title="example.py" # /// script # dependencies = [ # "httpx", # ] # /// import httpx resp = httpx.get("https://peps.python.org/api/peps.json") data = resp.json() print([(k, v["title"]) for k, v in data.items()][:10]) ``` The invocation `uv run example.py` would run _isolated_ from the project with only the given dependencies listed. ## Legacy scripts on Windows Support is provided for [legacy setuptools scripts](https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#scripts). These types of scripts are additional files installed by setuptools in `.venv\Scripts`. Currently only legacy scripts with the `.ps1`, `.cmd`, and `.bat` extensions are supported. For example, below is an example running a Command Prompt script. ```console $ uv run --with nuitka==2.6.7 -- nuitka.cmd --version ``` In addition, you don't need to specify the extension. `uv` will automatically look for files ending in `.ps1`, `.cmd`, and `.bat` in that order of execution on your behalf. ```console $ uv run --with nuitka==2.6.7 -- nuitka --version ``` ## Signal handling uv does not cede control of the process to the spawned command in order to provide better error messages on failure. Consequently, uv is responsible for forwarding some signals to the child process the requested command runs in. On Unix systems, uv will forward most signals (with the exception of SIGKILL, SIGCHLD, SIGIO, and SIGPOLL) to the child process. Since terminals send SIGINT to the foreground process group on Ctrl-C, uv will only forward a SIGINT to the child process if it is sent more than once or the child process group differs from uv's. On Windows, these concepts do not apply and uv ignores Ctrl-C events, deferring handling to the child process so it can exit cleanly. astral-sh-uv-9252ba6/docs/concepts/projects/sync.md000066400000000000000000000177431521221614700223060ustar00rootroot00000000000000# Locking and syncing Locking is the process of resolving your project's dependencies into a [lockfile](./layout.md#the-lockfile). Syncing is the process of installing a subset of packages from the lockfile into the [project environment](./layout.md#the-project-environment). ## Automatic lock and sync Locking and syncing are _automatic_ in uv. For example, when `uv run` is used, the project is locked and synced before invoking the requested command. This ensures the project environment is always up-to-date. Similarly, commands which read the lockfile, such as `uv tree`, will automatically update it before running. To disable automatic locking, use the `--locked` option: ```console $ uv run --locked ... ``` If the lockfile is not up-to-date, uv will raise an error instead of updating the lockfile. To use the lockfile without checking if it is up-to-date, use the `--frozen` option: ```console $ uv run --frozen ... ``` Similarly, to run a command without checking if the environment is up-to-date, use the `--no-sync` option: ```console $ uv run --no-sync ... ``` ## Checking the lockfile When considering if the lockfile is up-to-date, uv will check if it matches the project metadata. For example, if you add a dependency to your `pyproject.toml`, the lockfile will be considered outdated. Similarly, if you change the version constraints for a dependency such that the locked version is excluded, the lockfile will be considered outdated. However, if you change the version constraints such that the existing locked version is still included, the lockfile will still be considered up-to-date. You can check if the lockfile is up-to-date by passing the `--check` flag to `uv lock`: ```console $ uv lock --check ``` This is equivalent to the `--locked` flag for other commands. !!! important uv will not consider lockfiles outdated when new versions of packages are released — the lockfile needs to be explicitly updated if you want to upgrade dependencies. See the documentation on [upgrading locked package versions](#upgrading-locked-package-versions) for details. ## Creating the lockfile While the lockfile is created [automatically](#automatic-lock-and-sync), the lockfile may also be explicitly created or updated using `uv lock`: ```console $ uv lock ``` ## Syncing the environment While the environment is synced [automatically](#automatic-lock-and-sync), it may also be explicitly synced using `uv sync`: ```console $ uv sync ``` Syncing the environment manually is especially useful for ensuring your editor has the correct versions of dependencies. ### Editable installation When the environment is synced, uv will install the project (and other workspace members) as _editable_ packages, such that re-syncing is not necessary for changes to be reflected in the environment. To opt-out of this behavior, use the `--no-editable` option. !!! note If the project does not define a build system, it will not be installed. See the [build systems](./config.md#build-systems) documentation for details. ### Handling of extraneous packages `uv sync` performs "exact" syncing by default, which means it will remove any packages that are not present in the lockfile. To retain extraneous packages, use the `--inexact` flag: ```console $ uv sync --inexact ``` In contrast, `uv run` uses "inexact" syncing by default, ensuring that all required packages are installed but not removing extraneous packages. To enable exact syncing with `uv run`, use the `--exact` flag: ```console $ uv run --exact ... ``` ### Syncing optional dependencies uv reads optional dependencies from the `[project.optional-dependencies]` table. These are frequently referred to as "extras". uv does not sync extras by default. Use the `--extra` option to include an extra. ```console $ uv sync --extra foo ``` To quickly enable all extras, use the `--all-extras` option. See the [optional dependencies](./dependencies.md#optional-dependencies) documentation for details on how to manage optional dependencies. ### Syncing development dependencies uv reads development dependencies from the `[dependency-groups]` table (as defined in [PEP 735](https://peps.python.org/pep-0735/)). The `dev` group is special-cased and synced by default. See the [default groups](./dependencies.md#default-groups) documentation for details on changing the defaults. The `--no-dev` flag can be used to exclude the `dev` group. The `--only-dev` flag can be used to install the `dev` group _without_ the project and its dependencies. Additional groups can be included or excluded with the `--all-groups`, `--no-default-groups`, `--group `, `--only-group `, and `--no-group ` options. The semantics of `--only-group` are the same as `--only-dev`, the project will not be included. However, `--only-group` will also exclude default groups. Group exclusions always take precedence over inclusions, so given the command: ``` $ uv sync --no-group foo --group foo ``` The `foo` group would not be installed. See the [development dependencies](./dependencies.md#development-dependencies) documentation for details on how to manage development dependencies. ## Upgrading locked package versions With an existing `uv.lock` file, uv will prefer the previously locked versions of packages when running `uv sync` and `uv lock`. Package versions will only change if the project's dependency constraints exclude the previous, locked version. To upgrade all packages: ```console $ uv lock --upgrade ``` To upgrade a single package to the latest version, while retaining the locked versions of all other packages: ```console $ uv lock --upgrade-package ``` To upgrade a single package to a specific version: ```console $ uv lock --upgrade-package == ``` In all cases, upgrades are limited to the project's dependency constraints. For example, if the project defines an upper bound for a package then an upgrade will not go beyond that version. !!! note uv applies similar logic to Git dependencies. For example, if a Git dependency references the `main` branch, uv will prefer the locked commit SHA in an existing `uv.lock` file over the latest commit on the `main` branch, unless the `--upgrade` or `--upgrade-package` flags are used. These flags can also be provided to `uv sync` or `uv run` to update the lockfile _and_ the environment. ## Exporting the lockfile If you need to integrate uv with other tools or workflows, you can export `uv.lock` to different formats including `requirements.txt`, `pylock.toml` (PEP 751), and CycloneDX SBOM. ```console $ uv export --format requirements.txt $ uv export --format pylock.toml $ uv export --format cyclonedx1.5 ``` See the [export guide](./export.md) for comprehensive documentation on all export formats and their use cases. ## Partial installations Sometimes it's helpful to perform installations in multiple steps, e.g., for optimal layer caching while building a Docker image. `uv sync` has several flags for this purpose. - `--no-install-project`: Do not install the current project - `--no-install-workspace`: Do not install any workspace members, including the root project - `--no-install-package `: Do not install the given package(s) When these options are used, all the dependencies of the target are still installed. For example, `--no-install-project` will omit the _project_ but not any of its dependencies. If used improperly, these flags can result in a broken environment since a package can be missing its dependencies. ## Malware checks !!! important On-sync malware checking is in [preview](../preview.md), and is subject to change until stabilized. While syncing, uv can perform a lightweight scan of your lockfile for known malware by checking it against [OSV](https://osv.dev). OSV references MAL advisories from the OpenSSF's [malicious packages database](https://github.com/ossf/malicious-packages). If a locked dependency matches a malware advisory, the sync will be terminated. To enable malware checks, set `UV_MALWARE_CHECK=1` in your environment. astral-sh-uv-9252ba6/docs/concepts/projects/workspaces.md000066400000000000000000000175001521221614700235020ustar00rootroot00000000000000# Using workspaces Inspired by the [Cargo](https://doc.rust-lang.org/cargo/reference/workspaces.html) concept of the same name, a workspace is "a collection of one or more packages, called _workspace members_, that are managed together." Workspaces organize large codebases by splitting them into multiple packages with common dependencies. Think: a FastAPI-based web application, alongside a series of libraries that are versioned and maintained as separate Python packages, all in the same Git repository. In a workspace, each package defines its own `pyproject.toml`, but the workspace shares a single lockfile, ensuring that the workspace operates with a consistent set of dependencies. As such, `uv lock` operates on the entire workspace at once, while `uv run` and `uv sync` operate on the workspace root by default, though both accept a `--package` argument, allowing you to run a command in a particular workspace member from any workspace directory. ## Getting started To create a workspace, add a `tool.uv.workspace` table to a `pyproject.toml`, which will implicitly create a workspace rooted at that package. !!! tip By default, running `uv init` inside an existing package will add the newly created member to the workspace, creating a `tool.uv.workspace` table in the workspace root if it doesn't already exist. In defining a workspace, you must specify the `members` (required) and `exclude` (optional) keys, which direct the workspace to include or exclude specific directories as members respectively, and accept lists of globs: ```toml title="pyproject.toml" [project] name = "albatross" version = "0.1.0" requires-python = ">=3.12" dependencies = ["bird-feeder", "tqdm>=4,<5"] [tool.uv.sources] bird-feeder = { workspace = true } [tool.uv.workspace] members = ["packages/*"] exclude = ["packages/seeds"] ``` Every directory included by the `members` globs (and not excluded by the `exclude` globs) must contain a `pyproject.toml` file. However, workspace members can be _either_ [applications](./init.md#applications) or [libraries](./init.md#libraries); both are supported in the workspace context. Every workspace needs a root, which is _also_ a workspace member. In the above example, `albatross` is the workspace root, and the workspace members include all projects under the `packages` directory, except `seeds`. By default, `uv run` and `uv sync` operates on the workspace root. For example, in the above example, `uv run` and `uv run --package albatross` would be equivalent, while `uv run --package bird-feeder` would run the command in the `bird-feeder` package. ## Workspace sources Within a workspace, dependencies on workspace members are facilitated via [`tool.uv.sources`](./dependencies.md), as in: ```toml title="pyproject.toml" [project] name = "albatross" version = "0.1.0" requires-python = ">=3.12" dependencies = ["bird-feeder", "tqdm>=4,<5"] [tool.uv.sources] bird-feeder = { workspace = true } [tool.uv.workspace] members = ["packages/*"] [build-system] requires = ["uv_build>=0.11.20,<0.12"] build-backend = "uv_build" ``` In this example, the `albatross` project depends on the `bird-feeder` project, which is a member of the workspace. The `workspace = true` key-value pair in the `tool.uv.sources` table indicates the `bird-feeder` dependency should be provided by the workspace, rather than fetched from PyPI or another registry. !!! note Dependencies between workspace members are editable. Any `tool.uv.sources` definitions in the workspace root apply to all members, unless overridden in the `tool.uv.sources` of a specific member. For example, given the following `pyproject.toml`: ```toml title="pyproject.toml" [project] name = "albatross" version = "0.1.0" requires-python = ">=3.12" dependencies = ["bird-feeder", "tqdm>=4,<5"] [tool.uv.sources] bird-feeder = { workspace = true } tqdm = { git = "https://github.com/tqdm/tqdm" } [tool.uv.workspace] members = ["packages/*"] [build-system] requires = ["uv_build>=0.11.20,<0.12"] build-backend = "uv_build" ``` Every workspace member would, by default, install `tqdm` from GitHub, unless a specific member overrides the `tqdm` entry in its own `tool.uv.sources` table. !!! note If a workspace member provides `tool.uv.sources` for some dependency, it will ignore any `tool.uv.sources` for the same dependency in the workspace root, even if the member's source is limited by a [marker](dependencies.md#platform-specific-sources) that doesn't match the current platform. ## Workspace layouts The most common workspace layout can be thought of as a root project with a series of accompanying libraries. For example, continuing with the above example, this workspace has an explicit root at `albatross`, with two libraries (`bird-feeder` and `seeds`) in the `packages` directory: ```text albatross ├── packages │ ├── bird-feeder │ │ ├── pyproject.toml │ │ └── src │ │ └── bird_feeder │ │ ├── __init__.py │ │ └── foo.py │ └── seeds │ ├── pyproject.toml │ └── src │ └── seeds │ ├── __init__.py │ └── bar.py ├── pyproject.toml ├── README.md ├── uv.lock └── src └── albatross └── main.py ``` Since `seeds` was excluded in the `pyproject.toml`, the workspace has two members total: `albatross` (the root) and `bird-feeder`. ## When (not) to use workspaces Workspaces are intended to facilitate the development of multiple interconnected packages within a single repository. As a codebase grows in complexity, it can be helpful to split it into smaller, composable packages, each with their own dependencies and version constraints. Workspaces help enforce isolation and separation of concerns. For example, in uv, we have separate packages for the core library and the command-line interface, enabling us to test the core library independently of the CLI, and vice versa. Other common use cases for workspaces include: - A library with a performance-critical subroutine implemented in an extension module (Rust, C++, etc.). - A library with a plugin system, where each plugin is a separate workspace package with a dependency on the root. Workspaces are _not_ suited for cases in which members have conflicting requirements, or desire a separate virtual environment for each member. In this case, path dependencies are often preferable. For example, rather than grouping `albatross` and its members in a workspace, you can always define each package as its own independent project, with inter-package dependencies defined as path dependencies in `tool.uv.sources`: ```toml title="pyproject.toml" [project] name = "albatross" version = "0.1.0" requires-python = ">=3.12" dependencies = ["bird-feeder", "tqdm>=4,<5"] [tool.uv.sources] bird-feeder = { path = "packages/bird-feeder" } [build-system] requires = ["uv_build>=0.11.20,<0.12"] build-backend = "uv_build" ``` This approach conveys many of the same benefits, but allows for more fine-grained control over dependency resolution and virtual environment management (with the downside that `uv run --package` is no longer available; instead, commands must be run from the relevant package directory). Finally, uv's workspaces enforce a single `requires-python` for the entire workspace, taking the intersection of all members' `requires-python` values. If you need to support testing a given member on a Python version that isn't supported by the rest of the workspace, you may need to use `uv pip` to install that member in a separate virtual environment. !!! note As Python does not provide dependency isolation, uv can't ensure that a package uses its declared dependencies and nothing else. For workspaces specifically, uv can't ensure that packages don't import dependencies declared by another workspace member. astral-sh-uv-9252ba6/docs/concepts/python-versions.md000066400000000000000000000450701521221614700226620ustar00rootroot00000000000000# Python versions A Python version is composed of a Python interpreter (i.e. the `python` executable), the standard library, and other supporting files. ## Managed and system Python installations Since it is common for a system to have an existing Python installation, uv supports [discovering](#discovery-of-python-versions) Python versions. However, uv also supports [installing Python versions](#installing-a-python-version) itself. To distinguish between these two types of Python installations, uv refers to Python versions it installs as _managed_ Python installations and all other Python installations as _system_ Python installations. !!! note uv does not distinguish between Python versions installed by the operating system vs those installed and managed by other tools. For example, if a Python installation is managed with `pyenv`, it would still be considered a _system_ Python version in uv. ## Requesting a version A specific Python version can be requested with the `--python` flag in most uv commands. For example, when creating a virtual environment: ```console $ uv venv --python 3.11.6 ``` uv will ensure that Python 3.11.6 is available — downloading and installing it if necessary — then create the virtual environment with it. The following Python version request formats are supported: - `` (e.g., `3`, `3.12`, `3.12.3`) - `` (e.g., `>=3.12,<3.13`) - `` (e.g., `3.13t`, `3.12.0d`) - `+` (e.g., `3.13+freethreaded`, `3.12.0+debug`, `3.14+gil`) - `` (e.g., `cpython` or `cp`) - `@` (e.g., `cpython@3.12`) - `` (e.g., `cpython3.12` or `cp312`) - `` (e.g., `cpython>=3.12,<3.13`) - `----` (e.g., `cpython-3.12.3-macos-aarch64-none`) Additionally, a specific system Python interpreter can be requested with: - `` (e.g., `/opt/homebrew/bin/python3`) - `` (e.g., `mypython3`) - `` (e.g., `/some/environment/`) By default, uv will automatically download Python versions if they cannot be found on the system. This behavior can be [disabled with the `python-downloads` option](#disabling-automatic-python-downloads). ### Python version files The `.python-version` file can be used to create a default Python version request. uv searches for a `.python-version` file in the working directory and each of its parents. If none is found, uv will check the user-level configuration directory. Any of the request formats described above can be used, though use of a version number is recommended for interoperability with other tools. A `.python-version` file can be created in the current directory with the [`uv python pin`](../reference/cli.md/#uv-python-pin) command. A global `.python-version` file can be created in the user configuration directory with the [`uv python pin --global`](../reference/cli.md/#uv-python-pin) command. Discovery of `.python-version` files can be disabled with `--no-config`. uv will not search for `.python-version` files beyond project or workspace boundaries (except the user configuration directory). ## Installing a Python version uv bundles a list of downloadable CPython and PyPy distributions for macOS, Linux, and Windows. !!! tip By default, Python versions are automatically downloaded as needed without using `uv python install`. To install a Python version at a specific version: ```console $ uv python install 3.12.3 ``` To install the latest patch version: ```console $ uv python install 3.12 ``` To install a version that satisfies constraints: ```console $ uv python install '>=3.8,<3.10' ``` To install multiple versions: ```console $ uv python install 3.9 3.10 3.11 ``` To install a specific implementation: ```console $ uv python install pypy ``` All the [Python version request](#requesting-a-version) formats are supported except those that are used for requesting local interpreters such as a file path. By default `uv python install` will verify that a managed Python version is installed or install the latest version. If a `.python-version` file is present, uv will install the Python version listed in the file. A project that requires multiple Python versions may define a `.python-versions` file. If present, uv will install all the Python versions listed in the file. !!! important The available Python versions are frozen for each uv release. To install new Python versions, you may need upgrade uv. See the [storage documentation](../reference/storage.md#python-versions) for details about where installed Python versions are stored. ### Installing Python executables uv installs Python executables into your `PATH` by default, e.g., on Unix `uv python install 3.12` will install a Python executable into `~/.local/bin`, e.g., as `python3.12`. See the [storage documentation](../reference/storage.md#python-executables) for more details about the target directory. !!! tip If `~/.local/bin` is not in your `PATH`, you can add it with `uv python update-shell`. To install `python` and `python3` executables, include the experimental `--default` option: ```console $ uv python install 3.12 --default ``` When installing Python executables, uv will only overwrite an existing executable if it is managed by uv — e.g., if `~/.local/bin/python3.12` exists already uv will not overwrite it without the `--force` flag. uv will update executables that it manages. However, it will prefer the latest patch version of each Python minor version by default. For example: ```console $ uv python install 3.12.7 # Adds `python3.12` to `~/.local/bin` $ uv python install 3.12.6 # Does not update `python3.12` $ uv python install 3.12.8 # Updates `python3.12` to point to 3.12.8 ``` ## Upgrading Python versions !!! important Upgrades are only supported for uv-managed Python versions. Upgrades are not currently supported for PyPy, GraalPy, and Pyodide. uv allows transparently upgrading Python versions to the latest patch release, e.g., 3.13.4 to 3.13.5. uv does not allow transparently upgrading across minor Python versions, e.g., 3.12 to 3.13, because changing minor versions can affect dependency resolution. uv-managed Python versions can be upgraded to the latest supported patch release with the `python upgrade` command: To upgrade a Python version to the latest supported patch release: ```console $ uv python upgrade 3.12 ``` To upgrade all installed Python versions: ```console $ uv python upgrade ``` After an upgrade, uv will prefer the new version, but will retain the existing version as it may still be used by virtual environments. Virtual environments using the Python version will be automatically upgraded to the new patch version. If a virtual environment was created with an explicitly requested patch version, e.g., `uv venv -p 3.10.8`, it will not be transparently upgraded to a new version. ### Minor version directories Automatic upgrades for virtual environments are implemented using a directory with the Python minor version, e.g.: ``` ~/.local/share/uv/python/cpython-3.12-macos-aarch64-none ``` which is a symbolic link (on Unix) or junction (on Windows) pointing to a specific patch version: ```console $ readlink ~/.local/share/uv/python/cpython-3.12-macos-aarch64-none ~/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none ``` If this link is resolved by another tool, e.g., by canonicalizing the Python interpreter path, and used to create a virtual environment, it will not be automatically upgraded. ## Project Python versions uv will respect Python requirements defined in `requires-python` in the `pyproject.toml` file during project command invocations. The first Python version that is compatible with the requirement will be used, unless a version is otherwise requested, e.g., via a `.python-version` file or the `--python` flag. ## Viewing available Python versions To list installed and available Python versions: ```console $ uv python list ``` To filter the Python versions, provide a request, e.g., to show all Python 3.13 interpreters: ```console $ uv python list 3.13 ``` Or, to show all PyPy interpreters: ```console $ uv python list pypy ``` By default, downloads for other platforms and old patch versions are hidden. To view all versions: ```console $ uv python list --all-versions ``` To view Python versions for other platforms: ```console $ uv python list --all-platforms ``` To exclude downloads and only show installed Python versions: ```console $ uv python list --only-installed ``` See the [`uv python list`](../reference/cli.md#uv-python-list) reference for more details. ## Finding a Python executable To find a Python executable, use the `uv python find` command: ```console $ uv python find ``` By default, this will display the path to the first available Python executable. See the [discovery rules](#discovery-of-python-versions) for details about how executables are discovered. This interface also supports many [request formats](#requesting-a-version), e.g., to find a Python executable that has a version of 3.11 or newer: ```console $ uv python find '>=3.11' ``` By default, `uv python find` will include Python versions from virtual environments. If a `.venv` directory is found in the working directory or any of the parent directories or the `VIRTUAL_ENV` environment variable is set, it will take precedence over any Python executables on the `PATH`. To ignore virtual environments, use the `--system` flag: ```console $ uv python find --system ``` ## Discovery of Python versions When searching for a Python version, the following locations are checked: - Managed Python installations in the `UV_PYTHON_INSTALL_DIR`. - A Python interpreter on the `PATH` as `python`, `python3`, or `python3.x` on macOS and Linux, or `python.exe` on Windows. - On Windows, the Python interpreters in the Windows registry and Microsoft Store Python interpreters (see `py --list-paths`) that match the requested version. In some cases, uv allows using a Python version from a virtual environment. In this case, the virtual environment's interpreter will be checked for compatibility with the request before searching for an installation as described above. See the [pip-compatible virtual environment discovery](../pip/environments.md#discovery-of-python-environments) documentation for details. When performing discovery, non-executable files will be ignored. Each discovered executable is queried for metadata to ensure it meets the [requested Python version](#requesting-a-version). If the query fails, the executable will be skipped. If the executable satisfies the request, it is used without inspecting additional executables. When searching for a managed Python version, uv will prefer newer versions first. When searching for a system Python version, uv will use the first compatible version — not the newest version. If a Python version cannot be found on the system, uv will check for a compatible managed Python version download. ## Python pre-releases Python pre-releases will not be selected by default. Python pre-releases will be used if there is no other available installation matching the request. For example, if only a pre-release version is available it will be used but otherwise a stable release version will be used. Similarly, if the path to a pre-release Python executable is provided then no other Python version matches the request and the pre-release version will be used. If a pre-release Python version is available and matches the request, uv will not download a stable Python version instead. ## Free-threaded Python uv supports discovering and installing [free-threaded](https://docs.python.org/3.14/glossary.html#term-free-threading) Python variants in CPython 3.13+. For Python 3.13, free-threaded Python versions will not be selected by default. Free-threaded Python versions will only be selected when explicitly requested, e.g., with `3.13t` or `3.13+freethreaded`. For Python 3.14+, uv will allow use of free-threaded Python 3.14+ interpreters without explicit selection. The GIL-enabled build of Python will still be preferred, e.g., when performing an installation with `uv python install 3.14`. However, e.g., if a free-threaded interpreter comes before a GIL-enabled build on the `PATH`, it will be used. If both free-threaded and GIL-enabled Python versions are available on the system, and want to require the use of the GIL-enabled variant in a project, you can use the `+gil` variant specifier. ## Debug Python variants uv supports discovering and installing [debug builds](https://docs.python.org/3.14/using/configure.html#debug-build) of Python, i.e., with debug assertions enabled. !!! important Debug builds of Python are slower and are not appropriate for general use. Debug builds will be used if there is no other available installation matching the request. For example, if only a debug version is available it will be used but otherwise a stable release version will be used. Similarly, if the path to a debug Python executable is provided then no other Python version matches the request and the debug version will be used. Debug builds of Python can be explicitly requested with, e.g., `3.13d` or `3.13+debug`. !!! note CPython versions installed by uv usually have debug symbols stripped to reduce the distribution size. These debug builds do not have debug symbols stripped, which can be useful when debugging Python processes with a C-level debugger. ## Disabling automatic Python downloads By default, uv will automatically download Python versions when needed. The [`python-downloads`](../reference/settings.md#python-downloads) option can be used to disable this behavior. By default, it is set to `automatic`; set to `manual` to only allow Python downloads during `uv python install`. !!! tip The `python-downloads` setting can be set in a [persistent configuration file](./configuration-files.md) to change the default behavior, or the `--no-python-downloads` flag can be passed to any uv command. ## Requiring or disabling managed Python versions By default, uv will attempt to use Python versions found on the system and only download managed Python versions when necessary. To ignore system Python versions, and only use managed Python versions, use the `--managed-python` flag: ```console $ uv python list --managed-python ``` Similarly, to ignore managed Python versions and only use system Python versions, use the `--no-managed-python` flag: ```console $ uv python list --no-managed-python ``` To change uv's default behavior in a configuration file, use the [`python-preference` setting](#adjusting-python-version-preferences). ## Adjusting Python version preferences The [`python-preference`](../reference/settings.md#python-preference) setting determines whether to prefer using Python installations that are already present on the system, or those that are downloaded and installed by uv. By default, the `python-preference` is set to `managed` which prefers managed Python installations over system Python installations. However, system Python installations are still preferred over downloading a managed Python version. The following alternative options are available: - `only-managed`: Only use managed Python installations; never use system Python installations. Equivalent to `--managed-python`. - `system`: Prefer system Python installations over managed Python installations. - `only-system`: Only use system Python installations; never use managed Python installations. Equivalent to `--no-managed-python`. !!! note Automatic Python version downloads can be [disabled](#disabling-automatic-python-downloads) without changing the preference. ## Python implementation support uv supports the CPython, PyPy, Pyodide, and GraalPy Python implementations. If a Python implementation is not supported, uv will fail to discover its interpreter. The implementations may be requested with either the long or short name: - CPython: `cpython`, `cp` - PyPy: `pypy`, `pp` - GraalPy: `graalpy`, `gp` - Pyodide: `pyodide` Implementation name requests are not case-sensitive. See the [Python version request](#requesting-a-version) documentation for more details on the supported formats. ## Managed Python distributions uv supports downloading and installing CPython, PyPy, and Pyodide distributions. ### CPython distributions As Python does not publish official distributable CPython binaries, uv instead uses pre-built distributions from the Astral [`python-build-standalone`](https://github.com/astral-sh/python-build-standalone) project. `python-build-standalone` is also is used in many other Python projects, like [Mise](https://mise.jdx.dev/lang/python.html) and [bazelbuild/rules_python](https://github.com/bazelbuild/rules_python). The uv Python distributions are self-contained, highly-portable, and performant. While Python can be built from source, as in tools like `pyenv`, doing so requires preinstalled system dependencies, and creating optimized, performant builds (e.g., with PGO and LTO enabled) is very slow. These distributions have some behavior quirks, generally as a consequence of portability; see the [`python-build-standalone` quirks](https://gregoryszorc.com/docs/python-build-standalone/main/quirks.html) documentation for details. ### PyPy distributions !!! note PyPy versions lag behind CPython and currently only supports Python versions up to 3.11 PyPy distributions are provided by the [PyPy project](https://pypy.org). ### Pyodide distributions Pyodide distributions are provided by the [Pyodide project](https://github.com/pyodide/pyodide). Pyodide is a port of CPython for the WebAssembly / Emscripten platform. ## Transparent x86_64 emulation on aarch64 Both macOS and Windows support running x86_64 binaries on aarch64 through transparent emulation. This is called [Rosetta 2](https://support.apple.com/en-gb/102527) or [Windows on ARM (WoA) emulation](https://learn.microsoft.com/en-us/windows/arm/apps-on-arm-x86-emulation). It's possible to use x86_64 uv on aarch64, and also possible to use an x86_64 Python interpreter on aarch64. Either uv binary can use either Python interpreter, but a Python interpreter needs packages for its architecture, either all x86_64 or all aarch64. ## Registration in the Windows registry On Windows, installation of managed Python versions will register them with the Windows registry as defined by [PEP 514](https://peps.python.org/pep-0514/). After installation, the Python versions can be selected with the `py` launcher, e.g.: ```console $ uv python install 3.13.1 $ py -V:Astral/CPython3.13.1 ``` On uninstall, uv will remove the registry entry for the target version as well as any broken registry entries. astral-sh-uv-9252ba6/docs/concepts/resolution.md000066400000000000000000001107571521221614700217030ustar00rootroot00000000000000# Resolution Resolution is the process of taking a list of requirements and converting them to a list of package versions that fulfill the requirements. Resolution requires recursively searching for compatible versions of packages, ensuring that the requested requirements are fulfilled and that the requirements of the requested packages are compatible. ## Dependencies Most projects and packages have dependencies. Dependencies are other packages that are necessary in order for the current package to work. A package defines its dependencies as _requirements_, roughly a combination of a package name and acceptable versions. The dependencies defined by the current project are called _direct dependencies_. The dependencies added by each dependency of the current project are called _indirect_ or _transitive dependencies_. !!! note See the [dependency specifiers page](https://packaging.python.org/en/latest/specifications/dependency-specifiers/) in the Python Packaging documentation for details about dependencies. ## Basic examples To help demonstrate the resolution process, consider the following dependencies: - The project depends on `foo` and `bar`. - `foo` has one version, 1.0.0: - `foo 1.0.0` depends on `lib>=1.0.0`. - `bar` has one version, 1.0.0: - `bar 1.0.0` depends on `lib>=2.0.0`. - `lib` has two versions, 1.0.0 and 2.0.0. Both versions have no dependencies. In this example, the resolver must find a set of package versions which satisfies the project requirements. Since there is only one version of both `foo` and `bar`, those will be used. The resolution must also include the transitive dependencies, so a version of `lib` must be chosen. `foo 1.0.0` allows all available versions of `lib`, but `bar 1.0.0` requires `lib>=2.0.0` so `lib 2.0.0` must be used. In some resolutions, there may be more than one valid solution. Consider the following dependencies: - The project depends on `foo` and `bar`. - `foo` has two versions, 1.0.0 and 2.0.0: - `foo 1.0.0` has no dependencies. - `foo 2.0.0` depends on `lib==2.0.0`. - `bar` has two versions, 1.0.0 and 2.0.0: - `bar 1.0.0` has no dependencies. - `bar 2.0.0` depends on `lib==1.0.0` - `lib` has two versions, 1.0.0 and 2.0.0. Both versions have no dependencies. In this example, some version of both `foo` and `bar` must be selected; however, determining which version requires considering the dependencies of each version of `foo` and `bar`. `foo 2.0.0` and `bar 2.0.0` cannot be installed together as they conflict on their required version of `lib`, so the resolver must select either `foo 1.0.0` (along with `bar 2.0.0`) or `bar 1.0.0` (along with `foo 2.0.0`). Both are valid solutions, and different resolution algorithms may yield either result. ## Platform markers Markers allow attaching an expression to requirements that indicate when the dependency should be used. For example `bar ; python_version < "3.9"` indicates that `bar` should only be installed on Python 3.8 and earlier. Markers are used to adjust a package's dependencies based on the current environment or platform. For example, markers can be used to modify dependencies by operating system, CPU architecture, Python version, Python implementation, and more. !!! note See the [environment markers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers) section in the Python Packaging documentation for more details about markers. Markers are important for resolution because their values change the required dependencies. Typically, Python package resolvers use the markers of the _current_ platform to determine which dependencies to use since the package is often being _installed_ on the current platform. However, for _locking_ dependencies this is problematic — the lockfile would only work for developers using the same platform the lockfile was created on. To solve this problem, platform-independent, or "universal" resolvers exist. uv supports both [platform-specific](#platform-specific-resolution) and [universal](#universal-resolution) resolution. ## Platform-specific resolution By default, uv's pip interface, i.e., [`uv pip compile`](../pip/compile.md), produces a resolution that is platform-specific, like `pip-tools`. There is no way to use platform-specific resolution in the uv's project interface. uv also supports resolving for specific, alternate platforms and Python versions with the `--python-platform` and `--python-version` options. For example, if using Python 3.12 on macOS, `uv pip compile --python-platform linux --python-version 3.10 requirements.in` can be used to produce a resolution for Python 3.10 on Linux instead. Unlike universal resolution, during platform-specific resolution, the provided `--python-version` is the exact python version to use, not a lower bound. !!! note Python's environment markers expose far more information about the current machine than can be expressed by a simple `--python-platform` argument. For example, the `platform_version` marker on macOS includes the time at which the kernel was built, which can (in theory) be encoded in package requirements. uv's resolver makes a best-effort attempt to generate a resolution that is compatible with any machine running on the target `--python-platform`, which should be sufficient for most use cases, but may lose fidelity for complex package and platform combinations. ## Universal resolution uv's lockfile (`uv.lock`) is created with a universal resolution and is portable across platforms. This ensures that dependencies are locked for everyone working on the project, regardless of operating system, architecture, and Python version. The uv lockfile is created and modified by [project](../concepts/projects/index.md) commands such as `uv lock`, `uv sync`, and `uv add`. Universal resolution is also available in uv's pip interface, i.e., [`uv pip compile`](../pip/compile.md), with the `--universal` flag. The resulting requirements file will contain markers to indicate which platform each dependency is relevant for. During universal resolution, a package may be listed multiple times with different versions or URLs if different versions are needed for different platforms — the markers determine which version will be used. A universal resolution is often more constrained than a platform-specific resolution, since we need to take the requirements for all markers into account. During universal resolution, all required packages must be compatible with the _entire_ range of `requires-python` declared in the `pyproject.toml`. For example, if a project's `requires-python` is `>=3.8`, resolution will fail if all versions of given dependency require Python 3.9 or later, since the dependency lacks a usable version for (e.g.) Python 3.8, the lower bound of the project's supported range. In other words, the project's `requires-python` must be a subset of the `requires-python` of all its dependencies. When selecting the compatible version for a given dependency, uv will ([by default](#multi-version-resolution)) attempt to choose the latest compatible version for each supported Python version. For example, if a project's `requires-python` is `>=3.8`, and the latest version of a dependency requires Python 3.9 or later, while all prior versions supporting Python 3.8, the resolver will select the latest version for users running Python 3.9 or later, and previous versions for users running Python 3.8. When evaluating `requires-python` ranges for dependencies, uv only considers lower bounds and ignores upper bounds entirely. For example, `>=3.8, <4` is treated as `>=3.8`. Respecting upper bounds on `requires-python` often leads to formally correct but practically incorrect resolutions, as, e.g., resolvers will backtrack to the first published version that omits the upper bound (see: [`Requires-Python` upper limits](https://discuss.python.org/t/requires-python-upper-limits/12663)). ## Limited resolution environments By default, the universal resolver attempts to solve for all platforms and Python versions. If your project supports only a limited set of platforms or Python versions, you can constrain the set of solved platforms via the `environments` setting, which accepts a list of [PEP 508 environment markers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers). In other words, you can use the `environments` setting to _reduce_ the set of supported platforms. For example, to constrain the lockfile to macOS and Linux, and avoid solving for Windows: ```toml title="pyproject.toml" [tool.uv] environments = [ "sys_platform == 'darwin'", "sys_platform == 'linux'", ] ``` Or, to avoid solving for alternative Python implementations: ```toml title="pyproject.toml" [tool.uv] environments = [ "implementation_name == 'cpython'" ] ``` Entries in the `environments` setting must be disjoint (i.e., they must not overlap). For example, `sys_platform == 'darwin'` and `sys_platform == 'linux'` are disjoint, but `sys_platform == 'darwin'` and `python_version >= '3.9'` are not, since both could be true at the same time. ## Required environments In the Python ecosystem, packages can be published as source distributions, built distributions (wheels), or both; but to install a package, a built distribution is required. If a package lacks a built distribution, or lacks a distribution for the current platform or Python version (built distributions are often platform-specific), uv will attempt to build the package from source, then install the resulting built distribution. Some packages (like PyTorch) publish built distributions, but omit a source distribution. Such packages are _only_ installable on platforms for which a built distribution is available. For example, if a package publishes built distributions for Linux, but not macOS or Windows, then that package will _only_ be installable on Linux. Packages that lack source distributions cause problems for universal resolution, since there will typically be at least one platform or Python version for which the package is not installable. By default, uv requires each such package to include at least one wheel that is compatible with the target Python version. The `required-environments` setting can be used to ensure that the resulting resolution contains wheels for specific platforms, or fails if no such wheels are available. The setting accepts a list of [PEP 508 environment markers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers). While the `environments` setting _limits_ the set of environments that uv will consider when resolving dependencies, `required-environments` _expands_ the set of platforms that uv _must_ support when resolving dependencies. For example, `environments = ["sys_platform == 'darwin'"]` would limit uv to solving for macOS (and ignoring Linux and Windows). On the other hand, `required-environments = ["sys_platform == 'darwin'"]` would _require_ that any package without a source distribution include a wheel for macOS in order to be installable (and would fail if no such wheel is available). In practice, `required-environments` can be useful for declaring explicit support for non-latest platforms, since this often requires backtracking past the latest published versions of those packages. For example, to guarantee that any built distribution-only packages includes support for Intel macOS: ```toml title="pyproject.toml" [tool.uv] required-environments = [ "sys_platform == 'darwin' and platform_machine == 'x86_64'" ] ``` ## Common marker values The `environments` and `required-environments` settings accept [PEP 508 environment markers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers). The values for these markers are derived from the Python runtime (e.g., [`sys.platform`](https://docs.python.org/3/library/sys.html#sys.platform), [`platform.machine()`](https://docs.python.org/3/library/platform.html#platform.machine), [`platform.system()`](https://docs.python.org/3/library/platform.html#platform.system), and [`os.name`](https://docs.python.org/3/library/os.html#os.name)). For quick reference, the most common marker values by platform are: | Marker | Linux | macOS | Windows | | --------------------------- | ----------- | ---------- | ----------- | | `sys_platform` | `'linux'` | `'darwin'` | `'win32'` | | `platform_system` | `'Linux'` | `'Darwin'` | `'Windows'` | | `platform_machine` (x86-64) | `'x86_64'` | `'x86_64'` | `'AMD64'` | | `platform_machine` (ARM64) | `'aarch64'` | `'arm64'` | `'ARM64'` | | `os_name` | `'posix'` | `'posix'` | `'nt'` | !!! note On Windows, `sys_platform` is always `'win32'`, even on 64-bit systems. You can check the values for your current platform by running: ```console $ uvx python -c "import sysconfig; print(sysconfig.get_config_vars())" ``` ## Dependency preferences If resolution output file exists, i.e., a uv lockfile (`uv.lock`) or a requirements output file (`requirements.txt`), uv will _prefer_ the dependency versions listed there. Similarly, if installing a package into a virtual environment, uv will prefer the already installed version if present. This means that locked or installed versions will not change unless an incompatible version is requested or an upgrade is explicitly requested with `--upgrade`. ## Resolution strategy By default, uv tries to use the latest version of each package. For example, `uv pip install flask>=2.0.0` will install the latest version of Flask, e.g., 3.0.0. If `flask>=2.0.0` is a dependency of the project, only `flask` 3.0.0 will be used. This is important, for example, because running tests will not check that the project is actually compatible with its stated lower bound of `flask` 2.0.0. With `--resolution lowest`, uv will install the lowest possible version for all dependencies, both direct and indirect (transitive). Alternatively, `--resolution lowest-direct` will use the lowest compatible versions for all direct dependencies, while using the latest compatible versions for all other dependencies. uv will always use the latest versions for build dependencies. For example, given the following `requirements.in` file: ```python title="requirements.in" flask>=2.0.0 ``` Running `uv pip compile requirements.in -o requirements.txt` would produce the following `requirements.txt` file: ```python title="requirements.txt" # This file was autogenerated by uv via the following command: # uv pip compile requirements.in -o requirements.txt blinker==1.7.0 # via flask click==8.1.7 # via flask flask==3.0.0 itsdangerous==2.1.2 # via flask jinja2==3.1.2 # via flask markupsafe==2.1.3 # via # jinja2 # werkzeug werkzeug==3.0.1 # via flask ``` However, `uv pip compile --resolution lowest requirements.in -o requirements.txt` would instead produce: ```python title="requirements.txt" # This file was autogenerated by uv via the following command: # uv pip compile --resolution lowest requirements.in -o requirements.txt click==7.1.2 # via flask flask==2.0.0 itsdangerous==2.0.0 # via flask jinja2==3.0.0 # via flask markupsafe==2.0.0 # via jinja2 werkzeug==2.0.0 # via flask ``` When publishing libraries, it is recommended to separately run tests with `--resolution lowest` or `--resolution lowest-direct` in continuous integration to ensure compatibility with the declared lower bounds. ## Pre-release handling By default, uv will accept pre-release versions during dependency resolution in two cases: 1. If the package is a direct dependency, and its version specifiers include a pre-release specifier (e.g., `flask>=2.0.0rc1`). 1. If _all_ published versions of a package are pre-releases. If dependency resolution fails due to a transitive pre-release, uv will prompt use of `--prerelease allow` to allow pre-releases for all dependencies. Alternatively, the transitive dependency can be added as a [constraint](#dependency-constraints) or direct dependency (i.e. in `requirements.in` or `pyproject.toml`) with a pre-release version specifier (e.g., `flask>=2.0.0rc1`) to opt in to pre-release support for that specific dependency. Pre-releases are [notoriously difficult](https://pubgrub-rs-guide.netlify.app/limitations/prerelease_versions) to model, and are a frequent source of bugs in other packaging tools. uv's pre-release handling is _intentionally_ limited and requires user opt-in for pre-releases to ensure correctness. For more details, see [Pre-release compatibility](../pip/compatibility.md#pre-release-compatibility). ## Multi-version resolution During universal resolution, a package may be listed multiple times with different versions or URLs within the same lockfile, since different versions may be needed for different platforms or Python versions. The `--fork-strategy` setting can be used to control how uv trades off between (1) minimizing the number of selected versions and (2) selecting the latest-possible version for each platform. The former leads to greater consistency across platforms, while the latter leads to use of newer package versions where possible. By default (`--fork-strategy requires-python`), uv will optimize for selecting the latest version of each package for each supported Python version, while minimizing the number of selected versions across platforms. For example, when resolving `numpy` with a Python requirement of `>=3.8`, uv would select the following versions: ```txt numpy==1.24.4 ; python_version == "3.8" numpy==2.0.2 ; python_version == "3.9" numpy==2.2.0 ; python_version >= "3.10" ``` This resolution reflects the fact that NumPy 2.2.0 and later require at least Python 3.10, while earlier versions are compatible with Python 3.8 and 3.9. Under `--fork-strategy fewest`, uv will instead minimize the number of selected versions for each package, preferring older versions that are compatible with a wider range of supported Python versions or platforms. For example, when in the scenario above, uv would select `numpy==1.24.4` for all Python versions, rather than upgrading to `numpy==2.0.2` for Python 3.9 and `numpy==2.2.0` for Python 3.10 and later. ## Dependency constraints Like pip, uv supports constraint files (`--constraint constraints.txt`) which narrow the set of acceptable versions for the given packages. Constraint files are similar to requirements files, but being listed as a constraint alone will not cause a package to be included to the resolution. Instead, constraints only take effect if a requested package is already pulled in as a direct or transitive dependency. Constraints are useful for reducing the range of available versions for a transitive dependency. They can also be used to keep a resolution in sync with some other set of resolved versions, regardless of which packages are overlapping between the two. ## Dependency overrides Dependency overrides allow bypassing unsuccessful or undesirable resolutions by overriding a package's declared dependencies. Overrides are a useful last resort for cases in which you _know_ that a dependency is compatible with a certain version of a package, despite the metadata indicating otherwise. For example, if a transitive dependency declares the requirement `pydantic>=1.0,<2.0`, but _does_ work with `pydantic>=2.0`, the user can override the declared dependency by including `pydantic>=1.0,<3` in the overrides, thereby allowing the resolver to choose a newer version of `pydantic`. Concretely, if `pydantic>=1.0,<3` is included as an override, uv will ignore all declared requirements on `pydantic`, replacing them with the override. In the above example, the `pydantic>=1.0,<2.0` requirement would be ignored completely, and would instead be replaced with `pydantic>=1.0,<3`. While constraints can only _reduce_ the set of acceptable versions for a package, overrides can _expand_ the set of acceptable versions, providing an escape hatch for erroneous upper version bounds. As with constraints, overrides do not add a dependency on the package and only take effect if the package is requested in a direct or transitive dependency. In a `pyproject.toml`, use `tool.uv.override-dependencies` to define a list of overrides. In the pip-compatible interface, the `--override` option can be used to pass files with the same format as constraints files. If multiple overrides are provided for the same package, they must be differentiated with [markers](#platform-markers). If a package has a dependency with a marker, it is replaced unconditionally when using overrides — it does not matter if the marker evaluates to true or false. ## Dependency metadata During resolution, uv needs to resolve the metadata for each package it encounters, in order to determine its dependencies. This metadata is often available as a static file in the package index; however, for packages that only provide source distributions, the metadata may not be available upfront. In such cases, uv has to build the package to determine its metadata (e.g., by invoking `setup.py`). This can introduce a performance penalty during resolution. Further, it imposes the requirement that the package can be built on all platforms, which may not be true. For example, you may have a package that should only be built and installed on Linux, but doesn't build successfully on macOS or Windows. While uv can construct a perfectly valid lockfile for this scenario, doing so would require building the package, which would fail on non-Linux platforms. The `tool.uv.dependency-metadata` table can be used to provide static metadata for such dependencies upfront, thereby allowing uv to skip the build step and use the provided metadata instead. For example, to provide metadata for `chumpy` upfront, include its `dependency-metadata` in the `pyproject.toml`: ```toml [[tool.uv.dependency-metadata]] name = "chumpy" version = "0.70" requires-dist = ["numpy>=1.8.1", "scipy>=0.13.0", "six>=1.11.0"] ``` These declarations are intended for cases in which a package does _not_ declare static metadata upfront, though they are also useful for packages that require [disabling build isolation](./projects/config.md#build-isolation) In such cases, it may be easier to declare the package metadata upfront, rather than creating a custom build environment prior to resolving the package. For example, past versions of `flash-attn` did not declare static metadata. By declaring metadata for `flash-attn` upfront, uv can resolve `flash-attn` without building the package from source (which itself requires installing `torch`): ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.12" dependencies = ["flash-attn"] [tool.uv.sources] flash-attn = { git = "https://github.com/Dao-AILab/flash-attention", tag = "v2.6.3" } [[tool.uv.dependency-metadata]] name = "flash-attn" version = "2.6.3" requires-dist = ["torch", "einops"] ``` Like dependency overrides, `tool.uv.dependency-metadata` can also be used for cases in which a package's metadata is incorrect or incomplete, or when a package is not available in the package index. While dependency overrides allow overriding the allowed versions of a package globally, metadata overrides allow overriding the declared metadata of a _specific package_. !!! note The `version` field in `tool.uv.dependency-metadata` is optional for registry-based dependencies (when omitted, uv will assume the metadata applies to all versions of the package), but _required_ for direct URL dependencies (like Git dependencies). Entries in the `tool.uv.dependency-metadata` table follow the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) specification, though only `name`, `version`, `requires-dist`, `requires-python`, and `provides-extra` are read by uv. The `version` field is also considered optional. If omitted, the metadata will be used for all versions of the specified package. ## Conflicting dependencies uv requires that all dependencies declared by a project are compatible with each other and resolves all dependencies together when creating the lockfile. This includes project dependencies, optional dependencies ("extras"), and dependency groups (development dependencies). If dependencies declared in one extra are not compatible with those in another extra, uv will fail to resolve the requirements of the project with an error. For example, consider two sets of optional dependencies that conflict with one another: ```toml title="pyproject.toml" [project.optional-dependencies] extra1 = ["numpy==2.1.2"] extra2 = ["numpy==2.0.0"] ``` If you run `uv lock` with the above dependencies, resolution will fail: ```console $ uv lock x No solution found when resolving dependencies: `-> Because myproject[extra2] depends on numpy==2.0.0 and myproject[extra1] depends on numpy==2.1.2, we can conclude that myproject[extra1] and myproject[extra2] are incompatible. And because your project requires myproject[extra1] and myproject[extra2], we can conclude that your projects's requirements are unsatisfiable. ``` To work around this, uv supports explicit declaration of conflicts. If you specify that `extra1` and `extra2` are conflicting, uv will resolve them separately. Specify conflicts in the `tool.uv` section: ```toml title="pyproject.toml" [tool.uv] conflicts = [ [ { extra = "extra1" }, { extra = "extra2" }, ], ] ``` Now, running `uv lock` will succeed. However, now you cannot install both `extra1` and `extra2` at the same time: ```console $ uv sync --extra extra1 --extra extra2 Resolved 3 packages in 14ms error: extra `extra1`, extra `extra2` are incompatible with the declared conflicts: {`myproject[extra1]`, `myproject[extra2]`} ``` This error occurs because installing both `extra1` and `extra2` would result in installing two different versions of a package into the same environment. The above strategy for dealing with conflicting optional dependencies also works with dependency groups: ```toml title="pyproject.toml" [dependency-groups] group1 = ["numpy==2.1.2"] group2 = ["numpy==2.0.0"] [tool.uv] conflicts = [ [ { group = "group1" }, { group = "group2" }, ], ] ``` The only difference from conflicting extras is that you need to use the `group` key instead of `extra`. When using a workspace with multiple projects, the same restrictions apply — uv requires all workspace members to be compatible with each other. Similarly, conflicts can be declared across workspace members. For example, consider the following workspace: ```toml title="member1/pyproject.toml" [project] name = "member1" [project.optional-dependencies] extra1 = ["numpy==2.1.2"] ``` ```toml title="member2/pyproject.toml" [project] name = "member2" [project.optional-dependencies] extra2 = ["numpy==2.0.0"] ``` To declare a conflict between extras in these different workspace members, use the `package` key: ```toml title="pyproject.toml" [tool.uv] conflicts = [ [ { package = "member1", extra = "extra1" }, { package = "member2", extra = "extra2" }, ], ] ``` It's also possible for the project dependencies (i.e., `project.dependencies`) of one workspace member to conflict with the extra of another member, for example: ```toml title="member1/pyproject.toml" [project] name = "member1" dependencies = ["numpy==2.1.2"] ``` ```toml title="member2/pyproject.toml" [project] name = "member2" [project.optional-dependencies] extra2 = ["numpy==2.0.0"] ``` This conflict can also be declared using the `package` key: ```toml title="pyproject.toml" [tool.uv] conflicts = [ [ { package = "member1" }, { package = "member2", extra = "extra2" }, ], ] ``` Similarly, it's possible for some workspace members to have conflicting project dependencies: ```toml title="member1/pyproject.toml" [project] name = "member1" dependencies = ["numpy==2.1.2"] ``` ```toml title="member2/pyproject.toml" [project] name = "member2" dependencies = ["numpy==2.0.0"] ``` This conflict can also be declared using the `package` key: ```toml title="pyproject.toml" [tool.uv] conflicts = [ [ { package = "member1" }, { package = "member2" }, ], ] ``` These workspace members will not be installable together, e.g., the workspace root cannot define: ```toml title="pyproject.toml" [project] name = "root" dependencies = ["member1", "member2"] ``` ## Lower bounds By default, `uv add` adds lower bounds to dependencies and, when using uv to manage projects, uv will warn if direct dependencies don't have lower bounds. Lower bounds are not critical in the "happy path", but they are important for cases where there are dependency conflicts. For example, consider a project that requires two packages and those packages have conflicting dependencies. The resolver needs to check all combinations of all versions within the constraints for the two packages — if all of them conflict, an error is reported because the dependencies are not satisfiable. If there are no lower bounds, the resolver can (and often will) backtrack down to the oldest version of a package. This isn't only problematic because it's slow, the old version of the package often fails to build, or the resolver can end up picking a version that's old enough that it doesn't depend on the conflicting package, but also doesn't work with your code. Lower bounds are particularly critical when writing a library. It's important to declare the lowest version for each dependency that your library works with, and to validate that the bounds are correct — testing with [`--resolution lowest` or `--resolution lowest-direct`](#resolution-strategy). Otherwise, a user may receive an old, incompatible version of one of your library's dependencies and the library will fail with an unexpected error. ## Reproducible resolutions uv supports an `--exclude-newer` option to limit resolution to distributions uploaded before a specific date, allowing reproduction of installations regardless of new package releases. The date is compared against the upload time of each individual distribution artifact (i.e., when each file was uploaded to the package index), not the release date of the package version. The date may be specified as an [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) timestamp (e.g., `2006-12-02T02:07:43Z`) or a local date in the same format (e.g., `2006-12-02`) in your system's configured time zone. !!! important The package index must support the `upload-time` field as specified in [`PEP 700`](https://peps.python.org/pep-0700/). If the field is not present for a given distribution, the distribution will be treated as unavailable unless the package is opted out via `--exclude-newer-package =false`, or the index is configured with its own `exclude-newer` value, or the index is opted out via `[[tool.uv.index]] exclude-newer = false`. PyPI provides `upload-time` for all packages. To ensure reproducibility, messages for unsatisfiable resolutions will not mention that distributions were excluded due to the `--exclude-newer` flag — newer distributions will be treated as if they do not exist. !!! note The `--exclude-newer` option is only applied to packages that are read from a registry (as opposed to, e.g., Git dependencies). Further, when using the `uv pip` interface, uv will not downgrade previously installed packages unless the `--reinstall` flag is provided, in which case uv will perform a new resolution. This option is also supported in the `pyproject.toml`, e.g.: ```pyproject.toml [tool.uv] exclude-newer = "2006-12-02T02:07:43Z" ``` When specified in persistent configuration, local date times are not allowed. Values may also be specified for specific packages, e.g., `--exclude-newer-package setuptools=2006-12-02`, or: ```pyproject.toml [tool.uv] exclude-newer-package = { setuptools = "2006-12-02T02:07:43Z" } ``` The package option also accepts `=false` to opt a package out of the restriction, e.g., `--exclude-newer-package setuptools=false`, or: ```pyproject.toml [tool.uv] exclude-newer-package = { setuptools = false } ``` This is useful to temporarily use a newer version of package or to allow resolving a package from an index that does not publish upload times. Package-specific values will take precedence over both global and index-specific values. Likewise, an individual index can override the global cutoff: ```pyproject.toml [tool.uv] exclude-newer = "2006-12-02T02:07:43Z" [[tool.uv.index]] name = "internal" url = "https://internal.example.com/simple" exclude-newer = "7 days" ``` Or disable it entirely for that index: ```pyproject.toml [[tool.uv.index]] name = "internal" url = "https://internal.example.com/simple" exclude-newer = false ``` This is useful for private indexes that don't publish `upload-time`, or for applying a different reproducibility window to a specific index while preserving the global behavior elsewhere. ## Dependency cooldowns uv also supports dependency "cooldowns" in which resolution will ignore packages newer than a duration. This is a good way to improve security posture by delaying package updates until the community has had the opportunity to vet new versions of packages. This feature is available via the [`exclude-newer` option](#reproducible-resolutions) and shares the same semantics. Define a dependency cooldown by specifying a duration instead of an absolute value. Either a "friendly" duration (e.g., `24 hours`, `1 week`, `30 days`) or an ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`) can be used. !!! note Durations do not respect semantics of the local time zone and are always resolved to a fixed number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored). Calendar units such as months and years are not allowed since they are inherently inconsistent lengths. When a duration is used for resolution, a timestamp is calculated relative to the current time. When using a `uv.lock` file, the timestamp is included in the lockfile. uv will not update the lockfile when the current time changes, instead, uv will update the timestamp when a new resolution is performed, e.g., when `--upgrade` or `--refresh` is used. This option is also supported in the `pyproject.toml`, e.g.: ```pyproject.toml [tool.uv] exclude-newer = "1 week" ``` Values may also be specified for specific packages, e.g., `--exclude-newer-package "setuptools=30 days"`, or: ```pyproject.toml [tool.uv] exclude-newer = "1 week" exclude-newer-package = { setuptools = "30 days" } ``` ## Source distribution [PEP 625](https://peps.python.org/pep-0625/) specifies that packages must distribute source distributions as gzip tarball (`.tar.gz`) archives. Prior to this specification, other archive formats, which need to be supported for backward compatibility, were also allowed. uv supports reading and extracting archives in the following formats: - gzip tarball (`.tar.gz`, `.tgz`) - bzip2 tarball (`.tar.bz2`, `.tbz`) - xz tarball (`.tar.xz`, `.txz`) - zstd tarball (`.tar.zst`) - lzip tarball (`.tar.lz`) - lzma tarball (`.tar.lzma`) - zip (`.zip`) !!! important Using source distribution extensions other than `.tar.gz` is strongly discouraged, as these extensions are not widely or consistently supported across the Python packaging ecosystem. !!! warning "Deprecated" Support for source distribution extensions other than `.tar.gz` is deprecated and will be removed in a future release of uv. ## Lockfile versioning The `uv.lock` file uses a versioned schema. The schema version is included in the `version` field of the lockfile. Any given version of uv can read and write lockfiles with the same schema version, but will reject lockfiles with a greater schema version. For example, if your uv version supports schema v1, `uv lock` will error if it encounters an existing lockfile with schema v2. uv versions that support schema v2 _may_ be able to read lockfiles with schema v1 if the schema update was backwards-compatible. However, this is not guaranteed, and uv may exit with an error if it encounters a lockfile with an outdated schema version. The schema version is considered part of the public API, and so is only bumped in minor releases, as a breaking change (see [Versioning](../reference/policies/versioning.md)). As such, all uv patch versions within a given minor uv release are guaranteed to have full lockfile compatibility. In other words, lockfiles may only be rejected across minor releases. The `revision` field of the lockfile is used to track backwards compatible changes to the lockfile. For example, adding a new field to distributions. Changes to the revision will not cause older versions of uv to error. ## Learn more For more details about the internals of the resolver, see the [resolver reference](../reference/internals/resolver.md) documentation. astral-sh-uv-9252ba6/docs/concepts/tools.md000066400000000000000000000227511521221614700206340ustar00rootroot00000000000000# Tools Tools are Python packages that provide command-line interfaces. !!! note See the [tools guide](../guides/tools.md) for an introduction to working with the tools interface — this document discusses details of tool management. ## The `uv tool` interface uv includes a dedicated interface for interacting with tools. Tools can be invoked without installation using `uv tool run`, in which case their dependencies are installed in a temporary virtual environment isolated from the current project. Because it is very common to run tools without installing them, a `uvx` alias is provided for `uv tool run` — the two commands are exactly equivalent. For brevity, the documentation will mostly refer to `uvx` instead of `uv tool run`. Tools can also be installed with `uv tool install`, in which case their executables are [available on the `PATH`](#tool-executables) — an isolated virtual environment is still used, but it is not removed when the command completes. ## Execution vs installation In most cases, executing a tool with `uvx` is more appropriate than installing the tool. Installing the tool is useful if you need the tool to be available to other programs on your system, e.g., if some script you do not control requires the tool, or if you are in a Docker image and want to make the tool available to users. ## Tool environments When running a tool with `uvx`, a virtual environment is stored in the uv cache directory and is treated as disposable, i.e., if you run `uv cache clean` the environment will be deleted. The environment is only cached to reduce the overhead of repeated invocations. If the environment is removed, a new one will be created automatically. When installing a tool with `uv tool install`, a virtual environment is created in the [uv tools directory](../reference/storage.md#tools). The environment will not be removed unless the tool is uninstalled. If the environment is manually deleted, the tool will fail to run. !!! important Tool environments are _not_ intended to be mutated directly. It is strongly recommended never to mutate a tool environment manually, e.g., with a `pip` operation. ## Tool versions Unless a specific version is requested, `uv tool install` will install the latest available of the requested tool. `uvx` will use the latest available version of the requested tool _on the first invocation_. After that, `uvx` will use the cached version of the tool unless a different version is requested, the cache is pruned, or the cache is refreshed. For example, to run a specific version of Ruff: ```console $ uvx ruff@0.6.0 --version ruff 0.6.0 ``` A subsequent invocation of `uvx` will use the latest, not the cached, version. ```console $ uvx ruff --version ruff 0.6.2 ``` But, if a new version of Ruff was released, it would not be used unless the cache was refreshed. To request the latest version of Ruff and refresh the cache, use the `@latest` suffix: ```console $ uvx ruff@latest --version 0.6.2 ``` Once a tool is installed with `uv tool install`, `uvx` will use the installed version by default. For example, after installing an older version of Ruff: ```console $ uv tool install ruff==0.5.0 ``` The version of `ruff` and `uvx ruff` is the same: ```console $ ruff --version ruff 0.5.0 $ uvx ruff --version ruff 0.5.0 ``` However, you can ignore the installed version by requesting the latest version explicitly, e.g.: ```console $ uvx ruff@latest --version 0.6.2 ``` Or, by using the `--isolated` flag, which will avoid refreshing the cache but ignore the installed version: ```console $ uvx --isolated ruff --version 0.6.2 ``` `uv tool install` will also respect the `{package}@{version}` and `{package}@latest` specifiers, as in: ```console $ uv tool install ruff@latest $ uv tool install ruff@0.6.0 ``` ## Upgrading tools Tool environments may be upgraded via `uv tool upgrade`, or re-created entirely via subsequent `uv tool install` operations. To upgrade all packages in a tool environment ```console $ uv tool upgrade black ``` To upgrade a single package in a tool environment: ```console $ uv tool upgrade black --upgrade-package click ``` Tool upgrades will respect the version constraints provided when installing the tool. For example, `uv tool install black >=23,<24` followed by `uv tool upgrade black` will upgrade Black to the latest version in the range `>=23,<24`. To instead replace the version constraints, reinstall the tool with `uv tool install`: ```console $ uv tool install black>=24 ``` Similarly, tool upgrades will retain the settings provided when installing the tool. For example, `uv tool install black --prerelease allow` followed by `uv tool upgrade black` will retain the `--prerelease allow` setting. !!! note Tool upgrades will reinstall the tool executables, even if they have not changed. To reinstall packages during upgrade, use the `--reinstall` and `--reinstall-package` options. To reinstall all packages in a tool environment ```console $ uv tool upgrade black --reinstall ``` To reinstall a single package in a tool environment: ```console $ uv tool upgrade black --reinstall-package click ``` ## Including additional dependencies Additional packages can be included during tool execution: ```console $ uvx --with ``` And, during tool installation: ```console $ uv tool install --with ``` The `--with` option can be provided multiple times to include additional packages. The `--with` option supports package specifications, so a specific version can be requested: ```console $ uvx --with == ``` The `-w` shorthand can be used in place of the `--with` option: ```console $ uvx -w ``` If the requested version conflicts with the requirements of the tool package, package resolution will fail and the command will error. ## Installing executables from additional packages When installing a tool, you may want to include executables from additional packages in the same tool environment. This is useful when you have related tools that work together or when you want to install multiple executables that share dependencies. The `--with-executables-from` option allows you to specify additional packages whose executables should be installed alongside the main tool: ```console $ uv tool install --with-executables-from , ``` For example, to install Ansible along with executables from `ansible-core` and `ansible-lint`: ```console $ uv tool install --with-executables-from ansible-core,ansible-lint ansible ``` This will install all executables from the `ansible`, `ansible-core`, and `ansible-lint` packages into the same tool environment, making them all available on the `PATH`. The `--with-executables-from` option can be combined with other installation options: ```console $ uv tool install --with-executables-from ansible-core --with mkdocs-material ansible ``` Note that `--with-executables-from` differs from `--with` in that: - `--with` includes additional packages as dependencies but does not install their executables - `--with-executables-from` includes both the packages as dependencies and installs their executables ## Python versions Each tool environment is linked to a specific Python version. This uses the same Python version [discovery logic](./python-versions.md#discovery-of-python-versions) as other virtual environments created by uv, but will ignore non-global Python version requests like `.python-version` files and the `requires-python` value from a `pyproject.toml`. The `--python` option can be used to request a specific version. See the [Python version](./python-versions.md) documentation for more details. If the Python version used by a tool is _uninstalled_, the tool environment will be broken and the tool may be unusable. ## Tool executables Tool executables include all console entry points, script entry points, and binary scripts provided by a Python package. Tool executables are symlinked into the [executable directory](../reference/storage.md#tool-executables) on Unix and copied on Windows. !!! note Executables provided by dependencies of tool packages are not installed. The [executable directory](../reference/storage.md#executable-directory) must be in the `PATH` variable for tool executables to be available from the shell. If it is not in the `PATH`, a warning will be displayed. The `uv tool update-shell` command can be used to add the executable directory to the `PATH` in common shell configuration files. ### Overwriting executables Installation of tools will not overwrite executables in the executable directory that were not previously installed by uv. For example, if `pipx` has been used to install a tool, `uv tool install` will fail. The `--force` flag can be used to override this behavior. ## Relationship to `uv run` The invocation `uv tool run ` (or `uvx `) is nearly equivalent to: ```console $ uv run --no-project --with -- ``` However, there are a couple notable differences when using uv's tool interface: - The `--with` option is not needed — the required package is inferred from the command name. - The temporary environment is cached in a dedicated location. - The `--no-project` flag is not needed — tools are always run isolated from the project. - If a tool is already installed, `uv tool run` will use the installed version but `uv run` will not. If the tool should not be isolated from the project, e.g., when running `pytest` or `mypy`, then `uv run` should be used instead of `uv tool run`. astral-sh-uv-9252ba6/docs/getting-started/000077500000000000000000000000001521221614700204325ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/getting-started/features.md000066400000000000000000000104301521221614700225700ustar00rootroot00000000000000# Features uv provides essential features for Python development — from installing Python and hacking on simple scripts to working on large projects that support multiple Python versions and platforms. uv's interface can be broken down into sections, which are usable independently or together. ## Python versions Installing and managing Python itself. - `uv python install`: Install Python versions. - `uv python list`: View available Python versions. - `uv python find`: Find an installed Python version. - `uv python pin`: Pin the current project to use a specific Python version. - `uv python uninstall`: Uninstall a Python version. See the [guide on installing Python](../guides/install-python.md) to get started. ## Scripts Executing standalone Python scripts, e.g., `example.py`. - `uv run`: Run a script. - `uv add --script`: Add a dependency to a script. - `uv remove --script`: Remove a dependency from a script. See the [guide on running scripts](../guides/scripts.md) to get started. ## Projects Creating and working on Python projects, i.e., with a `pyproject.toml`. - `uv init`: Create a new Python project. - `uv add`: Add a dependency to the project. - `uv remove`: Remove a dependency from the project. - `uv sync`: Sync the project's dependencies with the environment. - `uv lock`: Create a lockfile for the project's dependencies. - `uv run`: Run a command in the project environment. - `uv tree`: View the dependency tree for the project. - `uv build`: Build the project into distribution archives. - `uv publish`: Publish the project to a package index. See the [guide on projects](../guides/projects.md) to get started. ## Tools Running and installing tools published to Python package indexes, e.g., `ruff` or `black`. - `uvx` / `uv tool run`: Run a tool in a temporary environment. - `uv tool install`: Install a tool user-wide. - `uv tool uninstall`: Uninstall a tool. - `uv tool list`: List installed tools. - `uv tool update-shell`: Update the shell to include tool executables. See the [guide on tools](../guides/tools.md) to get started. ## The pip interface Manually managing environments and packages — intended to be used in legacy workflows or cases where the high-level commands do not provide enough control. Creating virtual environments (replacing `venv` and `virtualenv`): - `uv venv`: Create a new virtual environment. See the documentation on [using environments](../pip/environments.md) for details. Managing packages in an environment (replacing [`pip`](https://github.com/pypa/pip) and [`pipdeptree`](https://github.com/tox-dev/pipdeptree)): - `uv pip install`: Install packages into the current environment. - `uv pip show`: Show details about an installed package. - `uv pip freeze`: List installed packages and their versions. - `uv pip check`: Check that the current environment has compatible packages. - `uv pip list`: List installed packages. - `uv pip uninstall`: Uninstall packages. - `uv pip tree`: View the dependency tree for the environment. See the documentation on [managing packages](../pip/packages.md) for details. Locking packages in an environment (replacing [`pip-tools`](https://github.com/jazzband/pip-tools)): - `uv pip compile`: Compile requirements into a lockfile. - `uv pip sync`: Sync an environment with a lockfile. See the documentation on [locking environments](../pip/compile.md) for details. !!! important These commands do not exactly implement the interfaces and behavior of the tools they are based on. The further you stray from common workflows, the more likely you are to encounter differences. Consult the [pip-compatibility guide](../pip/compatibility.md) for details. ## Utility Managing and inspecting uv's state, such as the cache, storage directories, or performing a self-update: - `uv cache clean`: Remove cache entries. - `uv cache prune`: Remove outdated cache entries. - `uv cache dir`: Show the uv cache directory path. - `uv tool dir`: Show the uv tool directory path. - `uv python dir`: Show the uv installed Python versions path. - `uv self update`: Update uv to the latest version. ## Next steps Read the [guides](../guides/index.md) for an introduction to each feature, check out the [concept](../concepts/index.md) pages for in-depth details about uv's features, or learn how to [get help](./help.md) if you run into any problems. astral-sh-uv-9252ba6/docs/getting-started/first-steps.md000066400000000000000000000010171521221614700232360ustar00rootroot00000000000000# First steps with uv After [installing uv](./installation.md), you can check that uv is available by running the `uv` command: ```console $ uv An extremely fast Python package manager. Usage: uv [OPTIONS] ... ``` You should see a help menu listing the available commands. ## Next steps Now that you've confirmed uv is installed, check out an [overview of features](./features.md), learn how to [get help](./help.md) if you run into any problems, or jump to the [guides](../guides/index.md) to start using uv. astral-sh-uv-9252ba6/docs/getting-started/help.md000066400000000000000000000040551521221614700217100ustar00rootroot00000000000000# Getting help ## Help menus The `--help` flag can be used to view the help menu for a command, e.g., for `uv`: ```console $ uv --help ``` To view the help menu for a specific command, e.g., for `uv init`: ```console $ uv init --help ``` When using the `--help` flag, uv displays a condensed help menu. To view a longer help menu for a command, use `uv help`: ```console $ uv help ``` To view the long help menu for a specific command, e.g., for `uv init`: ```console $ uv help init ``` When using the long help menu, uv will attempt to use `less` or `more` to "page" the output so it is not all displayed at once. To exit the pager, press `q`. ## Displaying verbose output The `-v` flag can be used to display verbose output for a command, e.g., for `uv sync`: ```console $ uv sync -v ``` The `-v` flag can be repeated to increase verbosity, e.g.: ```console $ uv sync -vv ``` Often, the verbose output will include additional information about why uv is behaving in a certain way. ## Viewing the version When seeking help, it's important to determine the version of uv that you're using — sometimes the problem is already solved in a newer version. To check the installed version: ```console $ uv self version ``` The following are also valid: ```console $ uv --version # Same output as `uv self version` $ uv -V # Will not include the build commit and date ``` !!! note Before uv 0.7.0, `uv version` was used instead of `uv self version`. ## Troubleshooting issues The reference documentation contains a [troubleshooting guide](../reference/troubleshooting/index.md) for common issues. ## Open an issue on GitHub The [issue tracker](https://github.com/astral-sh/uv/issues) on GitHub is a good place to report bugs and request features. Make sure to search for similar issues first, as it is common for someone else to encounter the same problem. ## Chat on Discord Astral has a [Discord server](https://discord.com/invite/astral-sh), which is a great place to ask questions, learn more about uv, and engage with other community members. astral-sh-uv-9252ba6/docs/getting-started/index.md000066400000000000000000000010441521221614700220620ustar00rootroot00000000000000# Getting started To help you get started with uv, we'll cover a few important topics: - [Installing uv](./installation.md) - [First steps after installation](./first-steps.md) - [An overview of uv's features](./features.md) - [How to get help](./help.md) Read on, or jump ahead to another section: - Get going quickly with [guides](../guides/index.md) for common workflows. - Learn more about the core [concepts](../concepts/index.md) in uv. - Use the [reference](../reference/index.md) documentation to find details about something specific. astral-sh-uv-9252ba6/docs/getting-started/installation.md000066400000000000000000000154641521221614700234670ustar00rootroot00000000000000# Installing uv ## Installation methods Install uv with our standalone installers or your package manager of choice. ### Standalone installer uv provides a standalone installer to download and install uv: === "macOS and Linux" Use `curl` to download the script and execute it with `sh`: ```console $ curl -LsSf https://astral.sh/uv/install.sh | sh ``` If your system doesn't have `curl`, you can use `wget`: ```console $ wget -qO- https://astral.sh/uv/install.sh | sh ``` Request a specific version by including it in the URL: ```console $ curl -LsSf https://astral.sh/uv/0.11.20/install.sh | sh ``` === "Windows" Use `irm` to download the script and execute it with `iex`: ```pwsh-session PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` Changing the [execution policy](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4#powershell-execution-policies) allows running a script from the internet. Request a specific version by including it in the URL: ```pwsh-session PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.11.20/install.ps1 | iex" ``` !!! tip The installation script may be inspected before use: === "macOS and Linux" ```console $ curl -LsSf https://astral.sh/uv/install.sh | less ``` === "Windows" ```pwsh-session PS> powershell -c "irm https://astral.sh/uv/install.ps1 | more" ``` Alternatively, the installer or binaries can be downloaded directly from [GitHub](#github-releases). See the reference documentation on the [installer](../reference/installer.md) for details on customizing your uv installation. ### PyPI For convenience, uv is published to [PyPI](https://pypi.org/project/uv/). If installing from PyPI, we recommend installing uv into an isolated environment, e.g., with `pipx`: ```console $ pipx install uv ``` However, `pip` can also be used: ```console $ pip install uv ``` !!! note uv ships with prebuilt distributions (wheels) for many platforms; if a wheel is not available for a given platform, uv will be built from source, which requires a Rust toolchain. See the [contributing setup guide](https://github.com/astral-sh/uv/blob/main/CONTRIBUTING.md#setup) for details on building uv from source. ### Homebrew uv is available in the core Homebrew packages. ```console $ brew install uv ``` ### MacPorts uv is available via [MacPorts](https://ports.macports.org/port/uv/). ```console $ sudo port install uv ``` ### WinGet uv is available via [WinGet](https://winstall.app/apps/astral-sh.uv). ```console $ winget install --id=astral-sh.uv -e ``` ### Scoop uv is available via [Scoop](https://scoop.sh/#/apps?q=uv). ```console $ scoop install main/uv ``` ### Docker uv provides a Docker image at [`ghcr.io/astral-sh/uv`](https://github.com/astral-sh/uv/pkgs/container/uv). See our guide on [using uv in Docker](../guides/integration/docker.md) for more details. ### GitHub Releases uv release artifacts can be downloaded directly from [GitHub Releases](https://github.com/astral-sh/uv/releases). Each release page includes binaries for all supported platforms as well as instructions for using the standalone installer via `github.com` instead of `astral.sh`. ### Cargo uv is available via [crates.io](https://crates.io). ```console $ cargo install --locked uv ``` !!! note This method builds uv from source, which requires a compatible Rust toolchain. ## Upgrading uv When uv is installed via the standalone installer, it can update itself on-demand: ```console $ uv self update ``` !!! tip Updating uv will re-run the installer and can modify your shell profiles. To disable this behavior, set `UV_NO_MODIFY_PATH=1`. When another installation method is used, self-updates are disabled. Use the package manager's upgrade method instead. For example, with `pip`: ```console $ pip install --upgrade uv ``` ## Shell autocompletion !!! tip You can run `echo $SHELL` to help you determine your shell. To enable shell autocompletion for uv commands, run one of the following: === "Bash" ```bash echo 'eval "$(uv generate-shell-completion bash)"' >> ~/.bashrc ``` === "Zsh" ```bash echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc ``` === "fish" ```bash echo 'uv generate-shell-completion fish | source' > ~/.config/fish/completions/uv.fish ``` === "Elvish" ```bash echo 'eval (uv generate-shell-completion elvish | slurp)' >> ~/.elvish/rc.elv ``` === "PowerShell / pwsh" ```powershell if (!(Test-Path -Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force } Add-Content -Path $PROFILE -Value '(& uv generate-shell-completion powershell) | Out-String | Invoke-Expression' ``` To enable shell autocompletion for uvx, run one of the following: === "Bash" ```bash echo 'eval "$(uvx --generate-shell-completion bash)"' >> ~/.bashrc ``` === "Zsh" ```bash echo 'eval "$(uvx --generate-shell-completion zsh)"' >> ~/.zshrc ``` === "fish" ```bash echo 'uvx --generate-shell-completion fish | source' > ~/.config/fish/completions/uvx.fish ``` === "Elvish" ```bash echo 'eval (uvx --generate-shell-completion elvish | slurp)' >> ~/.elvish/rc.elv ``` === "PowerShell / pwsh" ```powershell if (!(Test-Path -Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force } Add-Content -Path $PROFILE -Value '(& uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression' ``` Then restart the shell or source the shell config file. ## Uninstallation If you need to remove uv from your system, follow these steps: 1. Clean up stored data (optional): ```console $ uv cache clean $ rm -r "$(uv python dir)" $ rm -r "$(uv tool dir)" ``` !!! tip Before removing the binaries, you may want to remove any data that uv has stored. See the [storage reference](../reference/storage.md) for details on where uv stores data. 2. Remove the uv, uvx, and uvw binaries: === "macOS and Linux" ```console $ rm ~/.local/bin/uv ~/.local/bin/uvx ``` === "Windows" ```pwsh-session PS> rm $HOME\.local\bin\uv.exe PS> rm $HOME\.local\bin\uvx.exe PS> rm $HOME\.local\bin\uvw.exe ``` !!! note Prior to 0.5.0, uv was installed into `~/.cargo/bin`. The binaries can be removed from there to uninstall. Upgrading from an older version will not automatically remove the binaries from `~/.cargo/bin`. ## Next steps See the [first steps](./first-steps.md) or jump straight to the [guides](../guides/index.md) to start using uv. astral-sh-uv-9252ba6/docs/guides/000077500000000000000000000000001521221614700166055ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/guides/index.md000066400000000000000000000010541521221614700202360ustar00rootroot00000000000000# Guides overview Check out one of the core guides to get started: - [Installing Python versions](./install-python.md) - [Running scripts and declaring dependencies](./scripts.md) - [Running and installing applications as tools](./tools.md) - [Creating and working on projects](./projects.md) - [Building and publishing packages](./package.md) - [Integrate uv with other software, e.g., Docker, GitHub, PyTorch, and more](./integration/index.md) Or, explore the [concept documentation](../concepts/index.md) for comprehensive breakdown of each feature. astral-sh-uv-9252ba6/docs/guides/install-python.md000066400000000000000000000113741521221614700221220ustar00rootroot00000000000000--- title: Installing and managing Python description: A guide to using uv to install Python, including requesting specific versions, automatic installation, viewing installed versions, and more. --- # Installing Python If Python is already installed on your system, uv will [detect and use](#using-existing-python-versions) it without configuration. However, uv can also install and manage Python versions. uv [automatically installs](#automatic-python-downloads) missing Python versions as needed — you don't need to install Python to get started. ## Getting started To install the latest Python version: ```console $ uv python install ``` !!! note Python does not publish official distributable binaries. As such, uv uses distributions from the Astral [`python-build-standalone`](https://github.com/astral-sh/python-build-standalone) project. See the [Python distributions](../concepts/python-versions.md#managed-python-distributions) documentation for more details. Once Python is installed, it will be used by `uv` commands automatically. uv also adds the installed version to your `PATH`: ```console $ python3.13 ``` uv only installs a _versioned_ executable by default. To install `python` and `python3` executables, include the experimental `--default` option: ```console $ uv python install --default ``` !!! tip See the documentation on [installing Python executables](../concepts/python-versions.md#installing-python-executables) for more details. ## Installing a specific version To install a specific Python version: ```console $ uv python install 3.12 ``` To install multiple Python versions: ```console $ uv python install 3.11 3.12 ``` To install an alternative Python implementation, e.g., PyPy: ```console $ uv python install pypy@3.10 ``` See the [`python install`](../concepts/python-versions.md#installing-a-python-version) documentation for more details. ## Reinstalling Python To reinstall uv-managed Python versions, use `--reinstall`, e.g.: ```console $ uv python install --reinstall ``` This will reinstall all previously installed Python versions. Improvements are constantly being added to the Python distributions, so reinstalling may resolve bugs even if the Python version does not change. ## Viewing Python installations To view available and installed Python versions: ```console $ uv python list ``` See the [`python list`](../concepts/python-versions.md#viewing-available-python-versions) documentation for more details. ## Automatic Python downloads Python does not need to be explicitly installed to use uv. By default, uv will automatically download Python versions when they are required. For example, the following would download Python 3.12 if it was not installed: ```console $ uvx python@3.12 -c "print('hello world')" ``` Even if a specific Python version is not requested, uv will download the latest version on demand. For example, if there are no Python versions on your system, the following will install Python before creating a new virtual environment: ```console $ uv venv ``` !!! tip Automatic Python downloads can be [easily disabled](../concepts/python-versions.md#disabling-automatic-python-downloads) if you want more control over when Python is downloaded. ## Using existing Python versions uv will use existing Python installations if present on your system. There is no configuration necessary for this behavior: uv will use the system Python if it satisfies the requirements of the command invocation. See the [Python discovery](../concepts/python-versions.md#discovery-of-python-versions) documentation for details. To force uv to use the system Python, provide the `--no-managed-python` flag. See the [Python version preference](../concepts/python-versions.md#requiring-or-disabling-managed-python-versions) documentation for more details. ## Upgrading Python versions !!! important Support for upgrading Python patch versions is in _preview_. This means the behavior is experimental and subject to change. To upgrade a Python version to the latest supported patch release: ```console $ uv python upgrade 3.12 ``` To upgrade all uv-managed Python versions: ```console $ uv python upgrade ``` See the [`python upgrade`](../concepts/python-versions.md#upgrading-python-versions) documentation for more details. ## Next steps To learn more about `uv python`, see the [Python version concept](../concepts/python-versions.md) page and the [command reference](../reference/cli.md#uv-python). Or, read on to learn how to [run scripts](./scripts.md) and invoke Python with uv. astral-sh-uv-9252ba6/docs/guides/integration/000077500000000000000000000000001521221614700211305ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/guides/integration/aws-lambda.md000066400000000000000000000464401521221614700234720ustar00rootroot00000000000000--- title: Using uv with AWS Lambda description: A complete guide to using uv with AWS Lambda to manage Python dependencies and deploy serverless functions via Docker containers or zip archives. --- # Using uv with AWS Lambda [AWS Lambda](https://aws.amazon.com/lambda/) is a serverless computing service that lets you run code without provisioning or managing servers. You can use uv with AWS Lambda to manage your Python dependencies, build your deployment package, and deploy your Lambda functions. !!! tip Check out the [`uv-aws-lambda-example`](https://github.com/astral-sh/uv-aws-lambda-example) project for an example of best practices when using uv to deploy an application to AWS Lambda. ## Getting started To start, assume we have a minimal FastAPI application with the following structure: ```plaintext project ├── pyproject.toml └── app ├── __init__.py └── main.py ``` Where the `pyproject.toml` contains: ```toml title="pyproject.toml" [project] name = "uv-aws-lambda-example" version = "0.1.0" requires-python = ">=3.13" dependencies = [ # FastAPI is a modern web framework for building APIs with Python. "fastapi", # Mangum is a library that adapts ASGI applications to AWS Lambda and API Gateway. "mangum", ] [dependency-groups] dev = [ # In development mode, include the FastAPI development server. "fastapi[standard]>=0.115", ] ``` And the `main.py` file contains: ```python title="app/main.py" import logging from fastapi import FastAPI from mangum import Mangum logger = logging.getLogger() logger.setLevel(logging.INFO) app = FastAPI() handler = Mangum(app) @app.get("/") async def root() -> str: return "Hello, world!" ``` We can run this application locally with: ```console $ uv run fastapi dev ``` From there, opening http://127.0.0.1:8000/ in a web browser will display "Hello, world!" ## Deploying a Docker image To deploy to AWS Lambda, we need to build a container image that includes the application code and dependencies in a single output directory. We'll follow the principles outlined in the [Docker guide](./docker.md) (in particular, a multi-stage build) to ensure that the final image is as small and cache-friendly as possible. In the first stage, we'll populate a single directory with all application code and dependencies. In the second stage, we'll copy this directory over to the final image, omitting the build tools and other unnecessary files. ```dockerfile title="Dockerfile" FROM ghcr.io/astral-sh/uv:0.11.20 AS uv # First, bundle the dependencies into the task root. FROM public.ecr.aws/lambda/python:3.13 AS builder # Enable bytecode compilation, to improve cold-start performance. ENV UV_COMPILE_BYTECODE=1 # Disable installer metadata, to create a deterministic layer. ENV UV_NO_INSTALLER_METADATA=1 # Enable copy mode to support bind mount caching. ENV UV_LINK_MODE=copy # Bundle the dependencies into the Lambda task root via `uv pip install --target`. # # Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`). # This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock` # files change, but remains robust to changes in the application code. RUN --mount=from=uv,source=/uv,target=/bin/uv \ --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt && \ uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" FROM public.ecr.aws/lambda/python:3.13 # Copy the runtime dependencies from the builder stage. COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT} # Copy the application code. COPY ./app ${LAMBDA_TASK_ROOT}/app # Set the AWS Lambda handler. CMD ["app.main.handler"] ``` !!! tip To deploy to ARM-based AWS Lambda runtimes, replace `public.ecr.aws/lambda/python:3.13` with `public.ecr.aws/lambda/python:3.13-arm64`. We can build the image with, e.g.: ```console $ uv lock $ docker build -t fastapi-app . ``` The core benefits of this Dockerfile structure are as follows: 1. **Minimal image size.** By using a multi-stage build, we can ensure that the final image only includes the application code and dependencies. For example, the uv binary itself is not included in the final image. 2. **Maximal cache reuse.** By installing application dependencies separately from the application code, we can ensure that the Docker layer cache is only invalidated when the dependencies change. Concretely, rebuilding the image after modifying the application source code can reuse the cached layers, resulting in millisecond builds: ```console => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 1.31kB 0.0s => [internal] load metadata for public.ecr.aws/lambda/python:3.13 0.3s => [internal] load metadata for ghcr.io/astral-sh/uv:latest 0.3s => [internal] load .dockerignore 0.0s => => transferring context: 106B 0.0s => [uv 1/1] FROM ghcr.io/astral-sh/uv:latest@sha256:ea61e006cfec0e8d81fae901ad703e09d2c6cf1aa58abcb6507d124b50286f 0.0s => [builder 1/2] FROM public.ecr.aws/lambda/python:3.13@sha256:f5b51b377b80bd303fe8055084e2763336ea8920d12955b23ef 0.0s => [internal] load build context 0.0s => => transferring context: 185B 0.0s => CACHED [builder 2/2] RUN --mount=from=uv,source=/uv,target=/bin/uv --mount=type=cache,target=/root/.cache/u 0.0s => CACHED [stage-2 2/3] COPY --from=builder /var/task /var/task 0.0s => CACHED [stage-2 3/3] COPY ./app /var/task 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:6f8f9ef715a7cda466b677a9df4046ebbb90c8e88595242ade3b4771f547652d 0.0 ``` After building, we can push the image to [Elastic Container Registry (ECR)](https://aws.amazon.com/ecr/) with, e.g.: ```console $ aws ecr get-login-password --region region | docker login --username AWS --password-stdin aws_account_id.dkr.ecr.region.amazonaws.com $ docker tag fastapi-app:latest aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest $ docker push aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest ``` Finally, we can deploy the image to AWS Lambda using the AWS Management Console or the AWS CLI, e.g.: ```console $ aws lambda create-function \ --function-name myFunction \ --package-type Image \ --code ImageUri=aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest \ --role arn:aws:iam::111122223333:role/my-lambda-role ``` Where the [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html#permissions-executionrole-api) is created via: ```console $ aws iam create-role \ --role-name my-lambda-role \ --assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}' ``` Or, update an existing function with: ```console $ aws lambda update-function-code \ --function-name myFunction \ --image-uri aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest \ --publish ``` To test the Lambda, we can invoke it via the AWS Management Console or the AWS CLI, e.g.: ```console $ aws lambda invoke \ --function-name myFunction \ --payload file://event.json \ --cli-binary-format raw-in-base64-out \ response.json { "StatusCode": 200, "ExecutedVersion": "$LATEST" } ``` Where `event.json` contains the event payload to pass to the Lambda function: ```json title="event.json" { "httpMethod": "GET", "path": "/", "requestContext": {}, "version": "1.0" } ``` And `response.json` contains the response from the Lambda function: ```json title="response.json" { "statusCode": 200, "headers": { "content-length": "14", "content-type": "application/json" }, "multiValueHeaders": {}, "body": "\"Hello, world!\"", "isBase64Encoded": false } ``` For details, see the [AWS Lambda documentation](https://docs.aws.amazon.com/lambda/latest/dg/python-image.html). ### Workspace support If a project includes local dependencies (e.g., via [Workspaces](../../concepts/projects/workspaces.md)), those too must be included in the deployment package. We'll start by extending the above example to include a dependency on a locally-developed library named `library`. First, we'll create the library itself: ```console $ uv init --lib library $ uv add ./library ``` Running `uv init` within the `project` directory will automatically convert `project` to a workspace and add `library` as a workspace member: ```toml title="pyproject.toml" [project] name = "uv-aws-lambda-example" version = "0.1.0" requires-python = ">=3.13" dependencies = [ # FastAPI is a modern web framework for building APIs with Python. "fastapi", # A local library. "library", # Mangum is a library that adapts ASGI applications to AWS Lambda and API Gateway. "mangum", ] [dependency-groups] dev = [ # In development mode, include the FastAPI development server. "fastapi[standard]", ] [tool.uv.workspace] members = ["library"] [tool.uv.sources] lib = { workspace = true } ``` By default, `uv init --lib` will create a package that exports a `hello` function. We'll modify the application source code to call that function: ```python title="app/main.py" import logging from fastapi import FastAPI from mangum import Mangum from library import hello logger = logging.getLogger() logger.setLevel(logging.INFO) app = FastAPI() handler = Mangum(app) @app.get("/") async def root() -> str: return hello() ``` We can run the modified application locally with: ```console $ uv run fastapi dev ``` And confirm that opening http://127.0.0.1:8000/ in a web browser displays, "Hello from library!" (instead of "Hello, World!") Finally, we'll update the Dockerfile to include the local library in the deployment package: ```dockerfile title="Dockerfile" FROM ghcr.io/astral-sh/uv:0.11.20 AS uv # First, bundle the dependencies into the task root. FROM public.ecr.aws/lambda/python:3.13 AS builder # Enable bytecode compilation, to improve cold-start performance. ENV UV_COMPILE_BYTECODE=1 # Disable installer metadata, to create a deterministic layer. ENV UV_NO_INSTALLER_METADATA=1 # Enable copy mode to support bind mount caching. ENV UV_LINK_MODE=copy # Bundle the dependencies into the Lambda task root via `uv pip install --target`. # # Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`). # This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock` # files change, but remains robust to changes in the application code. RUN --mount=from=uv,source=/uv,target=/bin/uv \ --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt && \ uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" # If you have a workspace, copy it over and install it too. # # By omitting `--no-emit-workspace`, `library` will be copied into the task root. Using a separate # `RUN` command ensures that all third-party dependencies are cached separately and remain # robust to changes in the workspace. RUN --mount=from=uv,source=/uv,target=/bin/uv \ --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ --mount=type=bind,source=library,target=library \ uv export --frozen --no-dev --no-editable -o requirements.txt && \ uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" FROM public.ecr.aws/lambda/python:3.13 # Copy the runtime dependencies from the builder stage. COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT} # Copy the application code. COPY ./app ${LAMBDA_TASK_ROOT}/app # Set the AWS Lambda handler. CMD ["app.main.handler"] ``` !!! tip To deploy to ARM-based AWS Lambda runtimes, replace `public.ecr.aws/lambda/python:3.13` with `public.ecr.aws/lambda/python:3.13-arm64`. From there, we can build and deploy the updated image as before. ## Deploying a zip archive AWS Lambda also supports deployment via zip archives. For simple applications, zip archives can be a more straightforward and efficient deployment method than Docker images; however, zip archives are limited to [250 MB](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-update) in size. Returning to the FastAPI example, we can bundle the application dependencies into a local directory for AWS Lambda via: ```console $ uv export --frozen --no-dev --no-editable -o requirements.txt $ uv pip install \ --no-installer-metadata \ --no-compile-bytecode \ --python-platform x86_64-manylinux2014 \ --python 3.13 \ --target packages \ -r requirements.txt ``` !!! tip To deploy to ARM-based AWS Lambda runtimes, replace `x86_64-manylinux2014` with `aarch64-manylinux2014`. Following the [AWS Lambda documentation](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html), we can then bundle these dependencies into a zip as follows: ```console $ cd packages $ zip -r ../package.zip . $ cd .. ``` Finally, we can add the application code to the zip archive: ```console $ zip -r package.zip app ``` We can then deploy the zip archive to AWS Lambda via the AWS Management Console or the AWS CLI, e.g.: ```console $ aws lambda create-function \ --function-name myFunction \ --runtime python3.13 \ --zip-file fileb://package.zip \ --handler app.main.handler \ --role arn:aws:iam::111122223333:role/service-role/my-lambda-role ``` Where the [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html#permissions-executionrole-api) is created via: ```console $ aws iam create-role \ --role-name my-lambda-role \ --assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}' ``` Or, update an existing function with: ```console $ aws lambda update-function-code \ --function-name myFunction \ --zip-file fileb://package.zip ``` !!! note By default, the AWS Management Console assumes a Lambda entrypoint of `lambda_function.lambda_handler`. If your application uses a different entrypoint, you'll need to modify it in the AWS Management Console. For example, the above FastAPI application uses `app.main.handler`. To test the Lambda, we can invoke it via the AWS Management Console or the AWS CLI, e.g.: ```console $ aws lambda invoke \ --function-name myFunction \ --payload file://event.json \ --cli-binary-format raw-in-base64-out \ response.json { "StatusCode": 200, "ExecutedVersion": "$LATEST" } ``` Where `event.json` contains the event payload to pass to the Lambda function: ```json title="event.json" { "httpMethod": "GET", "path": "/", "requestContext": {}, "version": "1.0" } ``` And `response.json` contains the response from the Lambda function: ```json title="response.json" { "statusCode": 200, "headers": { "content-length": "14", "content-type": "application/json" }, "multiValueHeaders": {}, "body": "\"Hello, world!\"", "isBase64Encoded": false } ``` ### Using a Lambda layer AWS Lambda also supports the deployment of multiple composed [Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/python-layers.html) when working with zip archives. These layers are conceptually similar to layers in a Docker image, allowing you to separate application code from dependencies. In particular, we can create a lambda layer for application dependencies and attach it to the Lambda function, separate from the application code itself. This setup can improve cold-start performance for application updates, as the dependencies layer can be reused across deployments. To create a Lambda layer, we'll follow similar steps, but create two separate zip archives: one for the application code and one for the application dependencies. First, we'll create the dependency layer. Lambda layers are expected to follow a slightly different structure, so we'll use `--prefix` rather than `--target`: ```console $ uv export --frozen --no-dev --no-editable -o requirements.txt $ uv pip install \ --no-installer-metadata \ --no-compile-bytecode \ --python-platform x86_64-manylinux2014 \ --python 3.13 \ --prefix packages \ -r requirements.txt ``` We'll then zip the dependencies in adherence with the expected layout for Lambda layers: ```console $ mkdir python $ cp -r packages/lib python/ $ zip -r layer_content.zip python ``` !!! tip To generate deterministic zip archives, consider passing the `-X` flag to `zip` to exclude extended attributes and file system metadata. And publish the Lambda layer: ```console $ aws lambda publish-layer-version --layer-name dependencies-layer \ --zip-file fileb://layer_content.zip \ --compatible-runtimes python3.13 \ --compatible-architectures "x86_64" ``` We can then create the Lambda function as in the previous example, omitting the dependencies: ```console $ # Zip the application code. $ zip -r app.zip app $ # Create the Lambda function. $ aws lambda create-function \ --function-name myFunction \ --runtime python3.13 \ --zip-file fileb://app.zip \ --handler app.main.handler \ --role arn:aws:iam::111122223333:role/service-role/my-lambda-role ``` Finally, we can attach the dependencies layer to the Lambda function, using the ARN returned by the `publish-layer-version` step: ```console $ aws lambda update-function-configuration --function-name myFunction \ --cli-binary-format raw-in-base64-out \ --layers "arn:aws:lambda:region:111122223333:layer:dependencies-layer:1" ``` When the application dependencies change, the layer can be updated independently of the application by republishing the layer and updating the Lambda function configuration: ```console $ # Update the dependencies in the layer. $ aws lambda publish-layer-version --layer-name dependencies-layer \ --zip-file fileb://layer_content.zip \ --compatible-runtimes python3.13 \ --compatible-architectures "x86_64" $ # Update the Lambda function configuration. $ aws lambda update-function-configuration --function-name myFunction \ --cli-binary-format raw-in-base64-out \ --layers "arn:aws:lambda:region:111122223333:layer:dependencies-layer:2" ``` astral-sh-uv-9252ba6/docs/guides/integration/aws.md000066400000000000000000000101261521221614700222440ustar00rootroot00000000000000--- title: AWS CodeArtifact description: Using uv with AWS CodeArtifact for installing and publishing Python packages. --- # AWS CodeArtifact uv can install packages from [AWS CodeArtifact](https://docs.aws.amazon.com/codeartifact/latest/ug/using-python.html), either by using an access token, or using the [`keyring`](https://github.com/jaraco/keyring) package. !!! note This guide assumes that [`awscli`](https://aws.amazon.com/cli/) is installed and authenticated. The index can be declared like so: ```toml title="pyproject.toml" [[tool.uv.index]] name = "private-registry" url = "https://-.d.codeartifact..amazonaws.com/pypi//simple/" ``` ## Authenticate with an AWS access token Credentials can be provided via "Basic" HTTP authentication scheme. Include access token in the password field of the URL. Username must be `aws`, otherwise authentication will fail. Generate a token with `awscli`: ```bash export AWS_CODEARTIFACT_TOKEN="$( aws codeartifact get-authorization-token \ --domain \ --domain-owner \ --query authorizationToken \ --output text )" ``` !!! note You might need to pass extra parameters to properly generate the token (like `--region`), this is a basic example. Then set credentials for the index with: ```bash export UV_INDEX_PRIVATE_REGISTRY_USERNAME=aws export UV_INDEX_PRIVATE_REGISTRY_PASSWORD="$AWS_CODEARTIFACT_TOKEN" ``` !!! note `PRIVATE_REGISTRY` should match the name of the index defined in your `pyproject.toml`. ## Authenticate with `keyring` and `keyrings.codeartifact` You can also authenticate to Artifact Registry using [`keyring`](https://github.com/jaraco/keyring) package with the [`keyrings.codeartifact` plugin](https://github.com/jmkeyes/keyrings.codeartifact). Because these two packages are required to authenticate to Artifact Registry, they must be pre-installed from a source other than Artifact Registry. The `keyrings.codeartifact` plugin wraps [boto3](https://pypi.org/project/boto3/) to generate short-lived access tokens, securely store them in system keyring, and refresh them when they are expired. uv only supports using the `keyring` package in [subprocess mode](../../reference/settings.md#keyring-provider). The `keyring` executable must be in the `PATH`, i.e., installed globally or in the active environment. The `keyring` CLI requires a username in the URL and it must be `aws`. ```bash # Pre-install keyring and AWS CodeArtifact plugin from the public PyPI uv tool install keyring --with keyrings.codeartifact # Enable keyring authentication export UV_KEYRING_PROVIDER=subprocess # Set the username for the index export UV_INDEX_PRIVATE_REGISTRY_USERNAME=aws ``` !!! note The [`tool.uv.keyring-provider`](../../reference/settings.md#keyring-provider) setting can be used to enable keyring in your `uv.toml` or `pyproject.toml`. Similarly, the username for the index can be added directly to the index URL. ## Publishing packages If you also want to publish your own packages to AWS CodeArtifact, you can use `uv publish` as described in the [Building and publishing guide](../package.md). First, add a `publish-url` to the index you want to publish packages to. For example: ```toml title="pyproject.toml" hl_lines="4" [[tool.uv.index]] name = "private-registry" url = "https://-.d.codeartifact..amazonaws.com/pypi//simple/" publish-url = "https://-.d.codeartifact..amazonaws.com/pypi//" ``` Then, configure credentials (if not using keyring): ```console $ export UV_PUBLISH_USERNAME=aws $ export UV_PUBLISH_PASSWORD="$AWS_CODEARTIFACT_TOKEN" ``` And publish the package: ```console $ uv publish --index private-registry ``` To use `uv publish` without adding the `publish-url` to the project, you can set `UV_PUBLISH_URL`: ```console $ export UV_PUBLISH_URL=https://-.d.codeartifact..amazonaws.com/pypi// $ uv publish ``` Note this method is not preferable because uv cannot check if the package is already published before uploading artifacts. astral-sh-uv-9252ba6/docs/guides/integration/azure.md000066400000000000000000000104121521221614700225760ustar00rootroot00000000000000--- title: Azure Artifacts description: Using uv with Azure Artifacts for installing and publishing Python packages. --- # Azure Artifacts uv can install packages from [Azure Artifacts](https://learn.microsoft.com/en-us/azure/devops/artifacts/start-using-azure-artifacts?view=azure-devops&tabs=nuget%2Cnugetserver), either by using a [Personal Access Token](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows) (PAT), or using the [`keyring`](https://github.com/jaraco/keyring) package. To use Azure Artifacts, add the index to your project: ```toml title="pyproject.toml" [[tool.uv.index]] name = "private-registry" url = "https://pkgs.dev.azure.com///_packaging//pypi/simple/" ``` ## Authenticate with an Azure access token If there is a personal access token (PAT) available (e.g., [`$(System.AccessToken)` in an Azure pipeline](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#systemaccesstoken)), credentials can be provided via "Basic" HTTP authentication scheme. Include the PAT in the password field of the URL. A username must be included as well, but can be any string. For example, with the token stored in the `$AZURE_ARTIFACTS_TOKEN` environment variable, set credentials for the index with: ```bash export UV_INDEX_PRIVATE_REGISTRY_USERNAME=dummy export UV_INDEX_PRIVATE_REGISTRY_PASSWORD="$AZURE_ARTIFACTS_TOKEN" ``` !!! note `PRIVATE_REGISTRY` should match the name of the index defined in your `pyproject.toml`. ## Authenticate with `keyring` and `artifacts-keyring` You can also authenticate to Artifacts using [`keyring`](https://github.com/jaraco/keyring) package with the [`artifacts-keyring` plugin](https://github.com/Microsoft/artifacts-keyring). Because these two packages are required to authenticate to Azure Artifacts, they must be pre-installed from a source other than Artifacts. The `artifacts-keyring` plugin wraps the [Azure Artifacts Credential Provider tool](https://github.com/microsoft/artifacts-credprovider). The credential provider supports a few different authentication modes including interactive login — see the [tool's documentation](https://github.com/microsoft/artifacts-credprovider) for information on configuration. uv only supports using the `keyring` package in [subprocess mode](../../reference/settings.md#keyring-provider). The `keyring` executable must be in the `PATH`, i.e., installed globally or in the active environment. The `keyring` CLI requires a username in the URL, and it must be `VssSessionToken`. ```bash # Pre-install keyring and the Artifacts plugin from the public PyPI uv tool install keyring --with artifacts-keyring # Enable keyring authentication export UV_KEYRING_PROVIDER=subprocess # Set the username for the index export UV_INDEX_PRIVATE_REGISTRY_USERNAME=VssSessionToken ``` !!! note The [`tool.uv.keyring-provider`](../../reference/settings.md#keyring-provider) setting can be used to enable keyring in your `uv.toml` or `pyproject.toml`. Similarly, the username for the index can be added directly to the index URL. ## Publishing packages If you also want to publish your own packages to Azure Artifacts, you can use `uv publish` as described in the [Building and publishing guide](../package.md). First, add a `publish-url` to the index you want to publish packages to. For example: ```toml title="pyproject.toml" hl_lines="4" [[tool.uv.index]] name = "private-registry" url = "https://pkgs.dev.azure.com///_packaging//pypi/simple/" publish-url = "https://pkgs.dev.azure.com///_packaging//pypi/upload/" ``` Then, configure credentials (if not using keyring): ```console $ export UV_PUBLISH_USERNAME=dummy $ export UV_PUBLISH_PASSWORD="$AZURE_ARTIFACTS_TOKEN" ``` And publish the package: ```console $ uv publish --index private-registry ``` To use `uv publish` without adding the `publish-url` to the project, you can set `UV_PUBLISH_URL`: ```console $ export UV_PUBLISH_URL=https://pkgs.dev.azure.com///_packaging//pypi/upload/ $ uv publish ``` Note this method is not preferable because uv cannot check if the package is already published before uploading artifacts. astral-sh-uv-9252ba6/docs/guides/integration/bazel.md000066400000000000000000000025461521221614700225560ustar00rootroot00000000000000--- title: Using uv with Bazel description: Using uv to power package resolution with Bazel --- # Using uv with Bazel For broader Bazel workflows with uv, see the [`rules_py` uv guide](https://github.com/aspect-build/rules_py#dependency-resolution-with-uv) or the [`rules_python` uv guide](https://rules-python.readthedocs.io/en/latest/pypi/lock.html#uv-pip-compile-bzlmod-only). ## Authentication Bazel 7 and newer supports credential helpers via the `--credential_helper` option. To use credentials stored by uv for Bazel fetches, first authenticate uv with the service that hosts the files Bazel needs to fetch: ```console $ uv auth login https://packages.example.com ``` Then, configure Bazel to invoke [`uv auth helper`](../../concepts/authentication/cli.md#using-credentials-with-external-tools) for matching hosts: ```text title=".bazelrc" common --credential_helper=packages.example.com=%workspace%/bazel/uv-auth-helper common --credential_helper=files.example.com=%workspace%/bazel/uv-auth-helper ``` Replace the host patterns with the hosts that serve the index and files Bazel will fetch. Finally, add the wrapper script referenced by `.bazelrc`: ```bash title="bazel/uv-auth-helper" #!/usr/bin/env bash exec uv --preview-features auth-helper auth helper --protocol=bazel "$@" ``` The script must be executable: ```console $ chmod +x bazel/uv-auth-helper ``` astral-sh-uv-9252ba6/docs/guides/integration/coiled.md000066400000000000000000000110211521221614700227040ustar00rootroot00000000000000--- title: Using uv with Coiled description: A complete guide to using uv with Coiled to manage Python dependencies and deploy serverless scripts. --- # Using uv with Coiled [Coiled](https://coiled.io?utm_source=uv-docs) is a serverless, UX-focused cloud computing platform that makes it easy to run code on cloud hardware (AWS, GCP, and Azure). This guide shows how to run Python scripts on the cloud using uv for dependency management and Coiled for cloud deployment. ## Managing script dependencies with uv !!! note We'll use this concrete example throughout this guide, but any Python script can be used with uv and Coiled. We'll use the following script as an example: ```python title="process.py" hl_lines="1-8" # /// script # requires-python = ">=3.12" # dependencies = [ # "pandas", # "pyarrow", # "s3fs", # ] # /// import pandas as pd df = pd.read_parquet( "s3://coiled-data/uber/part.0.parquet", storage_options={"anon": True}, ) print(df.head()) ``` The script uses [`pandas`](https://pandas.pydata.org/docs/) to load a Parquet file hosted in a public bucket on S3, then prints the first few rows. It uses [inline script metadata](https://peps.python.org/pep-0723/) to enumerate its dependencies. When running this script locally, e.g., with: ```bash $ uv run process.py ``` uv will automatically create a virtual environment and installs its dependencies. To learn more about using inline script metadata with uv, see the [script guide](../scripts.md#declaring-script-dependencies). ## Running scripts on the cloud with Coiled Using inline script metadata makes the script fully self-contained: it includes the information that is needed to run it. This makes it easier to run on other machines, like a machine in the cloud. There are many use cases where resources beyond what's available on a local workstation are needed, e.g.: - Processing large amounts of cloud-hosted data - Needing accelerated hardware like GPUs or a big machine with more memory - Running the same script with hundreds or thousands of different inputs, in parallel Coiled makes it simple to run code on cloud hardware. First, authenticate with Coiled using [`coiled login`](https://docs.coiled.io/user_guide/api.html?utm_source=uv-docs#coiled-login) : ```bash $ uvx coiled login ``` You'll be prompted to create a Coiled account if you don't already have one — it's free to start using Coiled. To instruct Coiled to run the script on a virtual machine on AWS, add two comments to the top: ```python title="process.py" hl_lines="1-2" # COILED container ghcr.io/astral-sh/uv:debian-slim # COILED region us-east-2 # /// script # requires-python = ">=3.12" # dependencies = [ # "pandas", # "pyarrow", # "s3fs", # ] # /// import pandas as pd df = pd.read_parquet( "s3://coiled-data/uber/part.0.parquet", storage_options={"anon": True}, ) print(df.head()) ``` !!! tip While Coiled supports AWS, GCP, and Azure, this example assumes AWS is being used (see the `region` option above). If you're new to Coiled, you'll automatically have access to a free account running on AWS. If you're not running on AWS, you can either use a valid `region` for your cloud provider or remove the `region` line above. The comments tell Coiled to use the official [uv Docker image](../integration/docker.md) when running the script (ensuring uv is available) and to run in the `us-east-2` region on AWS (where this example data file happens to live) to avoid any data egress. To submit a batch job for Coiled to run, use [`coiled batch run`](https://docs.coiled.io/user_guide/api.html?utm_source=uv-docs#coiled-batch-run) to execute the `uv run` command in the cloud: ```bash hl_lines="1" $ uvx coiled batch run \ uv run process.py ``` The same process that previously ran locally is now running on a remote cloud VM on AWS. You can monitor the progress of the batch job in the UI at [cloud.coiled.io](https://cloud.coiled.io) or from the terminal using the `coiled batch status`, `coiled batch wait`, and `coiled batch logs` commands. ![Coiled UI](https://docs.coiled.io/_images/uv-coiled.png) Note there's additional configuration we could have specified, e.g., the instance type (the default is a 4-core virtual machine with 16 GiB of memory), disk size, whether to use spot instance, and more. See the [Coiled Batch documentation](https://docs.coiled.io/user_guide/batch.html?utm_source=uv-docs) for more details. For more details on Coiled, and how it can help with other use cases, see the [Coiled documentation](https://docs.coiled.io?utm_source=uv-docs). astral-sh-uv-9252ba6/docs/guides/integration/dependabot.md000066400000000000000000000026451521221614700235660ustar00rootroot00000000000000--- title: Using uv with Dependabot description: A guide to using uv with the Dependabot dependency bot. --- # Dependabot It is considered best practice to regularly update dependencies, to avoid being exposed to vulnerabilities, limit incompatibilities between dependencies, and avoid complex upgrades when upgrading from a too old version. Dependabot has announced support for uv, but there are some use cases that are not yet working. See [astral-sh/uv#2512](https://github.com/astral-sh/uv/issues/2512) for updates. Dependabot supports updating `uv.lock` files. To enable it, add the uv `package-ecosystem` to your `updates` list in the `dependabot.yml`: ```yaml title="dependabot.yml" version: 2 updates: - package-ecosystem: "uv" directory: "/" schedule: interval: "weekly" ``` ## Dependency cooldown If you use [`exclude-newer`](../../reference/settings.md#exclude-newer) option, it is recommended to also set the equivalent [`cooldown`](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference#cooldown-) option in Dependabot, to avoid ending up with pull requests where uv would not be able to lock the dependencies. For instance, if you've set `exclude-newer` to `1 week`, you can set: ```yaml title="dependabot.yml" version: 2 updates: - package-ecosystem: "uv" directory: "/" schedule: interval: "weekly" cooldown: default-days: 7 ``` astral-sh-uv-9252ba6/docs/guides/integration/docker.md000066400000000000000000000511401521221614700227220ustar00rootroot00000000000000--- title: Using uv in Docker description: A complete guide to using uv in Docker to manage Python dependencies while optimizing build times and image size via multi-stage builds, intermediate layers, and more. --- # Using uv in Docker ## Getting started !!! tip Check out the [`uv-docker-example`](https://github.com/astral-sh/uv-docker-example) project for an example of best practices when using uv to build an application in Docker. uv provides both _distroless_ Docker images, which are useful for [copying uv binaries](#installing-uv) into your own image builds, and images derived from popular base images, which are useful for using uv in a container. The distroless images do not contain anything but the uv binaries. In contrast, the derived images include an operating system with uv pre-installed. As an example, to run uv in a container using a Debian-based image: ```console $ docker run --rm -it ghcr.io/astral-sh/uv:debian uv --help ``` ### Available images The following distroless images are available: - `ghcr.io/astral-sh/uv:latest` - `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.11.20` - `ghcr.io/astral-sh/uv:{major}.{minor}`, e.g., `ghcr.io/astral-sh/uv:0.8` (the latest patch version) And the following derived images are available: - Based on `alpine:3.23`: - `ghcr.io/astral-sh/uv:alpine` - `ghcr.io/astral-sh/uv:alpine3.23` - Based on `alpine:3.22`: - `ghcr.io/astral-sh/uv:alpine3.22` - Based on `debian:trixie-slim`: - `ghcr.io/astral-sh/uv:debian-slim` - `ghcr.io/astral-sh/uv:trixie-slim` - Based on `buildpack-deps:trixie`: - `ghcr.io/astral-sh/uv:debian` - `ghcr.io/astral-sh/uv:trixie` - Based on `dhi.io/alpine-base:3.23`: - `ghcr.io/astral-sh/uv:alpine-dhi` - `ghcr.io/astral-sh/uv:alpine3.23-dhi` - Based on `dhi.io/debian-base:trixie-debian13`: - `ghcr.io/astral-sh/uv:debian-dhi` - `ghcr.io/astral-sh/uv:trixie-dhi` - Based on `dhi/python:3.x`: - `ghcr.io/astral-sh/uv:python3.14-dhi` - `ghcr.io/astral-sh/uv:python3.13-dhi` - `ghcr.io/astral-sh/uv:python3.12-dhi` - `ghcr.io/astral-sh/uv:python3.11-dhi` - `ghcr.io/astral-sh/uv:python3.10-dhi` - Based on `python3.x-alpine`: - `ghcr.io/astral-sh/uv:python3.14-alpine` - `ghcr.io/astral-sh/uv:python3.14-alpine3.23` - `ghcr.io/astral-sh/uv:python3.13-alpine` - `ghcr.io/astral-sh/uv:python3.13-alpine3.23` - `ghcr.io/astral-sh/uv:python3.12-alpine` - `ghcr.io/astral-sh/uv:python3.12-alpine3.23` - `ghcr.io/astral-sh/uv:python3.11-alpine` - `ghcr.io/astral-sh/uv:python3.11-alpine3.23` - `ghcr.io/astral-sh/uv:python3.10-alpine` - `ghcr.io/astral-sh/uv:python3.10-alpine3.23` - `ghcr.io/astral-sh/uv:python3.9-alpine` - `ghcr.io/astral-sh/uv:python3.9-alpine3.22` - Based on `python3.x-trixie`: - `ghcr.io/astral-sh/uv:python3.14-trixie` - `ghcr.io/astral-sh/uv:python3.13-trixie` - `ghcr.io/astral-sh/uv:python3.12-trixie` - `ghcr.io/astral-sh/uv:python3.11-trixie` - `ghcr.io/astral-sh/uv:python3.10-trixie` - `ghcr.io/astral-sh/uv:python3.9-trixie` - Based on `python3.x-slim-trixie`: - `ghcr.io/astral-sh/uv:python3.14-trixie-slim` - `ghcr.io/astral-sh/uv:python3.13-trixie-slim` - `ghcr.io/astral-sh/uv:python3.12-trixie-slim` - `ghcr.io/astral-sh/uv:python3.11-trixie-slim` - `ghcr.io/astral-sh/uv:python3.10-trixie-slim` - `ghcr.io/astral-sh/uv:python3.9-trixie-slim` As with the distroless image, each derived image is published with uv version tags as `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}-{base}` and `ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.11.20-alpine`. In addition, starting with `0.8` each derived image also sets `UV_TOOL_BIN_DIR` to `/usr/local/bin` to allow `uv tool install` to work as expected with the default user. For more details, see the [GitHub Container](https://github.com/astral-sh/uv/pkgs/container/uv) page. ### Installing uv Use one of the above images with uv pre-installed or install uv by copying the binary from the official distroless Docker image: ```dockerfile title="Dockerfile" FROM python:3.12-slim-trixie COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ ``` Or, with the installer: ```dockerfile title="Dockerfile" FROM python:3.12-slim-trixie # The installer requires curl (and certificates) to download the release archive RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates # Download the latest installer ADD https://astral.sh/uv/install.sh /uv-installer.sh # Run the installer then remove it RUN sh /uv-installer.sh && rm /uv-installer.sh # Ensure the installed binary is on the `PATH` ENV PATH="/root/.local/bin/:$PATH" ``` Note this requires `curl` to be available. In either case, it is best practice to pin to a specific uv version, e.g., with: ```dockerfile COPY --from=ghcr.io/astral-sh/uv:0.11.20 /uv /uvx /bin/ ``` !!! tip While the Dockerfile example above pins to a specific tag, it's also possible to pin a specific SHA256. Pinning a specific SHA256 is considered best practice in environments that require reproducible builds as tags can be moved across different commit SHAs. ```Dockerfile # e.g., using a hash from a previous release COPY --from=ghcr.io/astral-sh/uv@sha256:2381d6aa60c326b71fd40023f921a0a3b8f91b14d5db6b90402e65a635053709 /uv /uvx /bin/ ``` Or, with the installer: ```dockerfile ADD https://astral.sh/uv/0.11.20/install.sh /uv-installer.sh ``` ### Installing a project If you're using uv to manage your project, you can copy it into the image and install it: ```dockerfile title="Dockerfile" # Copy the project into the image COPY . /app # Disable development dependencies ENV UV_NO_DEV=1 # Sync the project into a new environment, asserting the lockfile is up to date WORKDIR /app RUN uv sync --locked ``` !!! important It is best practice to add `.venv` to a [`.dockerignore` file](https://docs.docker.com/build/concepts/context/#dockerignore-files) in your repository to prevent it from being included in image builds. The project virtual environment is dependent on your local platform and should be created from scratch in the image. Then, to start your application by default: ```dockerfile title="Dockerfile" # Presuming there is a `my_app` command provided by the project CMD ["uv", "run", "my_app"] ``` !!! tip It is best practice to use [intermediate layers](#intermediate-layers) separating installation of dependencies and the project itself to improve Docker image build times. See a complete example in the [`uv-docker-example` project](https://github.com/astral-sh/uv-docker-example/blob/main/Dockerfile). ### Using the environment Once the project is installed, you can either _activate_ the project virtual environment by placing its binary directory at the front of the path: ```dockerfile title="Dockerfile" ENV PATH="/app/.venv/bin:$PATH" ``` Or, you can use `uv run` for any commands that require the environment: ```dockerfile title="Dockerfile" RUN uv run some_script.py ``` !!! tip Alternatively, the [`UV_PROJECT_ENVIRONMENT` setting](../../concepts/projects/config.md#project-environment-path) can be set before syncing to install to the system Python environment and skip environment activation entirely. ### Using installed tools To use installed tools, ensure the [tool bin directory](../../concepts/tools.md#tool-executables) is on the path: ```dockerfile title="Dockerfile" ENV PATH=/root/.local/bin:$PATH RUN uv tool install cowsay ``` ```console $ docker run -it $(docker build -q .) /bin/bash -c "cowsay -t hello" _____ | hello | ===== \ \ ^__^ (oo)\_______ (__)\ )\/\ ||----w | || || ``` !!! note The tool bin directory's location can be determined by running the `uv tool dir --bin` command in the container. Alternatively, it can be set to a constant location: ```dockerfile title="Dockerfile" ENV UV_TOOL_BIN_DIR=/opt/uv-bin/ ``` ## Developing in a container When developing, it's useful to mount the project directory into a container. With this setup, changes to the project can be immediately reflected in a containerized service without rebuilding the image. However, it is important _not_ to include the project virtual environment (`.venv`) in the mount, because the virtual environment is platform specific and the one built for the image should be kept. ### Mounting the project with `docker run` Bind mount the project (in the working directory) to `/app` while retaining the `.venv` directory with an [anonymous volume](https://docs.docker.com/engine/storage/#volumes): ```console $ docker run --rm --volume .:/app --volume /app/.venv [...] ``` !!! tip The `--rm` flag is included to ensure the container and anonymous volume are cleaned up when the container exits. See a complete example in the [`uv-docker-example` project](https://github.com/astral-sh/uv-docker-example/blob/main/run.sh). ### Configuring `watch` with `docker compose` When using Docker compose, more sophisticated tooling is available for container development. The [`watch`](https://docs.docker.com/compose/file-watch/#compose-watch-versus-bind-mounts) option allows for greater granularity than is practical with a bind mount and supports triggering updates to the containerized service when files change. !!! note This feature requires Compose 2.22.0 which is bundled with Docker Desktop 4.24. Configure `watch` in your [Docker compose file](https://docs.docker.com/compose/compose-application-model/#the-compose-file) to mount the project directory without syncing the project virtual environment and to rebuild the image when the configuration changes: ```yaml title="compose.yaml" services: example: build: . # ... develop: # Create a `watch` configuration to update the app # watch: # Sync the working directory with the `/app` directory in the container - action: sync path: . target: /app # Exclude the project virtual environment ignore: - .venv/ # Rebuild the image on changes to the `pyproject.toml` - action: rebuild path: ./pyproject.toml ``` Then, run `docker compose watch` to run the container with the development setup. See a complete example in the [`uv-docker-example` project](https://github.com/astral-sh/uv-docker-example/blob/main/compose.yml). ## Optimizations ### Compiling bytecode Compiling Python source files to bytecode is typically desirable for production images as it tends to improve startup time (at the cost of increased installation time and image size). To enable bytecode compilation, use the `--compile-bytecode` flag: ```dockerfile title="Dockerfile" RUN uv python install --compile-bytecode RUN uv sync --compile-bytecode ``` Alternatively, you can set the `UV_COMPILE_BYTECODE` environment variable to ensure that all commands within the Dockerfile compile bytecode: ```dockerfile title="Dockerfile" ENV UV_COMPILE_BYTECODE=1 ``` !!! note uv will only compile the standard library of _managed_ Python versions during `uv python install`. The distributor of unmanaged Python versions decides if the standard library is pre-compiled. For example, the official `python` image will not have a compiled standard library. ### Caching A [cache mount](https://docs.docker.com/build/guide/mounts/#add-a-cache-mount) can be used to improve performance across builds: ```dockerfile title="Dockerfile" ENV UV_LINK_MODE=copy RUN --mount=type=cache,target=/root/.cache/uv \ uv sync ``` Changing the [`UV_LINK_MODE`](../../reference/settings.md#link-mode) silences warnings about not being able to link files since the cache and sync target are on separate file systems. If you're not mounting the cache, image size can be reduced by using the `--no-cache` flag or setting `UV_NO_CACHE`. By default, managed Python installations are not cached before being installed. Setting `UV_PYTHON_CACHE_DIR` can be used in combination with a cache mount: ```dockerfile title="Dockerfile" ENV UV_PYTHON_CACHE_DIR=/root/.cache/uv/python RUN --mount=type=cache,target=/root/.cache/uv \ uv python install ``` !!! note The cache directory's location can be determined by running the `uv cache dir` command in the container. Alternatively, the cache can be set to a constant location: ```dockerfile title="Dockerfile" ENV UV_CACHE_DIR=/opt/uv-cache/ ``` ### Intermediate layers If you're using uv to manage your project, you can improve build times by moving your transitive dependency installation into its own layer via the `--no-install` options. `uv sync --no-install-project` will install the dependencies of the project but not the project itself. Since the project changes frequently, but its dependencies are generally static, this can be a big time saver. ```dockerfile title="Dockerfile" # Install uv FROM python:3.12-slim COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # Change the working directory to the `app` directory WORKDIR /app # Install dependencies RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --locked --no-install-project # Copy the project into the image COPY . /app # Sync the project RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked ``` Note that the `pyproject.toml` is required to identify the project root and name, but the project _contents_ are not copied into the image until the final `uv sync` command. !!! tip If you want to remove additional, specific packages from the sync, use `--no-install-package `. #### Intermediate layers in workspaces If you're using a [workspace](../../concepts/projects/workspaces.md), then a couple changes are needed: - Use `--frozen` instead of `--locked` during the initially sync. - Use the `--no-install-workspace` flag which excludes the project _and_ any workspace members. ```dockerfile title="Dockerfile" # Install uv FROM python:3.12-slim COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ WORKDIR /app RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-install-workspace COPY . /app RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked ``` uv cannot assert that the `uv.lock` file is up-to-date without each of the workspace member `pyproject.toml` files, so we use `--frozen` instead of `--locked` to skip the check during the initial sync. The next sync, after all the workspace members have been copied, can still use `--locked` and will validate that the lockfile is correct for all workspace members. ### Non-editable installs By default, uv installs projects and workspace members in editable mode, such that changes to the source code are immediately reflected in the environment. `uv sync` and `uv run` both accept a `--no-editable` flag, which instructs uv to install the project in non-editable mode, removing any dependency on the source code. In the context of a multi-stage Docker image, `--no-editable` can be used to include the project in the synced virtual environment from one stage, then copy the virtual environment alone (and not the source code) into the final image. For example: ```dockerfile title="Dockerfile" # Install uv FROM python:3.12-slim AS builder COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # Use the system Python across both stages ENV UV_PYTHON_DOWNLOADS=0 # Change the working directory to the `app` directory WORKDIR /app # Install dependencies RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --locked --no-install-project --no-editable # Copy the project into the intermediate image COPY . /app # Sync the project RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked --no-editable FROM python:3.12-slim # Copy the environment, but not the source code COPY --from=builder /app/.venv /app/.venv # Run the application CMD ["/app/.venv/bin/hello"] ``` ### Using uv temporarily If uv isn't needed in the final image, the binary can be mounted in each invocation: ```dockerfile title="Dockerfile" RUN --mount=from=ghcr.io/astral-sh/uv,source=/uv,target=/bin/uv \ uv sync ``` ## Using the pip interface ### Installing a package The system Python environment is safe to use this context, since a container is already isolated. The `--system` flag can be used to install in the system environment: ```dockerfile title="Dockerfile" RUN uv pip install --system ruff ``` To use the system Python environment by default, set the `UV_SYSTEM_PYTHON` variable: ```dockerfile title="Dockerfile" ENV UV_SYSTEM_PYTHON=1 ``` Alternatively, a virtual environment can be created and activated: ```dockerfile title="Dockerfile" RUN uv venv /opt/venv # Use the virtual environment automatically ENV VIRTUAL_ENV=/opt/venv # Place entry points in the environment at the front of the path ENV PATH="/opt/venv/bin:$PATH" ``` When using a virtual environment, the `--system` flag should be omitted from uv invocations: ```dockerfile title="Dockerfile" RUN uv pip install ruff ``` ### Installing requirements To install requirements files, copy them into the container: ```dockerfile title="Dockerfile" COPY requirements.txt . RUN uv pip install -r requirements.txt ``` ### Installing a project When installing a project alongside requirements, it is best practice to separate copying the requirements from the rest of the source code. This allows the dependencies of the project (which do not change often) to be cached separately from the project itself (which changes very frequently). ```dockerfile title="Dockerfile" COPY pyproject.toml . RUN uv pip install -r pyproject.toml COPY . . RUN uv pip install -e . ``` ## Verifying image provenance The Docker images are signed during the build process to provide proof of their origin. These attestations can be used to verify that an image was produced from an official channel. For example, you can verify the attestations with the [GitHub CLI tool `gh`](https://cli.github.com/): ```console $ gh attestation verify --owner astral-sh oci://ghcr.io/astral-sh/uv:latest Loaded digest sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx for oci://ghcr.io/astral-sh/uv:latest Loaded 1 attestation from GitHub API The following policy criteria will be enforced: - OIDC Issuer must match:................... https://token.actions.githubusercontent.com - Source Repository Owner URI must match:... https://github.com/astral-sh - Predicate type must match:................ https://slsa.dev/provenance/v1 - Subject Alternative Name must match regex: (?i)^https://github.com/astral-sh/ ✓ Verification succeeded! sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx was attested by: REPO PREDICATE_TYPE WORKFLOW astral-sh/uv https://slsa.dev/provenance/v1 .github/workflows/build-docker.yml@refs/heads/main ``` This tells you that the specific Docker image was built by the official uv GitHub release workflow and hasn't been tampered with since. GitHub attestations build on the [sigstore.dev infrastructure](https://www.sigstore.dev/). As such you can also use the [`cosign` command](https://github.com/sigstore/cosign) to verify the attestation blob against the (multi-platform) manifest for `uv`: ```console $ REPO=astral-sh/uv $ gh attestation download --repo $REPO oci://ghcr.io/${REPO}:latest Wrote attestations to file sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.jsonl. Any previous content has been overwritten The trusted metadata is now available at sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.jsonl $ docker buildx imagetools inspect ghcr.io/${REPO}:latest --format "{{json .Manifest}}" > manifest.json $ cosign verify-blob-attestation \ --new-bundle-format \ --bundle "$(jq -r .digest manifest.json).jsonl" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ --certificate-identity-regexp="^https://github\.com/${REPO}/.*" \ <(jq -j '.|del(.digest,.size)' manifest.json) Verified OK ``` !!! tip These examples use `latest`, but best practice is to verify the attestation for a specific version tag, e.g., `ghcr.io/astral-sh/uv:0.11.20`, or (even better) the specific image digest, such as `ghcr.io/astral-sh/uv:0.5.27@sha256:5adf09a5a526f380237408032a9308000d14d5947eafa687ad6c6a2476787b4f`. astral-sh-uv-9252ba6/docs/guides/integration/fastapi.md000066400000000000000000000063711521221614700231100ustar00rootroot00000000000000--- title: Using uv with FastAPI description: A guide to using uv with FastAPI to manage Python dependencies, run applications, and deploy with Docker. --- # Using uv with FastAPI [FastAPI](https://github.com/fastapi/fastapi) is a modern, high-performance Python web framework. You can use uv to manage your FastAPI project, including installing dependencies, managing environments, running FastAPI applications, and more. !!! note You can view the source code for this guide in the [uv-fastapi-example](https://github.com/astral-sh/uv-fastapi-example) repository. ## Migrating an existing FastAPI project As an example, consider the sample application defined in the [FastAPI documentation](https://fastapi.tiangolo.com/tutorial/bigger-applications/), structured as follows: ```plaintext project └── app ├── __init__.py ├── main.py ├── dependencies.py ├── routers │ ├── __init__.py │ ├── items.py │ └── users.py └── internal ├── __init__.py └── admin.py ``` To use uv with this application, inside the `project` directory run: ```console $ uv init --app ``` This creates a [project with an application layout](../../concepts/projects/init.md#applications) and a `pyproject.toml` file. Then, add a dependency on FastAPI: ```console $ uv add fastapi --extra standard ``` You should now have the following structure: ```plaintext project ├── pyproject.toml └── app ├── __init__.py ├── main.py ├── dependencies.py ├── routers │ ├── __init__.py │ ├── items.py │ └── users.py └── internal ├── __init__.py └── admin.py ``` And the contents of the `pyproject.toml` file should look something like this: ```toml title="pyproject.toml" [project] name = "uv-fastapi-example" version = "0.1.0" description = "FastAPI project" readme = "README.md" requires-python = ">=3.12" dependencies = [ "fastapi[standard]", ] ``` From there, you can run the FastAPI application with: ```console $ uv run fastapi dev ``` `uv run` will automatically resolve and lock the project dependencies (i.e., create a `uv.lock` alongside the `pyproject.toml`), create a virtual environment, and run the command in that environment. Test the app by opening http://127.0.0.1:8000/?token=jessica in a web browser. ## Deployment To deploy the FastAPI application with Docker, you can use the following `Dockerfile`: ```dockerfile title="Dockerfile" FROM python:3.12-slim # Install uv. COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # Copy the application into the container. COPY . /app # Install the application dependencies. WORKDIR /app RUN uv sync --frozen --no-cache # Run the application. CMD ["/app/.venv/bin/fastapi", "run", "app/main.py", "--port", "80", "--host", "0.0.0.0"] ``` Build the Docker image with: ```console $ docker build -t fastapi-app . ``` Run the Docker container locally with: ```console $ docker run -p 8000:80 fastapi-app ``` Navigate to http://127.0.0.1:8000/?token=jessica in your browser to verify that the app is running correctly. !!! tip For more on using uv with Docker, see the [Docker guide](./docker.md). astral-sh-uv-9252ba6/docs/guides/integration/github.md000066400000000000000000000272031521221614700227400ustar00rootroot00000000000000--- title: Using uv in GitHub Actions description: A guide to using uv in GitHub Actions, including installation, setting up Python, installing dependencies, and more. --- # Using uv in GitHub Actions ## Installation For use with GitHub Actions, we recommend the official [`astral-sh/setup-uv`](https://github.com/astral-sh/setup-uv) action, which installs uv, adds it to PATH, (optionally) persists the cache, and more, with support for all uv-supported platforms. To install the latest version of uv: ```yaml title="example.yml" hl_lines="11 12" name: Example jobs: uv-example: name: python runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 ``` It is considered best practice to pin to a specific uv version, e.g., with: ```yaml title="example.yml" hl_lines="14 15" name: Example jobs: uv-example: name: python runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: # Install a specific version of uv. version: "0.11.20" ``` ## Setting up Python Python can be installed with the `python install` command: ```yaml title="example.yml" hl_lines="14 15" name: Example jobs: uv-example: name: python runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - name: Set up Python run: uv python install ``` This will respect the Python version pinned in the project. Alternatively, the official GitHub `setup-python` action can be used. This can be faster, because GitHub caches the Python versions alongside the runner. Set the [`python-version-file`](https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#using-the-python-version-file-input) option to use the pinned version for the project: ```yaml title="example.yml" hl_lines="14" name: Example jobs: uv-example: name: python runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: "Set up Python" uses: actions/setup-python@v6 with: python-version-file: ".python-version" - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 ``` Or, specify the `pyproject.toml` file to ignore the pin and use the latest version compatible with the project's `requires-python` constraint: ```yaml title="example.yml" hl_lines="14" name: Example jobs: uv-example: name: python runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: "Set up Python" uses: actions/setup-python@v6 with: python-version-file: "pyproject.toml" - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 ``` ## Multiple Python versions When using a matrix to test multiple Python versions, set the Python version using `astral-sh/setup-uv`, which will override the Python version specification in the `pyproject.toml` or `.python-version` files: ```yaml title="example.yml" hl_lines="17 18" jobs: build: name: continuous-integration runs-on: ubuntu-latest strategy: matrix: python-version: - "3.10" - "3.11" - "3.12" steps: - uses: actions/checkout@v6 - name: Install uv and set the Python version uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: python-version: ${{ matrix.python-version }} ``` If not using the `setup-uv` action, you can set the `UV_PYTHON` environment variable: ```yaml title="example.yml" hl_lines="12" jobs: build: name: continuous-integration runs-on: ubuntu-latest strategy: matrix: python-version: - "3.10" - "3.11" - "3.12" env: UV_PYTHON: ${{ matrix.python-version }} steps: - uses: actions/checkout@v6 ``` ## Syncing and running Once uv and Python are installed, the project can be installed with `uv sync` and commands can be run in the environment with `uv run`: ```yaml title="example.yml" hl_lines="15 17-22" name: Example jobs: uv-example: name: python runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - name: Install the project run: uv sync --locked --all-extras --dev - name: Run tests # For example, using `pytest` run: uv run pytest tests ``` !!! tip The [`UV_PROJECT_ENVIRONMENT` setting](../../concepts/projects/config.md#project-environment-path) can be used to install to the system Python environment instead of creating a virtual environment. ## Caching It may improve CI times to store uv's cache across workflow runs. The [`astral-sh/setup-uv`](https://github.com/astral-sh/setup-uv) has built-in support for persisting the cache: ```yaml title="example.yml" - name: Enable caching uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: enable-cache: true ``` Alternatively, you can manage the cache manually with the `actions/cache` action: ```yaml title="example.yml" jobs: install_job: env: # Configure a constant location for the uv cache UV_CACHE_DIR: /tmp/.uv-cache steps: # ... setup up Python and uv ... - name: Restore uv cache uses: actions/cache@v5 with: path: /tmp/.uv-cache key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }} restore-keys: | uv-${{ runner.os }}-${{ hashFiles('uv.lock') }} uv-${{ runner.os }} # ... install packages, run tests, etc ... - name: Minimize uv cache run: uv cache prune --ci ``` The `uv cache prune --ci` command is used to reduce the size of the cache and is optimized for CI. Its effect on performance is dependent on the packages being installed. !!! tip If using `uv pip`, use `requirements.txt` instead of `uv.lock` in the cache key. !!! note [post-job-hook]: https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job When using non-ephemeral, self-hosted runners the default cache directory can grow unbounded. In this case, it may not be optimal to share the cache between jobs. Instead, move the cache inside the GitHub Workspace and remove it once the job finishes using a [Post Job Hook][post-job-hook]. ```yaml install_job: env: # Configure a relative location for the uv cache UV_CACHE_DIR: ${{ github.workspace }}/.cache/uv ``` Using a post job hook requires setting the `ACTIONS_RUNNER_HOOK_JOB_STARTED` environment variable on the self-hosted runner to the path of a cleanup script such as the one shown below. ```sh title="clean-uv-cache.sh" #!/usr/bin/env sh uv cache clean ``` ## Using `uv pip` If using the `uv pip` interface instead of the uv project interface, uv requires a virtual environment by default. To allow installing packages into the system environment, use the `--system` flag on all `uv` invocations or set the `UV_SYSTEM_PYTHON` variable. The `UV_SYSTEM_PYTHON` variable can be defined in at different scopes. Opt-in for the entire workflow by defining it at the top level: ```yaml title="example.yml" env: UV_SYSTEM_PYTHON: 1 jobs: ... ``` Or, opt-in for a specific job in the workflow: ```yaml title="example.yml" jobs: install_job: env: UV_SYSTEM_PYTHON: 1 ... ``` Or, opt-in for a specific step in a job: ```yaml title="example.yml" steps: - name: Install requirements run: uv pip install -r requirements.txt env: UV_SYSTEM_PYTHON: 1 ``` To opt-out again, the `--no-system` flag can be used in any uv invocation. ## Private repos If your project has [dependencies](../../concepts/projects/dependencies.md#git) on private GitHub repositories, you will need to configure a [personal access token (PAT)][PAT] to allow uv to fetch them. After creating a PAT that has read access to the private repositories, add it as a [repository secret]. Then, you can use the [`gh`](https://cli.github.com/) CLI (which is installed in GitHub Actions runners by default) to configure a [credential helper for Git](../../concepts/authentication/git.md#git-credential-helpers) to use the PAT for queries to repositories hosted on `github.com`. For example, if you called your repository secret `MY_PAT`: ```yaml title="example.yml" steps: - name: Register the personal access token run: echo "${{ secrets.MY_PAT }}" | gh auth login --with-token - name: Configure the Git credential helper run: gh auth setup-git ``` [PAT]: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens [repository secret]: https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository ## Publishing to PyPI uv can be used to build and publish your package to PyPI from GitHub Actions. We provide a standalone example alongside this guide in [astral-sh/trusted-publishing-examples](https://github.com/astral-sh/trusted-publishing-examples). The workflow uses [trusted publishing](https://docs.pypi.org/trusted-publishers/), so no credentials need to be configured. In the example workflow, we use a script to test that the source distribution and the wheel are both functional and we didn't miss any files. This step is recommended, but optional. First, add a release workflow to your project: ```yaml title=".github/workflows/release.yml" name: "Publish release to PyPI" on: push: tags: # Publish on any tag starting with a `v`, e.g., v0.1.0 - v* jobs: run: runs-on: ubuntu-latest environment: name: pypi permissions: id-token: write contents: read steps: - name: Checkout uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - name: Install Python 3.13 run: uv python install 3.13 - name: Build run: uv build # Check that basic features work and we didn't miss to include crucial files - name: Smoke test (wheel) run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py - name: Smoke test (source distribution) run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py - name: Publish run: uv publish ``` Then, create the environment defined in the workflow in the GitHub repository under "Settings" -> "Environments". ![GitHub settings dialog showing how to add the "pypi" environment under "Settings" -> "Environments"](../../assets/github-add-environment.png) Add a [trusted publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) to your PyPI project in the project settings under "Publishing". Ensure that all fields match with your GitHub configuration. ![PyPI project publishing settings dialog showing how to set all fields for a trusted publisher configuration](../../assets/pypi-add-trusted-publisher.png) After saving: ![PyPI project publishing settings dialog showing the configured trusted publishing settings](../../assets/pypi-with-trusted-publisher.png) Finally, tag a release and push it. Make sure it starts with `v` to match the pattern in the workflow. ```console $ git tag -a v0.1.0 -m v0.1.0 $ git push --tags ``` astral-sh-uv-9252ba6/docs/guides/integration/gitlab.md000066400000000000000000000045571521221614700227270ustar00rootroot00000000000000--- title: Using uv in GitLab CI/CD description: A guide to using uv in GitLab CI/CD, including installation, setting up Python, installing dependencies, and more. --- # Using uv in GitLab CI/CD ## Using the uv image Astral provides [Docker images](docker.md#available-images) with uv preinstalled. Select a variant that is suitable for your workflow. ```yaml title=".gitlab-ci.yml" variables: UV_VERSION: "0.11.20" PYTHON_VERSION: "3.12" BASE_LAYER: trixie-slim # GitLab CI creates a separate mountpoint for the build directory, # so we need to copy instead of using hard links. UV_LINK_MODE: copy uv: image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER script: # your `uv` commands ``` !!! note If you are using a distroless image, you have to specify the entrypoint: ```yaml uv: image: name: ghcr.io/astral-sh/uv:$UV_VERSION entrypoint: [""] # ... ``` ## Caching Persisting the uv cache between workflow runs can improve performance. ```yaml uv-install: variables: UV_CACHE_DIR: .uv-cache cache: - key: files: - uv.lock paths: - $UV_CACHE_DIR script: # Your `uv` commands after_script: - uv cache prune --ci ``` See the [GitLab caching documentation](https://docs.gitlab.com/ee/ci/caching/) for more details on configuring caching. Using `uv cache prune --ci` at the end of the job is recommended to reduce cache size. See the [uv cache documentation](../../concepts/cache.md#caching-in-continuous-integration) for more details. ## Using `uv pip` If using the `uv pip` interface instead of the uv project interface, uv requires a virtual environment by default. To allow installing packages into the system environment, use the `--system` flag on all uv invocations or set the `UV_SYSTEM_PYTHON` variable. The `UV_SYSTEM_PYTHON` variable can be defined in at different scopes. You can read more about how [variables and their precedence works in GitLab here](https://docs.gitlab.com/ee/ci/variables/) Opt-in for the entire workflow by defining it at the top level: ```yaml title=".gitlab-ci.yml" variables: UV_SYSTEM_PYTHON: 1 # [...] ``` To opt-out again, the `--no-system` flag can be used in any uv invocation. When persisting the cache, you may want to use `requirements.txt` or `pyproject.toml` as your cache key files instead of `uv.lock`. astral-sh-uv-9252ba6/docs/guides/integration/google.md000066400000000000000000000101361521221614700227270ustar00rootroot00000000000000--- title: Google Artifact Registry description: Using uv with Google Artifact Registry for installing and publishing Python packages. --- # Google Artifact Registry uv can install packages from [Google Artifact Registry](https://cloud.google.com/artifact-registry/docs), either by using an access token, or using the [`keyring`](https://github.com/jaraco/keyring) package. !!! note This guide assumes that [`gcloud`](https://cloud.google.com/sdk/gcloud) CLI is installed and authenticated. To use Google Artifact Registry, add the index to your project: ```toml title="pyproject.toml" [[tool.uv.index]] name = "private-registry" url = "https://-python.pkg.dev///simple/" ``` ## Authenticate with a Google access token Credentials can be provided via "Basic" HTTP authentication scheme. Include access token in the password field of the URL. Username must be `oauth2accesstoken`, otherwise authentication will fail. Generate a token with `gcloud`: ```bash export ARTIFACT_REGISTRY_TOKEN=$( gcloud auth application-default print-access-token ) ``` !!! note You might need to pass extra parameters to properly generate the token (like `--project`), this is a basic example. Then set credentials for the index with: ```bash export UV_INDEX_PRIVATE_REGISTRY_USERNAME=oauth2accesstoken export UV_INDEX_PRIVATE_REGISTRY_PASSWORD="$ARTIFACT_REGISTRY_TOKEN" ``` !!! note `PRIVATE_REGISTRY` should match the name of the index defined in your `pyproject.toml`. ## Authenticate with `keyring` and `keyrings.google-artifactregistry-auth` You can also authenticate to Artifact Registry using [`keyring`](https://github.com/jaraco/keyring) package with the [`keyrings.google-artifactregistry-auth` plugin](https://github.com/GoogleCloudPlatform/artifact-registry-python-tools). Because these two packages are required to authenticate to Artifact Registry, they must be pre-installed from a source other than Artifact Registry. The `keyrings.google-artifactregistry-auth` plugin wraps [gcloud CLI](https://cloud.google.com/sdk/gcloud) to generate short-lived access tokens, securely store them in system keyring, and refresh them when they are expired. uv only supports using the `keyring` package in [subprocess mode](../../reference/settings.md#keyring-provider). The `keyring` executable must be in the `PATH`, i.e., installed globally or in the active environment. The `keyring` CLI requires a username in the URL and it must be `oauth2accesstoken`. ```bash # Pre-install keyring and Artifact Registry plugin from the public PyPI uv tool install keyring --with keyrings.google-artifactregistry-auth # Enable keyring authentication export UV_KEYRING_PROVIDER=subprocess # Set the username for the index export UV_INDEX_PRIVATE_REGISTRY_USERNAME=oauth2accesstoken ``` !!! note The [`tool.uv.keyring-provider`](../../reference/settings.md#keyring-provider) setting can be used to enable keyring in your `uv.toml` or `pyproject.toml`. Similarly, the username for the index can be added directly to the index URL. ## Publishing packages If you also want to publish your own packages to Google Artifact Registry, you can use `uv publish` as described in the [Building and publishing guide](../package.md). First, add a `publish-url` to the index you want to publish packages to. For example: ```toml title="pyproject.toml" hl_lines="4" [[tool.uv.index]] name = "private-registry" url = "https://-python.pkg.dev///simple/" publish-url = "https://-python.pkg.dev///" ``` Then, configure credentials (if not using keyring): ```console $ export UV_PUBLISH_USERNAME=oauth2accesstoken $ export UV_PUBLISH_PASSWORD="$ARTIFACT_REGISTRY_TOKEN" ``` And publish the package: ```console $ uv publish --index private-registry ``` To use `uv publish` without adding the `publish-url` to the project, you can set `UV_PUBLISH_URL`: ```console $ export UV_PUBLISH_URL=https://-python.pkg.dev/// $ uv publish ``` Note this method is not preferable because uv cannot check if the package is already published before uploading artifacts. astral-sh-uv-9252ba6/docs/guides/integration/index.md000066400000000000000000000016021521221614700225600ustar00rootroot00000000000000# Integration guides Learn how to integrate uv with other software: - [Using in Docker images](./docker.md) - [Using with Jupyter notebooks](./jupyter.md) - [Using with marimo notebooks](./marimo.md) - [Using with pre-commit](./pre-commit.md) - [Using in GitHub Actions](./github.md) - [Using in GitLab CI/CD](./gitlab.md) - [Installing PyTorch](./pytorch.md) - [Building a FastAPI application](./fastapi.md) - [Using with Bazel](./bazel.md) - [Using with Azure Artifacts](./azure.md) - [Using with Google Artifact Registry](./google.md) - [Using with AWS CodeArtifact](./aws.md) - [Using with JFrog Artifactory](./jfrog.md) - [Using with Renovate](./renovate.md) - [Using with Dependabot](./dependabot.md) - [Using with AWS Lambda](./aws-lambda.md) - [Using with Coiled](./coiled.md) Or, explore the [concept documentation](../../concepts/index.md) for comprehensive breakdown of each feature. astral-sh-uv-9252ba6/docs/guides/integration/jfrog.md000066400000000000000000000036501521221614700225650ustar00rootroot00000000000000--- title: JFrog Artifactory description: Using uv with JFrog Artifactory for installing and publishing Python packages. --- # JFrog Artifactory uv can install packages from JFrog Artifactory, either by using a username and password or a JWT token. To use it, add the index to your project: ```toml title="pyproject.toml" [[tool.uv.index]] name = "private-registry" url = "https://.jfrog.io/artifactory/api/pypi//simple" ``` ## Authenticate with username and password ```console $ export UV_INDEX_PRIVATE_REGISTRY_USERNAME="" $ export UV_INDEX_PRIVATE_REGISTRY_PASSWORD="" ``` ## Authenticate with JWT token ```console $ export UV_INDEX_PRIVATE_REGISTRY_USERNAME="" $ export UV_INDEX_PRIVATE_REGISTRY_PASSWORD="$JFROG_JWT_TOKEN" ``` !!! note Replace `PRIVATE_REGISTRY` in the environment variable names with the actual index name defined in your `pyproject.toml`. ## Publishing packages Add a `publish-url` to your index definition: ```toml title="pyproject.toml" [[tool.uv.index]] name = "private-registry" url = "https://.jfrog.io/artifactory/api/pypi//simple" publish-url = "https://.jfrog.io/artifactory/api/pypi/" ``` !!! important If you use `--token "$JFROG_TOKEN"` or `UV_PUBLISH_TOKEN` with JFrog, you will receive a 401 Unauthorized error as JFrog requires an empty username but uv passes `__token__` for as the username when `--token` is used. To authenticate, pass your token as the password and set the username to an empty string: ```console $ uv publish --index -u "" -p "$JFROG_TOKEN" ``` Alternatively, you can set environment variables: ```console $ export UV_PUBLISH_USERNAME="" $ export UV_PUBLISH_PASSWORD="$JFROG_TOKEN" $ uv publish --index private-registry ``` !!! note The publish environment variables (`UV_PUBLISH_USERNAME` and `UV_PUBLISH_PASSWORD`) do not include the index name. astral-sh-uv-9252ba6/docs/guides/integration/jupyter.md000066400000000000000000000150501521221614700231550ustar00rootroot00000000000000--- title: Using uv with Jupyter description: A complete guide to using uv with Jupyter notebooks for interactive computing, data analysis, and visualization, including kernel management and virtual environment integration. --- # Using uv with Jupyter The [Jupyter](https://jupyter.org/) notebook is a popular tool for interactive computing, data analysis, and visualization. You can use Jupyter with uv in a few different ways, either to interact with a project, or as a standalone tool. ## Using Jupyter within a project If you're working within a [project](../../concepts/projects/index.md), you can start a Jupyter server with access to the project's virtual environment via the following: ```console $ uv run --with jupyter jupyter lab ``` By default, `jupyter lab` will start the server at [http://localhost:8888/lab](http://localhost:8888/lab). Within a notebook, you can import your project's modules as you would in any other file in the project. For example, if your project depends on `requests`, `import requests` will import `requests` from the project's virtual environment. If you're looking for read-only access to the project's virtual environment, then there's nothing more to it. However, if you need to install additional packages from within the notebook, there are a few extra details to consider. ### Creating a kernel If you need to install packages from within the notebook, we recommend creating a dedicated kernel for your project. Kernels enable the Jupyter server to run in one environment, with individual notebooks running in their own, separate environments. In the context of uv, we can create a kernel for a project while installing Jupyter itself in an isolated environment, as in `uv run --with jupyter jupyter lab`. Creating a kernel for the project ensures that the notebook is hooked up to the correct environment, and that any packages installed from within the notebook are installed into the project's virtual environment. To create a kernel, you'll need to install `ipykernel` as a development dependency: ```console $ uv add --dev ipykernel ``` Then, you can create the kernel for `project` with: ```console $ uv run ipython kernel install --user --env VIRTUAL_ENV $(pwd)/.venv --name=project ``` From there, start the server with: ```console $ uv run --with jupyter jupyter lab ``` When creating a notebook, select the `project` kernel from the dropdown. Then use `!uv add pydantic` to add `pydantic` to the project's dependencies, or `!uv pip install pydantic` to install `pydantic` into the project's virtual environment without persisting the change to the project `pyproject.toml` or `uv.lock` files. Either command will make `import pydantic` work within the notebook. ### Installing packages without a kernel If you don't want to create a kernel, you can still install packages from within the notebook. However, there are a few caveats to consider. Though `uv run --with jupyter` runs in an isolated environment, within the notebook itself, `!uv add` and related commands will modify the _project's_ environment, even without a kernel. For example, running `!uv add pydantic` from within a notebook will add `pydantic` to the project's dependencies and virtual environment, such that `import pydantic` will work immediately, without further configuration or a server restart. However, since the Jupyter server is the "active" environment, `!uv pip install` will install package's into _Jupyter's_ environment, not the project environment. Such dependencies will persist for the lifetime of the Jupyter server, but may disappear on subsequent `jupyter` invocations. If you're working with a notebook that relies on pip (e.g., via the `%pip` magic), you can include pip in your project's virtual environment by running `uv venv --seed` prior to starting the Jupyter server. For example, given: ```console $ uv venv --seed $ uv run --with jupyter jupyter lab ``` Subsequent `%pip install` invocations within the notebook will install packages into the project's virtual environment. However, such modifications will _not_ be reflected in the project's `pyproject.toml` or `uv.lock` files. ## Using Jupyter as a standalone tool If you ever need ad hoc access to a notebook (i.e., to run a Python snippet interactively), you can start a Jupyter server at any time with `uv tool run jupyter lab`. This will run a Jupyter server in an isolated environment. ## Using Jupyter with a non-project environment If you need to run Jupyter in a virtual environment that isn't associated with a [project](../../concepts/projects/index.md) (e.g., has no `pyproject.toml` or `uv.lock`), you can do so by adding Jupyter to the environment directly. For example: === "macOS and Linux" ```console $ uv venv --seed $ uv pip install pydantic $ uv pip install jupyterlab $ .venv/bin/jupyter lab ``` === "Windows" ```pwsh-session PS> uv venv --seed PS> uv pip install pydantic PS> uv pip install jupyterlab PS> .venv\Scripts\jupyter lab ``` From here, `import pydantic` will work within the notebook, and you can install additional packages via `!uv pip install`, or even `!pip install`. ## Using Jupyter from VS Code You can also engage with Jupyter notebooks from within an editor like VS Code. To connect a uv-managed project to a Jupyter notebook within VS Code, we recommend creating a kernel for the project, as in the following: ```console # Create a project. $ uv init project # Move into the project directory. $ cd project # Add ipykernel as a dev dependency. $ uv add --dev ipykernel # Open the project in VS Code. $ code . ``` Once the project directory is open in VS Code, you can create a new Jupyter notebook by selecting "Create: New Jupyter Notebook" from the command palette. When prompted to select a kernel, choose "Python Environments" and select the virtual environment you created earlier (e.g., `.venv/bin/python` on macOS and Linux, or `.venv\Scripts\python` on Windows). !!! note VS Code requires `ipykernel` to be present in the project environment. If you'd prefer to avoid adding `ipykernel` as a dev dependency, you can install it directly into the project environment with `uv pip install ipykernel`. If you need to manipulate the project's environment from within the notebook, you may need to add `uv` as an explicit development dependency: ```console $ uv add --dev uv ``` From there, you can use `!uv add pydantic` to add `pydantic` to the project's dependencies, or `!uv pip install pydantic` to install `pydantic` into the project's virtual environment without updating the project's `pyproject.toml` or `uv.lock` files. astral-sh-uv-9252ba6/docs/guides/integration/marimo.md000066400000000000000000000066651521221614700227530ustar00rootroot00000000000000--- title: Using uv with marimo description: A complete guide to using uv with marimo notebooks for interactive computing, script execution, and data apps. --- # Using uv with marimo [marimo](https://github.com/marimo-team/marimo) is an open-source Python notebook that blends interactive computing with the reproducibility and reusability of traditional software, letting you version with Git, run as scripts, and share as apps. Because marimo notebooks are stored as pure Python scripts, they are able to integrate tightly with uv. You can readily use marimo as a standalone tool, as self-contained scripts, in projects, and in non-project environments. ## Using marimo as a standalone tool For ad-hoc access to marimo notebooks, start a marimo server at any time in an isolated environment with: ```console $ uvx marimo edit ``` Start a specific notebook with: ```console $ uvx marimo edit my_notebook.py ``` ## Using marimo with inline script metadata Because marimo notebooks are stored as Python scripts, they can encapsulate their own dependencies using inline script metadata, via uv's [support for scripts](../../guides/scripts.md). For example, to add `numpy` as a dependency to your notebook, use this command: ```console $ uv add --script my_notebook.py numpy ``` To interactively edit a notebook containing inline script metadata, use: ```console $ uvx marimo edit --sandbox my_notebook.py ``` marimo will automatically use uv to start your notebook in an isolated virtual environment with your script's dependencies. Packages installed from the marimo UI will automatically be added to the notebook's script metadata. You can optionally run these notebooks as Python scripts, without opening an interactive session: ```console $ uv run my_notebook.py ``` ## Using marimo within a project If you're working within a [project](../../concepts/projects/index.md), you can start a marimo notebook with access to the project's virtual environment via the following command (assuming marimo is a project dependency): ```console $ uv run marimo edit my_notebook.py ``` To make additional packages available to your notebook, either add them to your project with `uv add`, or use marimo's built-in package installation UI, which will invoke `uv add` on your behalf. If marimo is not a project dependency, you can still run a notebook with the following command: ```console $ uv run --with marimo marimo edit my_notebook.py ``` This will let you import your project's modules while editing your notebook. However, packages installed via marimo's UI when running in this way will not be added to your project, and may disappear on subsequent marimo invocations. ## Using marimo in a non-project environment To run marimo in a virtual environment that isn't associated with a [project](../../concepts/projects/index.md), add marimo to the environment directly: ```console $ uv venv $ uv pip install numpy $ uv pip install marimo $ uv run marimo edit ``` From here, `import numpy` will work within the notebook, and marimo's UI installer will add packages to the environment with `uv pip install` on your behalf. ## Running marimo notebooks as scripts Regardless of how your dependencies are managed (with inline script metadata, within a project, or with a non-project environment), you can run marimo notebooks as scripts with: ```console $ uv run my_notebook.py ``` This executes your notebook as a Python script, without opening an interactive session in your browser. astral-sh-uv-9252ba6/docs/guides/integration/pre-commit.md000066400000000000000000000042001521221614700235220ustar00rootroot00000000000000--- title: Using uv with pre-commit description: A guide to using uv with pre-commit to automatically update lock files, export requirements, and compile requirements files. --- # Using uv in pre-commit An official pre-commit hook is provided at [`astral-sh/uv-pre-commit`](https://github.com/astral-sh/uv-pre-commit). To use uv with pre-commit, add one of the following examples to the `repos` list in the `.pre-commit-config.yaml`. To make sure your `uv.lock` file is up to date even if your `pyproject.toml` file was changed: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/astral-sh/uv-pre-commit # uv version. rev: 0.11.20 hooks: - id: uv-lock ``` To keep a `requirements.txt` file in sync with your `uv.lock` file: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/astral-sh/uv-pre-commit # uv version. rev: 0.11.20 hooks: - id: uv-export ``` To compile requirements files: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/astral-sh/uv-pre-commit # uv version. rev: 0.11.20 hooks: # Compile requirements - id: pip-compile args: [requirements.in, -o, requirements.txt] ``` To compile alternative requirements files, modify `args` and `files`: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/astral-sh/uv-pre-commit # uv version. rev: 0.11.20 hooks: # Compile requirements - id: pip-compile args: [requirements-dev.in, -o, requirements-dev.txt] files: ^requirements-dev\.(in|txt)$ ``` To run the hook over multiple files at the same time, add additional entries: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/astral-sh/uv-pre-commit # uv version. rev: 0.11.20 hooks: # Compile requirements - id: pip-compile name: pip-compile requirements.in args: [requirements.in, -o, requirements.txt] - id: pip-compile name: pip-compile requirements-dev.in args: [requirements-dev.in, -o, requirements-dev.txt] files: ^requirements-dev\.(in|txt)$ ``` astral-sh-uv-9252ba6/docs/guides/integration/pytorch.md000066400000000000000000000337461521221614700231570ustar00rootroot00000000000000--- title: Using uv with PyTorch description: A guide to using uv with PyTorch, including installing PyTorch, configuring per-platform and per-accelerator builds, and more. --- # Using uv with PyTorch The [PyTorch](https://pytorch.org/) ecosystem is a popular choice for deep learning research and development. You can use uv to manage PyTorch projects and PyTorch dependencies across different Python versions and environments, even controlling for the choice of accelerator (e.g., CPU-only vs. CUDA). !!! note Some of the features outlined in this guide require uv version 0.5.3 or later. We recommend upgrading prior to configuring PyTorch. ## Installing PyTorch From a packaging perspective, PyTorch has a few uncommon characteristics: - Many PyTorch wheels are hosted on a dedicated index, rather than the Python Package Index (PyPI). As such, installing PyTorch often requires configuring a project to use the PyTorch index. - PyTorch produces distinct builds for each accelerator (e.g., CPU-only, CUDA). Since there's no standardized mechanism for specifying these accelerators when publishing or installing, PyTorch encodes them in the local version specifier. As such, PyTorch versions will often look like `2.11.0+cpu`, `2.11.0+cu130`, etc. - Builds for different accelerators are published to different indexes. For example, the `+cpu` builds are published on https://download.pytorch.org/whl/cpu, while the `+cu130` builds are published on https://download.pytorch.org/whl/cu130. As such, the necessary packaging configuration will vary depending on both the platforms you need to support and the accelerators you want to enable. To start, consider the following (default) configuration, which would be generated by running `uv init --python 3.14` followed by `uv add torch torchvision`. In this case, PyTorch would be installed from PyPI, which hosts CPU-only wheels for Windows and macOS, and GPU-accelerated wheels on Linux (targeting CUDA 13.0, as of PyTorch 2.11.0): ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.14" dependencies = [ "torch>=2.11.0", "torchvision>=0.26.0", ] ``` This is a valid configuration for projects that want to use CPU builds on Windows and macOS, and CUDA-enabled builds on Linux. However, if you need to support different platforms or accelerators, you'll need to configure the project accordingly. ## Using a PyTorch index In some cases, you may want to use a specific PyTorch variant across all platforms. For example, you may want to use the CPU-only builds on Linux too. In such cases, the first step is to add the relevant PyTorch index to your `pyproject.toml`: === "CPU-only" ```toml [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true ``` === "CUDA 11.8" ```toml [[tool.uv.index]] name = "pytorch-cu118" url = "https://download.pytorch.org/whl/cu118" explicit = true ``` === "CUDA 12.6" ```toml [[tool.uv.index]] name = "pytorch-cu126" url = "https://download.pytorch.org/whl/cu126" explicit = true ``` === "CUDA 12.8" ```toml [[tool.uv.index]] name = "pytorch-cu128" url = "https://download.pytorch.org/whl/cu128" explicit = true ``` === "CUDA 13.0" ```toml [[tool.uv.index]] name = "pytorch-cu130" url = "https://download.pytorch.org/whl/cu130" explicit = true ``` === "ROCm 7.2" ```toml [[tool.uv.index]] name = "pytorch-rocm" url = "https://download.pytorch.org/whl/rocm7.2" explicit = true ``` === "Intel GPUs" ```toml [[tool.uv.index]] name = "pytorch-xpu" url = "https://download.pytorch.org/whl/xpu" explicit = true ``` We recommend the use of `explicit = true` to ensure that the index is _only_ used for `torch`, `torchvision`, and other PyTorch-related packages, as opposed to generic dependencies like `jinja2`, which should continue to be sourced from the default index (PyPI). Next, update the `pyproject.toml` to point `torch` and `torchvision` to the desired index: === "CPU-only" ```toml [tool.uv.sources] torch = [ { index = "pytorch-cpu" }, ] torchvision = [ { index = "pytorch-cpu" }, ] ``` === "CUDA 11.8" PyTorch doesn't publish CUDA builds for macOS. As such, we gate on `sys_platform` to instruct uv to use the PyTorch index on Linux and Windows, but fall back to PyPI on macOS: ```toml [tool.uv.sources] torch = [ { index = "pytorch-cu118", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] torchvision = [ { index = "pytorch-cu118", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] ``` === "CUDA 12.6" PyTorch doesn't publish CUDA builds for macOS. As such, we gate on `sys_platform` to instruct uv to limit the PyTorch index to Linux and Windows, falling back to PyPI on macOS: ```toml [tool.uv.sources] torch = [ { index = "pytorch-cu126", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] torchvision = [ { index = "pytorch-cu126", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] ``` === "CUDA 12.8" PyTorch doesn't publish CUDA builds for macOS. As such, we gate on `sys_platform` to instruct uv to limit the PyTorch index to Linux and Windows, falling back to PyPI on macOS: ```toml [tool.uv.sources] torch = [ { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] torchvision = [ { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] ``` === "CUDA 13.0" PyTorch doesn't publish CUDA builds for macOS. As such, we gate on `sys_platform` to instruct uv to limit the PyTorch index to Linux and Windows, falling back to PyPI on macOS: ```toml [tool.uv.sources] torch = [ { index = "pytorch-cu130", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] torchvision = [ { index = "pytorch-cu130", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] ``` === "ROCm 7.2" PyTorch doesn't publish ROCm builds for macOS or Windows. As such, we gate on `sys_platform` to instruct uv to limit the PyTorch index to Linux, falling back to PyPI on macOS and Windows: ```toml [tool.uv.sources] torch = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] torchvision = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] # ROCm support relies on both Triton packages, which should also be installed from # the PyTorch index (and included in `project.dependencies`). pytorch-triton-rocm = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] triton-rocm = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] ``` === "Intel GPUs" PyTorch doesn't publish Intel GPU builds for macOS. As such, we gate on `sys_platform` to instruct uv to limit the PyTorch index to Linux and Windows, falling back to PyPI on macOS: ```toml [tool.uv.sources] torch = [ { index = "pytorch-xpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] torchvision = [ { index = "pytorch-xpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] # Intel GPU support relies on `triton-xpu`, which should also be installed from the PyTorch index # (and included in `project.dependencies`). triton-xpu = [ { index = "pytorch-xpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] ``` As a complete example, the following project would use PyTorch's CPU-only builds on all platforms: ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.14.0" dependencies = [ "torch>=2.11.0", "torchvision>=0.26.0", ] [tool.uv.sources] torch = [ { index = "pytorch-cpu" }, ] torchvision = [ { index = "pytorch-cpu" }, ] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true ``` ## Configuring accelerators with environment markers In some cases, you may want to use CPU-only builds in one environment (e.g., macOS and Windows), and CUDA-enabled builds in another (e.g., Linux). With `tool.uv.sources`, you can use environment markers to specify the desired index for each platform. For example, the following configuration would use PyTorch's CUDA-enabled builds on Linux, and CPU-only builds on all other platforms (e.g., macOS and Windows): ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.14.0" dependencies = [ "torch>=2.11.0", "torchvision>=0.26.0", ] [tool.uv.sources] torch = [ { index = "pytorch-cpu", marker = "sys_platform != 'linux'" }, { index = "pytorch-cu130", marker = "sys_platform == 'linux'" }, ] torchvision = [ { index = "pytorch-cpu", marker = "sys_platform != 'linux'" }, { index = "pytorch-cu130", marker = "sys_platform == 'linux'" }, ] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true [[tool.uv.index]] name = "pytorch-cu130" url = "https://download.pytorch.org/whl/cu130" explicit = true ``` Similarly, the following configuration would use PyTorch's AMD GPU builds on Linux, and CPU-only builds on Windows and macOS (by way of falling back to PyPI): ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.14.0" dependencies = [ "torch>=2.11.0", "torchvision>=0.26.0", "pytorch-triton-rocm>=3.5.1 ; sys_platform == 'linux'", "triton-rocm>=3.6.0 ; sys_platform == 'linux'", ] [tool.uv.sources] torch = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] torchvision = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] pytorch-triton-rocm = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] triton-rocm = [ { index = "pytorch-rocm", marker = "sys_platform == 'linux'" }, ] [[tool.uv.index]] name = "pytorch-rocm" url = "https://download.pytorch.org/whl/rocm7.2" explicit = true ``` Or, for Intel GPU builds: ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.14.0" dependencies = [ "torch>=2.11.0", "torchvision>=0.26.0", "triton-xpu>=3.7.0 ; sys_platform == 'win32' or sys_platform == 'linux'", ] [tool.uv.sources] torch = [ { index = "pytorch-xpu", marker = "sys_platform == 'win32' or sys_platform == 'linux'" }, ] torchvision = [ { index = "pytorch-xpu", marker = "sys_platform == 'win32' or sys_platform == 'linux'" }, ] triton-xpu = [ { index = "pytorch-xpu", marker = "sys_platform == 'win32' or sys_platform == 'linux'" }, ] [[tool.uv.index]] name = "pytorch-xpu" url = "https://download.pytorch.org/whl/xpu" explicit = true ``` ## Configuring accelerators with optional dependencies In some cases, you may want to use CPU-only builds in some cases, but CUDA-enabled builds in others, with the choice toggled by a user-provided extra (e.g., `uv sync --extra cpu` vs. `uv sync --extra cu130`). With `tool.uv.sources`, you can use extra markers to specify the desired index for each enabled extra. For example, the following configuration would use PyTorch's CPU-only for `uv sync --extra cpu` and CUDA-enabled builds for `uv sync --extra cu130`: ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.14.0" dependencies = [] [project.optional-dependencies] cpu = [ "torch>=2.11.0", "torchvision>=0.26.0", ] cu130 = [ "torch>=2.11.0", "torchvision>=0.26.0", ] [tool.uv] conflicts = [ [ { extra = "cpu" }, { extra = "cu130" }, ], ] [tool.uv.sources] torch = [ { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu130", extra = "cu130" }, ] torchvision = [ { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu130", extra = "cu130" }, ] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true [[tool.uv.index]] name = "pytorch-cu130" url = "https://download.pytorch.org/whl/cu130" explicit = true ``` !!! note Since GPU-accelerated builds aren't available on macOS, the above configuration will fail to install on macOS when the `cu130` extra is enabled. ## The `uv pip` interface While the above examples are focused on uv's project interface (`uv lock`, `uv sync`, `uv run`, etc.), PyTorch can also be installed via the `uv pip` interface. PyTorch itself offers a [dedicated interface](https://pytorch.org/get-started/locally/) to determine the appropriate pip command to run for a given target configuration. For example, you can install stable, CPU-only PyTorch on Linux with: ```shell $ pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu ``` To use the same workflow with uv, replace `pip3` with `uv pip`: ```shell $ uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu ``` ## Automatic backend selection uv supports automatic selection of the appropriate PyTorch index via the `--torch-backend=auto` command-line argument (or the `UV_TORCH_BACKEND=auto` environment variable), as in: ```shell $ # With a command-line argument. $ uv pip install torch --torch-backend=auto $ # With an environment variable. $ UV_TORCH_BACKEND=auto uv pip install torch ``` When enabled, uv will query for the installed CUDA driver, AMD GPU versions, and Intel GPU presence, then use the most-compatible PyTorch index for all relevant packages (e.g., `torch`, `torchvision`, etc.). If no such GPU is found, uv will fall back to the CPU-only index. uv will continue to respect existing index configuration for any packages outside the PyTorch ecosystem. You can also select a specific backend (e.g., CUDA 13.0) with `--torch-backend=cu130` (or `UV_TORCH_BACKEND=cu130`): ```shell $ # With a command-line argument. $ uv pip install torch torchvision --torch-backend=cu130 $ # With an environment variable. $ UV_TORCH_BACKEND=cu130 uv pip install torch torchvision ``` At present, `--torch-backend` is only available in the `uv pip` interface. astral-sh-uv-9252ba6/docs/guides/integration/renovate.md000066400000000000000000000054541521221614700233050ustar00rootroot00000000000000--- title: Using uv with Renovate description: A guide to using uv with the Renovate dependency bot. --- # Renovate It is considered best practice to regularly update dependencies, to avoid being exposed to vulnerabilities, limit incompatibilities between dependencies, and avoid complex upgrades when upgrading from a too old version. uv is supported by [Renovate](https://github.com/renovatebot/renovate). ## `uv.lock` output Renovate uses the presence of a `uv.lock` file to determine that uv is used for managing dependencies, and will suggest upgrades to [project dependencies](../../concepts/projects/dependencies.md#project-dependencies), [optional dependencies](../../concepts/projects/dependencies.md#optional-dependencies) and [development dependencies](../../concepts/projects/dependencies.md#development-dependencies). Renovate will update both the `pyproject.toml` and `uv.lock` files. The lockfile can also be refreshed on a regular basis (for instance to update transitive dependencies) by enabling the [`lockFileMaintenance`](https://docs.renovatebot.com/configuration-options/#lockfilemaintenance) option: ```jsx title="renovate.json5" { $schema: "https://docs.renovatebot.com/renovate-schema.json", lockFileMaintenance: { enabled: true, }, } ``` ## Inline script metadata Renovate supports updating dependencies defined using [inline script metadata](../scripts.md/#declaring-script-dependencies). Since it cannot automatically detect which Python files use inline script metadata, their locations need to be explicitly defined using [`managerFilePatterns`](https://docs.renovatebot.com/configuration-options/#managerfilepatterns), like so: ```jsx title="renovate.json5" { $schema: "https://docs.renovatebot.com/renovate-schema.json", pep723: { managerFilePatterns: [ "docs/build.py", "scripts/**/*.py", ], }, } ``` !!! note Renovate does not yet support updating the lock file associated to the script (https://github.com/renovatebot/renovate/issues/33591), so if you rely on this feature for a script, the lock file will need to be manually updated. ## Dependency cooldown If you use [`exclude-newer`](../../reference/settings.md#exclude-newer) option, it is recommended to also set the equivalent [`minimumReleaseAge`](https://docs.renovatebot.com/configuration-options/#minimumreleaseage) option in Renovate, to avoid ending up with pull requests where uv would not be able to lock the dependencies. For instance, if you've set `exclude-newer` to `1 week`, you can set: ```jsx title="renovate.json5" { $schema: "https://docs.renovatebot.com/renovate-schema.json", // Enable only for PyPI. packageRules: [ { matchDatasources: ["pypi"], minimumReleaseAge: "1 week", }, ], // Or enable for every ecosystem. minimumReleaseAge: "1 week", } ``` astral-sh-uv-9252ba6/docs/guides/migration/000077500000000000000000000000001521221614700205765ustar00rootroot00000000000000astral-sh-uv-9252ba6/docs/guides/migration/index.md000066400000000000000000000006761521221614700222400ustar00rootroot00000000000000# Migration guides Learn how to migrate from other tools to uv: - [Migrate from pip to uv projects](./pip-to-project.md) !!! note Other guides, such as migrating from another project management tool, or from pip to `uv pip` are not yet available. See [#5200](https://github.com/astral-sh/uv/issues/5200) to track progress. Or, explore the [integration guides](../integration/index.md) to learn how to use uv with other software. astral-sh-uv-9252ba6/docs/guides/migration/pip-to-project.md000066400000000000000000000362541521221614700240060ustar00rootroot00000000000000# Migrating from pip to a uv project This guide will discuss converting from a `pip` and `pip-tools` workflow centered on `requirements` files to uv's project workflow using a `pyproject.toml` and `uv.lock` file. !!! note If you're looking to migrate from `pip` and `pip-tools` to uv's drop-in interface or from an existing workflow where you're already using a `pyproject.toml`, those guides are not yet written. See [#5200](https://github.com/astral-sh/uv/issues/5200) to track progress. We'll start with an overview of developing with `pip`, then discuss migrating to uv. !!! tip If you're familiar with the ecosystem, you can jump ahead to the [requirements file import](#importing-requirements-files) instructions. ## Understanding pip workflows ### Project dependencies When you want to use a package in your project, you need to install it first. `pip` supports imperative installation of packages, e.g.: ```console $ pip install fastapi ``` This installs the package into the environment that `pip` is installed in. This may be a virtual environment, or, the global environment of your system's Python installation. Then, you can run a Python script that requires the package: ```python title="example.py" import fastapi ``` It's best practice to create a virtual environment for each project, to avoid mixing packages between them. For example: ```console $ python -m venv $ source .venv/bin/activate $ pip ... ``` We will revisit this topic in the [project environments section](#project-environments) below. ### Requirements files When sharing projects with others, it's useful to declare all the packages you require upfront. `pip` supports installing requirements from a file, e.g.: ```python title="requirements.txt" fastapi ``` ```console $ pip install -r requirements.txt ``` Notice above that `fastapi` is not "locked" to a specific version — each person working on the project may have a different version of `fastapi` installed. `pip-tools` was created to improve this experience. When using `pip-tools`, requirements files specify both the dependencies for your project and lock dependencies to a specific version — the file extension is used to differentiate between the two. For example, if you require `fastapi` and `pydantic`, you'd specify these in a `requirements.in` file: ```python title="requirements.in" fastapi pydantic>2 ``` Notice there's a version constraint on `pydantic` — this means only `pydantic` versions later than `2.0.0` can be used. In contrast, `fastapi` does not have a version constraint — any version can be used. These dependencies can be compiled into a `requirements.txt` file: ```console $ pip-compile requirements.in -o requirements.txt ``` ```python title="requirements.txt" annotated-types==0.7.0 # via pydantic anyio==4.8.0 # via starlette fastapi==0.115.11 # via -r requirements.in idna==3.10 # via anyio pydantic==2.10.6 # via # -r requirements.in # fastapi pydantic-core==2.27.2 # via pydantic sniffio==1.3.1 # via anyio starlette==0.46.1 # via fastapi typing-extensions==4.12.2 # via # fastapi # pydantic # pydantic-core ``` Here, all the versions constraints are _exact_. Only a single version of each package can be used. The above example was generated with `uv pip compile`, but could also be generated with `pip-compile` from `pip-tools`. Though less common, the `requirements.txt` can also be generated using `pip freeze`, by first installing the input dependencies into the environment then exporting the installed versions: ```console $ pip install -r requirements.in $ pip freeze > requirements.txt ``` ```python title="requirements.txt" annotated-types==0.7.0 anyio==4.8.0 fastapi==0.115.11 idna==3.10 pydantic==2.10.6 pydantic-core==2.27.2 sniffio==1.3.1 starlette==0.46.1 typing-extensions==4.12.2 ``` After compiling dependencies into a locked set of versions, these files are committed to version control and distributed with the project. Then, when someone wants to use the project, they install from the requirements file: ```console $ pip install -r requirements.txt ``` ### Development dependencies The requirements file format can only describe a single set of dependencies at once. This means if you have additional _groups_ of dependencies, such as development dependencies, they need separate files. For example, we'll create a `-dev` dependency file: ```python title="requirements-dev.in" -r requirements.in -c requirements.txt pytest ``` Notice the base requirements are included with `-r requirements.in`. This ensures your development environment considers _all_ of the dependencies together. The `-c requirements.txt` _constrains_ the package version to ensure that the `requirements-dev.txt` uses the same versions as `requirements.txt`. !!! note It's common to use `-r requirements.txt` directly instead of using both `-r requirements.in`, and `-c requirements.txt`. There's no difference in the resulting package versions, but using both files produces annotations which allow you to determine which dependencies are _direct_ (annotated with `-r requirements.in`) and which are _indirect_ (only annotated with `-c requirements.txt`). The compiled development dependencies look like: ```python title="requirements-dev.txt" annotated-types==0.7.0 # via # -c requirements.txt # pydantic anyio==4.8.0 # via # -c requirements.txt # starlette fastapi==0.115.11 # via # -c requirements.txt # -r requirements.in idna==3.10 # via # -c requirements.txt # anyio iniconfig==2.0.0 # via pytest packaging==24.2 # via pytest pluggy==1.5.0 # via pytest pydantic==2.10.6 # via # -c requirements.txt # -r requirements.in # fastapi pydantic-core==2.27.2 # via # -c requirements.txt # pydantic pytest==8.3.5 # via -r requirements-dev.in sniffio==1.3.1 # via # -c requirements.txt # anyio starlette==0.46.1 # via # -c requirements.txt # fastapi typing-extensions==4.12.2 # via # -c requirements.txt # fastapi # pydantic # pydantic-core ``` As with the base dependency files, these are committed to version control and distributed with the project. When someone wants to work on the project, they'll install from the requirements file: ```console $ pip install -r requirements-dev.txt ``` ### Platform-specific dependencies When compiling dependencies with `pip` or `pip-tools`, the result is only usable on the same platform as it is generated on. This poses a problem for projects which need to be usable on multiple platforms, such as Windows and macOS. For example, take a simple dependency: ```python title="requirements.in" tqdm ``` On Linux, this compiles to: ```python title="requirements-linux.txt" tqdm==4.67.1 # via -r requirements.in ``` While on Windows, this compiles to: ```python title="requirements-win.txt" colorama==0.4.6 # via tqdm tqdm==4.67.1 # via -r requirements.in ``` `colorama` is a Windows-only dependency of `tqdm`. When using `pip` and `pip-tools`, a project needs to declare a requirements lock file for each supported platform. !!! note uv's resolver can compile dependencies for multiple platforms at once (see ["universal resolution"](../../concepts/resolution.md#universal-resolution)), allowing you to use a single `requirements.txt` for all platforms: ```console $ uv pip compile --universal requirements.in ``` ```python title="requirements.txt" colorama==0.4.6 ; sys_platform == 'win32' # via tqdm tqdm==4.67.1 # via -r requirements.in ``` This resolution mode is also used when using a `pyproject.toml` and `uv.lock`. ## Migrating to a uv project ### The `pyproject.toml` The `pyproject.toml` is a standardized file for Python project metadata. It replaces `requirements.in` files, allowing you to represent arbitrary groups of project dependencies. It also provides a centralized location for metadata about your project, such as the build system or tool settings. For example, the `requirements.in` and `requirements-dev.in` files above can be translated to a `pyproject.toml` as follows: ```toml title="pyproject.toml" [project] name = "example" version = "0.0.1" dependencies = [ "fastapi", "pydantic>2" ] [dependency-groups] dev = ["pytest"] ``` We'll discuss the commands necessary to automate these imports below. ### The uv lockfile uv uses a lockfile (`uv.lock`) file to lock package versions. The format of this file is specific to uv, allowing uv to support advanced features. It replaces `requirements.txt` files. The lockfile will be automatically created and populated when adding dependencies, but you can explicitly create it with `uv lock`. Unlike `requirements.txt` files, the `uv.lock` file can represent arbitrary groups of dependencies, so multiple files are not needed to lock development dependencies. The uv lockfile is always [universal](../../concepts/resolution.md#universal-resolution), so multiple files are not needed to [lock dependencies for each platform](#platform-specific-dependencies). This ensures that all developers are using consistent, locked versions of dependencies regardless of their machine. The uv lockfile also supports concepts like [pinning packages to specific indexes](../../concepts/indexes.md#pinning-a-package-to-an-index), which is not representable in `requirements.txt` files. !!! tip If you only need to lock for a subset of platforms, use the [`tool.uv.environments`](../../concepts/resolution.md#limited-resolution-environments) setting to limit the resolution and lockfile. To learn more, see the [lockfile](../../concepts/projects/layout.md#the-lockfile) documentation. ### Importing requirements files First, create a `pyproject.toml` if you have not already: ```console $ uv init ``` Then, the easiest way to import requirements is with `uv add`: ```console $ uv add -r requirements.in ``` However, there is some nuance to this transition. Notice we used the `requirements.in` file, which does not pin to exact versions of packages so uv will solve for new versions of these packages. You may want to continue using your previously locked versions from your `requirements.txt` so, when switching over to uv, none of your dependency versions change. The solution is to add your locked versions as _constraints_. uv supports using these on `add` to preserve locked versions: ```console $ uv add -r requirements.in -c requirements.txt ``` Your existing versions will be retained when producing a `uv.lock` file. #### Importing platform-specific constraints If your platform-specific dependencies have been compiled into separate files, you can still transition to a universal lockfile. However, you cannot just use `-c` to specify constraints from your existing platform-specific `requirements.txt` files because they do not include markers describing the environment and will consequently conflict. To add the necessary markers, use `uv pip compile` to convert your existing files. For example, given the following: ```python title="requirements-win.txt" colorama==0.4.6 # via tqdm tqdm==4.67.1 # via -r requirements.in ``` The markers can be added with: ```console $ uv pip compile requirements.in -o requirements-win.txt --python-platform windows --no-strip-markers ``` Notice the resulting output includes a Windows marker on `colorama`: ```python title="requirements-win.txt" colorama==0.4.6 ; sys_platform == 'win32' # via tqdm tqdm==4.67.1 # via -r requirements.in ``` When using `-o`, uv will constrain the versions to match the existing output file, if it can. Markers can be added for other platforms by changing the `--python-platform` and `-o` values for each requirements file you need to import, e.g., to `linux` and `macos`. Once each `requirements.txt` file has been transformed, the dependencies can be imported to the `pyproject.toml` and `uv.lock` with `uv add`: ```console $ uv add -r requirements.in -c requirements-win.txt -c requirements-linux.txt ``` #### Importing development dependency files As discussed in the [development dependencies](#development-dependencies) section, it's common to have groups of dependencies for development purposes. To import development dependencies, use the `--dev` flag during `uv add`: ```console $ uv add --dev -r requirements-dev.in -c requirements-dev.txt ``` If the `requirements-dev.in` includes the parent `requirements.in` via `-r`, it will need to be stripped to avoid adding the base requirements to the `dev` dependency group. The following example uses `sed` to strip lines that start with `-r`, then pipes the result to `uv add`: ```console $ sed '/^-r /d' requirements-dev.in | uv add --dev -r - -c requirements-dev.txt ``` In addition to the `dev` dependency group, uv supports arbitrary group names. For example, if you also have a dedicated set of dependencies for building your documentation, those can be imported to a `docs` group: ```console $ uv add -r requirements-docs.in -c requirements-docs.txt --group docs ``` #### Importing dependency sources When importing requirements on local paths or Git repositories, for example: ```python title="requirements.in" ./path-dep -e ./editable-path-dep git-dep @ git+https://github.com/astral-sh/git-dep ``` uv will map them to [dependency sources](../../concepts/projects/dependencies.md#dependency-sources) in the `[tool.uv.sources]` table of the `pyproject.toml`: ```toml title="pyproject.toml" [project] dependencies = [ "path-dep", "editable-path-dep", "git-dep", ] [tool.uv.sources] path-dep = { path = "./path-dep" } editable-path-dep = { path = "./editable-path-dep", editable = true } git-dep = { git = "https://github.com/astral-sh/git-dep" } ``` ### Project environments Unlike `pip`, uv is not centered around the concept of an "active" virtual environment. Instead, uv uses a dedicated virtual environment for each project in a `.venv` directory. This environment is automatically managed, so when you run a command, like `uv add`, the environment is synced with the project dependencies. The preferred way to execute commands in the environment is with `uv run`, e.g.: ```console $ uv run pytest ``` Prior to every `uv run` invocation, uv will verify that the lockfile is up-to-date with the `pyproject.toml`, and that the environment is up-to-date with the lockfile, keeping your project in-sync without the need for manual intervention. `uv run` guarantees that your command is run in a consistent, locked environment. The project environment can also be explicitly created with `uv sync`, e.g., for use with editors. !!! note When in projects, uv will prefer a `.venv` in the project directory and ignore the active environment as declared by the `VIRTUAL_ENV` variable by default. You can opt-in to using the active environment with the `--active` flag. To learn more, see the [project environment](../../concepts/projects/layout.md#the-project-environment) documentation. ## Next steps Now that you've migrated to uv, take a look at the [project concept](../../concepts/projects/index.md) page for more details about uv projects. astral-sh-uv-9252ba6/docs/guides/package.md000066400000000000000000000166011521221614700205260ustar00rootroot00000000000000--- title: Building and publishing a package description: A guide to using uv to build and publish Python packages to a package index, like PyPI. --- # Building and publishing a package uv supports building Python packages into source and binary distributions via `uv build` and uploading them to a registry with `uv publish`. ## Preparing your project Before attempting to publish your project, you'll want to make sure it's ready to be packaged for distribution. If your project does not include a `[build-system]` definition in the `pyproject.toml`, uv will not build it during `uv sync` operations in the project, but will fall back to the legacy setuptools build system during `uv build`. We strongly recommend configuring a build system. Read more about build systems in the [project configuration](../concepts/projects/config.md#build-systems) documentation. ## Building your package Build your package with `uv build`: ```console $ uv build ``` By default, `uv build` will build the project in the current directory, and place the built artifacts in a `dist/` subdirectory. Alternatively, `uv build ` will build the package in the specified directory, while `uv build --package ` will build the specified package within the current workspace. !!! info By default, `uv build` respects `tool.uv.sources` when resolving build dependencies from the `build-system.requires` section of the `pyproject.toml`. When publishing a package, we recommend running `uv build --no-sources` to ensure that the package builds correctly when `tool.uv.sources` is disabled, as is the case when using other build tools, like [`pypa/build`](https://github.com/pypa/build). ## Updating your version The `uv version` command provides conveniences for updating the version of your package before you publish it. [See the project docs for reading your package's version](./projects.md#viewing-your-version). To update to an exact version, provide it as a positional argument: ```console $ uv version 1.0.0 hello-world 0.7.0 => 1.0.0 ``` To preview the change without updating the `pyproject.toml`, use the `--dry-run` flag: ```console $ uv version 2.0.0 --dry-run hello-world 1.0.0 => 2.0.0 $ uv version hello-world 1.0.0 ``` To increase the version of your package semantics, use the `--bump` option: ```console $ uv version --bump minor hello-world 1.2.3 => 1.3.0 ``` The `--bump` option supports the following common version components: `major`, `minor`, `patch`, `stable`, `alpha`, `beta`, `rc`, `post`, and `dev`. When provided more than once, the components will be applied in order, from largest (`major`) to smallest (`dev`). You can optionally provide a numeric value with `--bump =` to set the resulting component explicitly: ```console $ uv version --bump patch --bump dev=66463664 hello-world 0.0.1 => 0.0.2.dev66463664 ``` To move from a stable to pre-release version, bump one of the major, minor, or patch components in addition to the pre-release component: ```console $ uv version --bump patch --bump beta hello-world 1.3.0 => 1.3.1b1 $ uv version --bump major --bump alpha hello-world 1.3.0 => 2.0.0a1 ``` When moving from a pre-release to a new pre-release version, just bump the relevant pre-release component: ```console $ uv version --bump beta hello-world 1.3.0b1 => 1.3.0b2 ``` When moving from a pre-release to a stable version, the `stable` option can be used to clear the pre-release component: ```console $ uv version --bump stable hello-world 1.3.1b2 => 1.3.1 ``` !!! info By default, when `uv version` modifies the project it will perform a lock and sync. To prevent locking and syncing, use `--frozen`, or, to just prevent syncing, use `--no-sync`. ## Publishing your package !!! note A complete guide to publishing from GitHub Actions to PyPI can be found in the [GitHub Guide](integration/github.md#publishing-to-pypi) Publish your package with `uv publish`: ```console $ uv publish ``` Set a PyPI token with `--token` or `UV_PUBLISH_TOKEN`, or set a username with `--username` or `UV_PUBLISH_USERNAME` and password with `--password` or `UV_PUBLISH_PASSWORD`. For publishing to PyPI from GitHub Actions or another Trusted Publisher, you don't need to set any credentials. Instead, [add a trusted publisher to the PyPI project](https://docs.pypi.org/trusted-publishers/adding-a-publisher/). !!! note PyPI does not support publishing with username and password anymore, instead you need to generate a token. Using a token is equivalent to setting `--username __token__` and using the token as password. If you're using a custom index through `[[tool.uv.index]]`, add `publish-url` and use `uv publish --index `. For example: ```toml [[tool.uv.index]] name = "testpypi" url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true ``` !!! note When using `uv publish --index `, the `pyproject.toml` must be present, i.e., you need to have a checkout step in a publish CI job. Even though `uv publish` retries failed uploads, it can happen that publishing fails in the middle, with some files uploaded and some files still missing. With PyPI, you can retry the exact same command, existing identical files will be ignored. With other registries, use `--check-url ` with the index URL (not the publishing URL) the packages belong to. When using `--index`, the index URL is used as check URL. uv will skip uploading files that are identical to files in the registry, and it will also handle raced parallel uploads. Note that existing files need to match exactly with those previously uploaded to the registry, this avoids accidentally publishing source distribution and wheels with different contents for the same version. ### Uploading attestations with your package !!! note Some third-party package indexes may not support attestations, and may reject uploads that include them (rather than silently ignoring them). If you encounter issues when uploading, you can use `--no-attestations` or `UV_PUBLISH_NO_ATTESTATIONS` to disable uv's default behavior. !!! tip `uv publish` does not currently generate attestations; attestations must be created separately before publishing. `uv publish` supports uploading [attestations](https://peps.python.org/pep-0740/) to registries that support them, like PyPI. uv will automatically discover and match attestations. For example, given the following `dist/` directory, `uv publish` will upload the attestations along with their corresponding distributions: ```console $ ls dist/ hello_world-1.0.0-py3-none-any.whl hello_world-1.0.0-py3-none-any.whl.publish.attestation hello_world-1.0.0.tar.gz hello_world-1.0.0.tar.gz.publish.attestation ``` ## Installing your package Test that the package can be installed and imported with `uv run`: ```console $ uv run --with --no-project -- python -c "import " ``` The `--no-project` flag is used to avoid installing the package from your local project directory. !!! tip If you have recently installed the package, you may need to include the `--refresh-package ` option to avoid using a cached version of the package. ## Next steps To learn more about publishing packages, check out the [PyPA guides](https://packaging.python.org/en/latest/guides/section-build-and-publish/) on building and publishing. Or, read on for [guides](./integration/index.md) on integrating uv with other software. astral-sh-uv-9252ba6/docs/guides/projects.md000066400000000000000000000170541521221614700207670ustar00rootroot00000000000000--- title: Working on projects description: A guide to using uv to create and manage Python projects, including adding dependencies, running commands, and building publishable distributions. --- # Working on projects uv supports managing Python projects, which define their dependencies in a `pyproject.toml` file. ## Creating a new project You can create a new Python project using the `uv init` command: ```console $ uv init hello-world $ cd hello-world ``` Alternatively, you can initialize a project in the working directory: ```console $ mkdir hello-world $ cd hello-world $ uv init ``` uv will create the following files and directories: ```text ├── .git/ ├── .gitignore ├── .python-version ├── README.md ├── main.py └── pyproject.toml ``` The `main.py` file contains a simple "Hello world" program. Try it out with `uv run`: ```console $ uv run main.py Hello from hello-world! ``` ## Project structure A project consists of a few important parts that work together and allow uv to manage your project. In addition to the files created by `uv init`, uv will create a virtual environment and `uv.lock` file in the root of your project the first time you run a project command, i.e., `uv run`, `uv sync`, or `uv lock`. A complete listing would look like: ```text . ├── .git/ ├── .venv/ │   ├── bin │   ├── lib │   └── pyvenv.cfg ├── .gitignore ├── .python-version ├── README.md ├── main.py ├── pyproject.toml └── uv.lock ``` ### `pyproject.toml` The `pyproject.toml` contains metadata about your project: ```toml title="pyproject.toml" [project] name = "hello-world" version = "0.1.0" description = "Add your description here" readme = "README.md" dependencies = [] ``` You'll use this file to specify dependencies, as well as details about the project such as its description or license. You can edit this file manually, or use commands like `uv add` and `uv remove` to manage your project from the terminal. !!! tip See the official [`pyproject.toml` guide](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/) for more details on getting started with the `pyproject.toml` format. You'll also use this file to specify uv [configuration options](../concepts/configuration-files.md) in a [`[tool.uv]`](../reference/settings.md) section. ### `.python-version` The `.python-version` file contains the project's default Python version. This file tells uv which Python version to use when creating the project's virtual environment. ### `.venv` The `.venv` folder contains your project's virtual environment, a Python environment that is isolated from the rest of your system. This is where uv will install your project's dependencies. See the [project environment](../concepts/projects/layout.md#the-project-environment) documentation for more details. ### `uv.lock` `uv.lock` is a cross-platform lockfile that contains exact information about your project's dependencies. Unlike the `pyproject.toml` which is used to specify the broad requirements of your project, the lockfile contains the exact resolved versions that are installed in the project environment. This file should be checked into version control, allowing for consistent and reproducible installations across machines. `uv.lock` is a human-readable TOML file but is managed by uv and should not be edited manually. See the [lockfile](../concepts/projects/layout.md#the-lockfile) documentation for more details. ## Managing dependencies You can add dependencies to your `pyproject.toml` with the `uv add` command. This will also update the lockfile and project environment: ```console $ uv add requests ``` You can also specify version constraints or alternative sources: ```console $ # Specify a version constraint $ uv add 'requests==2.31.0' $ # Add a git dependency $ uv add git+https://github.com/psf/requests ``` If you're migrating from a `requirements.txt` file, you can use `uv add` with the `-r` flag to add all dependencies from the file: ```console $ # Add all dependencies from `requirements.txt`. $ uv add -r requirements.txt -c constraints.txt ``` To remove a package, you can use `uv remove`: ```console $ uv remove requests ``` To upgrade a package, run `uv lock` with the `--upgrade-package` flag: ```console $ uv lock --upgrade-package requests ``` The `--upgrade-package` flag will attempt to update the specified package to the latest compatible version, while keeping the rest of the lockfile intact. See the documentation on [managing dependencies](../concepts/projects/dependencies.md) for more details. ## Viewing your version The `uv version` command can be used to read your package's version. To get the version of your package, run `uv version`: ```console $ uv version hello-world 0.7.0 ``` To get the version without the package name, use the `--short` option: ```console $ uv version --short 0.7.0 ``` To get version information in a JSON format, use the `--output-format json` option: ```console $ uv version --output-format json { "package_name": "hello-world", "version": "0.7.0", "commit_info": null } ``` See the [publishing guide](./package.md#updating-your-version) for details on updating your package version. ## Running commands `uv run` can be used to run arbitrary scripts or commands in your project environment. Prior to every `uv run` invocation, uv will verify that the lockfile is up-to-date with the `pyproject.toml`, and that the environment is up-to-date with the lockfile, keeping your project in-sync without the need for manual intervention. `uv run` guarantees that your command is run in an environment with all required dependencies at their locked versions. !!! note `uv run` does not remove extraneous packages (those not in the lockfile) from the environment by default. See [handling of extraneous packages](../concepts/projects/sync.md#handling-of-extraneous-packages) for details. For example, to use `flask`: ```console $ uv add flask $ uv run -- flask run -p 3000 ``` Or, to run a script: ```python title="example.py" # Require a project dependency import flask print("hello world") ``` ```console $ uv run example.py ``` Alternatively, you can use `uv sync` to manually update the environment then activate it before executing a command: === "macOS and Linux" ```console $ uv sync $ source .venv/bin/activate $ flask run -p 3000 $ python example.py ``` === "Windows" ```pwsh-session PS> uv sync PS> .venv\Scripts\activate PS> flask run -p 3000 PS> python example.py ``` !!! note The virtual environment must be active to run scripts and commands in the project without `uv run`. Virtual environment activation differs per shell and platform. See the documentation on [running commands and scripts](../concepts/projects/run.md) in projects for more details. ## Building distributions `uv build` can be used to build source distributions and binary distributions (wheel) for your project. By default, `uv build` will build the project in the current directory, and place the built artifacts in a `dist/` subdirectory: ```console $ uv build $ ls dist/ hello-world-0.1.0-py3-none-any.whl hello-world-0.1.0.tar.gz ``` See the documentation on [building projects](../concepts/projects/build.md) for more details. ## Next steps To learn more about working on projects with uv, see the [projects concept](../concepts/projects/index.md) page and the [command reference](../reference/cli.md#uv). Or, read on to learn how to [export a uv lockfile to different formats](../concepts/projects/export.md). astral-sh-uv-9252ba6/docs/guides/scripts.md000066400000000000000000000252611521221614700206240ustar00rootroot00000000000000--- title: Running scripts description: A guide to using uv to run Python scripts, including support for inline dependency metadata, reproducible scripts, and more. --- # Running scripts A Python script is a file intended for standalone execution, e.g., with `python