diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..5657a8583 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [indygreg] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..25dee2ba3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "monthly" + cooldown: + default-days: 14 + groups: + rust: + patterns: + - "*" + - package-ecosystem: "github-actions" + directory: ".github/workflows" + schedule: + interval: "monthly" + cooldown: + default-days: 14 + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/apple.yml b/.github/workflows/apple.yml deleted file mode 100644 index 73a68ff65..000000000 --- a/.github/workflows/apple.yml +++ /dev/null @@ -1,288 +0,0 @@ -on: - push: - pull_request: - schedule: - - cron: '13 15 * * *' -jobs: - # We need to build our own sccache until e6326bc8a20ee06af37e16a3a7a14e3374c66c66 - # is in a released version. - sccache: - runs-on: 'macos-10.15' - steps: - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - default: true - profile: minimal - - - uses: actions/checkout@v2 - with: - repository: mozilla/sccache - # Update cache key if this changes. - ref: e6326bc8a20ee06af37e16a3a7a14e3374c66c66 - fetch-depth: 0 - - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-sccache-${{ hashFiles('Cargo.lock') }}-0 - - - name: Build sccache - run: | - cargo build --release - - - name: Upload sccache executable - uses: actions/upload-artifact@v2 - with: - name: sccache - path: target/release/sccache - - pythonbuild: - runs-on: 'macos-10.15' - steps: - - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - default: true - profile: minimal - - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-pythonbuild-${{ hashFiles('Cargo.lock') }} - - - name: Build - run: | - cargo build --release - - - name: Upload pythonbuild Executable - uses: actions/upload-artifact@v2 - with: - name: pythonbuild - path: target/release/pythonbuild - - toolchain: - needs: - - sccache - runs-on: 'macos-10.15' - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SCCACHE_BUCKET: 'python-build-standalone-sccache' - SCCACHE_S3_USE_SSL: '1' - SCCACHE_IDLE_TIMEOUT: '0' - steps: - - uses: actions/checkout@v2 - - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - - name: Download sccache - uses: actions/download-artifact@v2 - with: - name: sccache - - - name: Start sccache - run: | - chmod +x sccache - ./sccache --start-server - - - name: Build - run: | - python3.9 ./build-macos.py --make-target toolchain - - - name: Stop sccache - run: | - ./sccache -s - - - name: Upload Toolchain Archive - uses: actions/upload-artifact@v2 - with: - name: toolchain - path: build/clang-*.tar - - build: - strategy: - fail-fast: false - matrix: - build: - # macOS on Apple hardware. Can't do PGO because GitHub Apple hardware - # is Intel. Can't do Python 3.8 because we don't support cross-compiling - # until 3.9. - - target_triple: 'aarch64-apple-darwin' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'aarch64-apple-darwin' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'aarch64-apple-darwin' - py: 'cpython-3.9' - optimizations: 'lto' - - # macOS on Intel hardware. This is pretty straightforward. We exclude - # noopt because it doesn't provide any compelling advantages over PGO - # or LTO builds. - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.8' - optimizations: 'debug' - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.8' - optimizations: 'lto' - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.8' - optimizations: 'pgo' - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.8' - optimizations: 'pgo+lto' - - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.9' - optimizations: 'lto' - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.9' - optimizations: 'pgo' - - target_triple: 'x86_64-apple-darwin' - py: 'cpython-3.9' - optimizations: 'pgo+lto' - - # iOS targeting mobile hardware. Can't do PGO during cross-compiles. - # Can't cross-compile until Python 3.9. - - target_triple: 'aarch64-apple-ios' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'aarch64-apple-ios' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'aarch64-apple-ios' - py: 'cpython-3.9' - optimizations: 'lto' - - # iOS targeting simulator. Can't do PGO during cross-compiles. - # Can't cross-compile until Python 3.9. - - target_triple: 'x86_64-apple-ios' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'x86_64-apple-ios' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'x86_64-apple-ios' - py: 'cpython-3.9' - optimizations: 'lto' - needs: - - pythonbuild - - toolchain - runs-on: 'macos-10.15' - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - - name: Download pythonbuild - uses: actions/download-artifact@v2 - with: - name: pythonbuild - path: build - - - name: Download toolchain - uses: actions/download-artifact@v2 - with: - name: toolchain - path: build - - - name: Build - run: | - # Force use of 10.15 SDK on Python 3.8, as it has trouble with macOS 11 SDK. - # - # There is a bug in the 10.15 SDK in Xcode 11.4.1 through at least 12.1.1 where - # the __darwin_check_fd_set_overflow symbol isn't properly API guarded. This - # can cause relinking problems. See - # https://github.com/indygreg/PyOxidizer/issues/373#issuecomment-791979944 for - # the details. - # - # We work around this by using the 10.15 SDK from Xcode 11.3.1, which doesn't - # have the bug. - if [ "${{ matrix.build.py }}" = "cpython-3.8" ]; then - export APPLE_SDK_PATH=/Applications/Xcode_11.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk - export APPLE_HOST_SDK_PATH=/Applications/Xcode_11.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk - else - export APPLE_HOST_SDK_PATH=/Applications/Xcode_12.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk - - if [ "${{ matrix.build.target_triple }}" = "aarch64-apple-darwin" ]; then - export APPLE_SDK_PATH=/Applications/Xcode_12.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk - elif [ "${{ matrix.build.target_triple }}" = "aarch64-apple-ios" ]; then - export APPLE_SDK_PATH=/Applications/Xcode_12.4.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.4.sdk - elif [ "${{ matrix.build.target_triple }}" = "x86_64-apple-darwin" ]; then - export APPLE_SDK_PATH=/Applications/Xcode_12.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk - elif [ "${{ matrix.build.target_triple }}" = "x86_64-apple-ios" ]; then - export APPLE_SDK_PATH=/Applications/Xcode_12.4.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.4.sdk - else - echo "unhandled target triple: ${{ matrix.build.target_triple }}" - exit 1 - fi - fi - - ./build-macos.py --skip-toolchain --target-triple ${{ matrix.build.target_triple }} --python ${{ matrix.build.py }} --optimizations ${{ matrix.build.optimizations }} - - - name: Validate Distribution - run: | - chmod +x build/pythonbuild - - if [ "${{matrix.build.target_triple }}" = "x86_64-apple-darwin" ]; then - EXTRA_ARGS="--run" - fi - - build/pythonbuild validate-distribution ${EXTRA_ARGS} dist/*.tar.zst - - - name: Upload Distributions - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.build.py }}-${{ matrix.build.target_triple }} - path: dist/* - - install-only: - needs: - - build - runs-on: 'ubuntu-20.04' - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - - name: Download Python - uses: actions/download-artifact@v2 - with: - name: cpython-3.9-x86_64-apple-darwin - - - name: Repack Distribution - run: | - ./prune-distribution.py cpython-*-pgo+lto-* - - - name: Upload Distribution - uses: actions/upload-artifact@v2 - with: - name: cpython-install-only - path: cpython-*.tar.gz diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..f8c41f550 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,40 @@ +name: check + +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + FORCE_COLOR: 1 + +permissions: {} + +jobs: + check: + runs-on: "ubuntu-latest" + name: "check" + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + enable-cache: false + + - name: "Run checks" + run: | + ./check.py + + - name: "Run unit tests" + run: | + uv run python -m unittest tests/test_mirror.py + + - name: "Run mirror integration test" + run: | + uv run python -m unittest tests/test_mirror_integration.py diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2ea535bc0..2e934c5fd 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,347 +1,352 @@ +name: linux + on: push: + branches: [ main ] pull_request: - schedule: - - cron: '13 15 * * *' -jobs: - # We need to build our own sccache until e6326bc8a20ee06af37e16a3a7a14e3374c66c66 - # is in a released version. - sccache: - runs-on: 'ubuntu-20.04' - steps: - - name: Install System Dependencies - run: | - sudo apt update - sudo apt install -y --no-install-recommends musl-tools - - uses: actions-rs/toolchain@v1 - id: install-rust - with: - toolchain: stable - target: x86_64-unknown-linux-musl - default: true - profile: minimal +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} - - uses: actions/checkout@v2 - with: - repository: indygreg/sccache - ref: b0eae3f0d174b81e7582159cb9329dbea0af25c9 - fetch-depth: 0 +env: + FORCE_COLOR: 1 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-20.04-${{ steps.install-rust.outputs.rustc}}-sccache-${{ hashFiles('Cargo.lock') }} +permissions: { } - - name: Build sccache - run: | - cargo build --release --target x86_64-unknown-linux-musl - - - name: Upload sccache executable - uses: actions/upload-artifact@v2 - with: - name: sccache - path: target/x86_64-unknown-linux-musl/release/sccache - - pythonbuild: - runs-on: 'ubuntu-20.04' +jobs: + crate-build: + needs: + - generate-matrix + runs-on: ${{ matrix.runner }} + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.crate-build-matrix) }} + fail-fast: false + name: crate / ${{ matrix.arch }} steps: - - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - toolchain: stable - default: true - profile: minimal + persist-credentials: false - - uses: actions/cache@v2 + - name: Emit rustc version + run: | + rustc --version > .rustc-version + + - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: | ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-pythonbuild-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-pythonbuild-${{ hashFiles('Cargo.lock', '.rustc-version') }} - name: Build run: | cargo build --release - name: Upload pythonbuild Executable - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: pythonbuild + name: ${{ matrix.crate_artifact_name }} path: target/release/pythonbuild - toolchain: + image: + if: ${{ needs.generate-matrix.outputs.any_builds == 'true' }} needs: - - sccache - runs-on: 'ubuntu-20.04' - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SCCACHE_BUCKET: 'python-build-standalone-sccache' - SCCACHE_S3_USE_SSL: '1' - SCCACHE_IDLE_TIMEOUT: '0' + - generate-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.generate-matrix.outputs.docker-build-matrix) }} + name: image / ${{ matrix.arch }} / ${{ matrix.name }} + runs-on: ${{ matrix.runner }} + permissions: + packages: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - - name: Install Python - uses: actions/setup-python@v2 + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 with: - python-version: '3.9' + enable-cache: false + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - name: Download sccache - uses: actions/download-artifact@v2 + - name: Login to GitHub Container Registry + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: - name: sccache + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Start sccache + - name: Generate Dockerfiles run: | - chmod +x sccache - ./sccache --start-server - - - name: Build + ./build.py --make-target empty + repo_name=$(echo "${GITHUB_REPOSITORY,,}" | sed 's|\.|_|g') + git_ref_name=$(echo "${GITHUB_REF_NAME,,}" | sed 's|[^a-z0-9_-]|_|g') + echo "REPO_NAME=${repo_name}" >> "${GITHUB_ENV}" + echo "GIT_REF_NAME=${git_ref_name}" >> "${GITHUB_ENV}" + + - name: Build Image + id: build-image + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 + env: + SOURCE_DATE_EPOCH: 0 + DOCKER_BUILD_SUMMARY: false + DOCKER_BUILD_RECORD_UPLOAD: false + with: + context: . + file: build/${{ matrix.name }}.Dockerfile + labels: org.opencontainers.image.source=https://github.com/${{ env.REPO_NAME }} + # Cache from/to the current branch of the current repo as the primary cache key. + # Cache from the default branch of the current repo so branches can have cache hits. + # Cache from the default branch of the canonical repo so forks can have cache hits. + # Ignore errors on cache writes so CI of forks works without a valid GHCR config. + cache-from: | + type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.name }}-linux_${{ matrix.arch }}-${{ env.GIT_REF_NAME }} + type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.name }}-linux_${{ matrix.arch }}-main + type=registry,ref=ghcr.io/astral-sh/python-build-standalone:${{ matrix.name }}-linux_${{ matrix.arch }}-main + cache-to: | + type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:${{ matrix.name }}-linux_${{ matrix.arch }}-${{ env.GIT_REF_NAME }},ignore-error=true + outputs: | + type=docker,dest=build/image-${{ matrix.name }}.linux_${{ matrix.arch }}.tar + + - name: Compress Image run: | - python3.9 ./build-linux.py --make-target toolchain + echo ${STEPS_BUILD_IMAGE_OUTPUTS_IMAGEID} > build/image-${MATRIX_NAME}.linux_${MATRIX_ARCH} + zstd -v -T0 -6 --rm build/image-*.tar + touch -t 197001010000 build/image-* + env: + STEPS_BUILD_IMAGE_OUTPUTS_IMAGEID: ${{ steps.build-image.outputs.imageid }} + MATRIX_NAME: ${{ matrix.name }} + MATRIX_ARCH: ${{ matrix.arch }} + + - name: Upload Docker Image + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: image-${{ matrix.name }}-linux_${{ matrix.arch }} + path: build/image-* + compression-level: '0' + + generate-matrix: + name: Generate build matrix + runs-on: ubuntu-latest + outputs: + python-build-matrix-0: ${{ steps.set-matrix.outputs.python-build-matrix-0 }} + python-build-matrix-1: ${{ steps.set-matrix.outputs.python-build-matrix-1 }} + docker-build-matrix: ${{ steps.set-matrix.outputs.docker-build-matrix }} + crate-build-matrix: ${{ steps.set-matrix.outputs.crate-build-matrix }} + any_builds: ${{ steps.set-matrix.outputs.any_builds }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false - - name: Stop sccache + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + enable-cache: false + + - name: Get pull request labels + id: get-labels + env: + PULL_REQUEST_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} run: | - ./sccache -s + # Convert GitHub labels array to comma-separated string + LABELS=$(echo "${PULL_REQUEST_LABELS}" | jq -r 'join(",")') + echo "labels=$LABELS" >> $GITHUB_OUTPUT + + - name: Check if the `pythonbuild` crate changed + id: check-pythonbuild + env: + BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }} + run: | + merge_base=$(git merge-base HEAD "origin/${BASE_REF}") + if git diff --quiet "${merge_base}...HEAD" -- ':src/*.rs'; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi - - name: Upload Toolchain Archive - uses: actions/upload-artifact@v2 - with: - name: toolchain - path: build/*.tar + - name: Generate build matrix + id: set-matrix + run: | + uv run ci-matrix.py \ + --platform linux \ + --labels "${STEPS_GET_LABELS_OUTPUTS_LABELS}" \ + --max-shards 2 \ + ${{ (steps.check-pythonbuild.outputs.changed == 'true' || github.ref == 'refs/heads/main') && '--force-crate-build' || '' }} \ + > matrix.json + + echo "python-build-matrix-0=$(jq -c '."python-build"["0"]' matrix.json)" >> $GITHUB_OUTPUT + echo "python-build-matrix-1=$(jq -c '."python-build"["1"]' matrix.json)" >> $GITHUB_OUTPUT + echo "docker-build-matrix=$(jq -c '."docker-build"' matrix.json)" >> $GITHUB_OUTPUT + echo "crate-build-matrix=$(jq -c '."crate-build"' matrix.json)" >> $GITHUB_OUTPUT + + # Display the matrix for debugging too + cat matrix.json | jq + + if jq -e '."python-build"["0"].include | length > 0' matrix.json > /dev/null; then + # Build matrix has entries + echo "any_builds=true" >> $GITHUB_OUTPUT + else + # Build matrix is empty + echo "any_builds=false" >> $GITHUB_OUTPUT + fi + env: + STEPS_GET_LABELS_OUTPUTS_LABELS: ${{ steps.get-labels.outputs.labels }} - build: + build-0: + needs: + - generate-matrix + - crate-build + - image + # Permissions used for actions/attest-build-provenance + permissions: + id-token: write + attestations: write + runs-on: ${{ matrix.runner }} strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.python-build-matrix-0) }} fail-fast: false - matrix: - build: - # Cross-compiles can't do PGO and require Python 3.9. - - target_triple: 'aarch64-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'aarch64-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'aarch64-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'lto' - - # Cross-compiles can't do PGO and require Python 3.9. - - target_triple: 'armv7-unknown-linux-gnueabi' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'armv7-unknown-linux-gnueabi' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'armv7-unknown-linux-gnueabi' - py: 'cpython-3.9' - optimizations: 'lto' - - # Cross-compiles can't do PGO and require Python 3.9. - - target_triple: 'armv7-unknown-linux-gnueabihf' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'armv7-unknown-linux-gnueabihf' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'armv7-unknown-linux-gnueabihf' - py: 'cpython-3.9' - optimizations: 'lto' - - # We don't publish noopt builds when PGO is available. - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'debug' - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'lto' - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'pgo' - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'pgo+lto' - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'lto' - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'pgo' - - target_triple: 'i686-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'pgo+lto' - - # Cross-compiles can't do PGO and require Python 3.9. - - target_triple: 'mips-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'mips-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'mips-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'lto' - - # Cross-compiles can't do PGO and require Python 3.9. - - target_triple: 'mipsel-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'mipsel-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'mipsel-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'lto' - - # Cross-compiles can't do PGO and require Python 3.9. - - target_triple: 's390x-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 's390x-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 's390x-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'lto' - - # We don't publish noopt builds when PGO is available. - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'debug' - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'lto' - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'pgo' - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.8' - optimizations: 'pgo+lto' - - target_triple: 'x86_64-unknown-linux-gnu' - - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'lto' - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'pgo' - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.9' - optimizations: 'pgo+lto' - - # Python 3.10 is just spot coverage for now. - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.10' - optimizations: 'debug' - - target_triple: 'x86_64-unknown-linux-gnu' - py: 'cpython-3.10' - optimizations: 'pgo+lto' - - # musl doesn't support PGO. - - target_triple: 'x86_64-unknown-linux-musl' - py: 'cpython-3.8' - optimizations: 'debug' - - target_triple: 'x86_64-unknown-linux-musl' - py: 'cpython-3.8' - optimizations: 'noopt' - - target_triple: 'x86_64-unknown-linux-musl' - py: 'cpython-3.8' - optimizations: 'lto' - - target_triple: 'x86_64-unknown-linux-musl' - - py: 'cpython-3.9' - optimizations: 'debug' - - target_triple: 'x86_64-unknown-linux-musl' - py: 'cpython-3.9' - optimizations: 'noopt' - - target_triple: 'x86_64-unknown-linux-musl' - py: 'cpython-3.9' - optimizations: 'lto' - needs: - - pythonbuild - - toolchain - runs-on: 'ubuntu-20.04' - steps: - - uses: actions/checkout@v2 + name: ${{ matrix.target_triple }} / ${{ matrix.python }} / ${{ matrix.build_options }} + steps: &build_steps + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - - name: Install Python - uses: actions/setup-python@v2 + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 with: - python-version: '3.9' + enable-cache: false - name: Download pythonbuild - uses: actions/download-artifact@v2 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: - name: pythonbuild + name: ${{ matrix.crate_artifact_name }} path: build - - name: Download toolchain - uses: actions/download-artifact@v2 + - name: Download images + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: - name: toolchain + pattern: image-* path: build + merge-multiple: true - - name: Build - run: | - if [ "${{ matrix.build.target_triple }}" = "x86_64-unknown-linux-musl" ]; then - EXTRA_ARGS=--libressl - else - EXTRA_ARGS= - fi + - name: Cache downloads + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 + with: + path: build/downloads + key: ${{ matrix.target_triple }}-${{ hashFiles('pythonbuild/downloads.py')}} + restore-keys: | + ${{ matrix.target_triple }}-${{ hashFiles('pythonbuild/downloads.py')}} + ${{ matrix.target_triple }}- - ./build-linux.py --skip-toolchain --target-triple ${{ matrix.build.target_triple }} --python ${{ matrix.build.py }} --optimizations ${{ matrix.build.optimizations }} ${EXTRA_ARGS} + - name: Load Docker Images + run: | + set -euo pipefail + + # We need to keep the image-*.tar file since it is used as a + # Makefile dependency. + load() { + image="${1%.tar.zst}" + echo "decompressing ${image}.tar.zst" + zstd -d --rm "${image}.tar.zst" + docker load --input "${image}.tar" + } + + # Avoid loading images that aren't used. + case "$(uname -m)" in + aarch64) + want_suffix=linux_aarch64.tar.zst + ;; + x86_64) + want_suffix=linux_x86_64.tar.zst + ;; + *) + echo "unsupported host arch: $(uname -m)" + exit 1 + ;; + esac + + for f in build/image-*.tar.zst; do + if [[ "$f" == *"${want_suffix}" ]]; then + load "${f}" & + else + echo "skipping ${f}" + rm "${f}" + fi + done + + wait - - name: Validate Distribution + - name: Build + if: ${{ ! matrix.dry-run }} run: | - chmod +x build/pythonbuild - build/pythonbuild validate-distribution dist/*.tar.zst + # Do empty target so all generated files are touched. + ./build.py --make-target empty - - name: Upload Distribution - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.build.py }}-${{ matrix.build.target_triple }} - path: dist/* + # Touch mtimes of all images so they are newer than autogenerated files above. + touch build/image-* - install-only: - needs: - - build - runs-on: 'ubuntu-20.04' - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 + ./build.py --target-triple ${MATRIX_TARGET_TRIPLE} --python cpython-${MATRIX_PYTHON} --options ${MATRIX_BUILD_OPTIONS} + env: + MATRIX_TARGET_TRIPLE: ${{ matrix.target_triple }} + MATRIX_PYTHON: ${{ matrix.python }} + MATRIX_BUILD_OPTIONS: ${{ matrix.build_options }} - - name: Install Python - uses: actions/setup-python@v2 + - name: Generate attestations + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + if: ${{ github.ref == 'refs/heads/main' }} with: - python-version: '3.9' + subject-path: dist/* - - name: Download Python - uses: actions/download-artifact@v2 + - name: Upload Distribution + if: ${{ ! matrix.dry-run }} + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: cpython-3.9-x86_64-unknown-linux-gnu + name: cpython-${{ matrix.python }}-${{ matrix.target_triple }}-${{ matrix.build_options }} + path: dist/* + compression-level: '0' - - name: Repack Distribution + - name: Validate Distribution + if: ${{ ! matrix.dry-run }} run: | - ./prune-distribution.py cpython-*-pgo+lto-* + chmod +x build/pythonbuild - - name: Upload Distribution - uses: actions/upload-artifact@v2 - with: - name: cpython-install-only - path: cpython-*.tar.gz + build/pythonbuild validate-distribution dist/*.tar.zst + + if [ "${MATRIX_RUN}" == "true" ]; then + if [ "${MATRIX_LIBC}" == "musl" ]; then + sudo apt install musl-dev + + # GitHub's setup-python action sets `LD_LIBRARY_PATH` which overrides `RPATH` + # as used in the musl builds. + unset LD_LIBRARY_PATH + fi + + ./test-distribution.py dist/*.tar.zst + fi + env: + MATRIX_RUN: ${{ matrix.run }} + MATRIX_LIBC: ${{ matrix.libc }} + + build-1: + needs: + - generate-matrix + - crate-build + - image + # Permissions used for actions/attest-build-provenance + permissions: + id-token: write + attestations: write + runs-on: ${{ matrix.runner }} + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.python-build-matrix-1) }} + fail-fast: false + name: ${{ matrix.target_triple }} / ${{ matrix.python }} / ${{ matrix.build_options }} + steps: *build_steps diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 000000000..3a992a9a4 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,187 @@ +name: macos + +on: + push: + branches: [ main ] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + FORCE_COLOR: 1 + +permissions: { } + +jobs: + crate-build: + needs: + - generate-matrix + runs-on: ${{ matrix.runner }} + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.crate-build-matrix) }} + fail-fast: false + name: crate / ${{ matrix.arch }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Emit rustc version + run: | + rustc --version > .rustc-version + + - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-pythonbuild-${{ hashFiles('Cargo.lock', '.rustc-version') }} + + - name: Build + run: | + cargo build --release + + - name: Upload pythonbuild Executable + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ matrix.crate_artifact_name }} + path: target/release/pythonbuild + + generate-matrix: + name: Generate build matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + crate-build-matrix: ${{ steps.set-matrix.outputs.crate-build-matrix }} + any_builds: ${{ steps.set-matrix.outputs.any_builds }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + enable-cache: false + + - name: Get pull request labels + id: get-labels + env: + PULL_REQUEST_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} + run: | + # Convert GitHub labels array to comma-separated string + LABELS=$(echo "${PULL_REQUEST_LABELS}" | jq -r 'join(",")') + echo "labels=$LABELS" >> $GITHUB_OUTPUT + + - name: Check if the `pythonbuild` crate changed + id: check-pythonbuild + env: + BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }} + run: | + merge_base=$(git merge-base HEAD "origin/${BASE_REF}") + if git diff --quiet "${merge_base}...HEAD" -- ':src/*.rs'; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Generate build matrix + id: set-matrix + run: | + uv run ci-matrix.py --platform darwin --labels "${STEPS_GET_LABELS_OUTPUTS_LABELS}" ${{ (steps.check-pythonbuild.outputs.changed == 'true' || github.ref == 'refs/heads/main') && '--force-crate-build' || '' }} > matrix.json + + # Extract python-build matrix + echo "matrix=$(jq -c '."python-build"' matrix.json)" >> $GITHUB_OUTPUT + echo "crate-build-matrix=$(jq -c '."crate-build"' matrix.json)" >> $GITHUB_OUTPUT + + # Display the matrix for debugging too + cat matrix.json | jq + + if jq -e '."python-build".include | length > 0' matrix.json > /dev/null; then + # Build matrix has entries + echo "any_builds=true" >> $GITHUB_OUTPUT + else + # Build matrix is empty + echo "any_builds=false" >> $GITHUB_OUTPUT + fi + env: + STEPS_GET_LABELS_OUTPUTS_LABELS: ${{ steps.get-labels.outputs.labels }} + + build: + needs: + - generate-matrix + - crate-build + # Permissions used for actions/attest-build-provenance + permissions: + id-token: write + attestations: write + runs-on: ${{ matrix.runner }} + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + fail-fast: false + name: ${{ matrix.target_triple }} / ${{ matrix.python }} / ${{ matrix.build_options }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + enable-cache: false + + - name: Download pythonbuild + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: ${{ matrix.crate_artifact_name }} + path: build + + - name: Build + if: ${{ ! matrix.dry-run }} + run: | + ./build.py --target-triple ${MATRIX_TARGET_TRIPLE} --python cpython-${MATRIX_PYTHON} --options ${MATRIX_BUILD_OPTIONS} + env: + MATRIX_TARGET_TRIPLE: ${{ matrix.target_triple }} + MATRIX_PYTHON: ${{ matrix.python }} + MATRIX_BUILD_OPTIONS: ${{ matrix.build_options }} + + - name: Generate attestations + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + if: ${{ github.ref == 'refs/heads/main' }} + with: + subject-path: dist/* + + - name: Upload Distributions + if: ${{ ! matrix.dry-run }} + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: cpython-${{ matrix.python }}-${{ matrix.target_triple }}-${{ matrix.build_options }} + path: dist/* + compression-level: '0' + + - name: Checkout macOS SDKs for validation + if: ${{ ! matrix.dry-run }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: phracker/MacOSX-SDKs + ref: master + path: macosx-sdks + persist-credentials: false + + - name: Validate Distribution + if: ${{ ! matrix.dry-run }} + run: | + chmod +x build/pythonbuild + + build/pythonbuild validate-distribution --macos-sdks-path macosx-sdks dist/*.tar.zst + + if [ "${MATRIX_RUN}" == "true" ]; then + ./test-distribution.py dist/*.tar.zst + fi + env: + MATRIX_RUN: ${{ matrix.run }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..92a010c30 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,184 @@ +name: Release + +on: + workflow_dispatch: + inputs: + tag: + description: "The version to release (e.g., '20260414')." + type: string + sha: + description: "The full SHA of the commit to be released (e.g., 'd09ff921d92d6da8d8a608eaa850dc8c0f638194')." + type: string + dry-run: + description: "Dry run? Tests the release process without publishing." + default: false + required: false + type: boolean + +env: + FORCE_COLOR: 1 + +permissions: {} + +jobs: + release-gate: + name: Release gate + runs-on: ubuntu-latest + environment: release + steps: + - run: echo "Release approved" + + release: + name: Release + needs: release-gate + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + runs-on: depot-ubuntu-24.04-8 + environment: release + + permissions: + contents: write + packages: write + # Permissions used for actions/attest-build-provenance + id-token: write + attestations: write + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + submodules: recursive + persist-credentials: true # needed for git operations below + + - uses: extractions/setup-crate@4993624604c307fbca528d28a3c8b60fa5ecc859 # v1.4.0 + with: + repo: casey/just + version: 1.42.4 + + - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + + # Perform a release in dry-run mode. + - run: just release-dry-run ${GH_TOKEN} ${GITHUB_EVENT_INPUTS_SHA} ${GITHUB_EVENT_INPUTS_TAG} + if: ${{ github.event.inputs.dry-run == 'true' }} + env: + GITHUB_EVENT_INPUTS_SHA: ${{ github.event.inputs.sha }} + GITHUB_EVENT_INPUTS_TAG: ${{ github.event.inputs.tag }} + - name: Configure Git identity + if: ${{ github.event.inputs.dry-run == 'false' }} + run: | + git config --global user.name "$GITHUB_ACTOR" + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" + + # Create a GitHub draft release for the target commit. + - name: Create GitHub Release + if: ${{ github.event.inputs.dry-run == 'false' }} + run: just release-create ${GITHUB_EVENT_INPUTS_TAG} ${GITHUB_EVENT_INPUTS_SHA} + env: + GITHUB_EVENT_INPUTS_TAG: ${{ github.event.inputs.tag }} + GITHUB_EVENT_INPUTS_SHA: ${{ github.event.inputs.sha }} + + # Uploading the relevant artifact to the GitHub release. + - run: just release-run ${GH_TOKEN} ${GITHUB_EVENT_INPUTS_SHA} ${GITHUB_EVENT_INPUTS_TAG} + if: ${{ github.event.inputs.dry-run == 'false' }} + env: + GITHUB_EVENT_INPUTS_SHA: ${{ github.event.inputs.sha }} + GITHUB_EVENT_INPUTS_TAG: ${{ github.event.inputs.tag }} + - name: Generate attestations + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + if: ${{ github.event.inputs.dry-run == 'false' }} + with: + subject-path: | + dist/*.tar.gz + dist/*.tar.zst + + - name: Publish to Astral mirror + 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: python-build-standalone + VERSION: ${{ github.event.inputs.tag }} + DRY_RUN: ${{ github.event.inputs.dry-run }} + run: | + if [ "${DRY_RUN}" = 'true' ]; then + just release-upload-mirror-dry-run \ + ${R2_BUCKET} \ + github/${PROJECT}/releases/download/${VERSION}/ \ + ${VERSION} + else + just release-upload-mirror \ + ${R2_BUCKET} \ + github/${PROJECT}/releases/download/${VERSION}/ \ + ${VERSION} + fi + + - name: Generate versions metadata + if: ${{ github.event.inputs.dry-run == 'false' }} + env: + GITHUB_EVENT_INPUTS_TAG: ${{ github.event.inputs.tag }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: uv run generate-version-metadata.py > dist/versions.ndjson + + - name: Validate metadata + if: ${{ github.event.inputs.dry-run == 'false' }} + run: | + echo "Generated $(wc -l < dist/versions.ndjson) version entries" + head -c 1000 dist/versions.ndjson + + - name: Set branch name + if: ${{ github.event.inputs.dry-run == 'false' }} + env: + TAG: ${{ github.event.inputs.tag }} + run: echo "BRANCH_NAME=update-versions-$TAG-$(date +%s)" >> $GITHUB_ENV + + - name: Clone versions repo + if: ${{ github.event.inputs.dry-run == 'false' }} + run: git clone https://${{ secrets.ASTRAL_VERSIONS_PAT }}@github.com/astral-sh/versions.git astral-versions + + - name: Update versions + if: ${{ github.event.inputs.dry-run == 'false' }} + run: cat dist/versions.ndjson | uv run astral-versions/scripts/insert-versions.py --name python-build-standalone + + - name: Commit versions + if: ${{ github.event.inputs.dry-run == 'false' }} + env: + TAG: ${{ github.event.inputs.tag }} + 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 python-build-standalone to $TAG" + + - name: Create Pull Request + if: ${{ github.event.inputs.dry-run == 'false' }} + env: + TAG: ${{ github.event.inputs.tag }} + GH_TOKEN: ${{ secrets.ASTRAL_VERSIONS_PAT }} + working-directory: astral-versions + run: | + pull_request_title="Update python-build-standalone versions for $TAG" + + 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 $TAG" \ + --label "automation" + + - name: Merge Pull Request + if: ${{ github.event.inputs.dry-run == 'false' }} + env: + GH_TOKEN: ${{ secrets.ASTRAL_VERSIONS_PAT }} + working-directory: astral-versions + run: | + # Wait for PR to be created before merging + sleep 10 + gh pr merge --squash "$BRANCH_NAME" + diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1d01f4443..cc8b866f0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,123 +1,189 @@ +name: windows + on: push: + branches: [ main ] pull_request: - schedule: - - cron: '13 15 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + FORCE_COLOR: 1 + +permissions: { } + jobs: - pythonbuild: - runs-on: 'windows-2019' + crate-build: + needs: + - generate-matrix + runs-on: ${{ matrix.runner }} + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.crate-build-matrix) }} + fail-fast: false + name: crate / ${{ matrix.arch }} steps: - - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - toolchain: stable - default: true - profile: minimal + persist-credentials: false + + - name: Emit rustc version + run: | + rustc --version > .rustc-version - - uses: actions/cache@v2 + - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: | C:/Rust/.cargo/registry C:/Rust/.cargo/git target - key: ${{ runner.os }}-pythonbuild-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-pythonbuild-${{ hashFiles('Cargo.lock', '.rustc-version') }} - name: Build run: | cargo build --release - name: Upload executable - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: pythonbuild + name: ${{ matrix.crate_artifact_name }} path: target/release/pythonbuild.exe + generate-matrix: + name: Generate build matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + crate-build-matrix: ${{ steps.set-matrix.outputs.crate-build-matrix }} + any_builds: ${{ steps.set-matrix.outputs.any_builds }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + enable-cache: false + + - name: Get pull request labels + id: get-labels + env: + PULL_REQUEST_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} + run: | + # Convert GitHub labels array to comma-separated string + LABELS=$(echo "${PULL_REQUEST_LABELS}" | jq -r 'join(",")') + echo "labels=$LABELS" >> $GITHUB_OUTPUT + + - name: Check if the `pythonbuild` crate changed + id: check-pythonbuild + env: + BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }} + run: | + merge_base=$(git merge-base HEAD "origin/${BASE_REF}") + if git diff --quiet "${merge_base}...HEAD" -- ':src/*.rs'; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Generate build matrix + id: set-matrix + run: | + uv run ci-matrix.py --platform windows --labels "${STEPS_GET_LABELS_OUTPUTS_LABELS}" ${{ (steps.check-pythonbuild.outputs.changed == 'true' || github.ref == 'refs/heads/main') && '--force-crate-build' || '' }} > matrix.json + + # Extract python-build matrix + echo "matrix=$(jq -c '."python-build"' matrix.json)" >> $GITHUB_OUTPUT + echo "crate-build-matrix=$(jq -c '."crate-build"' matrix.json)" >> $GITHUB_OUTPUT + + # Display the matrix for debugging too + cat matrix.json | jq + + if jq -e '."python-build".include | length > 0' matrix.json > /dev/null; then + # Build matrix has entries + echo "any_builds=true" >> $GITHUB_OUTPUT + else + # Build matrix is empty + echo "any_builds=false" >> $GITHUB_OUTPUT + fi + env: + STEPS_GET_LABELS_OUTPUTS_LABELS: ${{ steps.get-labels.outputs.labels }} + build: + timeout-minutes: 90 + needs: + - generate-matrix + - crate-build + # Permissions used for actions/attest-build-provenance + permissions: + id-token: write + attestations: write + runs-on: ${{ matrix.runner }} strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} fail-fast: false - matrix: - py: - - 'cpython-3.8' - - 'cpython-3.9' - vcvars: - - 'vcvars32.bat' - - 'vcvars64.bat' - profile: - - 'static-noopt' - - 'shared-pgo' - needs: pythonbuild - runs-on: 'windows-2019' + name: ${{ matrix.target_triple }} / ${{ matrix.python }} / ${{ matrix.build_options }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@f86ccb21e7a2ba792c837fb9a2b267a9d6335531 + - name: Install Cygwin Environment + uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6 with: - platform: x64 packages: autoconf automake libtool - - name: Install Python - uses: actions/setup-python@v2 + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 with: - python-version: '3.9' + enable-cache: false - name: Download pythonbuild Executable - uses: actions/download-artifact@v2 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: - name: pythonbuild + name: ${{ matrix.crate_artifact_name }} # We need to do this before we activate the VC++ environment or else binary packages # don't get compiled properly. - name: Bootstrap Python environment run: | - py.exe build-windows.py --help + uv run --no-dev build.py --help - name: Build + if: ${{ ! matrix.dry-run }} shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\${{ matrix.vcvars }}" - py.exe build-windows.py --python ${{ matrix.py }} --sh c:\tools\cygwin\bin\sh.exe --profile ${{ matrix.profile }} - - - name: Validate Distribution - run: | - $Dists = Resolve-Path -Path "dist/*.tar.zst" -Relative - .\pythonbuild.exe validate-distribution --run $Dists + IF "%MATRIX_VS_VERSION%"=="2026" ( + call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\%MATRIX_VCVARS%" + ) ELSE ( + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\%MATRIX_VCVARS%" + ) + uv run --no-dev build.py --python cpython-%MATRIX_PYTHON% --sh c:\cygwin\bin\sh.exe --options %MATRIX_BUILD_OPTIONS% --vs %MATRIX_VS_VERSION% + env: + MATRIX_VCVARS: ${{ matrix.vcvars }} + MATRIX_PYTHON: ${{ matrix.python }} + MATRIX_BUILD_OPTIONS: ${{ matrix.build_options }} + MATRIX_VS_VERSION: ${{ matrix.vs_version }} + + - name: Generate attestations + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + if: ${{ github.ref == 'refs/heads/main' }} + with: + subject-path: dist/* - name: Upload Distributions - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: ${{ matrix.py }}-${{ matrix.vcvars }} + name: cpython-${{ matrix.python }}-${{ matrix.vcvars }}-${{ matrix.build_options }} path: dist/* + compression-level: '0' - install-only: - needs: - - build - runs-on: 'ubuntu-20.04' - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - - name: Download Python - uses: actions/download-artifact@v2 - with: - name: cpython-3.9-vcvars64.bat - - - name: Repack Distribution + - name: Validate Distribution + if: ${{ ! matrix.dry-run }} run: | - ./prune-distribution.py cpython-*-pgo-* - - - name: Upload Distribution - uses: actions/upload-artifact@v2 - with: - name: cpython-install-only - path: cpython-*.tar.gz + $Dists = Resolve-Path -Path "dist/*.tar.zst" -Relative + .\pythonbuild.exe validate-distribution $Dists + uv run --no-dev test-distribution.py $Dists diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000..b938ffe7e --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,24 @@ +name: zizmor + +on: + push: + branches: ["main"] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + name: Run zizmor + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000..e7920e8cf --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,5 @@ +rules: + unpinned-uses: + config: + policies: + "*": hash-pin diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..24ee5b1be --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 000000000..c7e0193b5 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,57 @@ +============ +Contributing +============ + +Building distributions +====================== + +See the [documentation](https://gregoryszorc.com/docs/python-build-standalone/main/building.html) +for instructions on building distributions locally. + +CI labels +========= +By default, submitting a pull request triggers a complete build of all +distributions in CI, which can be time-consuming. + +To conserve CI resources and reduce build times, you can limit the matrix of +distributions built by applying specific labels to your pull request. Only +distributions matching the specified labels will be built. + +The following label prefixes can be used to customize the build matrix: + +* `platform` +* `python` +* `build` +* `arch` +* `libc` + +To bypass CI entirely for changes that do not affect the build (such as +documentation updates), use the `ci:skip` label. + +Please utilize these tags when appropriate for your changes to minimize CI +resource consumption. + +Releases +======== + +To cut a release, wait for the "MacOS Python build", "Linux Python build", and +"Windows Python build" GitHub Actions to complete successfully on the target commit. + +Then, run the "Release" GitHub Action to create a draft release for the target commit, +populate the release artifacts (by downloading the artifacts from each workflow, and uploading +them to the GitHub Release), publish the release, and promote the SHA via the `latest-release` +branch. + +The "Release" GitHub Action takes, as input, a tag (assumed to be a date in `YYYYMMDD` format) and +the commit SHA referenced above. + +For example, to create a release on April 19, 2024 at commit `29abc56`, run the "Release" workflow +with the tag `20240419` and the commit SHA `29abc56954fbf5ea812f7fbc3e42d87787d46825` as inputs, +once the "MacOS Python build", "Linux Python build", and "Windows Python build" workflows have +run to completion on `29abc56`. + +When the "Release" workflow is complete, the release will have been published and version metadata +will have been updated. You can then refine the release notes in the GitHub UI. + +At any stage, you can run the "Release" workflow in dry-run mode to avoid uploading artifacts to +GitHub. Dry-run mode can be executed before or after creating the release itself. diff --git a/Cargo.lock b/Cargo.lock index c9c827ba8..9e071c55e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,503 +1,3935 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "apple-sdk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989b0d72f39d25f31c97729bdde1f3369d4ec0beece0cdcd817fc8baf9932c21" +dependencies = [ + "plist", + "serde", + "serde_json", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "async-trait" +version = "0.1.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "aws-lc-rs" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "bzip2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +dependencies = [ + "libbz2-rs-sys", +] + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "cargo_metadata" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "cc" +version = "1.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deflate64" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "libz-rs-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasip3", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "goblin" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51876e3748c4a347fe65b906f2b1ae46a1e55a497b22c94c1f4f2c469ff7673a" +dependencies = [ + "log", + "plain", + "scroll 0.13.0", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.1", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.0", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "10.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" +dependencies = [ + "base64", + "ed25519-dalek", + "getrandom 0.2.15", + "hmac", + "js-sys", + "p256", + "p384", + "pem", + "rand 0.8.5", + "rsa", + "serde", + "serde_json", + "sha2", + "signature", + "simple_asn1", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + +[[package]] +name = "libc" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.0", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "libz-rs-sys" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" +dependencies = [ + "zlib-rs", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lzma-rust2" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae" +dependencies = [ + "sha2", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normalize-path" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5438dd2b2ff4c6df6e1ce22d825ed2fa93ee2922235cc45186991717f0a892d" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271638cd5fa9cca89c4c304675ca658efc4e64a66c716b7cfe1afb4b9611dbbc" +dependencies = [ + "flate2", + "memchr", + "ruzstd", +] + +[[package]] +name = "octocrab" +version = "0.49.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "481d01ffe3fa4347e55474798e16d8d678aab19b8d7ca631ebb3c607cc87f9db" +dependencies = [ + "arc-swap", + "async-trait", + "base64", + "bytes", + "cargo_metadata", + "cfg-if", + "chrono", + "either", + "futures", + "futures-core", + "futures-util", + "getrandom 0.2.15", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-timeout", + "hyper-util", + "jsonwebtoken", + "once_cell", + "percent-encoding", + "pin-project", + "secrecy", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "snafu", + "tokio", + "tower", + "tower-http", + "tracing", + "url", + "web-time", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.0", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pdb" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863" +dependencies = [ + "fallible-iterator", + "scroll 0.11.0", + "uuid", +] + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pep440_rs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31095ca1f396e3de32745f42b20deef7bc09077f918b085307e8eab6ddd8fb9c" +dependencies = [ + "once_cell", + "serde", + "unicode-width", + "unscanny", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64", + "indexmap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppmd-rust" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pythonbuild" +version = "0.1.0" +dependencies = [ + "anyhow", + "apple-sdk", + "bytes", + "clap", + "flate2", + "futures", + "goblin", + "hex", + "normalize-path", + "object", + "octocrab", + "once_cell", + "pdb", + "pep440_rs", + "rayon", + "reqwest", + "reqwest-middleware", + "reqwest-retry", + "rustls", + "semver", + "serde", + "serde_json", + "sha2", + "tar", + "tempfile", + "text-stub-library", + "tokio", + "tokio-util", + "url", + "version-compare", + "zip", + "zstd", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls", + "socket2 0.5.8", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.1", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash 2.1.1", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.8", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.1", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "reqwest-middleware" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "199dda04a536b532d0cc04d7979e39b1c763ea749bf91507017069c00b96056f" +dependencies = [ + "anyhow", + "async-trait", + "http", + "reqwest", + "thiserror 2.0.18", + "tower-service", +] + +[[package]] +name = "reqwest-retry" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2412db2af7d2268e7a5406be0431f37d9eb67ff390f35b395716f5f06c2eaa" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "getrandom 0.2.15", + "http", + "hyper", + "reqwest", + "reqwest-middleware", + "retry-policies", + "thiserror 2.0.18", + "tokio", + "tracing", + "wasmtimer", +] + +[[package]] +name = "retry-policies" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a4bd6027df676bcb752d3724db0ea3c0c5fc1dd0376fec51ac7dcaf9cc69be" +dependencies = [ + "rand 0.9.2", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ruzstd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" + +[[package]] +name = "scroll" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed76efe62313ab6610570951494bdaa81568026e0318eaa55f167de70eeea67d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tar" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.1", + "once_cell", + "rustix 1.1.2", + "windows-sys 0.52.0", +] + +[[package]] +name = "text-stub-library" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48070939e80c2662b5dd403a0b09cb97e8467a248d67e373e23f85dbdacd882" +dependencies = [ + "serde", + "serde_yaml", + "yaml-rust", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "js-sys", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2 0.6.0", + "windows-sys 0.61.1", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "twox-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56" + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "unscanny" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] -name = "ansi_term" -version = "0.11.0" +name = "want" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "winapi", + "try-lock", ] [[package]] -name = "anyhow" -version = "1.0.39" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "atty" -version = "0.2.14" +name = "wasi" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "wit-bindgen-rt", ] [[package]] -name = "bitflags" -version = "1.2.1" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] [[package]] -name = "byteorder" -version = "1.4.3" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] [[package]] -name = "cc" -version = "1.0.67" +name = "wasm-bindgen" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ - "jobserver", + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "wasm-bindgen-futures" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] [[package]] -name = "clap" -version = "2.33.3" +name = "wasm-bindgen-macro" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "duct" -version = "0.13.5" +name = "wasm-bindgen-macro-support" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc6a0a59ed0888e0041cf708e66357b7ae1a82f1c67247e1f93b5e0818f7d8d" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ - "libc", - "once_cell", - "os_pipe", - "shared_child", + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", ] [[package]] -name = "filetime" -version = "0.2.14" +name = "wasm-bindgen-shared" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", + "unicode-ident", ] [[package]] -name = "getrandom" -version = "0.2.2" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ - "cfg-if", - "libc", - "wasi", + "leb128fmt", + "wasmparser", ] [[package]] -name = "goblin" -version = "0.3.4" +name = "wasm-metadata" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "669cdc3826f69a51d3f8fc3f86de81c2378110254f678b8407977736122057a4" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ - "log", - "plain", - "scroll", + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", ] [[package]] -name = "hermit-abi" -version = "0.1.18" +name = "wasm-streams" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ - "libc", + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "itoa" -version = "0.4.7" +name = "wasmparser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown", + "indexmap", + "semver", +] [[package]] -name = "jobserver" -version = "0.1.21" +name = "wasmtimer" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ - "libc", + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "web-sys" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] -name = "libc" -version = "0.2.91" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] [[package]] -name = "log" -version = "0.4.14" +name = "webpki-root-certs" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ - "cfg-if", + "rustls-pki-types", ] [[package]] -name = "once_cell" -version = "1.7.2" +name = "which" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] [[package]] -name = "os_pipe" -version = "0.9.2" +name = "winapi-util" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] -name = "plain" -version = "0.2.3" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] [[package]] -name = "ppv-lite86" -version = "0.2.10" +name = "windows-link" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "proc-macro2" -version = "1.0.24" +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-registry" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "unicode-xid", + "windows-link 0.1.3", + "windows-result", + "windows-strings", ] [[package]] -name = "pythonbuild" -version = "0.1.0" +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "anyhow", - "clap", - "duct", - "goblin", - "once_cell", - "scroll", - "serde", - "serde_json", - "tar", - "tempfile", - "tugger-binary-analysis", - "version-compare", - "zstd", + "windows-link 0.1.3", ] [[package]] -name = "quote" -version = "1.0.9" +name = "windows-strings" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "proc-macro2", + "windows-link 0.1.3", ] [[package]] -name = "rand" -version = "0.8.3" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "windows-targets 0.42.2", ] [[package]] -name = "rand_chacha" -version = "0.3.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "ppv-lite86", - "rand_core", + "windows-targets 0.52.6", ] [[package]] -name = "rand_core" -version = "0.6.2" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "getrandom", + "windows-targets 0.52.6", ] [[package]] -name = "rand_hc" -version = "0.3.0" +name = "windows-sys" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" dependencies = [ - "rand_core", + "windows-link 0.2.0", ] [[package]] -name = "redox_syscall" -version = "0.2.5" +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "bitflags", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "winapi", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] -name = "ryu" -version = "1.0.5" +name = "windows_aarch64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] -name = "scroll" -version = "0.10.2" +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" dependencies = [ - "scroll_derive", + "wit-bindgen-rust-macro", ] [[package]] -name = "scroll_derive" -version = "0.10.5" +name = "wit-bindgen-core" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ - "proc-macro2", - "quote", - "syn", + "anyhow", + "heck", + "wit-parser", ] [[package]] -name = "serde" -version = "1.0.125" +name = "wit-bindgen-rt" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "serde_derive", + "bitflags", ] [[package]] -name = "serde_derive" -version = "1.0.125" +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" dependencies = [ + "anyhow", + "prettyplease", "proc-macro2", "quote", "syn", + "wit-bindgen-core", + "wit-bindgen-rust", ] [[package]] -name = "serde_json" -version = "1.0.64" +name = "wit-component" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ - "itoa", - "ryu", + "anyhow", + "bitflags", + "indexmap", + "log", "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", ] [[package]] -name = "shared_child" -version = "0.3.5" +name = "wit-parser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6be9f7d5565b1483af3e72975e2dee33879b3b86bd48c0929fccf6585d79e65a" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ - "libc", - "winapi", + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", ] [[package]] -name = "strsim" -version = "0.8.0" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] -name = "syn" -version = "1.0.64" +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "xattr" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "libc", + "linux-raw-sys 0.4.15", + "rustix 0.38.44", ] [[package]] -name = "tar" -version = "0.4.33" +name = "yaml-rust" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0bcfbd6a598361fda270d82469fff3d65089dc33e175c9a131f7b4cd395f228" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ - "filetime", - "libc", - "xattr", + "linked-hash-map", ] [[package]] -name = "tempfile" -version = "3.2.0" +name = "yoke" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "yoke-derive" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ - "unicode-width", + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] -name = "tugger-binary-analysis" -version = "0.1.0-pre" -source = "git+https://github.com/indygreg/PyOxidizer.git?rev=e86b2f46ed6b449bdb912900b0ac83576ad5ebe9#e86b2f46ed6b449bdb912900b0ac83576ad5ebe9" +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "anyhow", "byteorder", - "goblin", - "lazy_static", - "version-compare", + "zerocopy-derive", ] [[package]] -name = "unicode-width" -version = "0.1.8" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "unicode-xid" -version = "0.2.1" +name = "zerofrom" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] [[package]] -name = "vec_map" -version = "0.8.2" +name = "zerofrom-derive" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] [[package]] -name = "version-compare" -version = "0.0.11" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +name = "zerovec" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] [[package]] -name = "winapi" -version = "0.3.9" +name = "zerovec-derive" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "zip" +version = "8.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "7756d0206d058333667493c4014f545f4b9603c4330ccd6d9b3f86dcab59f7d9" +dependencies = [ + "aes", + "bzip2", + "constant_time_eq", + "crc32fast", + "deflate64", + "flate2", + "getrandom 0.4.2", + "hmac", + "indexmap", + "lzma-rust2", + "memchr", + "pbkdf2", + "ppmd-rust", + "sha1", + "time", + "typed-path", + "zeroize", + "zopfli", + "zstd", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "zlib-rs" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" [[package]] -name = "xattr" -version = "0.2.2" +name = "zopfli" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" dependencies = [ - "libc", + "bumpalo", + "crc32fast", + "log", + "simd-adler32", ] [[package]] name = "zstd" -version = "0.6.1+zstd.1.4.9" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de55e77f798f205d8561b8fe2ef57abfb6e0ff2abe7fd3c089e119cdb5631a3" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "3.0.1+zstd.1.4.9" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1387cabcd938127b30ce78c4bf00b30387dddf704e3f0881dbc4ff62b5566f8c" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ - "libc", "zstd-sys", ] [[package]] name = "zstd-sys" -version = "1.4.20+zstd.1.4.9" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd5b733d7cf2d9447e2c3e76a5589b4f5e5ae065c22a2bc0b023cbc331b6c8e" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", - "libc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index a10f2c087..7cc511c48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,23 +2,38 @@ name = "pythonbuild" version = "0.1.0" authors = ["Gregory Szorc "] -edition = "2018" +edition = "2024" [dependencies] -anyhow = "1" -clap = "2" -duct = "0.13" -goblin = "0" -once_cell = "1" -scroll = "0" -serde_json = "1" -serde = { version = "1", features = ["derive"] } -tar = "0" -tempfile = "3" -version-compare = "0" -zstd = "0" - -[dependencies.tugger-binary-analysis] -version = "0.1.0-pre" -git = "https://github.com/indygreg/PyOxidizer.git" -rev = "e86b2f46ed6b449bdb912900b0ac83576ad5ebe9" +anyhow = "1.0.100" +apple-sdk = "0.6.0" +bytes = "1.11.0" +clap = "4.5.52" +flate2 = "1.1.5" +futures = "0.3.30" +goblin = "0.10.3" +hex = "0.4.3" +normalize-path = "0.2.1" +object = "0.38.1" +octocrab = { version = "0.49.5", features = ["rustls", "stream"] } +once_cell = "1.21.3" +pdb = "0.8.0" +pep440_rs = "0.7.3" +rayon = "1.11.0" +reqwest = { version = "0.13", features = ["rustls", "stream"] } +reqwest-middleware = "0.5" +reqwest-retry = "0.9" +rustls = { version = "0.23", default-features = false, features = ["ring"] } +semver = "1.0.27" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +sha2 = "0.10.9" +tar = "0.4.45" +tempfile = "3.23.0" +text-stub-library = "0.9.0" +tokio = "1.48.0" +tokio-util = "0.7.17" +url = "2.5.7" +version-compare = "0.2.1" +zip = "8.2.0" +zstd = "0.13.3" diff --git a/Justfile b/Justfile new file mode 100644 index 000000000..56acfa306 --- /dev/null +++ b/Justfile @@ -0,0 +1,142 @@ +# Diff 2 releases using diffoscope. +diff a b: + diffoscope \ + --html build/diff.html \ + --exclude 'python/build/**' \ + --exclude-command '^readelf.*' \ + --exclude-command '^xxd.*' \ + --exclude-command '^objdump.*' \ + --exclude-command '^strings.*' \ + --max-report-size 9999999999 \ + --max-page-size 999999999 \ + --max-diff-block-lines 100000 \ + --max-page-diff-block-lines 100000 \ + {{ a }} {{ b }} + +diff-python-json a b: + diffoscope \ + --html build/diff.html \ + --exclude 'python/build/**' \ + --exclude 'python/install/**' \ + --max-diff-block-lines 100000 \ + --max-page-diff-block-lines 100000 \ + {{ a }} {{ b }} + +cat-python-json archive: + tar -x --to-stdout -f {{ archive }} python/PYTHON.json + +# Run the mirror uploader integration test against a local moto S3 server. +test-mirror-integration: + uv run python -m unittest tests/test_mirror_integration.py + +# Download release artifacts from GitHub Actions +release-download-distributions token commit: + mkdir -p dist + cargo run --release -- fetch-release-distributions --token {{token}} --commit {{commit}} --dest dist + +# Upload release artifacts to a GitHub release. +release-upload-distributions token datetime tag: + cargo run --release -- upload-release-distributions --token {{token}} --datetime {{datetime}} --tag {{tag}} --dist dist + +# "Upload" release artifacts to a GitHub release in dry-run mode (skip upload). +release-upload-distributions-dry-run token datetime tag: + cargo run --release -- upload-release-distributions --token {{token}} --datetime {{datetime}} --tag {{tag}} --dist dist -n + +# Promote a tag to "latest" by pushing to the `latest-release` branch. +release-set-latest-release tag: + #!/usr/bin/env bash + set -euxo pipefail + + git fetch origin + git switch latest-release + git reset --hard origin/latest-release + + cat << EOF > latest-release.json + { + "version": 1, + "tag": "{{tag}}", + "release_url": "https://github.com/astral-sh/python-build-standalone/releases/tag/{{tag}}", + "asset_url_prefix": "https://github.com/astral-sh/python-build-standalone/releases/download/{{tag}}" + } + EOF + + # If the branch is dirty, we add and commit. + if ! git diff --quiet; then + git add latest-release.json + git commit -m 'set latest release to {{tag}}' + git push origin latest-release + else + echo "No changes to commit." + fi + + git switch main + +# Create a GitHub draft release for the target commit, or reuse an existing draft release. +release-create tag commit: + #!/usr/bin/env bash + set -euo pipefail + draft_exists=$(gh release view {{tag}} --json isDraft -t '{{{{.isDraft}}' 2>&1 || true) + case "$draft_exists" in + true) + echo "note: updating existing draft release {{tag}}" + ;; + false) + echo "error: release {{tag}} already exists and is not a draft" + exit 1 + ;; + "release not found") + gh release create {{tag}} --draft --title {{tag}} --notes TBD --target {{commit}} + ;; + *) + echo "error: unexpected gh cli output: $draft_exists" + exit 1 + ;; + esac + +# Publish the draft GitHub release and promote the tag to latest-release. +release-finalize tag: + #!/usr/bin/env bash + set -euo pipefail + gh release edit {{tag}} --draft=false --latest + just release-set-latest-release {{tag}} + +# Upload release artifacts to an S3-compatible mirror bucket with the correct release names. +# AWS credentials are read from the standard AWS_* environment variables. +# Requires `release-run` to have been run so that dist/SHA256SUMS exists. +release-upload-mirror bucket prefix tag: + uv run python -m pythonbuild.mirror \ + --dist dist \ + --tag {{tag}} \ + --bucket {{bucket}} \ + --prefix {{prefix}} + +# Dry-run the mirror upload without writing to the bucket. +# Requires `release-run` or `release-dry-run` to have been run so that dist/SHA256SUMS exists. +release-upload-mirror-dry-run bucket prefix tag: + uv run python -m pythonbuild.mirror \ + --dist dist \ + --tag {{tag}} \ + --bucket {{bucket}} \ + --prefix {{prefix}} \ + -n + +# Perform the release job. Assumes that the GitHub Release has been created. +release-run token commit tag: + #!/bin/bash + set -eo pipefail + + rm -rf dist + just release-download-distributions {{token}} {{commit}} + datetime=$(ls dist/cpython-3.10.*-x86_64-unknown-linux-gnu-install_only-*.tar.gz | awk -F- '{print $8}' | awk -F. '{print $1}') + just release-upload-distributions {{token}} ${datetime} {{tag}} + just release-finalize {{tag}} + +# Perform a release in dry-run mode. +release-dry-run token commit tag: + #!/bin/bash + set -eo pipefail + + rm -rf dist + just release-download-distributions {{token}} {{commit}} + datetime=$(ls dist/cpython-3.10.*-x86_64-unknown-linux-gnu-install_only-*.tar.gz | awk -F- '{print $8}' | awk -F. '{print $1}') + just release-upload-distributions-dry-run {{token}} ${datetime} {{tag}} diff --git a/LICENSE b/LICENSE index 0b8a7e86e..a612ad981 100644 --- a/LICENSE +++ b/LICENSE @@ -1,27 +1,373 @@ -Copyright (c) 2018 to present, Gregory Szorc -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors -may be used to endorse or promote products derived from this software without -specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/LICENSE.cpython.txt b/LICENSE.cpython.txt index efb72487f..1007a8052 100644 --- a/LICENSE.cpython.txt +++ b/LICENSE.cpython.txt @@ -59,6 +59,17 @@ direction to make these releases possible. B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON =============================================================== +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- @@ -73,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. @@ -180,9 +191,9 @@ version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the Internet using the following +Python 1.6.1 may be located on the internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the Internet +Agreement may also be obtained from a proxy server on the internet using the following URL: http://hdl.handle.net/1895.22/1013". 3. In the event Licensee prepares a derivative work that is based on @@ -253,6 +264,19 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. Licenses and Acknowledgements for Incorporated Software ======================================================= @@ -556,7 +580,7 @@ SipHash24 --------- The file :file:`Python/pyhash.c` contains Marek Majkowski' implementation of -Dan Bernstein's SipHash24 algorithm. The contains the following note:: +Dan Bernstein's SipHash24 algorithm. It contains the following note:: Copyright (c) 2013 Marek Majkowski @@ -709,3 +733,39 @@ library unless the build is configured ``--with-system-libmpdec``:: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +W3C C14N test suite +------------------- + +The C14N 2.0 test suite in the :mod:`test` package +(``Lib/test/xmltestdata/c14n-20/``) was retrieved from the W3C website at +https://www.w3.org/TR/xml-c14n2-testcases/ and is distributed under the +3-clause BSD license:: + + Copyright (c) 2013 W3C(R) (MIT, ERCIM, Keio, Beihang), + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of works must retain the original copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the original copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the W3C nor the names of its contributors may be + used to endorse or promote products derived from this work without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE.expat.txt b/LICENSE.expat.txt new file mode 100644 index 000000000..ce9e59392 --- /dev/null +++ b/LICENSE.expat.txt @@ -0,0 +1,21 @@ +Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper +Copyright (c) 2001-2022 Expat maintainers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.gdbm.txt b/LICENSE.gdbm.txt deleted file mode 100644 index 6a0130e4d..000000000 --- a/LICENSE.gdbm.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007, 2011 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/LICENSE.gettext.txt b/LICENSE.gettext.txt deleted file mode 100644 index e60008693..000000000 --- a/LICENSE.gettext.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/LICENSE.libffi.txt b/LICENSE.libffi.txt index a66fab4f2..acb2f7a07 100644 --- a/LICENSE.libffi.txt +++ b/LICENSE.libffi.txt @@ -1,4 +1,4 @@ -libffi - Copyright (c) 1996-2014 Anthony Green, Red Hat, Inc and others. +libffi - Copyright (c) 1996-2019 Anthony Green, Red Hat, Inc and others. See source files for details. Permission is hereby granted, free of charge, to any person obtaining diff --git a/LICENSE.liblzma.txt b/LICENSE.liblzma.txt index 4590b3a23..2d7885199 100644 --- a/LICENSE.liblzma.txt +++ b/LICENSE.liblzma.txt @@ -1 +1,13 @@ -liblzma is in the public domain. +Copyright (C) The XZ Utils authors and contributors + +Permission to use, copy, modify, and/or distribute this +software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/LICENSE.libressl.txt b/LICENSE.libressl.txt deleted file mode 100644 index f39280dd7..000000000 --- a/LICENSE.libressl.txt +++ /dev/null @@ -1,132 +0,0 @@ - - LibReSSL files are retained under the copyright of the authors. New - additions are ISC licensed as per OpenBSD's normal licensing policy, - or are placed in the public domain. - - The OpenSSL code is distributed under the terms of the original OpenSSL - licenses which follow: - - LICENSE ISSUES - ============== - - The OpenSSL toolkit stays under a dual license, i.e. both the conditions of - the OpenSSL License and the original SSLeay license apply to the toolkit. - See below for the actual license texts. In case of any license issues - related to OpenSSL please contact openssl-core@openssl.org. - - OpenSSL License - --------------- - -/* ==================================================================== - * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * openssl-core@openssl.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). - * - */ - - Original SSLeay License - ----------------------- - -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ diff --git a/LICENSE.mpdecimal.txt b/LICENSE.mpdecimal.txt new file mode 100644 index 000000000..c7688a928 --- /dev/null +++ b/LICENSE.mpdecimal.txt @@ -0,0 +1,24 @@ +Copyright (c) 2008-2020 Stefan Krah. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/LICENSE.ncurses.txt b/LICENSE.ncurses.txt index e4275f9e6..3a2297536 100644 --- a/LICENSE.ncurses.txt +++ b/LICENSE.ncurses.txt @@ -1,4 +1,5 @@ -Copyright (c) 1998-2017,2018 Free Software Foundation, Inc. +Copyright 2018-2020,2021 Thomas E. Dickey +Copyright 1998-2017,2018 Free Software Foundation, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -23,3 +24,6 @@ Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization. + +-- vile:txtmode fc=72 +-- $Id: COPYING,v 1.10 2021/01/01 09:54:30 tom Exp $ diff --git a/LICENSE.openssl.txt b/LICENSE.openssl-1.1.txt similarity index 100% rename from LICENSE.openssl.txt rename to LICENSE.openssl-1.1.txt diff --git a/LICENSE.openssl-3.txt b/LICENSE.openssl-3.txt new file mode 100644 index 000000000..49cc83d2e --- /dev/null +++ b/LICENSE.openssl-3.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/LICENSE.readline.txt b/LICENSE.readline.txt deleted file mode 100644 index 94a9ed024..000000000 --- a/LICENSE.readline.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README.rst b/README.rst index f956d1b37..1377f33b1 100644 --- a/README.rst +++ b/README.rst @@ -2,5 +2,7 @@ Python Standalone Builds ======================== +This project produces standalone, highly-redistributable builds of Python. + See the docs in ``docs/`` or online at -https://python-build-standalone.readthedocs.io/. +https://gregoryszorc.com/docs/python-build-standalone/main/. diff --git a/build-linux.py b/build-linux.py index cf5f2d8c4..1cf3712fb 100755 --- a/build-linux.py +++ b/build-linux.py @@ -8,7 +8,7 @@ import subprocess import sys import venv - +import warnings ROOT = pathlib.Path(os.path.abspath(__file__)).parent BUILD = ROOT / "build" @@ -22,7 +22,7 @@ def bootstrap(): BUILD.mkdir(exist_ok=True) - venv.create(VENV, with_pip=True) + venv.create(VENV, with_pip=True, symlinks=True) subprocess.run([str(PIP), "install", "-r", str(REQUIREMENTS)], check=True) @@ -50,6 +50,12 @@ def run(): if __name__ == "__main__": + warnings.warn( + "build-macos.py is deprecated and will be removed in the future.\n" + + "Please use ./build.py to build a distribution.", + FutureWarning, + stacklevel=2, + ) try: if "PYBUILD_BOOTSTRAPPED" not in os.environ: bootstrap() diff --git a/build-macos.py b/build-macos.py index fc83195c7..ff8c2fe95 100755 --- a/build-macos.py +++ b/build-macos.py @@ -8,7 +8,7 @@ import subprocess import sys import venv - +import warnings ROOT = pathlib.Path(os.path.abspath(__file__)).parent BUILD = ROOT / "build" @@ -50,6 +50,12 @@ def run(): if __name__ == "__main__": + warnings.warn( + "build-macos.py is deprecated and will be removed in the future.\n" + + "Please use ./build.py to build a distribution.", + FutureWarning, + stacklevel=2, + ) try: if "PYBUILD_BOOTSTRAPPED" not in os.environ: bootstrap() diff --git a/build-windows.py b/build-windows.py index adaab8719..4a8c89737 100644 --- a/build-windows.py +++ b/build-windows.py @@ -5,18 +5,19 @@ import os import pathlib +import platform import subprocess import sys import venv - ROOT = pathlib.Path(os.path.abspath(__file__)).parent BUILD = ROOT / "build" DIST = ROOT / "dist" VENV = BUILD / "venv" PIP = VENV / "Scripts" / "pip.exe" PYTHON = VENV / "Scripts" / "python.exe" -REQUIREMENTS = ROOT / "requirements.win.txt" +ARCH = "-arm64" if platform.machine() == "ARM64" else "" +REQUIREMENTS = ROOT / f"requirements.win{ARCH}.txt" WINDOWS_DIR = ROOT / "cpython-windows" diff --git a/build.py b/build.py new file mode 100755 index 000000000..12c3fdb1b --- /dev/null +++ b/build.py @@ -0,0 +1,46 @@ +#!/usr/bin/env -S uv run --no-dev +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import os +import pathlib +import platform +import subprocess +import sys + +ROOT = pathlib.Path(os.path.abspath(__file__)).parent + + +def run(): + env = dict(os.environ) + env["PYTHONUNBUFFERED"] = "1" + python = sys.executable + system = platform.system() + + if system == "Darwin" or system == "Linux": + args = [ + python, + "build-main.py", + *sys.argv[1:], + ] + make_dir = ROOT / "cpython-unix" + os.chdir(make_dir) + return os.execve(python, args, env) + elif system == "Windows": + args = [ + python, + "build.py", + *sys.argv[1:], + ] + cwd = str(ROOT / "cpython-windows") + return subprocess.run(args, cwd=cwd, env=env, check=True, bufsize=0) + else: + raise Exception(f"Unsupported host system: {system}") + + +if __name__ == "__main__": + try: + run() + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) diff --git a/check.py b/check.py new file mode 100755 index 000000000..0f3e41cac --- /dev/null +++ b/check.py @@ -0,0 +1,59 @@ +#!/usr/bin/env -S uv run --group check +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +from __future__ import annotations + +import argparse +import os +import subprocess +import sys + + +def run_command(command: list[str]) -> int: + print("$ " + " ".join(command)) + returncode = subprocess.run( + command, stdout=sys.stdout, stderr=sys.stderr + ).returncode + print() + return returncode + + +def run(): + env = dict(os.environ) + env["PYTHONUNBUFFERED"] = "1" + + parser = argparse.ArgumentParser(description="Check code.") + parser.add_argument( + "--fix", + action="store_true", + help="Fix problems", + ) + args = parser.parse_args() + + check_args = [] + format_args = [] + mypy_args = [] + + if args.fix: + check_args.append("--fix") + else: + format_args.append("--check") + + check_result = run_command(["ruff", "check"] + check_args) + format_result = run_command(["ruff", "format"] + format_args) + mypy_result = run_command(["mypy"] + mypy_args) + + if check_result + format_result + mypy_result: + print("Checks failed!") + sys.exit(1) + else: + print("Checks passed!") + + +if __name__ == "__main__": + try: + run() + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) diff --git a/ci-matrix.py b/ci-matrix.py new file mode 100644 index 000000000..c88f09b2e --- /dev/null +++ b/ci-matrix.py @@ -0,0 +1,450 @@ +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "packaging", +# "pyyaml", +# ] +# /// + +import argparse +import json +import sys +from typing import Any + +import yaml +from packaging.version import Version + +CI_TARGETS_YAML = "ci-targets.yaml" +CI_RUNNERS_YAML = "ci-runners.yaml" +CI_EXTRA_SKIP_LABELS = ["documentation"] +CI_MATRIX_SIZE_LIMIT = 256 # The maximum size of a matrix in GitHub Actions + + +# Docker images for building toolchains and dependencies +DOCKER_BUILD_IMAGES = [ + {"name": "build", "arch": "x86_64"}, + {"name": "build.cross", "arch": "x86_64"}, + {"name": "build.cross-riscv64", "arch": "x86_64"}, + {"name": "build.debian9", "arch": "aarch64"}, + {"name": "gcc", "arch": "x86_64"}, + {"name": "gcc.debian9", "arch": "aarch64"}, +] + + +def crate_artifact_name(platform: str, arch: str) -> str: + return f"crate-{platform}-{arch}" + + +def meets_conditional_version(version: str, min_version: str) -> bool: + return Version(version) >= Version(min_version) + + +def parse_labels(labels: str | None) -> dict[str, set[str]]: + """Parse labels into a dict of category filters.""" + if not labels: + return {} + + result: dict[str, set[str]] = { + "platform": set(), + "python": set(), + "build": set(), + "arch": set(), + "libc": set(), + "directives": set(), + } + + for label in labels.split(","): + label = label.strip() + + # Handle special labels + if label in CI_EXTRA_SKIP_LABELS: + result["directives"].add("skip") + continue + + if not label or ":" not in label: + continue + + category, value = label.split(":", 1) + + if category == "ci": + category = "directives" + + if category in result: + result[category].add(value) + + return result + + +def should_include_entry(entry: dict[str, str], filters: dict[str, set[str]]) -> bool: + """Check if an entry satisfies the label filters.""" + if filters.get("directives") and "skip" in filters["directives"]: + return False + + if filters.get("platform") and entry["platform"] not in filters["platform"]: + return False + + if filters.get("python") and entry["python"] not in filters["python"]: + return False + + if filters.get("arch") and entry["arch"] not in filters["arch"]: + return False + + if ( + filters.get("libc") + and entry.get("libc") + and entry["libc"] not in filters["libc"] + ): + return False + + if filters.get("build"): + build_options = set(entry.get("build_options", "").split("+")) + if not all(f in build_options for f in filters["build"]): + return False + + return True + + +def generate_docker_matrix_entries( + runners: dict[str, Any], + platform_filter: str | None = None, +) -> list[dict[str, str]]: + """Generate matrix entries for docker image builds.""" + if platform_filter and platform_filter != "linux": + return [] + + matrix_entries = [] + for image in DOCKER_BUILD_IMAGES: + # Find appropriate runner for Linux platform with the specified architecture + runner = find_runner(runners, "linux", image["arch"], False) + + entry = { + "name": image["name"], + "arch": image["arch"], + "runner": runner, + } + matrix_entries.append(entry) + + return matrix_entries + + +def generate_crate_build_matrix_entries( + python_entries: list[dict[str, str]], + runners: dict[str, Any], + config: dict[str, Any], + force_crate_build: bool = False, + platform_filter: str | None = None, +) -> list[dict[str, str]]: + """Generate matrix entries for crate builds based on python build matrix.""" + needed_builds = set() + for entry in python_entries: + # The crate build will need to match the runner's architecture + runner = runners[entry["runner"]] + needed_builds.add((entry["platform"], runner["arch"])) + + # If forcing crate build, also include all possible native builds + if force_crate_build: + for platform, platform_config in config.items(): + # Filter by platform if specified + if platform_filter and platform != platform_filter: + continue + + for target_config in platform_config.values(): + # Only include if native (run: true means native) + if not target_config.get("run"): + continue + + arch = target_config["arch"] + needed_builds.add((platform, arch)) + + # Create matrix entries for each needed build + return [ + { + "platform": platform, + "arch": arch, + # Use the GitHub runner for Windows, because the Depot one is + # missing a Rust toolchain. On Linux, it's important that the the + # `python-build` runner matches the `crate-build` runner because of + # GLIBC version mismatches. + "runner": find_runner( + runners, platform, arch, True if platform == "windows" else False + ), + "crate_artifact_name": crate_artifact_name( + platform, + arch, + ), + } + for platform, arch in needed_builds + if not platform_filter or platform == platform_filter + ] + + +def generate_python_build_matrix_entries( + config: dict[str, Any], + runners: dict[str, Any], + platform_filter: str | None = None, + label_filters: dict[str, set[str]] | None = None, +) -> list[dict[str, str]]: + """Generate matrix entries for python builds.""" + matrix_entries = [] + + for platform, platform_config in config.items(): + if platform_filter and platform != platform_filter: + continue + + for target_triple, target_config in platform_config.items(): + add_python_build_entries_for_config( + matrix_entries, + target_triple, + target_config, + platform, + runners, + label_filters.get("directives", set()) if label_filters else set(), + ) + + # Apply label filters if present + if label_filters: + matrix_entries = [ + entry + for entry in matrix_entries + if should_include_entry(entry, label_filters) + ] + + return matrix_entries + + +def find_runner(runners: dict[str, Any], platform: str, arch: str, free: bool) -> str: + # Find a matching platform first + match_platform = [ + runner + for runner in runners + if runners[runner]["platform"] == platform and runners[runner]["free"] == free + ] + + # Then, find a matching architecture + match_arch = [ + runner for runner in match_platform if runners[runner]["arch"] == arch + ] + + # If there's a matching architecture, use that + if match_arch: + return match_arch[0] + + # Otherwise, use the first with a matching platform + if match_platform: + return match_platform[0] + + raise RuntimeError( + f"No runner found for platform {platform!r} and arch {arch!r} with free={free}" + ) + + +def create_python_build_entry( + base_entry: dict[str, Any], + python_version: str, + build_option: str, + config: dict[str, Any], +) -> dict[str, Any]: + entry = base_entry.copy() + entry.update( + { + "python": python_version, + "build_options": build_option, + } + ) + if "vs_version_override_conditional" in config: + conditional = config["vs_version_override_conditional"] + min_version = conditional["minimum-python-version"] + if meets_conditional_version(python_version, min_version): + entry["vs_version"] = conditional["vs_version"] + # TODO remove once VS 2026 is available in 'standard' runnners + if entry.get("vs_version") == "2026": + entry["runner"] = "windows-2025-vs2026" + return entry + + +def add_python_build_entries_for_config( + matrix_entries: list[dict[str, str]], + target_triple: str, + config: dict[str, Any], + platform: str, + runners: dict[str, Any], + directives: set[str], +) -> None: + """Add python build matrix entries for a specific target configuration.""" + python_versions = config["python_versions"] + build_options = config["build_options"] + arch = config["arch"] + runner = find_runner(runners, platform, arch, False) + + # Create base entry that will be used for all variants + base_entry = { + "arch": arch, + "target_triple": target_triple, + "platform": platform, + "runner": runner, + # If `run` is in the config, use that — otherwise, default to if the + # runner architecture matches the build architecture + "run": str(config.get("run", runners[runner]["arch"] == arch)).lower(), + # Use the crate artifact built for the runner's architecture + "crate_artifact_name": crate_artifact_name(platform, runners[runner]["arch"]), + } + + # Add optional fields if they exist + if "arch_variant" in config: + base_entry["arch_variant"] = config["arch_variant"] + if "libc" in config: + base_entry["libc"] = config["libc"] + if "vcvars" in config: + base_entry["vcvars"] = config["vcvars"] + if "vs_version" in config: + base_entry["vs_version"] = config["vs_version"] + + if "dry-run" in directives: + base_entry["dry-run"] = "true" + + # Process regular build options + for python_version in python_versions: + for build_option in build_options: + entry = create_python_build_entry( + base_entry, python_version, build_option, config + ) + matrix_entries.append(entry) + + # Process conditional build options (e.g., freethreaded) + for conditional in config.get("build_options_conditional", []): + min_version = conditional["minimum-python-version"] + for python_version in python_versions: + if not meets_conditional_version(python_version, min_version): + continue + + for build_option in conditional["options"]: + entry = create_python_build_entry( + base_entry, python_version, build_option, config + ) + matrix_entries.append(entry) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Generate a JSON matrix for building distributions in CI" + ) + parser.add_argument( + "--platform", + choices=["darwin", "linux", "windows"], + help="Filter matrix entries by platform", + ) + parser.add_argument( + "--max-shards", + type=int, + default=0, + help="The maximum number of shards allowed; set to zero to disable ", + ) + parser.add_argument( + "--labels", + help="Comma-separated list of labels to filter by (e.g., 'platform:darwin,python:3.13,build:debug'), all must match.", + ) + parser.add_argument( + "--free-runners", + action="store_true", + help="If only free runners should be used.", + ) + parser.add_argument( + "--force-crate-build", + action="store_true", + help="Force crate builds to be included even without python builds.", + ) + parser.add_argument( + "--matrix-type", + choices=["python-build", "docker-build", "crate-build", "all"], + default="all", + help="Which matrix types to generate (default: all)", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + labels = parse_labels(args.labels) + + with open(CI_TARGETS_YAML) as f: + config = yaml.safe_load(f) + + with open(CI_RUNNERS_YAML) as f: + runners = yaml.safe_load(f) + + # If only free runners are allowed, reduce to a subset + if args.free_runners: + runners = { + runner: runner_config + for runner, runner_config in runners.items() + if runner_config.get("free") + } + + result = {} + + # Generate python build entries + python_entries = generate_python_build_matrix_entries( + config, + runners, + args.platform, + labels, + ) + + # Output python-build matrix if requested + if args.matrix_type in ["python-build", "all"]: + if args.max_shards: + python_build_matrix = {} + shards = (len(python_entries) // CI_MATRIX_SIZE_LIMIT) + 1 + if shards > args.max_shards: + print( + f"error: python-build matrix of size {len(python_entries)} requires {shards} shards, but the maximum is {args.max_shards}; consider increasing `--max-shards`", + file=sys.stderr, + ) + sys.exit(1) + for shard in range(args.max_shards): + shard_entries = python_entries[ + shard * CI_MATRIX_SIZE_LIMIT : (shard + 1) * CI_MATRIX_SIZE_LIMIT + ] + python_build_matrix[str(shard)] = {"include": shard_entries} + result["python-build"] = python_build_matrix + else: + if len(python_entries) > CI_MATRIX_SIZE_LIMIT: + print( + f"warning: python-build matrix of size {len(python_entries)} exceeds limit of {CI_MATRIX_SIZE_LIMIT} but sharding is not enabled; consider setting `--max-shards`", + file=sys.stderr, + ) + result["python-build"] = {"include": python_entries} + + # Generate docker-build matrix if requested + # Only include docker builds if there are Linux python builds + if args.matrix_type in ["docker-build", "all"]: + # Check if we have any Linux python builds + has_linux_builds = any( + entry.get("platform") == "linux" for entry in python_entries + ) + + # If no platform filter or explicitly requesting docker-build only, include docker builds + # Otherwise, only include if there are Linux python builds + if args.matrix_type == "docker-build" or has_linux_builds: + docker_entries = generate_docker_matrix_entries( + runners, + args.platform, + ) + result["docker-build"] = {"include": docker_entries} + + # Generate crate-build matrix if requested + if args.matrix_type in ["crate-build", "all"]: + crate_entries = generate_crate_build_matrix_entries( + python_entries, + runners, + config, + args.force_crate_build, + args.platform, + ) + result["crate-build"] = {"include": crate_entries} + + print(json.dumps(result)) + + +if __name__ == "__main__": + main() diff --git a/ci-runners.yaml b/ci-runners.yaml new file mode 100644 index 000000000..e81cac74c --- /dev/null +++ b/ci-runners.yaml @@ -0,0 +1,51 @@ +# Describes the runners that the CI system can use + +depot-ubuntu-22.04-4: + arch: x86_64 + platform: linux + free: false + +depot-ubuntu-22.04-arm-4: + arch: aarch64 + platform: linux + free: false + +depot-macos-latest: + arch: aarch64 + platform: darwin + free: false + +ubuntu-latest: + arch: x86_64 + platform: linux + free: true + +macos-latest: + arch: aarch64 + platform: darwin + free: true + +depot-windows-2022-8: + arch: x86_64 + platform: windows + free: false + +windows-latest-large: + arch: x86_64 + platform: windows + free: false + +windows-latest: + arch: x86_64 + platform: windows + free: true + +windows-2025-vs2026: + arch: x86_64 + platform: windows + free: true + +windows-11-arm: + arch: aarch64 + platform: windows + free: false diff --git a/ci-targets.yaml b/ci-targets.yaml new file mode 100644 index 000000000..9ffda5314 --- /dev/null +++ b/ci-targets.yaml @@ -0,0 +1,435 @@ +# Describes the targets that the CI system will build and test on. + +darwin: + aarch64-apple-darwin: + arch: aarch64 + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" + + x86_64-apple-darwin: + arch: x86_64 + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" + run: true + +linux: + aarch64-unknown-linux-gnu: + arch: aarch64 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" + + armv7-unknown-linux-gnueabi: + arch: armv7 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + + armv7-unknown-linux-gnueabihf: + arch: armv7 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + + s390x-unknown-linux-gnu: + arch: s390x + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + + ppc64le-unknown-linux-gnu: + arch: ppc64le + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + + riscv64-unknown-linux-gnu: + arch: riscv64 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + + x86_64-unknown-linux-gnu: + arch: x86_64 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" + run: true + + x86_64_v2-unknown-linux-gnu: + arch: x86_64 + arch_variant: v2 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" + run: true + + x86_64_v3-unknown-linux-gnu: + arch: x86_64 + arch_variant: v3 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" + run: true + + x86_64_v4-unknown-linux-gnu: + arch: x86_64 + arch_variant: v4 + libc: gnu + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug + - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" + run: true + + x86_64-unknown-linux-musl: + arch: x86_64 + libc: musl + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug+static + - noopt+static + - lto+static + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + run: true + + x86_64_v2-unknown-linux-musl: + arch: x86_64 + arch_variant: v2 + libc: musl + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug+static + - noopt+static + - lto+static + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + run: true + + x86_64_v3-unknown-linux-musl: + arch: x86_64 + arch_variant: v3 + libc: musl + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug+static + - noopt+static + - lto+static + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + run: true + + x86_64_v4-unknown-linux-musl: + arch: x86_64 + arch_variant: v4 + libc: musl + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + - debug+static + - noopt+static + - lto+static + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + run: true + + aarch64-unknown-linux-musl: + arch: aarch64 + libc: musl + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + build_options: + # TODO: Static support is current blocked by some compiler-rt linking issues + # - debug+static + # - noopt+static + # - lto+static + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" + run: true + +windows: + i686-pc-windows-msvc: + arch: x86 + vcvars: vcvars32.bat + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + vs_version: "2022" + build_options: + - pgo + build_options_conditional: + - options: + - freethreaded+pgo + minimum-python-version: "3.13" + + x86_64-pc-windows-msvc: + arch: x86_64 + vcvars: vcvars64.bat + python_versions: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + vs_version: "2022" + vs_version_override_conditional: + vs_version: "2026" + minimum-python-version: "3.15" + build_options: + - pgo + build_options_conditional: + - options: + - freethreaded+pgo + minimum-python-version: "3.13" + + aarch64-pc-windows-msvc: + arch: aarch64 + vcvars: vcvarsamd64_arm64.bat + python_versions: + # On 3.10, `_tkinter` is failing to be included in the build + # - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + - "3.15" + vs_version: "2022" + build_options: + - pgo + build_options_conditional: + - options: + - freethreaded+pgo + minimum-python-version: "3.13" diff --git a/cpython-unix/Makefile b/cpython-unix/Makefile index 1e3bc62f8..69dfe5534 100644 --- a/cpython-unix/Makefile +++ b/cpython-unix/Makefile @@ -4,35 +4,41 @@ OUTDIR := $(ROOT)/build BUILD := $(HERE)/build.py NULL := +SPACE := $(subst ,, ) + +ALL_PYTHON_VERSIONS := 3.10 3.11 3.12 3.13 3.14 3.15 ifndef PYBUILD_TARGET_TRIPLE $(error PYBUILD_TARGET_TRIPLE not defined) endif -ifndef PYBUILD_OPTIMIZATIONS - $(error PYBUILD_OPTIMIZATIONS not defined) +ifndef PYBUILD_BUILD_OPTIONS + $(error PYBUILD_BUILD_OPTIONS not defined) endif ifndef PYBUILD_HOST_PLATFORM $(error PYBUILD_HOST_PLATFORM not defined) endif +ifndef PYBUILD_PYTHON_SOURCE + $(error PYBUILD_PYTHON_SOURCE not defined) +endif + ifndef PYBUILD_PYTHON_VERSION $(error PYBUILD_PYTHON_VERSION not defined) endif -ifndef PYBUILD_PYTHON_MAJOR_VERSION - $(error PYBUILD_PYTHON_MAJOR_VERSION not defined) -endif +PYTHON_MAJOR_VERSION := $(subst $(SPACE),.,$(wordlist 1,2,$(subst .,$(SPACE),$(PYBUILD_PYTHON_VERSION)))) TARGET_TRIPLE := $(PYBUILD_TARGET_TRIPLE) HOST_PLATFORM := $(PYBUILD_HOST_PLATFORM) -PACKAGE_SUFFIX := $(TARGET_TRIPLE)-$(PYBUILD_OPTIMIZATIONS) +PACKAGE_SUFFIX := $(TARGET_TRIPLE)-$(PYBUILD_BUILD_OPTIONS) RUN_BUILD = $(BUILD) \ --host-platform $(HOST_PLATFORM) \ --target-triple $(TARGET_TRIPLE) \ - --optimizations $(PYBUILD_OPTIMIZATIONS) \ + --options $(PYBUILD_BUILD_OPTIONS) \ + --python-source $(PYBUILD_PYTHON_SOURCE) \ --dest-archive $@ \ $(NULL) @@ -42,7 +48,7 @@ endif # Always write out settings files. $(shell $(RUN_BUILD) placeholder_archive makefiles) -include $(OUTDIR)/Makefile.$(HOST_PLATFORM).$(TARGET_TRIPLE).$(PYBUILD_PYTHON_MAJOR_VERSION) +include $(OUTDIR)/Makefile.$(HOST_PLATFORM).$(TARGET_TRIPLE) include $(OUTDIR)/versions/VERSION.* # Always write out expanded Dockerfiles. @@ -50,8 +56,7 @@ $(shell $(RUN_BUILD) placeholder_archive dockerfiles) BASE_TOOLCHAIN_DEPENDS := \ $(if $(NEED_BINUTILS),$(OUTDIR)/binutils-$(BINUTILS_VERSION)-$(HOST_PLATFORM).tar) \ - $(if $(NEED_GCC),$(OUTDIR)/gcc-$(GCC_VERSION)-$(HOST_PLATFORM).tar) \ - $(OUTDIR)/clang-$(CLANG_VERSION)-$(HOST_PLATFORM).tar \ + $(OUTDIR)/$(CLANG_FILENAME) \ $(NULL) TOOLCHAIN_DEPENDS := \ @@ -60,84 +65,72 @@ TOOLCHAIN_DEPENDS := \ $(NULL) PYTHON_DEP_DEPENDS := \ - $(if $(PYBUILD_NO_DOCKER),,$(OUTDIR)/image-$(DOCKER_IMAGE_BUILD).tar) \ - $(if $(PYBUILD_SKIP_TOOLCHAIN),,$(TOOLCHAIN_DEPENDS)) \ + $(OUTDIR)/targets/$(TARGET_TRIPLE) \ + $(if $(PYBUILD_NO_DOCKER),,$(OUTDIR)/image-$(DOCKER_IMAGE_BUILD).$(HOST_PLATFORM).tar) \ + $(TOOLCHAIN_DEPENDS) \ $(NULL) -default: $(OUTDIR)/cpython-$(PYBUILD_PYTHON_VERSION)-$(PACKAGE_SUFFIX).tar +HOST_PYTHON_DEPENDS := $(OUTDIR)/cpython-$(PYTHON_MAJOR_VERSION)-$(CPYTHON_$(PYTHON_MAJOR_VERSION)_VERSION)-$(HOST_PLATFORM).tar + +default: $(OUTDIR)/cpython-$(CPYTHON_$(PYTHON_MAJOR_VERSION)_VERSION)-$(PACKAGE_SUFFIX).tar ifndef PYBUILD_NO_DOCKER -$(OUTDIR)/image-%.tar: $(OUTDIR)/%.Dockerfile +$(OUTDIR)/image-%.$(HOST_PLATFORM).tar: $(OUTDIR)/%.Dockerfile $(RUN_BUILD) --toolchain image-$* endif -$(OUTDIR)/binutils-$(BINUTILS_VERSION)-$(HOST_PLATFORM).tar: $(OUTDIR)/image-gcc.tar $(HERE)/build-binutils.sh - $(RUN_BUILD) --toolchain binutils - -GCC_DEPENDS := \ - $(OUTDIR)/binutils-$(BINUTILS_VERSION)-$(HOST_PLATFORM).tar \ - $(OUTDIR)/versions/VERSION.gmp \ - $(OUTDIR)/versions/VERSION.isl \ - $(OUTDIR)/versions/VERSION.mpc \ - $(OUTDIR)/versions/VERSION.mpfr \ - $(HERE)/build-gcc.sh \ - $(NULL) - -$(OUTDIR)/gcc-$(GCC_VERSION)-$(HOST_PLATFORM).tar: $(GCC_DEPENDS) - $(RUN_BUILD) --toolchain gcc - -CLANG_DEPENDS := \ - $(if $(NEED_BINUTILS),$(OUTDIR)/binutils-$(BINUTILS_VERSION)-$(HOST_PLATFORM).tar) \ - $(if $(NEED_GCC),$(OUTDIR)/gcc-$(GCC_VERSION)-$(HOST_PLATFORM).tar) \ - $(if $(PYBUILD_NO_DOCKER),,$(OUTDIR)/image-clang.tar) \ - $(HERE)/build-clang-$(HOST_PLATFORM).sh \ - $(NULL) +$(OUTDIR)/binutils-$(BINUTILS_VERSION)-$(HOST_PLATFORM).tar: $(OUTDIR)/image-$(DOCKER_IMAGE_GCC).$(HOST_PLATFORM).tar $(HERE)/build-binutils.sh + $(RUN_BUILD) --toolchain --docker-image $(DOCKER_IMAGE_GCC) binutils -$(OUTDIR)/clang-$(CLANG_VERSION)-$(HOST_PLATFORM).tar: $(CLANG_DEPENDS) - $(RUN_BUILD) --toolchain clang +$(OUTDIR)/$(CLANG_FILENAME): + $(RUN_BUILD) --toolchain clang --target-triple $(TARGET_TRIPLE) $(OUTDIR)/musl-$(MUSL_VERSION)-$(HOST_PLATFORM).tar: $(BASE_TOOLCHAIN_DEPENDS) $(HERE)/build-musl.sh - $(RUN_BUILD) --toolchain musl + $(RUN_BUILD) --toolchain musl --docker-image $(DOCKER_IMAGE_GCC) -ifeq ($(HOST_PLATFORM),linux64) +ifeq ($(HOST_PLATFORM),linux_x86_64) TOOLCHAIN_TARGET := $(OUTDIR)/musl-$(MUSL_VERSION)-$(HOST_PLATFORM).tar else - TOOLCHAIN_TARGET := $(OUTDIR)/clang-$(CLANG_VERSION)-$(HOST_PLATFORM).tar + TOOLCHAIN_TARGET := endif +empty: + toolchain: $(TOOLCHAIN_TARGET) +toolchain-image-%: $(OUTDIR)/%.Dockerfile + $(RUN_BUILD) --toolchain image-$* + +AUTOCONF_DEPENDS = \ + $(PYTHON_DEP_DEPENDS) \ + $(HERE)/build-autoconf.sh \ + $(OUTDIR)/m4-$(M4_VERSION)-$(PACKAGE_SUFFIX).tar \ + $(NULL) + +$(OUTDIR)/autoconf-$(AUTOCONF_VERSION)-$(PACKAGE_SUFFIX).tar: $(AUTOCONF_DEPENDS) + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) autoconf + $(OUTDIR)/bdb-$(BDB_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-bdb.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) bdb $(OUTDIR)/bzip2-$(BZIP2_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-bzip2.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) bzip2 -$(OUTDIR)/gdbm-$(GDBM_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-gdbm.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) gdbm - -$(OUTDIR)/gettext-$(GETTEXT_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-gettext.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) gettext - -$(OUTDIR)/inputproto-$(INPUTPROTO_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-inputproto.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) inputproto +$(OUTDIR)/expat-$(EXPAT_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-expat.sh + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) expat -$(OUTDIR)/kbproto-$(KBPROTO_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-kbproto.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) kbproto +$(OUTDIR)/libffi-3.3-$(LIBFFI_3.3_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-libffi-3.3.sh + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) libffi-3.3 $(OUTDIR)/libffi-$(LIBFFI_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-libffi.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) libffi -$(OUTDIR)/libpthread-stubs-$(LIBPTHREAD_STUBS_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-libpthread-stubs.sh $(OUTDIR)/image-$(DOCKER_IMAGE_BUILD).tar +$(OUTDIR)/libpthread-stubs-$(LIBPTHREAD_STUBS_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-libpthread-stubs.sh $(OUTDIR)/image-$(DOCKER_IMAGE_BUILD).$(HOST_PLATFORM).tar $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) libpthread-stubs LIBX11_DEPENDS = \ $(PYTHON_DEP_DEPENDS) \ $(HERE)/build-libX11.sh \ - $(OUTDIR)/xproto-$(XPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(OUTDIR)/xextproto-$(XEXTPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(OUTDIR)/kbproto-$(KBPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(OUTDIR)/inputproto-$(INPUTPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ $(OUTDIR)/libxcb-$(LIBXCB_VERSION)-$(PACKAGE_SUFFIX).tar \ $(OUTDIR)/xtrans-$(XTRANS_VERSION)-$(PACKAGE_SUFFIX).tar \ $(OUTDIR)/xorgproto-$(XORGPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ @@ -150,7 +143,7 @@ LIBXAU_DEPENDS = \ $(PYTHON_DEP_DEPENDS) \ $(HERE)/build-libXau.sh \ $(OUTDIR)/x11-util-macros-$(X11_UTIL_MACROS_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(OUTDIR)/xproto-$(XPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ + $(OUTDIR)/xorgproto-$(XORGPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ $(NULL) $(OUTDIR)/libXau-$(LIBXAU_VERSION)-$(PACKAGE_SUFFIX).tar: $(LIBXAU_DEPENDS) @@ -158,22 +151,28 @@ $(OUTDIR)/libXau-$(LIBXAU_VERSION)-$(PACKAGE_SUFFIX).tar: $(LIBXAU_DEPENDS) LIBXCB_DEPENDS = \ $(PYTHON_DEP_DEPENDS) \ + $(HOST_PYTHON_DEPENDS) \ $(HERE)/build-libxcb.sh \ - $(OUTDIR)/image-$(DOCKER_IMAGE_XCB).tar \ $(OUTDIR)/xcb-proto-$(XCB_PROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ $(OUTDIR)/libXau-$(LIBXAU_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(OUTDIR)/xproto-$(XPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(OUTDIR)/libpthread-stubs-$(LIBPTHREAD_STUBS_VERSION)-$(PACKAGE_SUFFIX).tar + $(OUTDIR)/xorgproto-$(XORGPROTO_VERSION)-$(PACKAGE_SUFFIX).tar \ + $(OUTDIR)/libpthread-stubs-$(LIBPTHREAD_STUBS_VERSION)-$(PACKAGE_SUFFIX).tar \ $(NULL) $(OUTDIR)/libxcb-$(LIBXCB_VERSION)-$(PACKAGE_SUFFIX).tar: $(LIBXCB_DEPENDS) - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_XCB) libxcb + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) --python-host-version $(PYBUILD_PYTHON_VERSION) libxcb + +$(OUTDIR)/m4-$(M4_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-m4.sh + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) m4 + +$(OUTDIR)/mpdecimal-$(MPDECIMAL_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-mpdecimal.sh + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) mpdecimal $(OUTDIR)/ncurses-$(NCURSES_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-ncurses.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) ncurses -$(OUTDIR)/openssl-$(OPENSSL_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-openssl.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) openssl +$(OUTDIR)/openssl-3.5-$(OPENSSL_3.5_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-openssl-3.5.sh + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) openssl-3.5 LIBEDIT_DEPENDS = \ $(PYTHON_DEP_DEPENDS) \ @@ -184,44 +183,30 @@ LIBEDIT_DEPENDS = \ $(OUTDIR)/libedit-$(LIBEDIT_VERSION)-$(PACKAGE_SUFFIX).tar: $(LIBEDIT_DEPENDS) $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) libedit -$(OUTDIR)/libressl-$(LIBRESSL_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-libressl.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) libressl - $(OUTDIR)/patchelf-$(PATCHELF_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-patchelf.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) patchelf -READLINE_DEPENDS = \ - $(PYTHON_DEP_DEPENDS) \ - $(OUTDIR)/ncurses-$(NCURSES_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(HERE)/build-readline.sh - -$(OUTDIR)/readline-$(READLINE_VERSION)-$(PACKAGE_SUFFIX).tar: $(READLINE_DEPENDS) - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) readline - $(OUTDIR)/sqlite-$(SQLITE_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-sqlite.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) sqlite -$(OUTDIR)/tcl-$(TCL_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-tcl.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) tcl - -TIX_DEPENDS = \ - $(HERE)/build-tix.sh \ - $(OUTDIR)/tcl-$(TCL_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(OUTDIR)/tk-$(TK_VERSION)-$(PACKAGE_SUFFIX).tar \ - $(if $(NEED_LIBX11),$(OUTDIR)/libX11-$(LIBX11_VERSION)-$(PACKAGE_SUFFIX).tar) \ +TCL_DEPENDS = \ + $(PYTHON_DEP_DEPENDS) \ + $(HERE)/build-tcl.sh \ + $(OUTDIR)/zlib-$(ZLIB_VERSION)-$(PACKAGE_SUFFIX).tar \ $(NULL) -$(OUTDIR)/tix-$(TIX_VERSION)-$(PACKAGE_SUFFIX).tar: $(TIX_DEPENDS) - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) tix +$(OUTDIR)/tcl-$(TCL_VERSION)-$(PACKAGE_SUFFIX).tar: $(TCL_DEPENDS) + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) tcl TK_DEPENDS = \ + $(HOST_PYTHON_DEPENDS) \ $(HERE)/build-tk.sh \ $(OUTDIR)/tcl-$(TCL_VERSION)-$(PACKAGE_SUFFIX).tar \ $(if $(NEED_LIBX11),$(OUTDIR)/libX11-$(LIBX11_VERSION)-$(PACKAGE_SUFFIX).tar) \ $(NULL) $(OUTDIR)/tk-$(TK_VERSION)-$(PACKAGE_SUFFIX).tar: $(TK_DEPENDS) - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_XCB) tk + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) --python-host-version $(PYBUILD_PYTHON_VERSION) tk $(OUTDIR)/uuid-$(UUID_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-uuid.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) uuid @@ -229,18 +214,12 @@ $(OUTDIR)/uuid-$(UUID_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HE $(OUTDIR)/x11-util-macros-$(X11_UTIL_MACROS_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-x11-util-macros.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) x11-util-macros -$(OUTDIR)/xcb-proto-$(XCB_PROTO_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-xcb-proto.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_XCB) xcb-proto - -$(OUTDIR)/xextproto-$(XEXTPROTO_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-xextproto.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) xextproto +$(OUTDIR)/xcb-proto-$(XCB_PROTO_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HOST_PYTHON_DEPENDS) $(HERE)/build-xcb-proto.sh + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) --python-host-version $(PYBUILD_PYTHON_VERSION) xcb-proto $(OUTDIR)/xorgproto-$(XORGPROTO_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-xorgproto.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) xorgproto -$(OUTDIR)/xproto-$(XPROTO_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-xproto.sh - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) xproto - $(OUTDIR)/xtrans-$(XTRANS_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-xtrans.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) xtrans @@ -250,37 +229,58 @@ $(OUTDIR)/xz-$(XZ_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/ $(OUTDIR)/zlib-$(ZLIB_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-zlib.sh $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) zlib -PYTHON_DEPENDS := \ - $(PYTHON_SUPPORT_FILES) \ - $(if $(NEED_BDB),$(OUTDIR)/bdb-$(BDB_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_BZIP2),$(OUTDIR)/bzip2-$(BZIP2_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_GDBM),$(OUTDIR)/gdbm-$(GDBM_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_GETTEXT),$(OUTDIR)/gettext-$(GETTEXT_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_LIBEDIT),$(OUTDIR)/libedit-$(LIBEDIT_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_LIBFFI),$(OUTDIR)/libffi-$(LIBFFI_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_LIBRESSL),$(OUTDIR)/libressl-$(LIBRESSL_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_NCURSES),$(OUTDIR)/ncurses-$(NCURSES_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_OPENSSL),$(OUTDIR)/openssl-$(OPENSSL_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_PATCHELF),$(OUTDIR)/patchelf-$(PATCHELF_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_READLINE),$(OUTDIR)/readline-$(READLINE_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_SQLITE),$(OUTDIR)/sqlite-$(SQLITE_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_TIX),$(OUTDIR)/tix-$(TIX_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_UUID),$(OUTDIR)/uuid-$(UUID_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_XZ),$(OUTDIR)/xz-$(XZ_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(if $(NEED_ZLIB),$(OUTDIR)/zlib-$(ZLIB_VERSION)-$(PACKAGE_SUFFIX).tar) \ - $(NULL) - -ALL_PYTHON_DEPENDS = \ - $(PYTHON_DEP_DEPENDS) \ - $(HERE)/build-cpython.sh \ - $(PYTHON_DEPENDS) \ - $(NULL) - -$(OUTDIR)/cpython-$(CPYTHON_3.8_VERSION)-$(PACKAGE_SUFFIX).tar: $(ALL_PYTHON_DEPENDS) - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) cpython-3.8 - -$(OUTDIR)/cpython-$(CPYTHON_3.9_VERSION)-$(PACKAGE_SUFFIX).tar: $(ALL_PYTHON_DEPENDS) - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) cpython-3.9 - -$(OUTDIR)/cpython-$(CPYTHON_3.10_VERSION)-$(PACKAGE_SUFFIX).tar: $(ALL_PYTHON_DEPENDS) - $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) cpython-3.10 +$(OUTDIR)/zstd-$(ZSTD_VERSION)-$(PACKAGE_SUFFIX).tar: $(PYTHON_DEP_DEPENDS) $(HERE)/build-zstd.sh + $(RUN_BUILD) --docker-image $(DOCKER_IMAGE_BUILD) zstd + +PYTHON_HOST_DEPENDS := \ + $(PYTHON_DEP_DEPENDS) \ + $(HERE)/build-cpython-host.sh \ + $(OUTDIR)/autoconf-$(AUTOCONF_VERSION)-$(PACKAGE_SUFFIX).tar \ + $(OUTDIR)/m4-$(M4_VERSION)-$(PACKAGE_SUFFIX).tar \ + $(NULL) + +# Each X.Y Python version has its own set of variables and targets. This independent +# definition allows multiple Python versions to be built using the same Makefile +# invocation. +define python_version_template +PYTHON_DEPENDS_$(1) := \ + $$(PYTHON_SUPPORT_FILES) \ + $$(OUTDIR)/versions/VERSION.pip \ + $$(OUTDIR)/versions/VERSION.setuptools \ + $$(OUTDIR)/cpython-$(1)-$$(CPYTHON_$(1)_VERSION)-$$(HOST_PLATFORM).tar \ + $$(if$$(NEED_AUTOCONF),$$(OUTDIR)/autoconf-$$(AUTOCONF_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_BDB),$$(OUTDIR)/bdb-$$(BDB_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_BZIP2),$$(OUTDIR)/bzip2-$$(BZIP2_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_EXPAT),$$(OUTDIR)/expat-$$(EXPAT_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_LIBEDIT),$$(OUTDIR)/libedit-$$(LIBEDIT_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_LIBFFI_3_3),$$(OUTDIR)/libffi-3.3-$$(LIBFFI_3.3_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_LIBFFI),$$(OUTDIR)/libffi-$$(LIBFFI_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_m4),$$(OUTDIR)/m4-$$(M4_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_MPDECIMAL),$$(OUTDIR)/mpdecimal-$$(MPDECIMAL_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_NCURSES),$$(OUTDIR)/ncurses-$$(NCURSES_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_OPENSSL_1_1),$$(OUTDIR)/openssl-1.1-$$(OPENSSL_1.1_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_OPENSSL_3_5),$$(OUTDIR)/openssl-3.5-$$(OPENSSL_3.5_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_PATCHELF),$$(OUTDIR)/patchelf-$$(PATCHELF_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_SQLITE),$$(OUTDIR)/sqlite-$$(SQLITE_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_TCL),$$(OUTDIR)/tcl-$$(TCL_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_TK),$$(OUTDIR)/tk-$$(TK_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_UUID),$$(OUTDIR)/uuid-$$(UUID_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_XZ),$$(OUTDIR)/xz-$$(XZ_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_ZLIB),$$(OUTDIR)/zlib-$$(ZLIB_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(if $$(NEED_ZSTD),$$(OUTDIR)/zstd-$$(ZSTD_VERSION)-$$(PACKAGE_SUFFIX).tar) \ + $$(NULL) + +ALL_PYTHON_DEPENDS_$(1) = \ + $$(PYTHON_DEP_DEPENDS) \ + $$(HERE)/build-cpython.sh \ + $$(PYTHON_DEPENDS_$(1)) \ + $$(NULL) + +$$(OUTDIR)/cpython-$(1)-$$(CPYTHON_$(1)_VERSION)-$$(HOST_PLATFORM).tar: $$(PYTHON_HOST_DEPENDS) + $$(RUN_BUILD) --docker-image $$(DOCKER_IMAGE_BUILD) cpython-$(1)-host + +$$(OUTDIR)/cpython-$$(CPYTHON_$(1)_VERSION)-$$(PACKAGE_SUFFIX).tar: $$(ALL_PYTHON_DEPENDS_$(1)) + $$(RUN_BUILD) --docker-image $$(DOCKER_IMAGE_BUILD) cpython-$(1) +endef + +$(foreach local_version,$(ALL_PYTHON_VERSIONS),$(eval $(call python_version_template,$(local_version)))) diff --git a/cpython-unix/base.Dockerfile b/cpython-unix/base.Dockerfile index 73b5ef1b3..4838db499 100644 --- a/cpython-unix/base.Dockerfile +++ b/cpython-unix/base.Dockerfile @@ -1,6 +1,6 @@ # Debian Jessie. FROM debian@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0 -MAINTAINER Gregory Szorc +LABEL org.opencontainers.image.authors="Gregory Szorc " RUN groupadd -g 1000 build && \ useradd -u 1000 -g 1000 -d /build -s /bin/bash -m build && \ @@ -17,8 +17,10 @@ ENV HOME=/build \ CMD ["/bin/bash", "--login"] WORKDIR '/build' +# Jessie's signing keys expired in late 2022. So need to add [trusted=yes] to force trust. +# Jessie stopped publishing snapshots in March 2023. RUN for s in debian_jessie debian_jessie-updates debian-security_jessie/updates; do \ - echo "deb http://snapshot.debian.org/archive/${s%_*}/20210404T083057Z/ ${s#*_} main"; \ + echo "deb [trusted=yes] http://snapshot.debian.org/archive/${s%_*}/20230322T152120Z/ ${s#*_} main"; \ done > /etc/apt/sources.list && \ ( echo 'quiet "true";'; \ echo 'APT::Get::Assume-Yes "true";'; \ @@ -27,8 +29,12 @@ RUN for s in debian_jessie debian_jessie-updates debian-security_jessie/updates; echo 'Acquire::Retries "5";'; \ ) > /etc/apt/apt.conf.d/99cpython-portable -RUN ( echo 'amd64'; \ - echo 'i386'; \ - ) > /var/lib/dpkg/arch - -RUN apt-get update +# apt iterates all available file descriptors up to rlim_max and calls +# fcntl(fd, F_SETFD, FD_CLOEXEC). This can result in millions of system calls +# (we've seen 1B in the wild) and cause operations to take seconds to minutes. +# Setting a fd limit mitigates. +# +# Attempts at enforcing the limit globally via /etc/security/limits.conf and +# /root/.bashrc were not successful. Possibly because container image builds +# don't perform a login or use a shell the way we expect. +RUN ulimit -n 10000 && apt-get update diff --git a/cpython-unix/base.debian9.Dockerfile b/cpython-unix/base.debian9.Dockerfile new file mode 100644 index 000000000..28078a59b --- /dev/null +++ b/cpython-unix/base.debian9.Dockerfile @@ -0,0 +1,38 @@ +# Debian Stretch. +FROM debian@sha256:c5c5200ff1e9c73ffbf188b4a67eb1c91531b644856b4aefe86a58d2f0cb05be +LABEL org.opencontainers.image.authors="Gregory Szorc " + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -d /build -s /bin/bash -m build && \ + mkdir /tools && \ + chown -R build:build /build /tools + +ENV HOME=/build \ + SHELL=/bin/bash \ + USER=build \ + LOGNAME=build \ + HOSTNAME=builder \ + DEBIAN_FRONTEND=noninteractive + +CMD ["/bin/bash", "--login"] +WORKDIR '/build' + +RUN for s in debian_stretch debian_stretch-updates debian-security_stretch/updates; do \ + echo "deb http://snapshot.debian.org/archive/${s%_*}/20230423T032736Z/ ${s#*_} main"; \ + done > /etc/apt/sources.list && \ + ( echo 'quiet "true";'; \ + echo 'APT::Get::Assume-Yes "true";'; \ + echo 'APT::Install-Recommends "false";'; \ + echo 'Acquire::Check-Valid-Until "false";'; \ + echo 'Acquire::Retries "5";'; \ + ) > /etc/apt/apt.conf.d/99cpython-portable + +# apt iterates all available file descriptors up to rlim_max and calls +# fcntl(fd, F_SETFD, FD_CLOEXEC). This can result in millions of system calls +# (we've seen 1B in the wild) and cause operations to take seconds to minutes. +# Setting a fd limit mitigates. +# +# Attempts at enforcing the limit globally via /etc/security/limits.conf and +# /root/.bashrc were not successful. Possibly because container image builds +# don't perform a login or use a shell the way we expect. +RUN ulimit -n 10000 && apt-get update diff --git a/cpython-unix/build-autoconf.sh b/cpython-unix/build-autoconf.sh new file mode 100755 index 000000000..483bdd12a --- /dev/null +++ b/cpython-unix/build-autoconf.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -ex + +ROOT=$(pwd) + +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH + +tar -xf "autoconf-${AUTOCONF_VERSION}.tar.gz" + +pushd "autoconf-${AUTOCONF_VERSION}" + +CC="${HOST_CC}" CXX="${HOST_CXX}" CFLAGS="${EXTRA_HOST_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_HOST_CFLAGS} -fPIC" LDFLAGS="${EXTRA_HOST_LDFLAGS}" ./configure \ + --build="${BUILD_TRIPLE}" \ + --prefix=/tools/host + +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-bdb.sh b/cpython-unix/build-bdb.sh index b6ed30a83..932bc4ae6 100755 --- a/cpython-unix/build-bdb.sh +++ b/cpython-unix/build-bdb.sh @@ -5,20 +5,34 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf db-${BDB_VERSION}.tar.gz +tar -xf "db-${BDB_VERSION}.tar.gz" -pushd db-${BDB_VERSION}/build_unix +pushd "db-${BDB_VERSION}/build_unix" -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" ../dist/configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ +CONFIGURE_FLAGS="--enable-dbm --disable-shared" + +# configure looks for pthread_yield(), which was dropped from glibc 2.34. +# Its replacement is sched_yield(). Fortunately, bdb's source code will fall +# back to sched_yield() if pthread_yield() isn't available. So we just lie +# to configure and tell it pthread_yield() isn't available. +CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_pthread_yield=no" + +CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" + +if [ "${CC}" = "clang" ]; then + # deprecated-non-prototype gets very chatty with Clang 15. Suppress it. + CFLAGS="${CFLAGS} -Wno-deprecated-non-prototype" +fi + +CFLAGS="${CFLAGS}" CPPFLAGS="${CFLAGS}" ../dist/configure \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ - --enable-dbm \ - --disable-shared + ${CONFIGURE_FLAGS} -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-binutils.sh b/cpython-unix/build-binutils.sh index 12ad45c4a..f57e13028 100755 --- a/cpython-unix/build-binutils.sh +++ b/cpython-unix/build-binutils.sh @@ -5,31 +5,28 @@ set -ex -ROOT=$(pwd) -SCCACHE="${ROOT}/sccache" - cd /build -tar -xf binutils-${BINUTILS_VERSION}.tar.xz +tar -xf "binutils-${BINUTILS_VERSION}.tar.xz" mkdir binutils-objdir pushd binutils-objdir -EXTRA_VARS= - -if [ -x "${SCCACHE}" ]; then - "${SCCACHE}" --start-server - export CC="${SCCACHE} /usr/bin/gcc" - export STAGE_CC_WRAPPER="${SCCACHE}" +if [ "$(uname -m)" = "x86_64" ]; then + triple="x86_64-unknown-linux-gnu" +else + triple="aarch64-unknown-linux-gnu" fi -../binutils-${BINUTILS_VERSION}/configure \ - --build=x86_64-unknown-linux-gnu \ +# gprofng requires a bison newer than what we have. So just disable it. +"../binutils-${BINUTILS_VERSION}/configure" \ + --build=${triple} \ --prefix=/tools/host \ --enable-plugins \ + --enable-gprofng=no \ --disable-nls \ --with-sysroot=/ -make -j `nproc` -make install -j `nproc` DESTDIR=/build/out +make -j "$(nproc)" +make install -j "$(nproc)" DESTDIR=/build/out popd diff --git a/cpython-unix/build-bzip2.sh b/cpython-unix/build-bzip2.sh index ab9c08b5f..075af9f56 100755 --- a/cpython-unix/build-bzip2.sh +++ b/cpython-unix/build-bzip2.sh @@ -5,23 +5,31 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -if [ -e ${TOOLS_PATH}/host/bin/${TOOLCHAIN_PREFIX}ar ]; then +if [ -e "${TOOLS_PATH}/host/bin/${TOOLCHAIN_PREFIX}ar" ]; then AR=${TOOLS_PATH}/host/bin/${TOOLCHAIN_PREFIX}ar else AR=ar fi -tar -xf bzip2-${BZIP2_VERSION}.tar.gz +tar -xf "bzip2-${BZIP2_VERSION}.tar.gz" -pushd bzip2-${BZIP2_VERSION} +pushd "bzip2-${BZIP2_VERSION}" -make -j ${NUM_CPUS} install \ - AR=${AR} \ +make -j "${NUM_CPUS}" install \ + AR="${AR}" \ CC="${CC}" \ CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" \ LDFLAGS="${EXTRA_TARGET_LDFLAGS}" \ - PREFIX=${ROOT}/out/tools/deps + PREFIX="${ROOT}/out/tools/deps" + +# bzip2's Makefile creates these symlinks with absolute paths to the build +# directory, which break after archive extraction. Only libbz2.a and headers +# are needed for building CPython - remove the shell utility symlinks. +rm "${ROOT}/out/tools/deps/bin/bzcmp" \ + "${ROOT}/out/tools/deps/bin/bzless" \ + "${ROOT}/out/tools/deps/bin/bzegrep" \ + "${ROOT}/out/tools/deps/bin/bzfgrep" diff --git a/cpython-unix/build-clang-linux64.sh b/cpython-unix/build-clang-linux64.sh deleted file mode 100755 index f10ac8cb6..000000000 --- a/cpython-unix/build-clang-linux64.sh +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -# We build the host/main Clang initially using GCC. Then we rebuild -# Clang using Clang. -# -# The behavior of library search paths is a bit wonky. -# -# The binutils/gcc/libstdc++ that we use to build are in a non-standard -# location: /tools/gcc. Furthermore, we want the produced Clang -# distribution to be self-contained and not have dependencies on -# a GCC install. -# -# To solve the latter requirement, we copy various GCC libraries -# and includes into the Clang install directory. When done the way -# we have, Clang automagically finds the header files. And since -# binaries have an rpath of $ORIGIN/../lib, libstdc++ and libgcc_s -# can be found at load time. -# -# However, as part of building itself, Clang executes binaries that -# it itself just built. These binaries need to load a modern libstdc++. -# (The system's libstdc++ is too old.) Since these just-built binaries -# aren't in an install location, the $ORIGIN/../lib rpath won't work. -# So, we set LD_LIBRARY_PATH when building so the modern libstdc++ -# can be located. -# -# Furthermore, Clang itself needs to link against a modern libstdc++. -# But the system library search paths take precedence when invoking -# the linker via clang. We force linking against a modern libstdc++ -# by passing -L to the linker when building Clang. -# -# All of these tricks combine to produce a Clang distribution with -# GNU libstdc++ and that uses GNU binutils. - -set -ex - -ROOT=$(pwd) -SCCACHE="${ROOT}/sccache" - -mkdir /tools/extra -tar -C /tools/extra --strip-components=1 -xf ${ROOT}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz - -unzip ninja-linux.zip -mv ninja /tools/extra/bin/ - -export PATH=/tools/extra/bin:/tools/host/bin:$PATH - -mkdir llvm -pushd llvm -tar --strip-components=1 -xf ${ROOT}/llvm-${LLVM_VERSION}.src.tar.xz -popd - -mkdir llvm/tools/clang -pushd llvm/tools/clang -tar --strip-components=1 -xf ${ROOT}/clang-${CLANG_VERSION}.src.tar.xz -popd - -mkdir llvm/tools/lld -pushd llvm/tools/lld -tar --strip-components=1 -xf ${ROOT}/lld-${LLD_VERSION}.src.tar.xz -popd - -mkdir llvm/projects/compiler-rt -pushd llvm/projects/compiler-rt -tar --strip-components=1 -xf ${ROOT}/compiler-rt-${CLANG_COMPILER_RT_VERSION}.src.tar.xz -popd - -mkdir llvm/projects/libcxx -pushd llvm/projects/libcxx -tar --strip-components=1 -xf ${ROOT}/libcxx-${LIBCXX_VERSION}.src.tar.xz -popd - -mkdir llvm/projects/libcxxabi -pushd llvm/projects/libcxxabi -tar --strip-components=1 -xf ${ROOT}/libcxxabi-${LIBCXXABI_VERSION}.src.tar.xz -popd - -mkdir libunwind -pushd libunwind -tar --strip-components=1 -xf ${ROOT}/libunwind-${LIBUNWIND_VERSION}.src.tar.xz -popd - -mkdir llvm-objdir -pushd llvm-objdir - -EXTRA_FLAGS= - -if [ -x "${SCCACHE}" ]; then - "${SCCACHE}" --start-server - EXTRA_FLAGS="${EXTRA_FLAGS} -DCMAKE_C_COMPILER_LAUNCHER=${SCCACHE} -DCMAKE_CXX_COMPILER_LAUNCHER=${SCCACHE}" -fi - -if [ -n "${CI}" ]; then - NUM_JOBS=${NUM_JOBS_AGGRESSIVE} -else - NUM_JOBS=${NUM_CPUS} -fi - -# Stage 1: Build with GCC. -mkdir stage1 -pushd stage1 -cmake \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=/tools/clang-stage1 \ - -DCMAKE_C_COMPILER=/tools/host/bin/gcc \ - -DCMAKE_CXX_COMPILER=/tools/host/bin/g++ \ - -DCMAKE_ASM_COMPILER=/tools/host/bin/gcc \ - -DCMAKE_CXX_FLAGS="-Wno-cast-function-type" \ - -DCMAKE_EXE_LINKER_FLAGS="-Wl,-Bsymbolic-functions" \ - -DCMAKE_SHARED_LINKER_FLAGS="-Wl,-Bsymbolic-functions" \ - -DLLVM_TARGETS_TO_BUILD=X86 \ - -DLLVM_TOOL_LIBCXX_BUILD=ON \ - -DLIBCXX_LIBCPPABI_VERSION="" \ - -DLLVM_BINUTILS_INCDIR=/tools/host/include \ - -DLLVM_LINK_LLVM_DYLIB=ON \ - -DLLVM_INSTALL_UTILS=ON \ - ${EXTRA_FLAGS} \ - ../../llvm - -LD_LIBRARY_PATH=/tools/host/lib64 ninja -j ${NUM_JOBS} install - -mkdir -p /tools/clang-stage1/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION} -cp -av /tools/host/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION}/* /tools/clang-stage1/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION}/ -cp -av /tools/host/lib64/* /tools/clang-stage1/lib/ -mkdir -p /tools/clang-stage1/lib32 -cp -av /tools/host/lib32/* /tools/clang-stage1/lib32/ -cp -av /tools/host/include/* /tools/clang-stage1/include/ - -popd - -find /tools/clang-stage1 | sort - -# Stage 2: Build with GCC built Clang. -mkdir stage2 -pushd stage2 -cmake \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=/tools/clang-stage2 \ - -DCMAKE_C_COMPILER=/tools/clang-stage1/bin/clang \ - -DCMAKE_CXX_COMPILER=/tools/clang-stage1/bin/clang++ \ - -DCMAKE_ASM_COMPILER=/tools/clang-stage1/bin/clang \ - -DCMAKE_C_FLAGS="-fPIC" \ - -DCMAKE_CXX_FLAGS="-fPIC -Qunused-arguments -L/tools/clang-stage1/lib" \ - -DCMAKE_EXE_LINKER_FLAGS="-Wl,-Bsymbolic-functions -L/tools/clang-stage1/lib" \ - -DCMAKE_SHARED_LINKER_FLAGS="-Wl,-Bsymbolic-functions -L/tools/clang-stage1/lib" \ - -DLLVM_TARGETS_TO_BUILD=X86 \ - -DLLVM_TOOL_LIBCXX_BUILD=ON \ - -DLIBCXX_LIBCPPABI_VERSION="" \ - -DLLVM_BINUTILS_INCDIR=/tools/host/include \ - -DLLVM_LINK_LLVM_DYLIB=ON \ - -DLLVM_INSTALL_UTILS=ON \ - ${EXTRA_FLAGS} \ - ../../llvm - -LD_LIBRARY_PATH=/tools/clang-stage1/lib ninja -j ${NUM_JOBS} install - -mkdir -p /tools/clang-stage2/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION} -cp -av /tools/host/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION}/* /tools/clang-stage2/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION}/ -cp -av /tools/host/lib64/* /tools/clang-stage2/lib/ -mkdir -p /tools/clang-stage2/lib32 -cp -av /tools/host/lib32/* /tools/clang-stage2/lib32/ -cp -av /tools/host/include/* /tools/clang-stage2/include/ - -popd - -find /tools/clang-stage2 | sort - -# Stage 3: Build with Clang built Clang. -# -# We remove LLVM_TARGETS_TO_BUILD from this configuration, enabling -# support for all targets. The stage 1 and 2 builds don't benefit from -# non-native target support, which is why we exclude host target support -# above. -mkdir stage3 -pushd stage3 -cmake \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=/tools/clang-linux64 \ - -DCMAKE_C_COMPILER=/tools/clang-stage2/bin/clang \ - -DCMAKE_CXX_COMPILER=/tools/clang-stage2/bin/clang++ \ - -DCMAKE_ASM_COMPILER=/tools/clang-stage2/bin/clang \ - -DCMAKE_C_FLAGS="-fPIC" \ - -DCMAKE_CXX_FLAGS="-fPIC -Qunused-arguments -L/tools/clang-stage2/lib" \ - -DCMAKE_EXE_LINKER_FLAGS="-Wl,-Bsymbolic-functions -L/tools/clang-stage2/lib" \ - -DCMAKE_SHARED_LINKER_FLAGS="-Wl,-Bsymbolic-functions -L/tools/clang-stage2/lib" \ - -DLLVM_TOOL_LIBCXX_BUILD=ON \ - -DLIBCXX_LIBCPPABI_VERSION="" \ - -DLLVM_BINUTILS_INCDIR=/tools/host/include \ - -DLLVM_LINK_LLVM_DYLIB=ON \ - -DLLVM_INSTALL_UTILS=ON \ - ${EXTRA_FLAGS} \ - ../../llvm - -LD_LIBRARY_PATH=/tools/clang-stage2/lib DESTDIR=${ROOT}/out ninja -j ${NUM_JOBS} install - -mkdir -p ${ROOT}/out/tools/clang-linux64/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION} -cp -av /tools/host/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION}/* ${ROOT}/out/tools/clang-linux64/lib/gcc/x86_64-unknown-linux-gnu/${GCC_VERSION}/ -cp -av /tools/host/lib64/* ${ROOT}/out/tools/clang-linux64/lib/ -mkdir -p ${ROOT}/out/tools/clang-linux64/lib32/ -cp -av /tools/host/lib32/* ${ROOT}/out/tools/clang-linux64/lib32/ -cp -av /tools/host/include/* ${ROOT}/out/tools/clang-linux64/include/ - -popd - -# Move out of objdir -popd diff --git a/cpython-unix/build-clang-macos.sh b/cpython-unix/build-clang-macos.sh deleted file mode 100755 index 94ad4fcb5..000000000 --- a/cpython-unix/build-clang-macos.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=$(pwd) -SCCACHE="${ROOT}/sccache" - -tar --strip-components=1 -xf ${ROOT}/cmake-${CMAKE_VERSION}-macos-universal.tar.gz - -mkdir ninja -pushd ninja -unzip ${ROOT}/ninja-mac.zip -popd - -export PATH=${ROOT}/CMake.app/Contents/bin:${ROOT}/ninja/:${PATH} - -mkdir llvm -pushd llvm -tar --strip-components=1 -xf ${ROOT}/llvm-${LLVM_VERSION}.src.tar.xz -popd - -mkdir llvm/tools/clang -pushd llvm/tools/clang -tar --strip-components=1 -xf ${ROOT}/clang-${CLANG_VERSION}.src.tar.xz -popd - -mkdir llvm/tools/lld -pushd llvm/tools/lld -tar --strip-components=1 -xf ${ROOT}/lld-${LLD_VERSION}.src.tar.xz -popd - -mkdir llvm/projects/compiler-rt -pushd llvm/projects/compiler-rt -tar --strip-components=1 -xf ${ROOT}/compiler-rt-${CLANG_COMPILER_RT_VERSION}.src.tar.xz -popd - -mkdir llvm/projects/libcxx -pushd llvm/projects/libcxx -tar --strip-components=1 -xf ${ROOT}/libcxx-${LIBCXX_VERSION}.src.tar.xz -popd - -mkdir llvm/projects/libcxxabi -pushd llvm/projects/libcxxabi -tar --strip-components=1 -xf ${ROOT}/libcxxabi-${LIBCXXABI_VERSION}.src.tar.xz -popd - -mkdir llvm-objdir -pushd llvm-objdir - -# Configure a compiler wrapper if one is defined. -if [ -x "${SCCACHE}" ]; then - EXTRA_FLAGS="${EXTRA_FLAGS} -DCMAKE_C_COMPILER_LAUNCHER=${SCCACHE} -DCMAKE_CXX_COMPILER_LAUNCHER=${SCCACHE}" -fi - -# Stage 1: Build with system Clang -mkdir stage1 -pushd stage1 -cmake \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=/tools/clang-macos \ - -DCMAKE_C_COMPILER=/usr/bin/clang \ - -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \ - -DCMAKE_ASM_COMPILER=/usr/bin/clang \ - -DLLVM_ENABLE_LIBCXX=ON \ - -DLLVM_OPTIMIZED_TABLEGEN=ON \ - -DLLVM_LINK_LLVM_DYLIB=ON \ - ${EXTRA_FLAGS} \ - ../../llvm - -if [ -n "${CI}" ]; then - NUM_JOBS=${NUM_JOBS_AGGRESSIVE} -else - NUM_JOBS=${NUM_CPUS} -fi - -DESTDIR=${ROOT}/out ninja -j ${NUM_JOBS} install - -# We should arguably do a 2nd build using Clang to build Clang. - -# Move out of objdir -popd diff --git a/cpython-unix/build-cpython-host.sh b/cpython-unix/build-cpython-host.sh new file mode 100755 index 000000000..e81c9242d --- /dev/null +++ b/cpython-unix/build-cpython-host.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -ex + +export ROOT=$(pwd) + +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:${TOOLS_PATH}/deps/bin:$PATH + +# autoconf has some paths hardcoded into scripts. These paths just work in +# the containerized build environment. But from macOS the paths are wrong. +# Explicitly point to the proper path via environment variable overrides. +export AUTOCONF=${TOOLS_PATH}/host/bin/autoconf +export AUTOHEADER=${TOOLS_PATH}/host/bin/autoheader +export AUTOM4TE=${TOOLS_PATH}/host/bin/autom4te +export autom4te_perllibdir=${TOOLS_PATH}/host/share/autoconf +export AC_MACRODIR=${TOOLS_PATH}/host/share/autoconf +export M4=${TOOLS_PATH}/host/bin/m4 +export trailer_m4=${TOOLS_PATH}/host/share/autoconf/autoconf/trailer.m4 + +# The share/autoconf/autom4te.cfg file also hard-codes some paths. Rewrite +# those to the real tools path. +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + sed_args="-i '' -e" +else + sed_args="-i" +fi + +sed ${sed_args} "s|/tools/host|${TOOLS_PATH}/host|g" "${TOOLS_PATH}/host/share/autoconf/autom4te.cfg" + +tar -xf "Python-${PYTHON_VERSION}.tar.xz" + +pushd "Python-${PYTHON_VERSION}" + +autoconf + +# When cross-compiling, we need to build a host Python that has working zlib +# and ctypes extensions, otherwise various things fail. (`make install` fails +# without zlib and setuptools / pip used by target install fail due to missing +# ctypes.) +# +# On Apple, the dependencies are present in the Apple SDK and missing extensions +# are built properly by setup.py. However, on other platforms, we need to teach +# the host build system where to find things. +# +# Adding /usr paths on Linux is a bit funky. This is a side-effect or our +# custom Clang purposefully omitting default system search paths to help +# prevent unwanted dependencies from sneaking in. +case "${BUILD_TRIPLE}" in + x86_64-unknown-linux-gnu) + EXTRA_HOST_CFLAGS="${EXTRA_HOST_CFLAGS} -I/usr/include/x86_64-linux-gnu" + EXTRA_HOST_CPPFLAGS="${EXTRA_HOST_CPPFLAGS} -I/usr/include/x86_64-linux-gnu" + EXTRA_HOST_LDFLAGS="${EXTRA_HOST_LDFLAGS} -L/usr/lib/x86_64-linux-gnu" + ;; + aarch64-unknown-linux-gnu) + EXTRA_HOST_CFLAGS="${EXTRA_HOST_CFLAGS} -I/usr/include/aarch64-linux-gnu" + EXTRA_HOST_CPPFLAGS="${EXTRA_HOST_CPPFLAGS} -I/usr/include/aarch64-linux-gnu" + EXTRA_HOST_LDFLAGS="${EXTRA_HOST_LDFLAGS} -L/usr/lib/aarch64-linux-gnu" + ;; + *) + ;; +esac + +EXTRA_CONFIGURE_FLAGS= + +# We may not have a usable libraries to build against. Forcefully disable extensions +# that may not build. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + for m in _hashlib _ssl; do + EXTRA_CONFIGURE_FLAGS="${EXTRA_CONFIGURE_FLAGS} py_cv_module_${m}=n/a" + done + fi + +CC="${HOST_CC}" CXX="${HOST_CXX}" CFLAGS="${EXTRA_HOST_CFLAGS}" CPPFLAGS="${EXTRA_HOST_CFLAGS}" LDFLAGS="${EXTRA_HOST_LDFLAGS}" ./configure \ + --prefix /tools/host \ + --without-ensurepip \ + ${EXTRA_CONFIGURE_FLAGS} + +# Ideally we'd do `make install` here and be done with it. But there's a race +# condition in CPython's build system related to directory creation that gets +# tickled when we do this. https://github.com/python/cpython/issues/109796. +make -j "${NUM_CPUS}" +make -j sharedinstall DESTDIR="${ROOT}/out" +make -j install DESTDIR="${ROOT}/out" + +popd diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 7448131a5..77f9ec5cd 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -5,484 +5,351 @@ set -ex -export ROOT=`pwd` +export ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:${TOOLS_PATH}/deps/bin:$PATH +# Ensure that `pkg-config` (run by CPython's configure script) can find our dependencies +export PKG_CONFIG_PATH=${TOOLS_PATH}/deps/share/pkgconfig:${TOOLS_PATH}/deps/lib/pkgconfig + +# Ensure that `pkg-config` invocations include the static libraries +export PKG_CONFIG="pkg-config --static" + # configure somehow has problems locating llvm-profdata even though it is in # PATH. The macro it is using allows us to specify its path via an # environment variable. export LLVM_PROFDATA=${TOOLS_PATH}/${TOOLCHAIN}/bin/llvm-profdata +# autoconf has some paths hardcoded into scripts. These paths just work in +# the containerized build environment. But from macOS the paths are wrong. +# Explicitly point to the proper path via environment variable overrides. +export AUTOCONF=${TOOLS_PATH}/host/bin/autoconf +export AUTOHEADER=${TOOLS_PATH}/host/bin/autoheader +export AUTOM4TE=${TOOLS_PATH}/host/bin/autom4te +export autom4te_perllibdir=${TOOLS_PATH}/host/share/autoconf +export AC_MACRODIR=${TOOLS_PATH}/host/share/autoconf +export M4=${TOOLS_PATH}/host/bin/m4 +export trailer_m4=${TOOLS_PATH}/host/share/autoconf/autoconf/trailer.m4 + +# The share/autoconf/autom4te.cfg file also hard-codes some paths. Rewrite +# those to the real tools path. +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + sed_args=(-i '' -e) +else + sed_args=(-i) +fi + +sed "${sed_args[@]}" "s|/tools/host|${TOOLS_PATH}/host|g" "${TOOLS_PATH}/host/share/autoconf/autom4te.cfg" + # We force linking of external static libraries by removing the shared # libraries. This is hacky. But we're building in a temporary container # and it gets the job done. -find ${TOOLS_PATH}/deps -name '*.so*' -exec rm {} \; - -tar -xf Python-${PYTHON_VERSION}.tar.xz -tar -xf setuptools-${SETUPTOOLS_VERSION}.tar.gz -tar -xf pip-${PIP_VERSION}.tar.gz - -# If we are cross-compiling, we need to build a host Python to use during -# the build. -if [ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" ]; then - pushd "Python-${PYTHON_VERSION}" - - # When cross-compiling, we need to build a host Python that has working zlib - # and ctypes extensions, otherwise various things fail. (`make install` fails - # without zlib and setuptools / pip used by target install fail due to missing - # ctypes.) - # - # On Apple, the dependencies are present in the Apple SDK and missing extensions - # are built properly by setup.py. However, on other platforms, we need to teach - # the host build system where to find things. - # - # Adding /usr paths on Linux is a bit funky. This is a side-effect or our - # custom Clang purposefully omitting default system search paths to help - # prevent unwanted dependencies from sneaking in. - case "${TARGET_TRIPLE}" in - i686-unknown-linux-gnu) - EXTRA_HOST_CFLAGS="${EXTRA_HOST_CFLAGS} -I/usr/include/x86_64-linux-gnu" - EXTRA_HOST_CPPFLAGS="${EXTRA_HOST_CPPFLAGS} -I/usr/include/x86_64-linux-gnu" - EXTRA_HOST_LDFLAGS="${EXTRA_HOST_LDFLAGS} -L/usr/lib/x86_64-linux-gnu" - ;; - *) - ;; - esac - - CC="${HOST_CC}" CFLAGS="${EXTRA_HOST_CFLAGS}" CPPFLAGS="${EXTRA_HOST_CFLAGS}" LDFLAGS="${EXTRA_HOST_LDFLAGS}" ./configure --prefix "${TOOLS_PATH}/pyhost" - - # When building on macOS 10.15 (and possibly earlier) using the 11.0 - # SDK, the _ctypes extension fails to import due to a missing symbol on - # _dyld_shared_cache_contains_path. The cause of this is unclear. - # But the missing _ctypes extension causes problems later in the - # build. - # - # We work around this by disabling the feature, which isn't required - # for our host builds. - if [[ "${PYBUILD_PLATFORM}" = "macos" ]]; then - sed -i "" "s/#define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH 1//g" pyconfig.h || true - fi - - make -j "${NUM_CPUS}" install - - # configure will look for a pythonX.Y executable. Install our host Python - # at the front of PATH. - export PATH="${TOOLS_PATH}/pyhost/bin:${PATH}" - - popd - # Nuke and re-pave the source directory out of paranoia. - rm -rf "Python-${PYTHON_VERSION}" - tar -xf "Python-${PYTHON_VERSION}.tar.xz" -fi +find "${TOOLS_PATH}/deps" -name '*.so*' -a \! \( -name 'libtcl*.so*' -or -name 'libtk*.so*' \) -exec rm {} \; + +tar -xf "Python-${PYTHON_VERSION}.tar.xz" + +PIP_WHEEL="${ROOT}/pip-${PIP_VERSION}-py3-none-any.whl" +SETUPTOOLS_WHEEL="${ROOT}/setuptools-${SETUPTOOLS_VERSION}-py3-none-any.whl" cat Setup.local -mv Setup.local Python-${PYTHON_VERSION}/Modules/Setup.local +mv Setup.local "Python-${PYTHON_VERSION}/Modules/Setup.local" cat Makefile.extra -pushd Python-${PYTHON_VERSION} +pushd "Python-${PYTHON_VERSION}" # configure doesn't support cross-compiling on Apple. Teach it. -patch -p1 << "EOF" -diff --git a/configure b/configure -index 1252335472..6665645839 100755 ---- a/configure -+++ b/configure -@@ -3301,6 +3301,15 @@ then - *-*-cygwin*) - ac_sys_system=Cygwin - ;; -+ *-apple-ios*) -+ ac_sys_system=iOS -+ ;; -+ *-apple-tvos*) -+ ac_sys_system=tvOS -+ ;; -+ *-apple-watchos*) -+ ac_sys_system=watchOS -+ ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -3351,6 +3360,19 @@ if test "$cross_compiling" = yes; then - *-*-cygwin*) - _host_cpu= - ;; -+ *-*-darwin*) -+ _host_cpu= -+ ;; -+ *-apple-*) -+ case "$host_cpu" in -+ arm*) -+ _host_cpu=arm -+ ;; -+ *) -+ _host_cpu=$host_cpu -+ ;; -+ esac -+ ;; - *-*-vxworks*) - _host_cpu=$host_cpu - ;; -@@ -3359,7 +3381,22 @@ if test "$cross_compiling" = yes; then - MACHDEP="unknown" - as_fn_error $? "cross build not supported for $host" "$LINENO" 5 - esac -- _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" -+ -+ case "$host" in -+ # The _PYTHON_HOST_PLATFORM environment variable is used to -+ # override the platform name in distutils and sysconfig when -+ # cross-compiling. On Apple, the platform name expansion logic -+ # is non-trivial, including renaming MACHDEP=darwin to macosx -+ # and including the deployment target (or current OS version if -+ # not set). Our hack here is not generic, but gets the job done -+ # for python-build-standalone's cross-compile use cases. -+ aarch64-apple-darwin*) -+ _PYTHON_HOST_PLATFORM="macosx-${MACOSX_DEPLOYMENT_TARGET}-arm64" -+ ;; -+ *) -+ _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" -+ esac -+ - fi - - # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -5968,7 +6005,7 @@ $as_echo "#define Py_ENABLE_SHARED 1" >>confdefs.h - BLDLIBRARY='-Wl,+b,$(LIBDIR) -L. -lpython$(LDVERSION)' - RUNSHARED=SHLIB_PATH=`pwd`${SHLIB_PATH:+:${SHLIB_PATH}} - ;; -- Darwin*) -+ Darwin*|iOS*|tvOS*|watchOS*) - LDLIBRARY='libpython$(LDVERSION).dylib' - BLDLIBRARY='-L. -lpython$(LDVERSION)' - RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} -@@ -6205,16 +6242,6 @@ esac - fi - fi - --if test "$cross_compiling" = yes; then -- case "$READELF" in -- readelf|:) -- as_fn_error $? "readelf for the host is required for cross builds" "$LINENO" 5 -- ;; -- esac --fi -- -- -- - case $MACHDEP in - hp*|HP*) - # install -d does not work on HP-UX -@@ -9541,6 +9568,11 @@ then - BLDSHARED="$LDSHARED" - fi - ;; -+ iOS*|tvOS*|watchOS*) -+ LDSHARED='$(CC) -bundle -undefined dynamic_lookup' -+ LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' -+ BLDSHARED="$LDSHARED" -+ ;; - Linux*|GNU*|QNX*|VxWorks*) - LDSHARED='$(CC) -shared' - LDCXXSHARED='$(CXX) -shared';; -EOF +if [[ "${PYBUILD_PLATFORM}" = macos* && -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_13}" ]]; then + if [ "${PYTHON_MAJMIN_VERSION}" = "3.12" ]; then + patch -p1 -i "${ROOT}/patch-apple-cross-3.12.patch" + elif [ "${PYTHON_MAJMIN_VERSION}" = "3.13" ]; then + patch -p1 -i "${ROOT}/patch-apple-cross-3.13.patch" + else + patch -p1 -i "${ROOT}/patch-apple-cross.patch" + fi +fi -# This patch is slightly different on Python 3.10+. -if [ "${PYTHON_MAJMIN_VERSION}" = "3.10" ]; then - patch -p1 << "EOF" -diff --git a/configure b/configure -index 2d379feb4b..3eb8dbe9ea 100755 ---- a/configure -+++ b/configure -@@ -3426,6 +3448,12 @@ $as_echo "#define _BSD_SOURCE 1" >>confdefs.h - define_xopen_source=no;; - Darwin/[12][0-9].*) - define_xopen_source=no;; -+ iOS/*) -+ define_xopen_source=no;; -+ tvOS/*) -+ define_xopen_source=no;; -+ watchOS/*) -+ define_xopen_source=no;; - # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from - # defining NI_NUMERICHOST. - QNX/6.3.2) -EOF -else - patch -p1 << "EOF" -diff --git a/configure b/configure -index 2d379feb4b..3eb8dbe9ea 100755 ---- a/configure -+++ b/configure -@@ -3426,6 +3448,12 @@ $as_echo "#define _BSD_SOURCE 1" >>confdefs.h - define_xopen_source=no;; - Darwin/[12][0-9].*) - define_xopen_source=no;; -+ iOS/*) -+ define_xopen_source=no;; -+ tvOS/*) -+ define_xopen_source=no;; -+ watchOS/*) -+ define_xopen_source=no;; - # On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but - # used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined - # or has another value. By not (re)defining it, the defaults come in place. -EOF +# configure doesn't support cross-compiling on LoongArch. Teach it. +if [ "${PYBUILD_PLATFORM}" != "macos" ]; then + case "${PYTHON_MAJMIN_VERSION}" in + 3.10|3.11) + patch -p1 -i "${ROOT}/patch-configure-add-loongarch-triplet.patch" + ;; + esac fi -# Configure nerfs RUNSHARED when cross-compiling, which prevents i386 PGO from -# running from an x86_64 environment. Undo that, as we can run i386 from x86_64. -if [[ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" && "${TARGET_TRIPLE}" = "i686-unknown-linux-gnu" ]]; then - patch -p1 << "EOF" -diff --git a/configure b/configure -index 1252335472..33c11fbade 100755 ---- a/configure -+++ b/configure -@@ -5989,10 +5989,6 @@ else # shared is disabled - esac - fi - --if test "$cross_compiling" = yes; then -- RUNSHARED= --fi -- - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LDLIBRARY" >&5 - $as_echo "$LDLIBRARY" >&6; } - -EOF +# disable readelf check when cross-compiling on older Python versions +if [ -n "${CROSS_COMPILING}" ]; then + if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-cross-readelf.patch" + fi +fi + +# LIBTOOL_CRUFT is unused and breaks cross-compiling on macOS. Nuke it. +# Submitted upstream at https://github.com/python/cpython/pull/101048. +if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-configure-remove-libtool-cruft.patch" +fi + +# Configure nerfs RUNSHARED when cross-compiling, which prevents PGO from running when +# we can in fact run the target binaries (e.g. x86_64 host and i686 target). Undo that. +# TODO this may not be needed after removing support for i686 builds. But it +# may still be useful since CPython's definition of cross-compiling has historically +# been very liberal and kicks in when it arguably shouldn't. +# Merged upstream in Python 3.15, https://github.com/python/cpython/pull/141958 +if [[ -n "${CROSS_COMPILING}" && -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_14}" ]]; then + if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then + patch -p1 -i "${ROOT}/patch-dont-clear-runshared-14.patch" + elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]; then + patch -p1 -i "${ROOT}/patch-dont-clear-runshared-13.patch" + elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-dont-clear-runshared.patch" + else + patch -p1 -i "${ROOT}/patch-dont-clear-runshared-legacy.patch" + fi +fi + +# CPython <=3.10 doesn't properly detect musl. CPython <=3.12 tries, but fails +# in our environment because of an autoconf bug. CPython >=3.13 is fine. +if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_10}" ]; then + patch -p1 -i "${ROOT}/patch-cpython-configure-target-triple-musl-3.10.patch" +elif [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_12}" ]; then + patch -p1 -i "${ROOT}/patch-cpython-configure-target-triple-musl-3.12.patch" +fi + +# Python 3.11 supports using a provided Python to use during bootstrapping +# (e.g. freezing). Normally it only uses this Python during cross-compiling. +# This patch forces always using it. See comment related to +# `--with-build-python` for more. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-always-build-python-for-freeze.patch" fi # Add a make target to write the PYTHON_FOR_BUILD variable so we can # invoke the host Python on our own. -patch -p1 << "EOF" -diff --git a/Makefile.pre.in b/Makefile.pre.in -index f128444b98..d2013a2987 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -1930,6 +1930,12 @@ patchcheck: @DEF_MAKE_RULE@ - - Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h - -+write-python-for-build: -+ echo "#!/bin/sh" > python-for-build -+ echo "set -e" >> python-for-build -+ echo "exec env $(PYTHON_FOR_BUILD) \$$@" >> python-for-build -+ chmod +x python-for-build -+ - # Declare targets that aren't real files - .PHONY: all build_all sharedmods check-clean-src oldsharedmods test quicktest - .PHONY: install altinstall oldsharedinstall bininstall altbininstall -EOF +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + patch -p1 -i "${ROOT}/patch-write-python-for-build-3.12.patch" +else + patch -p1 -i "${ROOT}/patch-write-python-for-build.patch" +fi -# We build all extensions statically. So remove the auto-generated make -# rules that produce shared libraries for them. -patch -p1 << "EOF" -diff --git a/Modules/makesetup b/Modules/makesetup ---- a/Modules/makesetup -+++ b/Modules/makesetup -@@ -241,18 +241,11 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | - case $doconfig in - yes) OBJS="$OBJS $objs";; - esac -- for mod in $mods -- do -- file="$srcdir/$mod\$(EXT_SUFFIX)" -- case $doconfig in -- no) SHAREDMODS="$SHAREDMODS $file";; -- esac -- rule="$file: $objs" -- rule="$rule; \$(BLDSHARED) $objs $libs $ExtraLibs -o $file" -- echo "$rule" >>$rulesf -- done - done - -+ # Deduplicate OBJS. -+ OBJS=$(echo $OBJS | tr ' ' '\n' | sort -u | xargs) -+ - case $SHAREDMODS in - '') ;; - *) DEFS="SHAREDMODS=$SHAREDMODS$NL$DEFS";; -EOF +# Object files can get listed multiple times leading to duplicate symbols +# when linking. Prevent this. +if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_10}" ]; then + patch -p1 -i "${ROOT}/patch-makesetup-deduplicate-objs.patch" +fi # The default build rule for the macOS dylib doesn't pick up libraries # from modules / makesetup. So patch it accordingly. -patch -p1 << "EOF" -diff --git a/Makefile.pre.in b/Makefile.pre.in ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -628,7 +628,7 @@ libpython3.so: libpython$(LDVERSION).so - $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ - - libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) -- $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ -+ $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ - - - libpython$(VERSION).sl: $(LIBRARY_OBJS) -EOF +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]; then + patch -p1 -i "${ROOT}/patch-macos-link-extension-modules-13.patch" +else + patch -p1 -i "${ROOT}/patch-macos-link-extension-modules.patch" +fi # Also on macOS, the `python` executable is linked against libraries defined by statically # linked modules. But those libraries should only get linked into libpython, not the # executable. This behavior is kinda suspect on all platforms, as it could be adding # library dependencies that shouldn't need to be there. -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then - if [ "${PYTHON_MAJMIN_VERSION}" = "3.8" ]; then - patch -p1 <<"EOF" -diff --git a/Makefile.pre.in b/Makefile.pre.in ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -563,7 +563,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c - - # Build the interpreter - $(BUILDPYTHON): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) -- $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) -+ $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(SYSLIBS) - - platform: $(BUILDPYTHON) pybuilddir.txt - $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform -EOF - else - patch -p1 <<"EOF" -diff --git a/Makefile.pre.in b/Makefile.pre.in ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -563,7 +563,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c - - # Build the interpreter - $(BUILDPYTHON): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) $(EXPORTSYMS) -- $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) -+ $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(SYSLIBS) - - platform: $(BUILDPYTHON) pybuilddir.txt - $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform -EOF +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]; then + patch -p1 -i "${ROOT}/patch-python-link-modules-3.15.patch" + elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-python-link-modules-3.11.patch" + elif [ "${PYTHON_MAJMIN_VERSION}" = "3.10" ]; then + patch -p1 -i "${ROOT}/patch-python-link-modules-3.10.patch" fi fi +# The macOS code for sniffing for _dyld_shared_cache_contains_path falls back on a +# possibly inappropriate code path if a configure time check fails. This is not +# appropriate for certain cross-compiling scenarios. See discussion at +# https://bugs.python.org/issue44689. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-ctypes-callproc.patch" +else + patch -p1 -i "${ROOT}/patch-ctypes-callproc-legacy.patch" +fi + +# On Windows, CPython looks for the Tcl/Tk libraries relative to the base prefix, +# which we want. But on Unix, it doesn't. This patch applies similar behavior on Unix, +# thereby ensuring that the Tcl/Tk libraries are found in the correct location. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then + patch -p1 -i "${ROOT}/patch-tkinter-3.14.patch" +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]; then + patch -p1 -i "${ROOT}/patch-tkinter-3.13.patch" +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + patch -p1 -i "${ROOT}/patch-tkinter-3.12.patch" +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-tkinter-3.11.patch" +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_10}" ]; then + patch -p1 -i "${ROOT}/patch-tkinter-3.10.patch" +fi + # Code that runs at ctypes module import time does not work with # non-dynamic binaries. Patch Python to work around this. # See https://bugs.python.org/issue37060. -patch -p1 << EOF -diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py ---- a/Lib/ctypes/__init__.py -+++ b/Lib/ctypes/__init__.py -@@ -441,7 +441,10 @@ if _os.name == "nt": - elif _sys.platform == "cygwin": - pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) - else: -- pythonapi = PyDLL(None) -+ try: -+ pythonapi = PyDLL(None) -+ except OSError: -+ pythonapi = None - - - if _os.name == "nt": -EOF +patch -p1 -i "${ROOT}/patch-ctypes-static-binary.patch" -# libedit on non-macOS requires various hacks because readline.c assumes -# libedit is only used on macOS and its readline/libedit detection code -# makes various assumptions about the macOS environment. +# We build against libedit instead of readline in all environments. # -# USE_LIBEDIT comes from our static-modules file. +# On macOS, we use the system/SDK libedit, which is likely somewhat old. # -# TODO make upstream patches to readline.c to properly support libedit -# on other platforms. -cp Modules/readline.c Modules/readline-libedit.c - -# readline.c assumes that a modern readline API version has a free_history_entry(). -# but libedit does not. Change the #ifdef accordingly. +# On Linux, we use our own libedit, which should be modern. # -# Similarly, we invoke configure using readline, which sets -# HAVE_RL_COMPLETION_SUPPRESS_APPEND improperly. So hack that. This is a bug -# in our build system, as we should probably be invoking configure again when -# using libedit. -patch -p1 << EOF -diff --git a/Modules/readline-libedit.c b/Modules/readline-libedit.c -index 1e74f997b0..56a36e26e6 100644 ---- a/Modules/readline-libedit.c -+++ b/Modules/readline-libedit.c -@@ -511,7 +511,7 @@ set the word delimiters for completion"); - - /* _py_free_history_entry: Utility function to free a history entry. */ - --#if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0500 -+#ifndef USE_LIBEDIT - - /* Readline version >= 5.0 introduced a timestamp field into the history entry - structure; this needs to be freed to avoid a memory leak. This version of -@@ -1055,7 +1055,7 @@ flex_complete(const char *text, int start, int end) - #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER - rl_completion_append_character ='\0'; - #endif --#ifdef HAVE_RL_COMPLETION_SUPPRESS_APPEND -+#ifndef USE_LIBEDIT - rl_completion_suppress_append = 0; - #endif - -@@ -1241,7 +1241,7 @@ readline_until_enter_or_signal(const char *prompt, int *signal) - PyEval_SaveThread(); - if (s < 0) { - rl_free_line_state(); --#if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0700 -+#ifndef USE_LIBEDIT - rl_callback_sigcleanup(); - #endif - rl_cleanup_after_signal(); -EOF - -# Modules/readline.c has various libedit conditions behind an -# ``#ifdef __APPLE__`` instead of a more specific feature flag. All -# occurrences of __APPLE__ in that file are related to libedit. So we -# just replace the content. -sed s/__APPLE__/USE_LIBEDIT/g Modules/readline-libedit.c > tmp -mv tmp Modules/readline-libedit.c - -# Modules/_hashopenssl.c redefines some libcrypto symbols on Python 3.9 and -# this makes the linker unhappy. So rename the symbols to work around. -# https://bugs.python.org/issue41949. -if [ "${PYTHON_MAJMIN_VERSION}" = "3.9" ]; then - patch -p1 < -+#if TARGET_OS_IPHONE -+# undef HAVE_SYSTEM -+#endif -+#endif -+ - _Py_IDENTIFIER(__fspath__); - - /*[clinic input] -EOF +# Python 3.11 makes this a configure time check, so we don't need the patch there. +if [[ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_10}" ]]; then + patch -p1 -i "${ROOT}/patch-posixmodule-remove-system.patch" +fi + +# Python 3.11 has configure support for configuring extension modules. We really, +# really, really want to use this feature because it looks promising. But at the +# time we added this code the functionality didn't support all extension modules +# nor did it easily support static linking, including static linking of extra +# libraries (which appears to be a limitation of `makesetup`). So for now we +# disable the functionality and require our auto-generated Setup.local to provide +# everything. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + # This sets MODULE__STATE=disabled in the Makefile for all extension + # modules that are not unavailable (n/a) based on the platform. + # Valid STATE variables are needed to create the _missing_stdlib_info.py + # file during the build in Python 3.15+ + patch -p1 -i "${ROOT}/patch-configure-disable-stdlib-mod-3.12.patch" + else + patch -p1 -i "${ROOT}/patch-configure-disable-stdlib-mod.patch" + fi + + # This hack also prevents the conditional definition of the pwd module in + # Setup.bootstrap.in from working. So we remove that conditional. + patch -p1 -i "${ROOT}/patch-pwd-remove-conditional.patch" +fi + +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + # Additional BOLT optimizations, being upstreamed in + # https://github.com/python/cpython/issues/128514 + patch -p1 -i "${ROOT}/patch-configure-bolt-apply-flags-128514.patch" + + # Disable unsafe identical code folding. Objects/typeobject.c + # update_one_slot requires that wrap_binaryfunc != wrap_binaryfunc_l, + # despite the functions being identical. + # https://github.com/python/cpython/pull/134642 + patch -p1 -i "${ROOT}/patch-configure-bolt-icf-safe.patch" + + # Tweak --skip-funcs to work with our toolchain. + if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]; then + patch -p1 -i "${ROOT}/patch-configure-bolt-skip-funcs-3.15.patch" + else + patch -p1 -i "${ROOT}/patch-configure-bolt-skip-funcs.patch" + fi + + # Remove -use-gnu-stack from the BOLT optimization flags as it incorrectly + # removes the PT_GNU_STACK segment. This patch can be removed when this bug + # is fixed in LLVM. + # https://github.com/llvm/llvm-project/issues/174191 + patch -p1 -i "${ROOT}/patch-configure-bolt-remove-use-gnu-stack.patch" +fi + +# The optimization make targets are both phony and non-phony. This leads +# to PGO targets getting reevaluated after a build when you use multiple +# make invocations. e.g. `make install` like we do below. Fix that. +if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-pgo-make-targets.patch" +fi + +# Show PGO instrumentation statistics to aid debugging PGO. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + patch -p1 -i "${ROOT}/patch-pgo-print-statistics.patch" +else + patch -p1 -i "${ROOT}/patch-pgo-print-statistics-3.11.patch" +fi + +# Use a pool of PGO data files with merging to prevent data loss. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + patch -p1 -i "${ROOT}/patch-pgo-file-pool.patch" +else + patch -p1 -i "${ROOT}/patch-pgo-file-pool-3.11.patch" +fi + +# There's a post-build Python script that verifies modules were +# built correctly. Ideally we'd invoke this. But our nerfing of +# the configure-based module building and replacing it with our +# own Setup-derived version completely breaks assumptions in this +# script. So leave it off for now... at our own peril. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]; then + patch -p1 -i "${ROOT}/patch-checksharedmods-disable-3.15.patch" +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + patch -p1 -i "${ROOT}/patch-checksharedmods-disable.patch" +fi + +# CPython < 3.11 always linked against libcrypt. We backport part of +# upstream commit be21706f3760bec8bd11f85ce02ed6792b07f51f to avoid this +# behavior. +if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_10}" ]; then + patch -p1 -i "${ROOT}/patch-configure-crypt-no-modify-libs.patch" +fi + +# Backport Tcl/Tk 9.0 support from 3.12 to Python 3.10 and 3.11 +if [ "${PYTHON_MAJMIN_VERSION}" = "3.10" ]; then + # git checkout v3.10.19 + # git cherry-pick 625887e6 27cbeb08 d4680b9e ec139c8f + # manually change int argc/objc -> Tcl_Size argc/objc in file + # git diff v3.10.19 Modules/_tkinter.c > patch-tkinter-backport-tcl-9-310.patch + patch -p1 -i "${ROOT}/patch-tkinter-backport-tcl-9-310.patch" +fi +if [ "${PYTHON_MAJMIN_VERSION}" = "3.11" ]; then + # git checkout v3.11.14 + # git cherry-pick 625887e6 27cbeb08 d4680b9e ec139c8f + # git diff v3.11.14 Modules/_tkinter.c > patch-tkinter-backport-tcl-9-311.patch + patch -p1 -i "${ROOT}/patch-tkinter-backport-tcl-9-311.patch" +fi + +# BOLT instrumented binaries segfault in some test_embed tests for unknown reasons. +# On 3.12 (minimum BOLT version), the segfault causes the test harness to +# abort and BOLT optimization uses the partial test results. On 3.13, the segfault +# is a fatal error. Fixed in 3.14+, https://github.com/python/cpython/pull/128474 +if [ "${PYTHON_MAJMIN_VERSION}" = 3.12 ] || [ "${PYTHON_MAJMIN_VERSION}" = 3.13 ]; then + patch -p1 -i "${ROOT}/patch-test-embed-prevent-segfault.patch" +fi + +# RHEL 8 (supported until 2029) and below, including Fedora 33 and below, do not +# ship an /etc/ssl/cert.pem or a hashed /etc/ssl/cert/ directory. Patch to look at +# /etc/pki/tls/cert.pem instead, if that file exists and /etc/ssl/cert.pem does not. +patch -p1 -i ${ROOT}/patch-cpython-redhat-cert-file.patch + +# Cherry-pick an upstream change in Python 3.15 to build _asyncio as +# static (which we do anyway in our own fashion) and more importantly to +# take this into account when finding the AsyncioDebug section. +if [ "${PYTHON_MAJMIN_VERSION}" = 3.14 ]; then + patch -p1 -i "${ROOT}/patch-python-3.14-asyncio-static.patch" +fi + +# Ensure the new build-details.json file reports relocatable paths. +# There is not yet a flag in ./configure for this, sadly. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then + patch -p1 -i "${ROOT}/patch-python-relative-build-details.patch" +fi + +# Mark _Py_jit_entry as extern in _testiternalcapi/interpreter.c to avoid a duplicate symbols. +# The symbol is not actually used in the module, a better solution should be found, see: +# https://github.com/python/cpython/issues/144712 +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]; then + patch -p1 -i "${ROOT}/patch-testinternalcapi-interpreter-extern.patch" fi # Most bits look at CFLAGS. But setup.py only looks at CPPFLAGS. @@ -490,27 +357,129 @@ fi CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -I${TOOLS_PATH}/deps/include -I${TOOLS_PATH}/deps/include/ncursesw" LDFLAGS="${EXTRA_TARGET_LDFLAGS} -L${TOOLS_PATH}/deps/lib" -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then +# Some target configurations use `-fvisibility=hidden`. Python's configure handles +# symbol visibility properly itself. So let it do its thing. +CFLAGS=${CFLAGS//-fvisibility=hidden/} + +# But some symbols from some dependency libraries are still non-hidden for some +# reason. We force the linker to do our bidding. +if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + LDFLAGS="${LDFLAGS} -Wl,--exclude-libs,ALL" +fi + +EXTRA_CONFIGURE_FLAGS= + +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then CFLAGS="${CFLAGS} -I${TOOLS_PATH}/deps/include/uuid" # Prevent using symbols not supported by current macOS SDK target. CFLAGS="${CFLAGS} -Werror=unguarded-availability-new" fi +# Always build against libedit instead of the default of readline. +# macOS always uses the system libedit, so no tweaks are needed. +if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + # Add configure flag for proper configure support for libedit. + EXTRA_CONFIGURE_FLAGS="${EXTRA_CONFIGURE_FLAGS} --with-readline=editline" +fi + +# On Python 3.14+, enable the tail calling interpreter which is more performant. +# This is only available on Clang 19+ +# https://docs.python.org/3.14/using/configure.html#cmdoption-with-tail-call-interp +if [[ "${CC}" = "clang" && -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]]; then + EXTRA_CONFIGURE_FLAGS="${EXTRA_CONFIGURE_FLAGS} --with-tail-call-interp" +fi + +# On Python 3.12+ we need to link the special hacl library provided some SHA-256 +# implementations. Since we hack up the regular extension building mechanism, we +# need to reinvent this wheel. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + LDFLAGS="${LDFLAGS} -LModules/_hacl" +fi + +# On PPC we need to prevent the glibc 2.22 __tls_get_addr_opt symbol +# from being introduced to preserve runtime compatibility with older +# glibc. +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" && "${TARGET_TRIPLE}" = "ppc64le-unknown-linux-gnu" ]]; then + LDFLAGS="${LDFLAGS} -Wl,--no-tls-get-addr-optimize" +fi + +# We're calling install_name_tool -add_rpath on extension modules, which +# eats up 0x20 bytes of space in the Mach-O header, and we need to make +# sure there's still enough room to add a code signature (0x10 bytes) on +# non-arm64 where there's no automatic ad-hoc signature. We are somehow +# on a toolchain that doesn't make sure there's enough space by default +# so give it plenty of space. +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + LDFLAGS="${LDFLAGS} -Wl,-headerpad,40" +fi + CPPFLAGS=$CFLAGS CONFIGURE_FLAGS=" - --build=${BUILD_TRIPLE} \ + --build=${BUILD_TRIPLE} --host=${TARGET_TRIPLE} --prefix=/install --with-openssl=${TOOLS_PATH}/deps - --without-ensurepip" + --with-system-expat + --with-system-libmpdec + --without-ensurepip + ${EXTRA_CONFIGURE_FLAGS}" + + +# Build a libpython3.x.so, but statically link the interpreter against +# libpython. +# +# For now skip this on macos, because it causes some linker failures. Note that +# this patch mildly conflicts with the macos-only patch-python-link-modules +# applied above, so you will need to resolve that conflict if you re-enable +# this for macos. +if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + patch -p1 -i "${ROOT}/patch-python-configure-add-enable-static-libpython-for-interpreter.patch" + else + patch -p1 -i "${ROOT}/patch-python-configure-add-enable-static-libpython-for-interpreter-${PYTHON_MAJMIN_VERSION}.patch" + fi + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --enable-static-libpython-for-interpreter" +fi if [ "${CC}" = "musl-clang" ]; then + # In order to build the _blake2 extension module with SSE3+ instructions, we need + # musl-clang to find headers that provide access to the intrinsics, as they are not + # provided by musl. These are part of the include files that are part of clang. + # But musl-clang eliminates them from the default include path. So copy them into + # place. + for h in /tools/${TOOLCHAIN}/lib/clang/*/include/*intrin.h /tools/${TOOLCHAIN}/lib/clang/*/include/{__wmmintrin_aes.h,__wmmintrin_pclmul.h,mm_malloc.h,cpuid.h}; do + filename=$(basename "$h") + if [ -e "/tools/host/include/${filename}" ]; then + echo "${filename} already exists; don't need to copy!" + exit 1 + fi + cp "$h" /tools/host/include/ + done +fi + +# To enable mimalloc (which is hard requirement for free-threaded versions, but preferred in +# general), we need `stdatomic.h` which is not provided by musl. It's a part of the include files +# that are part of clang. But musl-clang eliminates them from the default include path. So copy it +# into place. +if [[ "${CC}" = "musl-clang" && -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]]; then + for h in /tools/${TOOLCHAIN}/lib/clang/*/include/stdatomic.h; do + filename=$(basename "$h") + if [ -e "/tools/host/include/${filename}" ]; then + echo "${filename} already exists; don't need to copy!" + exit 1 + fi + cp "$h" /tools/host/include/ + done +fi + + +if [ -n "${CPYTHON_STATIC}" ]; then CFLAGS="${CFLAGS} -static" CPPFLAGS="${CPPFLAGS} -static" LDFLAGS="${LDFLAGS} -static" - PYBUILD_SHARED=0 + PYBUILD_SHARED=0 else CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --enable-shared" PYBUILD_SHARED=1 @@ -520,34 +489,72 @@ if [ -n "${CPYTHON_DEBUG}" ]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-pydebug" fi +# Explicitly enable mimalloc on 3.13+, it's already included by default but with this it'll fail +# if it's missing from the system. +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-mimalloc" +fi + +if [ -n "${CPYTHON_FREETHREADED}" ]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --disable-gil" +fi + if [ -n "${CPYTHON_OPTIMIZED}" ]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --enable-optimizations" + if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" && -n "${BOLT_CAPABLE}" ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --enable-bolt" + fi + + # Allow users to enable the experimental JIT on 3.13+ + if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]]; then + + # Do not enable on x86-64 macOS because the JIT requires macOS 11+ and we are currently + # using 10.15 as a minimum version. + # Do not enable when free-threading, because they're not compatible yet. + if [[ ! ( "${TARGET_TRIPLE}" == "x86_64-apple-darwin" || -n "${CPYTHON_FREETHREADED}" ) ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --enable-experimental-jit=yes-off" + fi + + # Respect CFLAGS during JIT compilation. + # + # Backports https://github.com/python/cpython/pull/134276 + if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" && -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_13}" ]]; then + patch -p1 -i "${ROOT}/patch-jit-cflags-313.patch" + fi + + + if [[ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_13}" ]]; then + # On 3.13, LLVM 18 is hard-coded into the configure script. Override it to our toolchain + # version. + patch -p1 -i "${ROOT}/patch-jit-llvm-version-3.13.patch" + fi + + if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" && -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_14}" ]]; then + patch -p1 -i "${ROOT}/patch-jit-llvm-version-3.14.patch" + fi + + # Python 3.15+ supports configuration via `LLVM_VERSION` + # https://github.com/astral-sh/python-build-standalone/issues/881 + fi fi if [ -n "${CPYTHON_LTO}" ]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-lto" fi -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then +# Python 3.11 introduces a --with-build-python to denote the host Python. +# It is required when cross-compiling. But we always build a host Python +# to avoid complexity with the bootstrap Python binary. +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-build-python=${TOOLS_PATH}/host/bin/python${PYTHON_MAJMIN_VERSION}" +fi + +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then # Configure may detect libintl from non-system sources, such # as Homebrew or MacPorts. So nerf the check to prevent this. CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_lib_intl_textdomain=no" - # When building against an 11.0+ SDK, preadv() and pwritev() are - # detected and used, despite only being available in the 11.0+ SDK. This - # prevents object files from re-linking when built with older SDKs. - # So we disable them. But not in aarch64-apple-darwin, as that target - # requires the 11.0 SDK. - # - # This solution is less than ideal. Modern versions of Python support - # weak linking and it should be possible to coerce these functions into - # being weakly linked. - if [ "${TARGET_TRIPLE}" != "aarch64-apple-darwin" ]; then - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_preadv=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_pwritev=no" - fi - - if [ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" ]; then + if [ -n "${CROSS_COMPILING}" ]; then # Python's configure doesn't support cross-compiling on macOS. So we need # to explicitly set MACHDEP to avoid busted checks. The code for setting # MACHDEP also sets ac_sys_system/ac_sys_release, so we have to set @@ -556,26 +563,10 @@ if [ "${PYBUILD_PLATFORM}" = "macos" ]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} MACHDEP=darwin" CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_system=Darwin" CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_release=$(uname -r)" - elif [ "${TARGET_TRIPLE}" = "aarch64-apple-ios" ]; then - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} MACHDEP=iOS" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_system=iOS" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_release=" - # clock_settime() not available on iOS. - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_clock_settime=no" - # getentropy() not available on iOS. - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_getentropy=no" elif [ "${TARGET_TRIPLE}" = "x86_64-apple-darwin" ]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} MACHDEP=darwin" CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_system=Darwin" CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_release=$(uname -r)" - elif [ "${TARGET_TRIPLE}" = "x86_64-apple-ios" ]; then - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} MACHDEP=iOS" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_system=iOS" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_sys_release=" - # clock_settime() not available on iOS. - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_clock_settime=no" - # getentropy() not available on iOS. - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_getentropy=no" else echo "unsupported target triple: ${TARGET_TRIPLE}" exit 1 @@ -592,94 +583,203 @@ if [ "${PYBUILD_PLATFORM}" = "macos" ]; then export MACOSX_DEPLOYMENT_TARGET="${APPLE_MIN_DEPLOYMENT_TARGET}" fi -if [ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" ]; then +# ptsrname_r is only available in SDK 13.4+, but we target a lower version for compatibility. +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_ptsname_r=no" +fi + +# explicit_bzero is only available in glibc 2.25+, but we target a lower version for compatibility. +# it's only needed for the HACL Blake2 implementation in Python 3.14+ +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_explicit_bzero=no" +fi + +# Define the base PGO profiling task, which we'll extend below with ignores +export PROFILE_TASK='-m test --pgo' + +# Run tests in parallel to reduce wall time. +# +# LLVM's PGO instruments compiled code to increment counters to track +# call count. Same story for BOLT in instrumented mode, which we also use. +# This approach is in contrast to sample based profiling where the program is +# sampled periodically to see which code is active. In instrumented mode, +# the wall time execution doesn't matter: a counter will be incremented +# regardless of how fast the code runs. +# +# Use of instrumented mode means that it is safe to profile in parallel +# and there will be no loss in profile quality. +PROFILE_TASK="${PROFILE_TASK} -j ${NUM_CPUS}" + +# On 3.14+ `test_strftime_y2k` fails when cross-compiling for `x86_64_v2` and `x86_64_v3` targets on +# Linux, so we ignore it. See https://github.com/python/cpython/issues/128104 +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" && -n "${CROSS_COMPILING}" && "${PYBUILD_PLATFORM}" != macos* ]]; then + PROFILE_TASK="${PROFILE_TASK} --ignore test_strftime_y2k" +fi + +# On 3.14+ `test_json.test_recursion.TestCRecursion.test_highly_nested_objects_decoding` fails during +# PGO due to RecursionError not being raised as expected. See https://github.com/python/cpython/issues/140125 +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]]; then + PROFILE_TASK="${PROFILE_TASK} --ignore test_json" +fi + +# PGO optimized / BOLT instrumented binaries segfault in a test_bytes test. Skip it. +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" && "${TARGET_TRIPLE}" == x86_64* ]]; then + PROFILE_TASK="${PROFILE_TASK} --ignore test.test_bytes.BytesTest.test_from_format" +fi + +# ./configure tries to auto-detect whether it can build 128-bit and 256-bit SIMD helpers for HACL, +# but on x86-64 that requires v2 and v3 respectively, and on arm64 the performance is bad as noted +# in the comments, so just don't even try. (We should check if we can make this conditional) +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]]; then + patch -p1 -i "${ROOT}/patch-python-configure-hacl-no-simd.patch" +fi + +# We use ndbm on macOS and BerkeleyDB elsewhere. +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-dbmliborder=ndbm" +else + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-dbmliborder=bdb" +fi + +if [ -n "${CROSS_COMPILING}" ]; then + # configure assumes cross compiling when host != target and doesn't + # provide a way to override. Our target triple normalization may + # lead configure into thinking we aren't cross-compiling when we + # are. So force a static "yes" value when our build system says we + # are cross-compiling. + # See also https://savannah.gnu.org/support/?110348 + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} cross_compiling=yes" + # configure doesn't like a handful of scenarios when cross-compiling. # # getaddrinfo buggy test fails for some reason. So we short-circuit it. - # + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" # The /dev/* check also fails for some reason. + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" + + # When cross-compiling, configure cannot detect if the target system has a + # working tzset function in C. This influences whether or not the compiled + # python will end up with the time.tzset function or not. All linux targets, + # however, should have a working tzset function via libc. So we manually + # indicate this to the configure script. + if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_working_tzset=yes" + fi + + # Also, it cannot detect whether the compiler supports -pthread or + # not, and conservatively defaults to no, which is not the right + # default on relatively modern compilers. + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_pthread=yes" + + # Also, it cannot detect whether misaligned memory accesses should + # be avoided, and conservatively defaults to yes, which makes it + # pick the 'fnv' hash instead of 'siphash', which numba does not + # like (#683, see also comment in cpython/configure.ac). These + # answers are taken from the Linux kernel source's Kconfig files, + # search for HAVE_EFFICIENT_UNALIGNED_ACCESS. case "${TARGET_TRIPLE}" in - *-apple-*) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - i686-unknown-linux-gnu) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - aarch64-unknown-linux-gnu) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - armv7-unknown-linux-gnueabi) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - armv7-unknown-linux-gnueabihf) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - mips-unknown-linux-gnu) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - mipsel-unknown-linux-gnu) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - mips64el-unknown-linux-gnuabi64) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - s390x-unknown-linux-gnu) - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_buggy_getaddrinfo=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptc=no" - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_file__dev_ptmx=no" - ;; - x86_64-unknown-linux-musl) - ;; - *) - echo "unhandled cross-compiling triple; may run into issues" + arm64*|aarch64*|armv7*|thumb7*|ppc64*|s390*|x86*) + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_aligned_required=no" ;; esac + + # When cross-compiling, configure defaults to assuming `sem_getvalue` is broken, + # which causes `multiprocessing.Queue.qsize()` to raise `NotImplementedError`. + # Linux has a working `sem_getvalue`, so we explicitly tell configure it's not broken. + # macOS is excluded because it genuinely has a non-functional `sem_getvalue`. + # See: https://github.com/astral-sh/python-build-standalone/issues/977 + # See also: https://github.com/python/cpython/issues/101094 (macOS `sem_getvalue` issue) + if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_broken_sem_getvalue=no" + fi + + # TODO: There are probably more of these, see #599. fi -CFLAGS=$CFLAGS CPPFLAGS=$CFLAGS LDFLAGS=$LDFLAGS \ - ./configure ${CONFIGURE_FLAGS} +# Apply weak sem_clockwait patch for runtime detection on old glibc. +# When building against glibc headers older than 2.30, configure cannot detect +# sem_clockwait, causing threading.Event.wait() to use CLOCK_REALTIME instead of +# CLOCK_MONOTONIC. This makes waits hang when the system clock jumps backward. +# The patch declares sem_clockwait as a weak symbol and checks at runtime. +if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]; then + patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.15.patch + elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]; then + patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.13.patch + elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.12.patch + elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.11.patch + else + patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.10.patch + fi +fi + +# Adjust the Python startup logic (getpath.py) to properly locate the installation, even when +# invoked through a symlink or through an incorrect argv[0]. Because this Python is relocatable, we +# don't get to rely on the fallback to the compiled-in installation prefix. +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]]; then + if [ -e "${ROOT}/patch-python-getpath-backport-${PYTHON_MAJMIN_VERSION}.patch" ]; then + # Sync the getpath logic in older minor releases to the current version. + patch -p1 -i "${ROOT}/patch-python-getpath-backport-${PYTHON_MAJMIN_VERSION}.patch" + fi + patch -p1 -i "${ROOT}/patch-python-getpath-library.patch" +fi -# configure checks for the presence of functions and blindly uses them, -# even if they aren't available in the target macOS SDK. Work around that. -# But only on Python 3.8, as Python 3.9.1 improved this functionality. -if [[ "${PYBUILD_PLATFORM}" = "macos" && "${PYTHON_MAJMIN_VERSION}" = "3.8" ]]; then - sed -i "" "s/#define HAVE_UTIMENSAT 1//g" pyconfig.h - sed -i "" "s/#define HAVE_FUTIMENS 1//g" pyconfig.h +# Another, similar change to getpath: When reading inside a venv use the base_executable path to +# determine executable_dir when valid. This allows venv to be created from symlinks and covers some +# cases the above patch doesn't. See: +# https://github.com/python/cpython/issues/106045#issuecomment-2594628161 +# 3.10 does not use getpath.py only getpath.c, no patch is applied +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then + patch -p1 -i "${ROOT}/patch-getpath-use-base_executable-for-executable_dir-314.patch" +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then + patch -p1 -i "${ROOT}/patch-getpath-use-base_executable-for-executable_dir.patch" fi +# We patched configure.ac above. Reflect those changes. +autoconf + +# Ensure `CFLAGS` are propagated to JIT compilation for 3.13+ (note this variable has no effect on +# 3.12 and earlier) +CFLAGS_JIT="${CFLAGS}" + +# In 3.14+, the JIT compiler on x86-64 Linux uses a model that conflicts with `-fPIC`, so strip it +# from the flags. See: +# - https://github.com/python/cpython/issues/135690 +# - https://github.com/python/cpython/pull/130097 +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" && "${TARGET_TRIPLE}" == x86_64* ]]; then + CFLAGS_JIT="${CFLAGS_JIT//-fPIC/}" +fi + +CFLAGS=$CFLAGS CPPFLAGS=$CFLAGS CFLAGS_JIT=$CFLAGS_JIT LDFLAGS=$LDFLAGS \ + ./configure ${CONFIGURE_FLAGS} + # Supplement produced Makefile with our modifications. cat ../Makefile.extra >> Makefile -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out/python +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" sharedinstall DESTDIR="${ROOT}/out/python" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out/python" -if [ -n "${CPYTHON_DEBUG}" ]; then - PYTHON_BINARY_SUFFIX=d + +if [ -n "${CPYTHON_FREETHREADED}" ]; then + PYTHON_BINARY_SUFFIX=t + PYTHON_LIB_SUFFIX=t else PYTHON_BINARY_SUFFIX= + PYTHON_LIB_SUFFIX= +fi +if [ -n "${CPYTHON_DEBUG}" ]; then + PYTHON_BINARY_SUFFIX="${PYTHON_BINARY_SUFFIX}d" fi # Python interpreter to use during the build. When cross-compiling, # we have the Makefile emit a script which sets some environment # variables that force the invoked Python to pick up the configuration # of the target Python but invoke the host binary. -if [ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" ]; then +if [ -n "${CROSS_COMPILING}" ]; then make write-python-for-build BUILD_PYTHON=$(pwd)/python-for-build else @@ -690,117 +790,303 @@ fi # This ensures we can run the binary in any location without # LD_LIBRARY_PATH pointing to the directory containing libpython. if [ "${PYBUILD_SHARED}" = "1" ]; then - if [ "${PYBUILD_PLATFORM}" = "macos" ]; then + if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + # There's only 1 dylib produced on macOS and it has the binary suffix. LIBPYTHON_SHARED_LIBRARY_BASENAME=libpython${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}.dylib LIBPYTHON_SHARED_LIBRARY=${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} - # There's only 1 dylib produced on macOS and it has the binary suffix. + # Fix the Python binary to reference libpython via @rpath and add + # an rpath entry so it can find the library. install_name_tool \ - -change /install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} @executable_path/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} \ - ${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION} + -change "/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" "@rpath/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" \ + -add_rpath @executable_path/../lib \ + "${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}" # Python's build system doesn't make this file writable. - chmod 755 ${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} + chmod 755 "${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" + # Set libpython's install name to @rpath so binaries linking against it + # can locate it via their own rpath entries. install_name_tool \ - -change /install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} @executable_path/${LIBPYTHON_SHARED_LIBRARY_BASENAME} \ - ${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} + -id "@rpath/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" \ + "${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" # We also normalize /tools/deps/lib/libz.1.dylib to the system location. install_name_tool \ -change /tools/deps/lib/libz.1.dylib /usr/lib/libz.1.dylib \ - ${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION} + "${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}" install_name_tool \ -change /tools/deps/lib/libz.1.dylib /usr/lib/libz.1.dylib \ - ${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} + "${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" if [ -n "${PYTHON_BINARY_SUFFIX}" ]; then install_name_tool \ - -change /install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} @executable_path/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} \ - ${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX} + -change "/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" "@rpath/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" \ + -add_rpath @executable_path/../lib \ + "${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}" fi - else + + # At the moment, python3 and libpython don't have shared-library + # dependencies, but at some point we will want to run this for + # them too. + for module in ${ROOT}/out/python/install/lib/python*/lib-dynload/*.so; do + install_name_tool -add_rpath @loader_path/../.. "$module" + done + else # (not macos) LIBPYTHON_SHARED_LIBRARY_BASENAME=libpython${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}.so.1.0 LIBPYTHON_SHARED_LIBRARY=${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} - # If we simply set DT_RUNPATH via --set-rpath, LD_LIBRARY_PATH would be used before - # DT_RUNPATH, which could result in confusion at run-time. But if DT_NEEDED - # contains a slash, the explicit path is used. - patchelf --replace-needed ${LIBPYTHON_SHARED_LIBRARY_BASENAME} "\$ORIGIN/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" \ - ${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION} + # Although we are statically linking libpython, some extension + # modules link against libpython.so even though they are not + # supposed to do that. If you try to import them on an + # interpreter statically linking libpython, all the symbols they + # need are resolved from the main program (because neither glibc + # nor musl has two-level namespaces), so there is hopefully no + # correctness risk, but they need to be able to successfully + # find libpython.so in order to load the module. To allow such + # extensions to load, we set an rpath to point at our lib + # directory, so that if anyone ever tries to find a libpython, + # they successfully find one. See + # https://github.com/astral-sh/python-build-standalone/issues/619 + # for some reports of extensions that need this workaround. + # + # Note that this matches the behavior of Debian/Ubuntu/etc.'s + # interpreter (if package libpython3.x is installed, which it + # usually is thanks to gdb, vim, etc.), because libpython is in + # the system lib directory, as well as the behavior in practice + # on conda-forge miniconda and probably other Conda-family + # Python distributions, which too set an rpath. + # + # There is a downside of making this libpython locatable: some user + # code might do e.g. + # ctypes.CDLL(f"libpython3.{sys.version_info.minor}.so.1.0") + # to get at things in the CPython API not exposed to pure + # Python. This code may _silently misbehave_ on a + # static-libpython interpreter, because you are actually using + # the second copy of libpython. For loading static data or using + # accessors, you might get lucky and things will work, with the + # full set of dangers of C undefined behavior being possible. + # However, there are a few reasons we think this risk is + # tolerable. First, we can't actually fix it by not setting the + # rpath - user code may well find a system libpython3.x.so or + # something which is even more likely to break. Second, this + # exact problem happens with Debian, Conda, etc., so it is very + # unlikely (compared to the extension modules case above) that + # any widely-used code has this problem; the risk is largely + # backwards incompatibility of our own builds. Also, it's quite + # easy for users to fix: simply do + # ctypes.CDLL(None) + # (i.e., dlopen(NULL)), to use symbols already in the process; + # this will work reliably on all interpreters regardless of + # whether they statically or dynamically link libpython. Finally, + # we can (and should, at some point) add a warning, error, or + # silent fix to ctypes for user code that does this, which will + # also cover the case of other libpython3.x.so files on the + # library search path that we cannot suppress. + # + # In the past, when we dynamically linked libpython, we avoided + # using an rpath and instead used a DT_NEEDED entry with + # $ORIGIN/../lib/libpython.so, because LD_LIBRARY_PATH takes + # precedence over DT_RUNPATH, and it's not uncommon to have an + # LD_LIBRARY_PATH that points to some sort of unwanted libpython + # (e.g., actions/setup-python does this as of May 2025). + # Now, though, because we're not actually using code from the + # libpython that's loaded and just need _any_ file of that name + # to satisfy the link, that's not a problem. (This also implies + # another approach to the problem: ensure that libraries find an + # empty dummy libpython.so, which allows the link to succeed but + # ensures they do not use any unwanted symbols. That might be + # worth doing at some point.) + patchelf --force-rpath --set-rpath "\$ORIGIN/../lib" \ + "${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}" - # libpython3.so isn't present in debug builds. - if [ -z "${CPYTHON_DEBUG}" ]; then - patchelf --replace-needed ${LIBPYTHON_SHARED_LIBRARY_BASENAME} "\$ORIGIN/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" \ - ${ROOT}/out/python/install/lib/libpython3.so + if [ -n "${PYTHON_BINARY_SUFFIX}" ]; then + patchelf --force-rpath --set-rpath "\$ORIGIN/../lib" \ + "${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}" fi - if [ -n "${PYTHON_BINARY_SUFFIX}" ]; then - patchelf --replace-needed ${LIBPYTHON_SHARED_LIBRARY_BASENAME} "\$ORIGIN/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" \ - ${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX} + # For libpython3.so (the ABI3 library for embedders), we do + # still dynamically link libpython3.x.so.1.0 (the + # version-specific library), because there is no particular + # speedup/benefit in statically linking libpython into + # libpython3.so, and we'd just be shipping a third copy of the + # libpython code. Therefore we use the old logic for that and + # set an $ORIGIN-relative DT_NEEDED, at least for glibc. + # Unfortunately, musl does not (as of May 2025) support $ORIGIN + # in DT_NEEDED, only in DT_RUNPATH/RPATH, so we did set an rpath + # for bin/python3, and still do for libpython3.so. In both + # cases, we have no concerns/need no workarounds for code + # referencing libpython3.x.so.1.0, because we are actually + # dynamically linking it and so all code will get the real + # libpython3.x.so.1.0 that they want (and it's fine to use + # DT_RUNPATH instead of DT_RPATH). + if [ "${CC}" == "musl-clang" ]; then + # libpython3.so isn't present in debug builds. + if [ -z "${CPYTHON_DEBUG}" ]; then + patchelf --set-rpath "\$ORIGIN/../lib" \ + "${ROOT}/out/python/install/lib/libpython3.so" + fi + else + # libpython3.so isn't present in debug builds. + if [ -z "${CPYTHON_DEBUG}" ]; then + patchelf --replace-needed "${LIBPYTHON_SHARED_LIBRARY_BASENAME}" "\$ORIGIN/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}" \ + "${ROOT}/out/python/install/lib/libpython3.so" + fi fi fi fi -# Install pip so we can patch it to work with non-dynamic executables -# and work around https://github.com/pypa/pip/issues/6543. But pip's bundled -# setuptools has the same bug! So we need to install a patched version. -pushd ${ROOT}/setuptools-${SETUPTOOLS_VERSION} -patch -p1 < "${ROOT}/hack_sysconfig.py" << EOF +import json +import os +import sys +import sysconfig + +ROOT = sys.argv[1] + +FREETHREADED = sysconfig.get_config_var("Py_GIL_DISABLED") +MAJMIN = ".".join([str(sys.version_info[0]), str(sys.version_info[1])]) +LIB_SUFFIX = "t" if FREETHREADED else "" +PYTHON_CONFIG = os.path.join(ROOT, "install", "bin", "python%s-config" % MAJMIN) +PLATFORM_CONFIG = os.path.join(ROOT, sysconfig.get_config_var("LIBPL").lstrip("/")) +MAKEFILE = os.path.join(PLATFORM_CONFIG, "Makefile") +SYSCONFIGDATA = os.path.join( + ROOT, + "install", + "lib", + "python%s%s" % (MAJMIN, LIB_SUFFIX), + "%s.py" % sysconfig._get_sysconfigdata_name(), +) + +def replace_in_file(path, search, replace): + with open(path, "rb") as fh: + data = fh.read() + + if search.encode("utf-8") in data: + print("replacing '%s' in %s with '%s'" % (search, path, replace)) + else: + print("warning: '%s' not in %s" % (search, path)) + + data = data.replace(search.encode("utf-8"), replace.encode("utf-8")) + + with open(path, "wb") as fh: + fh.write(data) + + +def replace_in_all(search, replace): + replace_in_file(PYTHON_CONFIG, search, replace) + replace_in_file(MAKEFILE, search, replace) + replace_in_file(SYSCONFIGDATA, search, replace) + + +def replace_in_sysconfigdata(search, replace, keys): + """Replace a string in the sysconfigdata file for select keys.""" + with open(SYSCONFIGDATA, "rb") as fh: + data = fh.read() + + globals_dict = {} + locals_dict = {} + exec(data, globals_dict, locals_dict) + build_time_vars = locals_dict['build_time_vars'] + + for key in keys: + if key in build_time_vars: + build_time_vars[key] = build_time_vars[key].replace(search, replace) + + with open(SYSCONFIGDATA, "wb") as fh: + fh.write(b'# system configuration generated and used by the sysconfig module\n') + fh.write(('build_time_vars = %s' % json.dumps(build_time_vars, indent=4, sort_keys=True)).encode("utf-8")) + fh.close() + + +def format_sysconfigdata(): + """Reformat the sysconfigdata file to avoid implicit string concatenations. + + In some Python versions, the sysconfigdata file contains implicit string + concatenations that extend over multiple lines, which make string replacement + much harder. This function reformats the file to avoid this issue. + + See: https://github.com/python/cpython/blob/a03efb533a58fd13fb0cc7f4a5c02c8406a407bd/Mac/BuildScript/build-installer.py#L1360C1-L1385C15. + """ + with open(SYSCONFIGDATA, "rb") as fh: + data = fh.read() + + globals_dict = {} + locals_dict = {} + exec(data, globals_dict, locals_dict) + build_time_vars = locals_dict['build_time_vars'] + + with open(SYSCONFIGDATA, "wb") as fh: + fh.write(b'# system configuration generated and used by the sysconfig module\n') + fh.write(('build_time_vars = %s' % json.dumps(build_time_vars, indent=4, sort_keys=True)).encode("utf-8")) + fh.close() + + +# Format sysconfig to ensure that string replacements take effect. +format_sysconfigdata() + +# Remove `-Werror=unguarded-availability-new` from `CFLAGS` and `CPPFLAGS`. +# These flags are passed along when building extension modules. In that context, +# `-Werror=unguarded-availability-new` can cause builds that would otherwise +# succeed to fail. While the issues raised by `-Werror=unguarded-availability-new` +# are legitimate, enforcing them in extension modules is stricter than CPython's +# own behavior. +replace_in_sysconfigdata( + "-Werror=unguarded-availability-new", + "", + ["CFLAGS", "CPPFLAGS"], +) + +# Remove the Xcode path from the compiler flags. +# +# CPython itself will drop this from `sysconfig.get_config_var("CFLAGS")` and +# similar calls, but _not_ if `CFLAGS` is set in the environment (regardless of +# the `CFLAGS` value). It will almost always be wrong, so we drop it unconditionally. +xcode_path = os.getenv("APPLE_SDK_PATH") +if xcode_path: + replace_in_all("-isysroot %s" % xcode_path, "") + +# -fdebug-default-version is Clang only. Strip so compiling works on GCC. +replace_in_all("-fdebug-default-version=4", "") + +# Remove some build environment paths. +# This is /tools on Linux but can be a dynamic path / temp directory on macOS +# and when not using container builds. +tools_path = os.environ["TOOLS_PATH"] +replace_in_all("-I%s/deps/include/ncursesw" % tools_path, "") +replace_in_all("-I%s/deps/include/uuid" % tools_path, "") +replace_in_all("-I%s/deps/include" % tools_path, "") +replace_in_all("-L%s/deps/lib" % tools_path, "") +# See https://github.com/python/cpython/issues/145810#issuecomment-4068139183 +replace_in_all("-LModules/_hacl", "") -# pip 21 shipped DOS line endings. https://github.com/pypa/pip/issues/9638. -# Let's fix that. -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then - find . -name '*.py' -exec perl -i -pe 's/\r\n$/\n/g' {} \; -else - find . -name '*.py' -exec sed -i 's/\r$//g' {} \; -fi - -patch -p1 < ${ROOT}/generate_metadata.py << EOF +cat > "${ROOT}/generate_metadata.py" << EOF import codecs import importlib.machinery import importlib.util @@ -810,11 +1096,31 @@ import sys import sysconfig # When doing cross builds, sysconfig still picks up abiflags from the -# host Python, which is never built in debug mode. Patch abiflags accordingly. +# host Python, which is never built in debug or free-threaded mode. Patch abiflags accordingly. +if os.environ.get("CPYTHON_FREETHREADED") and "t" not in sysconfig.get_config_var("abiflags"): + sys.abiflags += "t" + sysconfig._CONFIG_VARS["abiflags"] += "t" if os.environ.get("CPYTHON_DEBUG") and "d" not in sysconfig.get_config_var("abiflags"): sys.abiflags += "d" sysconfig._CONFIG_VARS["abiflags"] += "d" +# importlib.machinery.EXTENSION_SUFFIXES picks up its value from #define in C +# code. When we're doing a cross-build, the C code is the build machine, not +# the host/target and is wrong. The logic here essentially reimplements the +# logic for _PyImport_DynLoadFiletab in dynload_shlib.c, which is what +# importlib.machinery.EXTENSION_SUFFIXES ultimately calls into. +extension_suffixes = [".%s.so" % sysconfig.get_config_var("SOABI")] + +alt_soabi = sysconfig.get_config_var("ALT_SOABI") +if alt_soabi: + # The value can be double quoted for some reason. + extension_suffixes.append(".%s.so" % alt_soabi.strip('"')) + +# Always version 3 in Python 3. +extension_suffixes.append(".abi3.so") + +extension_suffixes.append(".so") + metadata = { "python_abi_tag": sys.abiflags, "python_implementation_cache_tag": sys.implementation.cache_tag, @@ -825,7 +1131,7 @@ metadata = { "python_suffixes": { "bytecode": importlib.machinery.BYTECODE_SUFFIXES, "debug_bytecode": importlib.machinery.DEBUG_BYTECODE_SUFFIXES, - "extension": importlib.machinery.EXTENSION_SUFFIXES, + "extension": extension_suffixes, "optimized_bytecode": importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES, "source": importlib.machinery.SOURCE_SUFFIXES, }, @@ -865,27 +1171,32 @@ with open(sys.argv[1], "w") as fh: json.dump(metadata, fh, sort_keys=True, indent=4) EOF -${BUILD_PYTHON} ${ROOT}/generate_metadata.py ${ROOT}/metadata.json -cat ${ROOT}/metadata.json +${BUILD_PYTHON} "${ROOT}/generate_metadata.py" "${ROOT}/metadata.json" +cat "${ROOT}/metadata.json" if [ "${CC}" != "musl-clang" ]; then - objdump -T ${LIBPYTHON_SHARED_LIBRARY} | grep GLIBC_ | awk '{print $5}' | awk -F_ '{print $2}' | sort -V | tail -n 1 > ${ROOT}/glibc_version.txt - cat ${ROOT}/glibc_version.txt + objdump -T "${LIBPYTHON_SHARED_LIBRARY}" | grep GLIBC_ | awk '{print $5}' | awk -F_ '{print $2}' | sort -V | tail -n 1 > "${ROOT}/glibc_version.txt" + cat "${ROOT}/glibc_version.txt" fi # Downstream consumers don't require bytecode files. So remove them. # Ideally we'd adjust the build system. But meh. -find ${ROOT}/out/python/install -type d -name __pycache__ -print0 | xargs -0 rm -rf +find "${ROOT}/out/python/install" -type d -name __pycache__ -print0 | xargs -0 rm -rf # Ensure lib-dynload exists, or Python complains on startup. -LIB_DYNLOAD=${ROOT}/out/python/install/lib/python${PYTHON_MAJMIN_VERSION}/lib-dynload +LIB_DYNLOAD=${ROOT}/out/python/install/lib/python${PYTHON_MAJMIN_VERSION}${PYTHON_LIB_SUFFIX}/lib-dynload mkdir -p "${LIB_DYNLOAD}" touch "${LIB_DYNLOAD}/.empty" # Symlink libpython so we don't have 2 copies. +# TODO(geofft): Surely we can get PYTHON_ARCH out of the build? case "${TARGET_TRIPLE}" in -aarch64-unknown-linux-gnu) - PYTHON_ARCH="aarch64-linux-gnu" +aarch64-unknown-linux-*) + if [[ "${CC}" = "musl-clang" ]]; then + PYTHON_ARCH="aarch64-linux-musl" + else + PYTHON_ARCH="aarch64-linux-gnu" + fi ;; # This is too aggressive. But we don't have patches in place for # setting the platform name properly on non-Darwin. @@ -898,8 +1209,8 @@ armv7-unknown-linux-gnueabi) armv7-unknown-linux-gnueabihf) PYTHON_ARCH="arm-linux-gnueabihf" ;; -i686-unknown-linux-gnu) - PYTHON_ARCH="i386-linux-gnu" +loongarch64-unknown-linux-gnu) + PYTHON_ARCH="loongarch64-linux-gnu" ;; mips-unknown-linux-gnu) PYTHON_ARCH="mips-linux-gnu" @@ -910,11 +1221,21 @@ mipsel-unknown-linux-gnu) mips64el-unknown-linux-gnuabi64) PYTHON_ARCH="mips64el-linux-gnuabi64" ;; +ppc64le-unknown-linux-gnu) + PYTHON_ARCH="powerpc64le-linux-gnu" + ;; +riscv64-unknown-linux-gnu) + PYTHON_ARCH="riscv64-linux-gnu" + ;; s390x-unknown-linux-gnu) PYTHON_ARCH="s390x-linux-gnu" ;; x86_64-unknown-linux-*) - PYTHON_ARCH="x86_64-linux-gnu" + if [[ "${CC}" = "musl-clang" ]]; then + PYTHON_ARCH="x86_64-linux-musl" + else + PYTHON_ARCH="x86_64-linux-gnu" + fi ;; *) echo "unhandled target triple: ${TARGET_TRIPLE}" @@ -923,41 +1244,57 @@ esac LIBPYTHON=libpython${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}.a ln -sf \ - python${PYTHON_MAJMIN_VERSION}/config-${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}-${PYTHON_ARCH}/${LIBPYTHON} \ - ${ROOT}/out/python/install/lib/${LIBPYTHON} + "python${PYTHON_MAJMIN_VERSION}${PYTHON_LIB_SUFFIX}/config-${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}-${PYTHON_ARCH}/${LIBPYTHON}" \ + "${ROOT}/out/python/install/lib/${LIBPYTHON}" if [ -n "${PYTHON_BINARY_SUFFIX}" ]; then # Ditto for Python executable. ln -sf \ - python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX} \ - ${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION} + "python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}" \ + "${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}" fi -if [ ! -f ${ROOT}/out/python/install/bin/python3 ]; then +if [ ! -f "${ROOT}/out/python/install/bin/python3" ]; then echo "python3 executable does not exist" exit 1 fi +ln -sf \ + "$(readlink "${ROOT}/out/python/install/bin/python3")" \ + "${ROOT}/out/python/install/bin/python" + # Fixup shebangs in Python scripts to reference the local python interpreter. -cat > ${ROOT}/fix_shebangs.py << EOF +cat > "${ROOT}/fix_shebangs.py" << EOF import os import sys ROOT = sys.argv[1] -for f in sorted(os.listdir(ROOT)): - full = os.path.join(ROOT, f) - +def fix_shebang(full): if os.path.islink(full) or not os.path.isfile(full): - continue + return with open(full, "rb") as fh: - initial = fh.read(64) + initial = fh.read(256) if not initial.startswith(b"#!"): - continue + return + + if b"\n" not in initial: + raise Exception("could not find end of shebang line; consider increasing read count") + + initial = initial.splitlines()[0].decode("utf-8", "replace") - print("rewriting shebang in %s" % full) + # Some shebangs are allowed. + if "bin/env" in initial or "bin/sh" in initial or "bin/bash" in initial: + print("ignoring %s due to non-python shebang (%s)" % (full, initial)) + return + + # Make sure it is a Python script and not something else. + if "/python" not in initial: + raise Exception("unexpected shebang (%s) in %s" % (initial, full)) + + print("rewriting Python shebang (%s) in %s" % (initial, full)) lines = [] @@ -966,42 +1303,48 @@ for f in sorted(os.listdir(ROOT)): lines.extend([ b"#!/bin/sh\n", - b'"exec" "\$(dirname \$0)/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}" "\$0" "\$@"\n', + b"'''exec' \"\$(dirname -- \"\$(realpath -- \"\$0\")\")/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}\" \"\$0\" \"\$@\"\n", + b"' '''\n", ]) lines.extend(fh) with open(full, "wb") as fh: fh.write(b"".join(lines)) + + +for root, dirs, files in os.walk(ROOT): + dirs[:] = sorted(dirs) + + for f in sorted(files): + fix_shebang(os.path.join(root, f)) EOF -${BUILD_PYTHON} ${ROOT}/fix_shebangs.py ${ROOT}/out/python/install/bin +${BUILD_PYTHON} "${ROOT}/fix_shebangs.py" "${ROOT}/out/python/install" # Also copy object files so they can be linked in a custom manner by # downstream consumers. -for d in Modules Objects Parser Parser/pegen Programs Python; do - # Parser/pegen only exists in 3.9+ - if [ -d $d ]; then - mkdir -p ${ROOT}/out/python/build/$d - cp -av $d/*.o ${ROOT}/out/python/build/$d/ - fi +OBJECT_DIRS="Objects Parser Parser/lexer Parser/pegen Parser/tokenizer Programs Python Python/deepfreeze" +OBJECT_DIRS="${OBJECT_DIRS} Modules" +for ext in _blake2 cjkcodecs _ctypes _ctypes/darwin _decimal _expat _hacl _io _multiprocessing _remote_debugging _sha3 _sqlite _sre _testcapi _testinternalcapi _testlimitedcapi _xxtestfuzz _zstd; do + OBJECT_DIRS="${OBJECT_DIRS} Modules/${ext}" done -# Also copy extension variant metadata files. -if [ "${PYBUILD_PLATFORM}" != "macos" ]; then - cp -av Modules/VARIANT-*.data ${ROOT}/out/python/build/Modules/ -fi +for d in ${OBJECT_DIRS}; do + # Not all directories are in all Python versions. And some directories may + # exist but not have object files. + if compgen -G "${d}/*.o" > /dev/null; then + mkdir -p "${ROOT}/out/python/build/$d" + cp -av "$d"/*.o "${ROOT}/out/python/build/$d"/ + fi +done # The object files need to be linked against library dependencies. So copy # library files as well. -mkdir ${ROOT}/out/python/build/lib -cp -av ${TOOLS_PATH}/deps/lib/*.a ${ROOT}/out/python/build/lib/ - -if [ -d "${TOOLS_PATH}/deps/libedit" ]; then - cp -av ${TOOLS_PATH}/deps/libedit/lib/*.a ${ROOT}/out/python/build/lib/ -fi +mkdir "${ROOT}/out/python/build/lib" +cp -av "${TOOLS_PATH}/deps/lib"/*.a "${ROOT}/out/python/build/lib/" -# On Apple, Python 3.9+ uses __builtin_available() to sniff for feature +# On Apple, Python uses __builtin_available() to sniff for feature # availability. This symbol is defined by clang_rt, which isn't linked # by default. When building a static library, one must explicitly link # against clang_rt or you will get an undefined symbol error for @@ -1009,20 +1352,33 @@ fi # # We copy the libclang_rt..a library from our clang into the # distribution so it is available. See documentation in quirks.rst for more. -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then - cp -av $(dirname $(which clang))/../lib/clang/*/lib/darwin/libclang_rt.osx.a ${ROOT}/out/python/build/lib/ +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + cp -av "$(dirname "$(which clang)")/../lib/clang"/*/lib/darwin/libclang_rt.osx.a "${ROOT}/out/python/build/lib/" fi # And prune libraries we never reference. -rm -f ${ROOT}/out/python/build/lib/{libdb-6.0,libxcb-*,libX11-xcb}.a +rm -f "${ROOT}/out/python/build/lib"/{libdb-6.0,libxcb-*,libX11-xcb}.a -if [ -d "${TOOLS_PATH}/deps/lib/tcl8" ]; then - # Copy tcl/tk/tix resources needed by tkinter. - mkdir ${ROOT}/out/python/install/lib/tcl +if [ -d "${TOOLS_PATH}/deps/lib/tcl9" ]; then + # Copy tcl/tk resources needed by tkinter. + mkdir "${ROOT}/out/python/install/lib/tcl" # Keep this list in sync with tcl_library_paths. - for source in ${TOOLS_PATH}/deps/lib/{tcl8,tcl8.6,thread2.8.5,Tix8.4.3,tk8.6}; do - cp -av $source ${ROOT}/out/python/install/lib/ + for source in ${TOOLS_PATH}/deps/lib/{itcl4.3.5,tcl9,tcl9.0,thread3.0.4,tk9.0}; do + cp -av "$source" "${ROOT}/out/python/install/lib/" done + + ( + shopt -s nullglob + dylibs=(${TOOLS_PATH}/deps/lib/lib*.dylib ${TOOLS_PATH}/deps/lib/lib*.so) + if [ "${#dylibs[@]}" -gt 0 ]; then + cp -av "${dylibs[@]}" "${ROOT}/out/python/install/lib/" + fi + ) +fi + +# Copy the terminfo database if present. +if [ -d "${TOOLS_PATH}/deps/usr/share/terminfo" ]; then + cp -av "${TOOLS_PATH}/deps/usr/share/terminfo" "${ROOT}/out/python/install/share/" fi # config.c defines _PyImport_Inittab and extern references to modules, which @@ -1031,13 +1387,23 @@ fi # frozen.c is something similar for frozen modules. # Setup.dist/Setup.local are useful to parse for active modules and library # dependencies. -cp -av Modules/config.c ${ROOT}/out/python/build/Modules/ -cp -av Modules/config.c.in ${ROOT}/out/python/build/Modules/ -cp -av Python/frozen.c ${ROOT}/out/python/build/Python/ -cp -av Modules/Setup* ${ROOT}/out/python/build/Modules/ +cp -av Modules/config.c "${ROOT}/out/python/build/Modules/" +cp -av Modules/config.c.in "${ROOT}/out/python/build/Modules/" +cp -av Python/frozen.c "${ROOT}/out/python/build/Python/" +cp -av Modules/Setup* "${ROOT}/out/python/build/Modules/" # Copy the test hardness runner for convenience. -cp -av Tools/scripts/run_tests.py ${ROOT}/out/python/build/ +# As of Python 3.13, the test harness runner has been removed so we provide a compatibility script +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]; then + cp -av "${ROOT}/run_tests-13.py" "${ROOT}/out/python/build/run_tests.py" +else + cp -av Tools/scripts/run_tests.py "${ROOT}/out/python/build/" +fi + +# Don't hard-code the build-time prefix into the pkg-config files. See +# the description of `pcfiledir` in `man pkg-config`. +find "${ROOT}/out/python/install/lib/pkgconfig" -name \*.pc -type f -exec \ + sed "${sed_args[@]}" 's|^prefix=/install|prefix=${pcfiledir}/../..|' {} + -mkdir ${ROOT}/out/python/licenses -cp ${ROOT}/LICENSE.*.txt ${ROOT}/out/python/licenses/ +mkdir "${ROOT}/out/python/licenses" +cp "${ROOT}"/LICENSE.*.txt "${ROOT}/out/python/licenses/" diff --git a/cpython-unix/build-expat.sh b/cpython-unix/build-expat.sh new file mode 100755 index 000000000..87a4d0100 --- /dev/null +++ b/cpython-unix/build-expat.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -ex + +ROOT=$(pwd) + +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH + +tar -xf "expat-${EXPAT_VERSION}.tar.xz" + +pushd "expat-${EXPAT_VERSION}" + +# xmlwf isn't needed by CPython. +# Disable -fexceptions because we don't need it and it adds a dependency on libgcc_s, which +# is softly undesirable. +CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" ./configure \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ + --prefix=/tools/deps \ + --disable-shared \ + --without-examples \ + --without-tests \ + --without-xmlwf \ + ax_cv_check_cflags___fexceptions=no + +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-gcc.sh b/cpython-unix/build-gcc.sh deleted file mode 100755 index 0a6d7b363..000000000 --- a/cpython-unix/build-gcc.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -cd /build - -ROOT=$(pwd) -SCCACHE="${ROOT}/sccache" - -tar -C /tools -xf /build/binutils-${BINUTILS_VERSION}-linux64.tar -export PATH=/tools/host/bin:$PATH - -tar -xf gcc-${GCC_VERSION}.tar.xz -tar -xf gmp-${GMP_VERSION}.tar.xz -tar -xf isl-${ISL_VERSION}.tar.bz2 -tar -xf mpc-${MPC_VERSION}.tar.gz -tar -xf mpfr-${MPFR_VERSION}.tar.xz - -pushd gcc-${GCC_VERSION} -ln -sf ../gmp-${GMP_VERSION} gmp -ln -sf ../isl-${ISL_VERSION} isl -ln -sf ../mpc-${MPC_VERSION} mpc -ln -sf ../mpfr-${MPFR_VERSION} mpfr -popd - -if [ -x "${SCCACHE}" ]; then - "${SCCACHE}" --start-server - export CC="${SCCACHE} /usr/bin/gcc" - export CXX="${SCCACHE} /usr/bin/g++" -fi - -mkdir gcc-objdir - -pushd gcc-objdir - -# We don't use GCC for anything other than building llvm/clang. So -# we can skip the 3 stage bootstrap to save time. -../gcc-${GCC_VERSION}/configure \ - --build=x86_64-unknown-linux-gnu \ - --prefix=/tools/host \ - --disable-bootstrap \ - --enable-languages=c,c++ \ - --disable-nls \ - --disable-gnu-unique-object \ - --enable-__cxa_atexit \ - --with-sysroot=/ - -make -j `nproc` -make -j `nproc` install DESTDIR=/build/out -popd diff --git a/cpython-unix/build-gdbm.sh b/cpython-unix/build-gdbm.sh deleted file mode 100755 index 0c578bc08..000000000 --- a/cpython-unix/build-gdbm.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH - -tar -xf gdbm-${GDBM_VERSION}.tar.gz - -pushd gdbm-${GDBM_VERSION} - -# Patch to work with -fno-common, which LLVM 11 enabled. -patch -p1 <"; - void (*parseopt_help_hook) (FILE *stream); - -EOF - - -# CPython setup.py looks for libgdbm_compat and gdbm-ndbm.h, -# which require --enable-libgdbm-compat. -CLFAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps \ - --disable-shared \ - --enable-libgdbm-compat - -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-gettext.sh b/cpython-unix/build-gettext.sh deleted file mode 100755 index 96dd0ae57..000000000 --- a/cpython-unix/build-gettext.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH - -tar -xf gettext-${GETTEXT_VERSION}.tar.gz - -pushd gettext-${GETTEXT_VERSION} - -# If libunistring exists on the system, it can get picked up and introduce -# an added dependency. So we force use of the bundled version. -CLFAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps \ - --disable-shared \ - --disable-java \ - --disable-dependency-tracking \ - --with-included-libcroco \ - --with-included-gettext \ - --with-included-glib \ - --with-included-libunistring \ - --with-included-libxml \ - --without-libiconv-prefix \ - --without-libintl-prefix \ - --without-libncurses-prefix \ - --without-libtermcap-prefix \ - --without-libxcurses-prefix \ - --without-libcurses-prefix \ - --without-libtextstyle-prefix \ - --without-libunistring-prefix \ - --without-libxml2-prefix \ - --without-git - -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-inputproto.sh b/cpython-unix/build-inputproto.sh deleted file mode 100755 index 9ab523639..000000000 --- a/cpython-unix/build-inputproto.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -pkg-config --version - -export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH -export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig - -tar -xf inputproto-${INPUTPROTO_VERSION}.tar.gz -pushd inputproto-${INPUTPROTO_VERSION} - -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps - -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-kbproto.sh b/cpython-unix/build-kbproto.sh deleted file mode 100755 index 031493e87..000000000 --- a/cpython-unix/build-kbproto.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -pkg-config --version - -export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH -export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig - -tar -xf kbproto-${KBPROTO_VERSION}.tar.gz -pushd kbproto-${KBPROTO_VERSION} - -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps - -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-libX11.sh b/cpython-unix/build-libX11.sh index 777acb0fd..b16c5be78 100755 --- a/cpython-unix/build-libX11.sh +++ b/cpython-unix/build-libX11.sh @@ -5,13 +5,13 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig:/tools/deps/lib/pkgconfig -tar -xf libX11-${LIBX11_VERSION}.tar.gz -pushd libX11-${LIBX11_VERSION} +tar -xf "libX11-${LIBX11_VERSION}.tar.gz" +pushd "libX11-${LIBX11_VERSION}" patch -p1 << 'EOF' diff --git a/configure b/configure @@ -43,7 +43,7 @@ fi # configure doesn't support cross-compiling in malloc(0) returns null test. # So we have to force a value. -if [ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" ]; then +if [ -n "${CROSS_COMPILING}" ]; then case "${TARGET_TRIPLE}" in aarch64-unknown-linux-gnu) EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" @@ -54,7 +54,7 @@ if [ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" ]; then armv7-unknown-linux-gnueabihf) EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" ;; - i686-unknown-linux-gnu) + loongarch64-unknown-linux-gnu) EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" ;; mips-unknown-linux-gnu) @@ -66,15 +66,50 @@ if [ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" ]; then mips64el-unknown-linux-gnuabi64) EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" ;; + ppc64le-unknown-linux-gnu) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + riscv64-unknown-linux-gnu) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; s390x-unknown-linux-gnu) EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" ;; + x86_64-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + aarch64-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + loongarch64-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + mips-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + mipsel-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + ppc64le-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + riscv64-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; + s390x-unknown-linux-musl) + EXTRA_FLAGS="${EXTRA_FLAGS} --enable-malloc0returnsnull" + ;; *) echo "cross-compiling but malloc(0) override not set; failures possible" ;; esac fi +# Avoid dlopen("libXcursor.so.1") from the OS, which can go horribly wrong. We +# might not need to avoid this if we switch to shipping X11 as shared +# libraries, and ideally if we ship libXcursor ourselves. +EXTRA_FLAGS="${EXTRA_FLAGS} --disable-loadable-xcursor" + # CC_FOR_BUILD is here because configure doesn't look for `clang` when # cross-compiling. So we force it. CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -I/tools/deps/include" \ @@ -84,11 +119,11 @@ CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -I/tools/deps/include" \ CFLAGS_FOR_BUILD="-I/tools/deps/include" \ CPPFLAGS_FOR_BUILD="-I/tools/deps/include" \ ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ --disable-silent-rules \ ${EXTRA_FLAGS} -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-libXau.sh b/cpython-unix/build-libXau.sh index 3150a18dc..d5f5175f5 100755 --- a/cpython-unix/build-libXau.sh +++ b/cpython-unix/build-libXau.sh @@ -5,23 +5,23 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig:/tools/deps/lib/pkgconfig -tar -xf libXau-${LIBXAU_VERSION}.tar.gz -pushd libXau-${LIBXAU_VERSION} +tar -xf "libXau-${LIBXAU_VERSION}.tar.gz" +pushd "libXau-${LIBXAU_VERSION}" if [ "${CC}" = "musl-clang" ]; then EXTRA_FLAGS="--disable-shared" fi CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ ${EXTRA_FLAGS} -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-libedit.sh b/cpython-unix/build-libedit.sh index 30dad5cb5..a1e8e2d14 100755 --- a/cpython-unix/build-libedit.sh +++ b/cpython-unix/build-libedit.sh @@ -5,13 +5,13 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf libedit-${LIBEDIT_VERSION}.tar.gz +tar -xf "libedit-${LIBEDIT_VERSION}.tar.gz" -pushd libedit-${LIBEDIT_VERSION} +pushd "libedit-${LIBEDIT_VERSION}" # libedit's configure isn't smart enough to look for ncursesw. So we teach it # to. Ideally we would edit configure.ac and run autoconf. But Jessie's autoconf @@ -19,13 +19,13 @@ pushd libedit-${LIBEDIT_VERSION} # run-time. So we hack up the configure script instead. patch -p1 << "EOF" diff --git a/configure b/configure -index 26dd8d0..4b6d47c 100755 +index 614795f..4671f1b 100755 --- a/configure +++ b/configure -@@ -12921,14 +12921,14 @@ test -n "$NROFF" || NROFF="/bin/false" - - - +@@ -14154,14 +14154,14 @@ test -n "$NROFF" || NROFF="/bin/false" + + + -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tgetent in -lncurses" >&5 -printf %s "checking for tgetent in -lncurses... " >&6; } -if test ${ac_cv_lib_ncurses_tgetent+y} @@ -34,26 +34,28 @@ index 26dd8d0..4b6d47c 100755 +if test ${ac_cv_lib_ncursesw_tgetent+y} then : printf %s "(cached) " >&6 - else $as_nop - ac_check_lib_save_LIBS=$LIBS + else case e in #( + e) ac_check_lib_save_LIBS=$LIBS -LIBS="-lncurses $LIBS" +LIBS="-lncursesw $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -@@ -12946,21 +12946,21 @@ return tgetent (); + +@@ -14185,9 +14185,9 @@ return tgetent (); _ACEOF if ac_fn_c_try_link "$LINENO" then : - ac_cv_lib_ncurses_tgetent=yes + ac_cv_lib_ncursesw_tgetent=yes - else $as_nop -- ac_cv_lib_ncurses_tgetent=no -+ ac_cv_lib_ncursesw_tgetent=no + else case e in #( +- e) ac_cv_lib_ncurses_tgetent=no ;; ++ e) ac_cv_lib_ncursesw_tgetent=no ;; + esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - LIBS=$ac_check_lib_save_LIBS +@@ -14195,13 +14195,13 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \ + LIBS=$ac_check_lib_save_LIBS ;; + esac fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_tgetent" >&5 -printf "%s\n" "$ac_cv_lib_ncurses_tgetent" >&6; } @@ -63,21 +65,47 @@ index 26dd8d0..4b6d47c 100755 +if test "x$ac_cv_lib_ncursesw_tgetent" = xyes then : printf "%s\n" "#define HAVE_LIBNCURSES 1" >>confdefs.h - + - LIBS="-lncurses $LIBS" + LIBS="-lncursesw $LIBS" - - else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tgetent in -lcurses" >&5 -@@ -13089,7 +13089,7 @@ then : + + else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tgetent in -lcurses" >&5 +@@ -14354,7 +14354,7 @@ then : LIBS="-ltinfo $LIBS" - - else $as_nop -- as_fn_error $? "libncurses, libcurses, libtermcap or libtinfo is required!" "$LINENO" 5 -+ as_fn_error $? "libncursesw, libcurses, libtermcap or libtinfo is required!" "$LINENO" 5 - + + else case e in #( +- e) as_fn_error $? "libncurses, libcurses, libtermcap or libtinfo is required!" "$LINENO" 5 ++ e) as_fn_error $? "libncursesw, libcurses, libtermcap or libtinfo is required!" "$LINENO" 5 + ;; + esac fi - +EOF + +# When libedit receives a signal, it re-broadcasts it to its entire pgroup. +# This seems intended to preserve normal ^C behavior in "raw" mode when the +# terminal's ISIG flag is cleared? However, libedit does not in fact clear +# ISIG. (And Jack can't find any evidence of any version that ever did.) This +# sometimes results in the parent process receiving ^C twice back-to-back, +# depending on the vagaries of signal coalescing. More pathologically, if the +# parent tries to signal the child directly with e.g. `kill(pid, SIGTERM)`, +# libedit *signals the parent right back* (not to mention any other pgroup +# siblings or grandparents). This is just wild behavior, even though it's +# probably rare that it matters in practice. Patch it out. See also: +# https://github.com/astral-sh/uv/issues/13919#issuecomment-2960501229. +patch -p1 << "EOF" +diff --git i/src/sig.c w/src/sig.c +index d2b77e7..884b2dd 100644 +--- i/src/sig.c ++++ w/src/sig.c +@@ -107,7 +107,7 @@ sig_handler(int signo) + sel->el_signal->sig_action[i].sa_flags = 0; + sigemptyset(&sel->el_signal->sig_action[i].sa_mask); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +- (void) kill(0, signo); ++ (void) raise(signo); + errno = save_errno; + } EOF cflags="${EXTRA_TARGET_CFLAGS} -fPIC -I${TOOLS_PATH}/deps/include -I${TOOLS_PATH}/deps/include/ncursesw" @@ -88,20 +116,19 @@ if [ "${CC}" = "musl-clang" ]; then cflags="${cflags} -D__STDC_ISO_10646__=201103L" fi -# Install to /tools/deps/libedit so it doesn't conflict with readline's files. -CLFAGS="${cflags}" CPPFLAGS="${cflags}" LDFLAGS="${ldflags}" \ +CFLAGS="${cflags}" CPPFLAGS="${cflags}" LDFLAGS="${ldflags}" \ ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps/libedit \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ + --prefix=/tools/deps \ --disable-shared -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" # Alias readline/{history.h, readline.h} for readline compatibility. -if [ -e ${ROOT}/out/tools/deps/libedit/include ]; then - mkdir ${ROOT}/out/tools/deps/libedit/include/readline - ln -s ../editline/readline.h ${ROOT}/out/tools/deps/libedit/include/readline/readline.h - ln -s ../editline/readline.h ${ROOT}/out/tools/deps/libedit/include/readline/history.h +if [ -e "${ROOT}/out/tools/deps/include" ]; then + mkdir "${ROOT}/out/tools/deps/include/readline" + ln -s ../editline/readline.h "${ROOT}/out/tools/deps/include/readline/readline.h" + ln -s ../editline/readline.h "${ROOT}/out/tools/deps/include/readline/history.h" fi diff --git a/cpython-unix/build-libffi-3.3.sh b/cpython-unix/build-libffi-3.3.sh new file mode 100755 index 000000000..77afc9e77 --- /dev/null +++ b/cpython-unix/build-libffi-3.3.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -ex + +ROOT=$(pwd) + +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH + +tar -xf "libffi-${LIBFFI_3_3_VERSION}.tar.gz" + +pushd "libffi-${LIBFFI_3_3_VERSION}" + +EXTRA_CONFIGURE= + +# mkostemp() was introduced in macOS 10.10 and libffi doesn't have +# runtime guards for it. So ban the symbol when targeting old macOS. +if [ "${APPLE_MIN_DEPLOYMENT_TARGET}" = "10.9" ]; then + EXTRA_CONFIGURE="${EXTRA_CONFIGURE} ac_cv_func_mkostemp=no" +fi + +CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ + --prefix=/tools/deps \ + --disable-shared \ + ${EXTRA_CONFIGURE} + +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-libffi.sh b/cpython-unix/build-libffi.sh index 701a3a84a..25fa721fc 100755 --- a/cpython-unix/build-libffi.sh +++ b/cpython-unix/build-libffi.sh @@ -5,19 +5,390 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf libffi-${LIBFFI_VERSION}.tar.gz +tar -xf "libffi-${LIBFFI_VERSION}.tar.gz" -pushd libffi-${LIBFFI_VERSION} +pushd "libffi-${LIBFFI_VERSION}" + +# Patches needed to fix compilation on aarch64. Will presumably be in libffi +# 3.4.7 or 3.5. + +# Commit f64141ee3f9e455a060bd09e9ab72b6c94653d7c. +patch -p1 <<'EOF' +diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S +index fdd0e8b..60cfa50 100644 +--- a/src/aarch64/sysv.S ++++ b/src/aarch64/sysv.S +@@ -68,7 +68,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #define BTI_J hint #36 + /* + * The ELF Notes section needs to indicate if BTI is supported, as the first ELF loaded that doesn't +- * declare this support disables it for the whole process. ++ * declare this support disables it for memory region containing the loaded library. + */ + # define GNU_PROPERTY_AARCH64_BTI (1 << 0) /* Has Branch Target Identification */ + .text +@@ -527,6 +527,7 @@ L(do_closure): + #if defined(FFI_EXEC_STATIC_TRAMP) + .align 4 + CNAME(ffi_closure_SYSV_V_alt): ++ BTI_C + /* See the comments above trampoline_code_table. */ + ldr x17, [sp, #8] /* Load closure in x17 */ + add sp, sp, #16 /* Restore the stack */ +@@ -541,6 +542,7 @@ CNAME(ffi_closure_SYSV_V_alt): + + .align 4 + CNAME(ffi_closure_SYSV_alt): ++ BTI_C + /* See the comments above trampoline_code_table. */ + ldr x17, [sp, #8] /* Load closure in x17 */ + add sp, sp, #16 /* Restore the stack */ +diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am +index d286cf7..6ba98e1 100644 +--- a/testsuite/Makefile.am ++++ b/testsuite/Makefile.am +@@ -8,7 +8,7 @@ CLEANFILES = *.exe core* *.log *.sum + + EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ + emscripten/node-tests.sh emscripten/test.html emscripten/test_libffi.py \ +- emscripten/build-tests.sh lib/libffi.exp lib/target-libpath.exp \ ++ emscripten/build-tests.sh lib/libffi.exp lib/target-libpath.exp \ + lib/wrapper.exp libffi.bhaible/Makefile libffi.bhaible/README \ + libffi.bhaible/alignof.h libffi.bhaible/bhaible.exp libffi.bhaible/test-call.c \ + libffi.bhaible/test-callback.c libffi.bhaible/testcases.c libffi.call/align_mixed.c \ +EOF + +# Commit 45d284f2d066cc3a080c5be88e51b4d934349797. +patch -p1 <<'EOF' +diff --git a/configure.ac b/configure.ac +index 816bfd6..b35a999 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -189,17 +189,17 @@ AC_CACHE_CHECK([whether compiler supports pointer authentication], + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ + #ifdef __clang__ + # if __has_feature(ptrauth_calls) +-# define HAVE_PTRAUTH 1 ++# define HAVE_ARM64E_PTRAUTH 1 + # endif + #endif + +-#ifndef HAVE_PTRAUTH ++#ifndef HAVE_ARM64E_PTRAUTH + # error Pointer authentication not supported + #endif + ]])],[libffi_cv_as_ptrauth=yes],[libffi_cv_as_ptrauth=no]) + ]) + if test "x$libffi_cv_as_ptrauth" = xyes; then +- AC_DEFINE(HAVE_PTRAUTH, 1, ++ AC_DEFINE(HAVE_ARM64E_PTRAUTH, 1, + [Define if your compiler supports pointer authentication.]) + fi + +diff --git a/include/ffi_cfi.h b/include/ffi_cfi.h +index f4c292d..8565663 100644 +--- a/include/ffi_cfi.h ++++ b/include/ffi_cfi.h +@@ -49,6 +49,7 @@ + # define cfi_personality(enc, exp) .cfi_personality enc, exp + # define cfi_lsda(enc, exp) .cfi_lsda enc, exp + # define cfi_escape(...) .cfi_escape __VA_ARGS__ ++# define cfi_window_save .cfi_window_save + + #else + +@@ -71,6 +72,7 @@ + # define cfi_personality(enc, exp) + # define cfi_lsda(enc, exp) + # define cfi_escape(...) ++# define cfi_window_save + + #endif /* HAVE_AS_CFI_PSEUDO_OP */ + #endif /* FFI_CFI_H */ +diff --git a/src/aarch64/ffi.c b/src/aarch64/ffi.c +index b13738e..964934d 100644 +--- a/src/aarch64/ffi.c ++++ b/src/aarch64/ffi.c +@@ -63,7 +63,7 @@ struct call_context + #if FFI_EXEC_TRAMPOLINE_TABLE + + #ifdef __MACH__ +-#ifdef HAVE_PTRAUTH ++#ifdef HAVE_ARM64E_PTRAUTH + #include + #endif + #include +@@ -877,7 +877,7 @@ ffi_prep_closure_loc (ffi_closure *closure, + + #if FFI_EXEC_TRAMPOLINE_TABLE + # ifdef __MACH__ +-# ifdef HAVE_PTRAUTH ++# ifdef HAVE_ARM64E_PTRAUTH + codeloc = ptrauth_auth_data(codeloc, ptrauth_key_function_pointer, 0); + # endif + void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE); +diff --git a/src/aarch64/internal.h b/src/aarch64/internal.h +index b5d102b..c39f9cb 100644 +--- a/src/aarch64/internal.h ++++ b/src/aarch64/internal.h +@@ -81,20 +81,62 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + /* Helpers for writing assembly compatible with arm ptr auth */ + #ifdef LIBFFI_ASM + +-#ifdef HAVE_PTRAUTH +-#define SIGN_LR pacibsp +-#define SIGN_LR_WITH_REG(x) pacib lr, x +-#define AUTH_LR_AND_RET retab +-#define AUTH_LR_WITH_REG(x) autib lr, x +-#define BRANCH_AND_LINK_TO_REG blraaz +-#define BRANCH_TO_REG braaz +-#else +-#define SIGN_LR +-#define SIGN_LR_WITH_REG(x) +-#define AUTH_LR_AND_RET ret +-#define AUTH_LR_WITH_REG(x) +-#define BRANCH_AND_LINK_TO_REG blr +-#define BRANCH_TO_REG br +-#endif +- +-#endif ++ #if defined(HAVE_ARM64E_PTRAUTH) ++ /* ARM64E ABI For Darwin */ ++ #define SIGN_LR pacibsp ++ #define SIGN_LR_WITH_REG(x) pacib lr, x ++ #define AUTH_LR_AND_RET retab ++ #define AUTH_LR_WITH_REG(x) autib lr, x ++ #define BRANCH_AND_LINK_TO_REG blraaz ++ #define BRANCH_TO_REG braaz ++ #define PAC_CFI_WINDOW_SAVE ++ /* Linux PAC Support */ ++ #elif defined(__ARM_FEATURE_PAC_DEFAULT) ++ #define GNU_PROPERTY_AARCH64_POINTER_AUTH (1 << 1) ++ #define PAC_CFI_WINDOW_SAVE cfi_window_save ++ #define TMP_REG x9 ++ #define BRANCH_TO_REG br ++ #define BRANCH_AND_LINK_TO_REG blr ++ #define SIGN_LR_LINUX_ONLY SIGN_LR ++ /* Which key to sign with? */ ++ #if (__ARM_FEATURE_PAC_DEFAULT & 1) == 1 ++ /* Signed with A-key */ ++ #define SIGN_LR hint #25 /* paciasp */ ++ #define AUTH_LR hint #29 /* autiasp */ ++ #else ++ /* Signed with B-key */ ++ #define SIGN_LR hint #27 /* pacibsp */ ++ #define AUTH_LR hint #31 /* autibsp */ ++ #endif /* __ARM_FEATURE_PAC_DEFAULT */ ++ #define AUTH_LR_WITH_REG(x) _auth_lr_with_reg x ++.macro _auth_lr_with_reg modifier ++ mov TMP_REG, sp ++ mov sp, \modifier ++ AUTH_LR ++ mov sp, TMP_REG ++.endm ++ #define SIGN_LR_WITH_REG(x) _sign_lr_with_reg x ++.macro _sign_lr_with_reg modifier ++ mov TMP_REG, sp ++ mov sp, \modifier ++ SIGN_LR ++ mov sp, TMP_REG ++.endm ++ #define AUTH_LR_AND_RET _auth_lr_and_ret modifier ++.macro _auth_lr_and_ret modifier ++ AUTH_LR ++ ret ++.endm ++ #undef TMP_REG ++ ++ /* No Pointer Auth */ ++ #else ++ #define SIGN_LR ++ #define SIGN_LR_WITH_REG(x) ++ #define AUTH_LR_AND_RET ret ++ #define AUTH_LR_WITH_REG(x) ++ #define BRANCH_AND_LINK_TO_REG blr ++ #define BRANCH_TO_REG br ++ #define PAC_CFI_WINDOW_SAVE ++ #endif /* HAVE_ARM64E_PTRAUTH */ ++#endif /* LIBFFI_ASM */ +diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S +index 60cfa50..6a9a561 100644 +--- a/src/aarch64/sysv.S ++++ b/src/aarch64/sysv.S +@@ -92,27 +92,27 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + cfi_startproc + CNAME(ffi_call_SYSV): + BTI_C +- /* Sign the lr with x1 since that is where it will be stored */ ++ PAC_CFI_WINDOW_SAVE ++ /* Sign the lr with x1 since that is the CFA which is the modifier used in auth instructions */ + SIGN_LR_WITH_REG(x1) + +- /* Use a stack frame allocated by our caller. */ +-#if defined(HAVE_PTRAUTH) && defined(__APPLE__) ++#if defined(HAVE_ARM64E_PTRAUTH) && defined(__APPLE__) + /* darwin's libunwind assumes that the cfa is the sp and that's the data + * used to sign the lr. In order to allow unwinding through this + * function it is necessary to point the cfa at the signing register. + */ + cfi_def_cfa(x1, 0); +-#else +- cfi_def_cfa(x1, 40); + #endif ++ /* Use a stack frame allocated by our caller. */ + stp x29, x30, [x1] ++ cfi_def_cfa_register(x1) ++ cfi_rel_offset (x29, 0) ++ cfi_rel_offset (x30, 8) + mov x9, sp + str x9, [x1, #32] + mov x29, x1 +- mov sp, x0 + cfi_def_cfa_register(x29) +- cfi_rel_offset (x29, 0) +- cfi_rel_offset (x30, 8) ++ mov sp, x0 + + mov x9, x2 /* save fn */ + mov x8, x3 /* install structure return */ +@@ -326,6 +326,7 @@ CNAME(ffi_closure_SYSV_V): + cfi_startproc + BTI_C + SIGN_LR ++ PAC_CFI_WINDOW_SAVE + stp x29, x30, [sp, #-ffi_closure_SYSV_FS]! + cfi_adjust_cfa_offset (ffi_closure_SYSV_FS) + cfi_rel_offset (x29, 0) +@@ -351,6 +352,7 @@ CNAME(ffi_closure_SYSV_V): + CNAME(ffi_closure_SYSV): + BTI_C + SIGN_LR ++ PAC_CFI_WINDOW_SAVE + stp x29, x30, [sp, #-ffi_closure_SYSV_FS]! + cfi_adjust_cfa_offset (ffi_closure_SYSV_FS) + cfi_rel_offset (x29, 0) +@@ -648,6 +650,8 @@ CNAME(ffi_go_closure_SYSV_V): + cfi_startproc + CNAME(ffi_go_closure_SYSV): + BTI_C ++ SIGN_LR_LINUX_ONLY ++ PAC_CFI_WINDOW_SAVE + stp x29, x30, [sp, #-ffi_closure_SYSV_FS]! + cfi_adjust_cfa_offset (ffi_closure_SYSV_FS) + cfi_rel_offset (x29, 0) +diff --git a/src/closures.c b/src/closures.c +index 67a94a8..02cf78f 100644 +--- a/src/closures.c ++++ b/src/closures.c +@@ -164,7 +164,7 @@ ffi_tramp_is_present (__attribute__((unused)) void *ptr) + + #include + #include +-#ifdef HAVE_PTRAUTH ++#ifdef HAVE_ARM64E_PTRAUTH + #include + #endif + #include +@@ -223,7 +223,7 @@ ffi_trampoline_table_alloc (void) + /* Remap the trampoline table on top of the placeholder page */ + trampoline_page = config_page + PAGE_MAX_SIZE; + +-#ifdef HAVE_PTRAUTH ++#ifdef HAVE_ARM64E_PTRAUTH + trampoline_page_template = (vm_address_t)(uintptr_t)ptrauth_auth_data((void *)&ffi_closure_trampoline_table_page, ptrauth_key_function_pointer, 0); + #else + trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page; +@@ -268,7 +268,7 @@ ffi_trampoline_table_alloc (void) + ffi_trampoline_table_entry *entry = &table->free_list_pool[i]; + entry->trampoline = + (void *) (trampoline_page + (i * FFI_TRAMPOLINE_SIZE)); +-#ifdef HAVE_PTRAUTH ++#ifdef HAVE_ARM64E_PTRAUTH + entry->trampoline = ptrauth_sign_unauthenticated(entry->trampoline, ptrauth_key_function_pointer, 0); + #endif + +EOF + +# Commit 9c9e8368e49804c4f7c35ac9f0d7c1d0d533308b. +patch -p1 <<'EOF' +diff --git a/src/aarch64/internal.h b/src/aarch64/internal.h +index c39f9cb..50fa5c1 100644 +--- a/src/aarch64/internal.h ++++ b/src/aarch64/internal.h +@@ -88,6 +88,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #define AUTH_LR_AND_RET retab + #define AUTH_LR_WITH_REG(x) autib lr, x + #define BRANCH_AND_LINK_TO_REG blraaz ++ #define SIGN_LR_LINUX_ONLY + #define BRANCH_TO_REG braaz + #define PAC_CFI_WINDOW_SAVE + /* Linux PAC Support */ +@@ -136,6 +137,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #define AUTH_LR_AND_RET ret + #define AUTH_LR_WITH_REG(x) + #define BRANCH_AND_LINK_TO_REG blr ++ #define SIGN_LR_LINUX_ONLY + #define BRANCH_TO_REG br + #define PAC_CFI_WINDOW_SAVE + #endif /* HAVE_ARM64E_PTRAUTH */ +EOF + +# Commit 8308bed5b2423878aa20d7884a99cf2e30b8daf7. +patch -p1 <<'EOF' +diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S +index 6a9a561..e83bc65 100644 +--- a/src/aarch64/sysv.S ++++ b/src/aarch64/sysv.S +@@ -89,8 +89,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + x5 closure + */ + +- cfi_startproc + CNAME(ffi_call_SYSV): ++ cfi_startproc + BTI_C + PAC_CFI_WINDOW_SAVE + /* Sign the lr with x1 since that is the CFA which is the modifier used in auth instructions */ +@@ -348,8 +348,8 @@ CNAME(ffi_closure_SYSV_V): + #endif + + .align 4 +- cfi_startproc + CNAME(ffi_closure_SYSV): ++ cfi_startproc + BTI_C + SIGN_LR + PAC_CFI_WINDOW_SAVE +@@ -647,8 +647,8 @@ CNAME(ffi_go_closure_SYSV_V): + #endif + + .align 4 +- cfi_startproc + CNAME(ffi_go_closure_SYSV): ++ cfi_startproc + BTI_C + SIGN_LR_LINUX_ONLY + PAC_CFI_WINDOW_SAVE +EOF + +EXTRA_CONFIGURE= + +# mkostemp() was introduced in macOS 10.10 and libffi doesn't have +# runtime guards for it. So ban the symbol when targeting old macOS. +if [ "${APPLE_MIN_DEPLOYMENT_TARGET}" = "10.9" ]; then + EXTRA_CONFIGURE="${EXTRA_CONFIGURE} ac_cv_func_mkostemp=no" +fi CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ - --disable-shared + --disable-shared \ + ${EXTRA_CONFIGURE} -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-libpthread-stubs.sh b/cpython-unix/build-libpthread-stubs.sh index 4d0717952..1d6b0083b 100755 --- a/cpython-unix/build-libpthread-stubs.sh +++ b/cpython-unix/build-libpthread-stubs.sh @@ -5,20 +5,20 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) pkg-config --version export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig -tar -xf libpthread-stubs-${LIBPTHREAD_STUBS_VERSION}.tar.gz -pushd libpthread-stubs-${LIBPTHREAD_STUBS_VERSION} +tar -xf "libpthread-stubs-${LIBPTHREAD_STUBS_VERSION}.tar.gz" +pushd "libpthread-stubs-${LIBPTHREAD_STUBS_VERSION}" CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-libxcb.sh b/cpython-unix/build-libxcb.sh index 0b6cb9111..5d77c47e9 100755 --- a/cpython-unix/build-libxcb.sh +++ b/cpython-unix/build-libxcb.sh @@ -5,23 +5,29 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig:/tools/deps/lib/pkgconfig -tar -xf libxcb-${LIBXCB_VERSION}.tar.gz -pushd libxcb-${LIBXCB_VERSION} +tar -xf "libxcb-${LIBXCB_VERSION}.tar.gz" +pushd "libxcb-${LIBXCB_VERSION}" + +if [[ "${TARGET_TRIPLE}" = loongarch64* ]]; then + rm -f build-aux/config.guess build-aux/config.sub + curl -sSL -o build-aux/config.guess 'https://git.savannah.gnu.org/cgit/config.git/plain/config.guess' + curl -sSL -o build-aux/config.sub 'https://git.savannah.gnu.org/cgit/config.git/plain/config.sub' +fi if [ "${CC}" = "musl-clang" ]; then EXTRA_FLAGS="--disable-shared" fi CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ ${EXTRA_FLAGS} -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-m4.sh b/cpython-unix/build-m4.sh new file mode 100755 index 000000000..418acc57f --- /dev/null +++ b/cpython-unix/build-m4.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -ex + +ROOT=$(pwd) + +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH + +tar -xf "m4-${M4_VERSION}.tar.xz" + +pushd "m4-${M4_VERSION}" + +CC="${HOST_CC}" CXX="${HOST_CXX}" CFLAGS="${EXTRA_HOST_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_HOST_CFLAGS} -fPIC" LDFLAGS="${EXTRA_HOST_LDFLAGS}" ./configure \ + --build="${BUILD_TRIPLE}" \ + --prefix=/tools/host + +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-main.py b/cpython-unix/build-main.py index a113a41b9..0e1f6a7cf 100755 --- a/cpython-unix/build-main.py +++ b/cpython-unix/build-main.py @@ -4,15 +4,18 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. import argparse +import multiprocessing import os import pathlib -import platform import subprocess import sys +from pythonbuild.cpython import meets_python_minimum_version from pythonbuild.downloads import DOWNLOADS from pythonbuild.utils import ( compress_python_archive, + current_host_platform, + default_target_triple, get_target_settings, release_tag_from_git, supported_targets, @@ -26,48 +29,47 @@ def main(): - if sys.platform == "linux": - host_platform = "linux64" - default_target_triple = "x86_64-unknown-linux-gnu" - elif sys.platform == "darwin": - host_platform = "macos" - machine = platform.machine() - - if machine == "arm64": - default_target_triple = "aarch64-apple-darwin" - elif machine == "x86_64": - default_target_triple = "x86_64-apple-darwin" - else: - raise Exception("unhandled macOS machine value: %s" % machine) - else: - print("unsupport build platform: %s" % sys.platform) - return 1 + host_platform = current_host_platform() + # Note these arguments must be synced with `build.py` parser = argparse.ArgumentParser() parser.add_argument( "--target-triple", - default=default_target_triple, + default=default_target_triple(), choices=supported_targets(TARGETS_CONFIG), help="Target host triple to build for", ) + # Construct possible options, we use a set here for canonical ordering + options = set() + options.update({"debug", "noopt", "pgo", "lto", "pgo+lto"}) + options.update({f"freethreaded+{option}" for option in options}) + options.update({f"{option}+static" for option in options}) parser.add_argument( - "--optimizations", - choices={"debug", "noopt", "pgo", "lto", "pgo+lto"}, + "--options", + choices=options, default="noopt", - help="Optimizations to apply when compiling Python", - ) - - parser.add_argument( - "--libressl", action="store_true", help="Build LibreSSL instead of OpenSSL" + help="Build options to apply when compiling Python", ) parser.add_argument( "--python", - choices={"cpython-3.8", "cpython-3.9", "cpython-3.10"}, - default="cpython-3.9", + choices={ + "cpython-3.10", + "cpython-3.11", + "cpython-3.12", + "cpython-3.13", + "cpython-3.14", + "cpython-3.15", + }, + default="cpython-3.11", help="Python distribution to build", ) + parser.add_argument( + "--python-source", + default=None, + help="A custom path to CPython source files to use", + ) parser.add_argument( "--break-on-failure", action="store_true", @@ -77,16 +79,29 @@ def main(): "--no-docker", action="store_true", default=True if sys.platform == "darwin" else False, - help="Disable building in Docker", + help="Disable building in Docker on Linux hosts.", ) parser.add_argument( - "--skip-toolchain", + "--serial", action="store_true", - help="Skip building the toolchain (requires a tar file in expected location)", + help="Build packages serially, without parallelism", ) parser.add_argument( "--make-target", - choices={"default", "toolchain"}, + choices={ + "default", + "empty", + "toolchain", + "toolchain-image-build", + "toolchain-image-build.cross", + "toolchain-image-build.cross-riscv64", + "toolchain-image-build.cross-loongarch64", + "toolchain-image-build.debian9", + "toolchain-image-gcc", + "toolchain-image-xcb", + "toolchain-image-xcb.cross", + "toolchain-image-xcb.cross-riscv64", + }, default="default", help="The make target to evaluate", ) @@ -106,43 +121,92 @@ def main(): ) return 1 + python_source = ( + (str(pathlib.Path(args.python_source).resolve())) + if args.python_source + else "null" + ) + musl = "musl" in target_triple + # Linux targets can be built on a macOS host using Docker. + building_linux_from_macos = sys.platform == "darwin" and "linux" in target_triple + if building_linux_from_macos: + print("Note: Using Docker to build for Linux on macOS") + args.no_docker = False + env = dict(os.environ) - env["PYBUILD_HOST_PLATFORM"] = host_platform + # When building Linux targets from macOS using Docker, map to the equivalent + # Linux host platform. + effective_host_platform = host_platform + if building_linux_from_macos: + if host_platform == "macos_arm64": + effective_host_platform = "linux_aarch64" + else: + raise Exception(f"Unhandled macOS platform: {host_platform}") + print( + f"Building Linux target from macOS using Docker ({effective_host_platform} toolchain)" + ) + env["PYBUILD_HOST_PLATFORM"] = effective_host_platform env["PYBUILD_TARGET_TRIPLE"] = target_triple - env["PYBUILD_OPTIMIZATIONS"] = args.optimizations - if args.libressl or musl: - env["PYBUILD_LIBRESSL"] = "1" + env["PYBUILD_BUILD_OPTIONS"] = args.options + env["PYBUILD_PYTHON_SOURCE"] = python_source if musl: env["PYBUILD_MUSL"] = "1" if args.break_on_failure: env["PYBUILD_BREAK_ON_FAILURE"] = "1" if args.no_docker: env["PYBUILD_NO_DOCKER"] = "1" - if args.skip_toolchain: - env["PYBUILD_SKIP_TOOLCHAIN"] = "1" - entry = DOWNLOADS[args.python] - env["PYBUILD_PYTHON_VERSION"] = entry["version"] - env["PYBUILD_PYTHON_MAJOR_VERSION"] = ".".join(entry["version"].split(".")[0:2]) + if not args.python_source: + entry = DOWNLOADS[args.python] + env["PYBUILD_PYTHON_VERSION"] = cpython_version = entry["version"] + else: + # TODO consider parsing version from source checkout. Or defining version + # from CLI argument. + if "PYBUILD_PYTHON_VERSION" not in env: + print("PYBUILD_PYTHON_VERSION must be set when using `--python-source`") + return 1 + cpython_version = env["PYBUILD_PYTHON_VERSION"] + + python_majmin = ".".join(cpython_version.split(".")[0:2]) if "PYBUILD_RELEASE_TAG" in os.environ: release_tag = os.environ["PYBUILD_RELEASE_TAG"] else: release_tag = release_tag_from_git() + # Guard against accidental misuse of the free-threaded flag with older versions + if "freethreaded" in args.options and not meets_python_minimum_version( + python_majmin, "3.13" + ): + print( + "Invalid build option: 'freethreaded' is only compatible with CPython 3.13+ (got %s)" + % cpython_version + ) + return 1 + archive_components = [ - "cpython-%s" % entry["version"], + "cpython-%s" % cpython_version, target_triple, - args.optimizations, + args.options, ] build_basename = "-".join(archive_components) + ".tar" dist_basename = "-".join(archive_components + [release_tag]) - subprocess.run(["make", args.make_target], env=env, check=True) + # We run make with static parallelism no greater than the machine's CPU count + # because we can get some speedup from parallel operations. But we also don't + # share a make job server with each build. So if we didn't limit the + # parallelism we could easily oversaturate the CPU. Higher levels of + # parallelism don't result in meaningful build speedups because tk has + # a long, serial dependency chain that can't be built in parallel. + parallelism = min(1 if args.serial else 4, multiprocessing.cpu_count()) + + subprocess.run( + ["make", "-j%d" % parallelism, args.make_target], env=env, check=True + ) DIST.mkdir(exist_ok=True) diff --git a/cpython-unix/build-libressl.sh b/cpython-unix/build-mpdecimal.sh similarity index 53% rename from cpython-unix/build-libressl.sh rename to cpython-unix/build-mpdecimal.sh index 43eba9b6d..d48fb4c5a 100755 --- a/cpython-unix/build-libressl.sh +++ b/cpython-unix/build-mpdecimal.sh @@ -5,20 +5,20 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) -export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf libressl-${LIBRESSL_VERSION}.tar.gz +tar -xf "mpdecimal-${MPDECIMAL_VERSION}.tar.gz" -pushd libressl-${LIBRESSL_VERSION} +pushd "mpdecimal-${MPDECIMAL_VERSION}" CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ - --with-openssldir=/etc/ssl \ + --disable-cxx \ --disable-shared -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-musl.sh b/cpython-unix/build-musl.sh index d7fa2fdfc..6d0dcfbc1 100755 --- a/cpython-unix/build-musl.sh +++ b/cpython-unix/build-musl.sh @@ -3,22 +3,23 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -set -e +set -ex cd /build export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export CC=clang -tar -xf musl-${MUSL_VERSION}.tar.gz +tar -xf "musl-${MUSL_VERSION}.tar.gz" -pushd musl-${MUSL_VERSION} +pushd "musl-${MUSL_VERSION}" # Debian as of at least bullseye ships musl 1.2.1. musl 1.2.2 # added reallocarray(), which gets used by at least OpenSSL. # Here, we disable this single function so as to not introduce # symbol dependencies on clients using an older musl version. -patch -p1 < +-#include +- +-void *reallocarray(void *ptr, size_t m, size_t n) +-{ +- if (n && m > -1 / n) { +- errno = ENOMEM; +- return 0; +- } +- +- return realloc(ptr, m * n); +-} +EOF +fi + +SHARED= +if [ -n "${STATIC}" ]; then + SHARED="--disable-shared" +else + SHARED="--enable-shared" + CFLAGS="${CFLAGS} -fPIC" CPPFLAGS="${CPPFLAGS} -fPIC" +fi + -./configure \ +CFLAGS="${CFLAGS}" CPPFLAGS="${CPPFLAGS}" ./configure \ --prefix=/tools/host \ - --disable-shared + "${SHARED}" -make -j `nproc` -make -j `nproc` install DESTDIR=/build/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR=/build/out popd diff --git a/cpython-unix/build-ncurses.sh b/cpython-unix/build-ncurses.sh index 44d2a5858..4ed0f86dd 100755 --- a/cpython-unix/build-ncurses.sh +++ b/cpython-unix/build-ncurses.sh @@ -5,39 +5,51 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf ncurses-${NCURSES_VERSION}.tar.gz +tar -xf "ncurses-${NCURSES_VERSION}.tar.gz" # When cross-compiling, ncurses uses the host `tic` to build the terminfo # database. But our build environment's `tic` is too old to process this # ncurses version. Our workaround is to build ncurses for the host when # cross-compiling then make its `tic` available to the target ncurses # build. -if [[ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" && "${PYBUILD_PLATFORM}" != "macos" ]]; then +if [[ -n "${CROSS_COMPILING}" && "${PYBUILD_PLATFORM}" != macos* ]]; then echo "building host ncurses to provide modern tic for cross-compile" - pushd ncurses-${NCURSES_VERSION} - CC="${HOST_CC}" ./configure --prefix=${TOOLS_PATH}/host --without-cxx --without-tests --without-manpages --enable-widec - make -j ${NUM_CPUS} - make -j ${NUM_CPUS} install + pushd ncurses-"${NCURSES_VERSION}" + + CC="${HOST_CC}" ./configure \ + --prefix="${TOOLS_PATH}/host" \ + --without-cxx \ + --without-tests \ + --without-manpages \ + --enable-widec \ + --disable-db-install \ + --enable-symlinks + make -j "${NUM_CPUS}" + make -j "${NUM_CPUS}" install popd # Nuke and re-pave the source directory. - rm -rf ncurses-${NCURSES_VERSION} - tar -xf ncurses-${NCURSES_VERSION}.tar.gz + rm -rf "ncurses-${NCURSES_VERSION}" + tar -xf "ncurses-${NCURSES_VERSION}.tar.gz" fi -pushd ncurses-${NCURSES_VERSION} +pushd "ncurses-${NCURSES_VERSION}" # `make install` will strip installed programs (like tic) by default. This is # fine. However, cross-compiles can run into issues where `strip` doesn't # recognize the target architecture. We could fix this by overriding strip. # But we don't care about the installed binaries, so we simply disable # stripping of the binaries. +# --enable-symlinks is needed to force use of symbolic links in the terminfo +# database. By default hardlinks are used, which are wonky to tar up. Be sure +# this is set on the host native `tic` build above, as it is the entity writing +# symlinks! CONFIGURE_FLAGS=" --build=${BUILD_TRIPLE} --host=${TARGET_TRIPLE} @@ -46,26 +58,61 @@ CONFIGURE_FLAGS=" --without-tests --without-manpages --disable-stripping - --enable-widec" + --enable-widec + --enable-symlinks + " # ncurses wants --with-build-cc when cross-compiling. But it insists on CC # and this value not being equal, even though using the same binary with # different compiler flags is doable! -if [[ "${BUILD_TRIPLE}" != "${TARGET_TRIPLE}" && "${PYBUILD_PLATFORM}" != "macos" ]]; then +if [[ -n "${CROSS_COMPILING}" && "${PYBUILD_PLATFORM}" != macos* ]]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-build-cc=$(which "${HOST_CC}")" fi -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then +# The terminfo database exists as a set of standalone files. The absolute +# paths to these files need to be hardcoded into the binary at build time. +# +# Since our final distributions are "relocatable," the absolute path of the +# terminfo database can't be known at build time: there needs to be something +# that sniffs for well-known directories and attempts to locate it. Ideally +# that could find the terminfo database that we ship! +# +# All is not lost, however. +# +# On macOS, the system terminfo database location is well known: /usr/share/terminfo. +# +# On Linux, common distributions tend to place the terminfo database in only a +# few well-known locations. We define default search paths that overlap with +# Debian and RedHat distros. This often results in at least a partially working +# terminfo lookup in most Linux environments. +# +# configure appears to use --with-default-terminfo-dir for both a) where to +# install the terminfo database to b) default TERMINFO value compiled into the +# binary. So we provide a suitable runtime value and then move files at install +# time. + +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --datadir=/usr/share --sysconfdir=/etc --sharedstatedir=/usr/com + --with-default-terminfo-dir=/usr/share/terminfo --with-terminfo-dirs=/usr/share/terminfo + " +else + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} + --datadir=/tools/deps/usr/share + --sysconfdir=/tools/deps/etc + --sharedstatedir=/tools/deps/usr/com --with-default-terminfo-dir=/usr/share/terminfo - --disable-db-install + --with-terminfo-dirs=/etc/terminfo:/lib/terminfo:/usr/share/terminfo " fi +mkdir -p "${ROOT}/out/usr/lib" + CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure ${CONFIGURE_FLAGS} -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" + +mv "${ROOT}/out/usr/share/terminfo" "${ROOT}/out/tools/deps/usr/share/" diff --git a/cpython-unix/build-openssl-3.5.sh b/cpython-unix/build-openssl-3.5.sh new file mode 100755 index 000000000..0c440a894 --- /dev/null +++ b/cpython-unix/build-openssl-3.5.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -ex + +ROOT=$(pwd) + +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH + +tar -xf "openssl-${OPENSSL_3_5_VERSION}.tar.gz" + +pushd "openssl-${OPENSSL_3_5_VERSION}" + +# Fedora and RHEL patch OpenSSL to selectively disallow SHA1 signatures via the +# 'rh-allow-sha1-signatures' configuration entry. +# Patch OpenSSL so that this key is allowed in configuration files but has no effect. +# For details see: https://github.com/astral-sh/python-build-standalone/issues/999 +if [[ "$TARGET_TRIPLE" =~ "linux" ]]; then + patch -p1 << 'EOF' +diff --git a/crypto/evp/evp_cnf.c b/crypto/evp/evp_cnf.c +index 184bab9..7dc8037 100644 +--- a/crypto/evp/evp_cnf.c ++++ b/crypto/evp/evp_cnf.c +@@ -51,6 +51,13 @@ static int alg_module_init(CONF_IMODULE *md, const CONF *cnf) + ERR_raise(ERR_LIB_EVP, EVP_R_SET_DEFAULT_PROPERTY_FAILURE); + return 0; + } ++ } else if (strcmp(oval->name, "rh-allow-sha1-signatures") == 0) { ++ int m; ++ ++ /* Detailed error already reported. */ ++ if (!X509V3_get_value_bool(oval, &m)) ++ return 0; ++ + } else if (strcmp(oval->name, "default_properties") == 0) { + if (!evp_set_default_properties_int(NCONF_get0_libctx((CONF *)cnf), + oval->value, 0, 0)) { +EOF +fi + +# Otherwise it gets set to /tools/deps/ssl by default. +case "${TARGET_TRIPLE}" in + *apple*) + EXTRA_FLAGS="--openssldir=/private/etc/ssl" + ;; + *) + EXTRA_FLAGS="--openssldir=/etc/ssl" + ;; +esac + +# musl is missing support for various primitives. +# TODO disable secure memory is a bit scary. We should look into a proper +# workaround. +if [ "${CC}" = "musl-clang" ]; then + EXTRA_FLAGS="${EXTRA_FLAGS} no-async -DOPENSSL_NO_ASYNC -D__STDC_NO_ATOMICS__=1 no-engine -DOPENSSL_NO_SECURE_MEMORY" +fi + +# The -arch cflags confuse Configure. And OpenSSL adds them anyway. +# Strip them. +EXTRA_TARGET_CFLAGS=${EXTRA_TARGET_CFLAGS/\-arch arm64/} +EXTRA_TARGET_CFLAGS=${EXTRA_TARGET_CFLAGS/\-arch x86_64/} + +EXTRA_FLAGS="${EXTRA_FLAGS} ${EXTRA_TARGET_CFLAGS}" + +/usr/bin/perl ./Configure \ + --prefix=/tools/deps \ + --libdir=lib \ + "${OPENSSL_TARGET}" \ + no-legacy \ + no-shared \ + no-tests \ + ${EXTRA_FLAGS} + +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install_sw install_ssldirs DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-openssl.sh b/cpython-unix/build-openssl.sh deleted file mode 100755 index bdbb4b04b..000000000 --- a/cpython-unix/build-openssl.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH - -tar -xf openssl-${OPENSSL_VERSION}.tar.gz - -pushd openssl-${OPENSSL_VERSION} - -# Otherwise it gets set to /tools/deps/ssl by default. -case "${TARGET_TRIPLE}" in - *apple*) - EXTRA_FLAGS="--openssldir=/private/etc/ssl" - ;; - *) - EXTRA_FLAGS="--openssldir=/etc/ssl" - ;; -esac - -# musl is missing support for various primitives. -# TODO disable secure memory is a bit scary. We should look into a proper -# workaround. -if [ "${CC}" = "musl-clang" ]; then - EXTRA_FLAGS="${EXTRA_FLAGS} no-async -DOPENSSL_NO_ASYNC -D__STDC_NO_ATOMICS__=1 no-engine -DOPENSSL_NO_SECURE_MEMORY" -fi - -# The -arch cflags confuse Configure. And OpenSSL adds them anyway. -# Strip them. -EXTRA_TARGET_CFLAGS=${EXTRA_TARGET_CFLAGS/\-arch arm64/} -EXTRA_TARGET_CFLAGS=${EXTRA_TARGET_CFLAGS/\-arch x86_64/} - -EXTRA_FLAGS="${EXTRA_FLAGS} ${EXTRA_TARGET_CFLAGS}" - -/usr/bin/perl ./Configure --prefix=/tools/deps ${OPENSSL_TARGET} no-shared no-tests ${EXTRA_FLAGS} - -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-patchelf.sh b/cpython-unix/build-patchelf.sh index 055f5fcdd..c21663303 100755 --- a/cpython-unix/build-patchelf.sh +++ b/cpython-unix/build-patchelf.sh @@ -5,30 +5,30 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH -tar -xf patchelf-${PATCHELF_VERSION}.tar.bz2 +tar -xf "patchelf-${PATCHELF_VERSION}.tar.bz2" -pushd patchelf-0.12.20200827.8d3a16e +pushd patchelf-0.13.1.20211127.72b6d44 -CC="${HOST_CC}" CXX="${HOST_CXX}" CLFAGS="${EXTRA_HOST_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_HOST_CFLAGS} -fPIC" \ +CC="${HOST_CC}" CXX="${HOST_CXX}" CFLAGS="${EXTRA_HOST_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_HOST_CFLAGS} -fPIC" \ ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/host -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" # Update DT_NEEDED to use the host toolchain's shared libraries, otherwise # the defaults of the OS may be used, which would be too old. We run the # patched binary afterwards to verify it works without LD_LIBRARY_PATH # modification. -if [ -d /tools/clang-linux64/lib ]; then - LD_LIBRARY_PATH=/tools/clang-linux64/lib src/patchelf --replace-needed libstdc++.so.6 /tools/clang-linux64/lib/libstdc++.so.6 ${ROOT}/out/tools/host/bin/patchelf - LD_LIBRARY_PATH=/tools/clang-linux64/lib src/patchelf --replace-needed libgcc_s.so.1 /tools/clang-linux64/lib/libgcc_s.so.1 ${ROOT}/out/tools/host/bin/patchelf +if [ -d "/tools/${TOOLCHAIN}/lib" ]; then + LD_LIBRARY_PATH=/tools/${TOOLCHAIN}/lib src/patchelf --replace-needed libstdc++.so.6 "/tools/${TOOLCHAIN}/lib/libstdc++.so.6" "${ROOT}/out/tools/host/bin/patchelf" + LD_LIBRARY_PATH=/tools/${TOOLCHAIN}/lib src/patchelf --replace-needed libgcc_s.so.1 "/tools/${TOOLCHAIN}/lib/libgcc_s.so.1" "${ROOT}/out/tools/host/bin/patchelf" fi -${ROOT}/out/tools/host/bin/patchelf --version \ No newline at end of file +"${ROOT}/out/tools/host/bin/patchelf" --version diff --git a/cpython-unix/build-readline.sh b/cpython-unix/build-readline.sh deleted file mode 100755 index d3cf893ca..000000000 --- a/cpython-unix/build-readline.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH - -tar -xf readline-${READLINE_VERSION}.tar.gz - -pushd readline-${READLINE_VERSION} - -CLFAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="-L/tools/deps/lib" \ - ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps \ - --disable-shared \ - --with-curses - -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-sqlite.sh b/cpython-unix/build-sqlite.sh index 38a8e9ba7..a34b36e99 100755 --- a/cpython-unix/build-sqlite.sh +++ b/cpython-unix/build-sqlite.sh @@ -5,24 +5,37 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf sqlite-autoconf-${SQLITE_VERSION}.tar.gz -pushd sqlite-autoconf-${SQLITE_VERSION} +tar -xf "sqlite-autoconf-${SQLITE_VERSION}.tar.gz" +pushd "sqlite-autoconf-${SQLITE_VERSION}" CONFIGURE_FLAGS="--build=${BUILD_TRIPLE} --host=${TARGET_TRIPLE}" -CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --prefix /tools/deps --disable-shared" - -if [ "${TARGET_TRIPLE}" = "aarch64-apple-ios" ]; then - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_search_system=no" -elif [ "${TARGET_TRIPLE}" = "x86_64-apple-ios" ]; then - CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_search_system=no" -fi - -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure ${CONFIGURE_FLAGS} - -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --prefix=/tools/deps --disable-shared" + +# The SQLite autosetup looks for the C++ compiler if the variable is set and will fail if it's not +# found, even if it's not needed. We don't actually have a C++ compiler in some builds, so ensure +# it's not looked for. +unset CXX + +# Please try to keep the build flags in sync with cpython-windows/build.py +CC_FOR_BUILD="${HOST_CC}" \ +CFLAGS="${EXTRA_TARGET_CFLAGS} \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_FTS3 \ + -DSQLITE_ENABLE_FTS3_PARENTHESIS \ + -DSQLITE_ENABLE_FTS4 \ + -DSQLITE_ENABLE_FTS5 \ + -DSQLITE_ENABLE_GEOPOLY \ + -DSQLITE_ENABLE_RTREE \ + -fPIC" \ +CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" \ +LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure ${CONFIGURE_FLAGS} + +make -j "${NUM_CPUS}" libsqlite3.a +make install-lib DESTDIR="${ROOT}/out" +make install-headers DESTDIR="${ROOT}/out" +make install-pc DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-tcl.sh b/cpython-unix/build-tcl.sh index 39f15708a..140aaeb76 100755 --- a/cpython-unix/build-tcl.sh +++ b/cpython-unix/build-tcl.sh @@ -5,28 +5,61 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) + +# Force linking to static libraries from our dependencies. +# TODO(geofft): This is copied from build-cpython.sh. Really this should +# be done at the end of the build of each dependency, rather than before +# the build of each consumer. +find "${TOOLS_PATH}/deps" -name '*.so*' -exec rm {} \; export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH export PKG_CONFIG_PATH=${TOOLS_PATH}/deps/share/pkgconfig:${TOOLS_PATH}/deps/lib/pkgconfig -tar -xf tcl${TCL_VERSION}-src.tar.gz -pushd tcl${TCL_VERSION} +tar -xf "tcl${TCL_VERSION}-src.tar.gz" +pushd "tcl${TCL_VERSION}" + +EXTRA_CONFIGURE= -patch -p1 << 'EOF' +if [ -n "${STATIC}" ]; then + patch -p1 << 'EOF' diff --git a/unix/Makefile.in b/unix/Makefile.in --- a/unix/Makefile.in +++ b/unix/Makefile.in -@@ -1724,7 +1724,7 @@ configure-packages: - $$i/configure --with-tcl=../.. \ - --with-tclinclude=$(GENERIC_DIR) \ - $(PKG_CFG_ARGS) --libdir=$(PACKAGE_DIR) \ -- --enable-shared --enable-threads; ) || exit $$?; \ -+ --enable-shared=no --enable-threads; ) || exit $$?; \ - fi; \ +@@ -2062,7 +2062,7 @@ configure-packages: + $$i/configure --with-tcl8 --with-tcl=../.. \ + --with-tclinclude=$(GENERIC_DIR) \ + $(PKG_CFG_ARGS) --libdir=$(PACKAGE_DIR) \ +- --enable-shared; ) || exit $$?; \ ++ --enable-shared=no; ) || exit $$?; \ + fi; \ + mkdir -p $(PKG_DIR)/$$pkg; \ + if [ ! -f $(PKG_DIR)/$$pkg/Makefile ] ; then \ +@@ -2070,7 +2070,7 @@ configure-packages: + $$i/configure --with-tcl=../.. \ + --with-tclinclude=$(GENERIC_DIR) \ + $(PKG_CFG_ARGS) --libdir=$(PACKAGE_DIR) \ +- --enable-shared; ) || exit $$?; \ ++ --enable-shared=no; ) || exit $$?; \ + fi; \ + fi; \ fi; \ - fi; \ EOF +fi + +# Disable the use of fts64_* functions on the 32-bit armv7 platform as these +# functions are not available in glibc 2.17 +if [[ ${TARGET_TRIPLE} = armv7* ]]; then + EXTRA_CONFIGURE="${EXTRA_CONFIGURE} tcl_cv_flag__file_offset_bits=no" +fi + +# musl does not include queue.h +# https://wiki.musl-libc.org/faq#Q:-Why-is-%3Ccode%3Esys/queue.h%3C/code%3E-not-included? +# It is a self contained header file, use a copy from the container. +# https://core.tcl-lang.org/tcl/tktview/3ff2d724d03ba7d6edb8 +if [ "${CC}" = "musl-clang" ]; then + cp "/usr/include/$(uname -m)-linux-gnu/sys/queue.h" /tools/host/include/sys +fi # Remove packages we don't care about and can pull in unwanted symbols. rm -rf pkgs/sqlite* pkgs/tdbc* @@ -34,17 +67,33 @@ rm -rf pkgs/sqlite* pkgs/tdbc* pushd unix CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -I${TOOLS_PATH}/deps/include" +LDFLAGS="${EXTRA_TARGET_LDFLAGS} -L${TOOLS_PATH}/deps/lib" +if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + LDFLAGS="${LDFLAGS} -Wl,--exclude-libs,ALL" +fi + +# Tcl configures and builds packages (itcl, threads, ...) as make targets. +# These do not pick up environment variables passed to ./configure +# Export compiler flags to make them available when configuring and building +# these packages. +# An alternative is to include these when calling ./configure AND make +export CFLAGS LDFLAGS +export CPPFLAGS="${CFLAGS}" -CFLAGS="${CFLAGS}" CPPFLAGS="${CFLAGS}" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ +./configure \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ - --enable-shared=no \ - --enable-threads + --enable-shared"${STATIC:+=no}" \ + --enable-threads \ + --disable-zipfs \ + ${EXTRA_CONFIGURE} -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out -make -j ${NUM_CPUS} install-private-headers DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" DYLIB_INSTALL_DIR=@rpath +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" DYLIB_INSTALL_DIR=@rpath +make -j "${NUM_CPUS}" install-private-headers DESTDIR="${ROOT}/out" -# For some reason libtcl*.a have weird permissions. Fix that. -chmod 644 ${ROOT}/out/tools/deps/lib/libtcl*.a \ No newline at end of file +if [ -n "${STATIC}" ]; then + # For some reason libtcl*.a have weird permissions. Fix that. + chmod 644 "${ROOT}/out/tools/deps/lib"/libtcl*.a +fi diff --git a/cpython-unix/build-tix.sh b/cpython-unix/build-tix.sh deleted file mode 100755 index b7a9574fb..000000000 --- a/cpython-unix/build-tix.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -export PATH=${TOOLS_PATH}/deps/bin:${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -export PKG_CONFIG_PATH=${TOOLS_PATH}/deps/share/pkgconfig:${TOOLS_PATH}/deps/lib/pkgconfig - -# We need the tcl/tk source extracted because tix looks for private symbols. -tar -xf tcl${TCL_VERSION}-src.tar.gz -tar -xf tk${TK_VERSION}-src.tar.gz - -tar -xf tix-${TIX_VERSION}.tar.gz - -cd cpython-source-deps-tix-${TIX_VERSION} - -# Yes, really. -chmod +x configure - -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -DUSE_INTERP_RESULT" - -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then - CFLAGS="${CFLAGS} -I${TOOLS_PATH}/deps/include" - EXTRA_CONFIGURE_FLAGS="--without-x" -else - EXTRA_CONFIGURE_FLAGS="--x-includes=/tools/deps/include --x-libraries=/tools/deps/lib" -fi - -# -DUSE_INTERP_RESULT is to allow tix to use deprecated fields or something -# like that. -CFLAGS="${CFLAGS}" CPPFLAGS="${CFLAGS}" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps \ - --with-tcl=${TOOLS_PATH}/deps/lib \ - --with-tk=${TOOLS_PATH}/deps/lib \ - --enable-shared=no \ - ${EXTRA_CONFIGURE_FLAGS} - -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out - -# For some reason libtk*.a have weird permissions. Fix that. -chmod 644 ${ROOT}/out/tools/deps/lib/Tix*/libTix*.a diff --git a/cpython-unix/build-tk.sh b/cpython-unix/build-tk.sh index 32b5a8394..d90f47ce3 100755 --- a/cpython-unix/build-tk.sh +++ b/cpython-unix/build-tk.sh @@ -5,54 +5,72 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) + +# Force linking to static libraries from our dependencies. +# TODO(geofft): This is copied from build-cpython.sh. Really this should +# be done at the end of the build of each dependency, rather than before +# the build of each consumer. +find "${TOOLS_PATH}/deps" -name '*.so*' -exec rm {} \; export PATH=${TOOLS_PATH}/deps/bin:${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH export PKG_CONFIG_PATH=${TOOLS_PATH}/deps/share/pkgconfig:${TOOLS_PATH}/deps/lib/pkgconfig -tar -xf tk${TK_VERSION}-src.tar.gz +tar -xf "tk${TK_VERSION}-src.tar.gz" -pushd tk${TK_VERSION}/unix +pushd tk*/unix CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" -if [ "${PYBUILD_PLATFORM}" = "macos" ]; then +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then CFLAGS="${CFLAGS} -I${TOOLS_PATH}/deps/include -Wno-availability" CFLAGS="${CFLAGS} -Wno-deprecated-declarations -Wno-unknown-attributes -Wno-typedef-redefinition" - LDFLAGS="-L${TOOLS_PATH}/deps/lib" + LDFLAGS="${LDFLAGS} -L${TOOLS_PATH}/deps/lib" EXTRA_CONFIGURE_FLAGS="--enable-aqua=yes --without-x" else + LDFLAGS="${LDFLAGS} -Wl,--exclude-libs,ALL" EXTRA_CONFIGURE_FLAGS="--x-includes=${TOOLS_PATH}/deps/include --x-libraries=${TOOLS_PATH}/deps/lib" fi CFLAGS="${CFLAGS}" CPPFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ - --with-tcl=${TOOLS_PATH}/deps/lib \ - --enable-shared=no \ + --with-tcl="${TOOLS_PATH}/deps/lib" \ + --enable-shared"${STATIC:+=no}" \ --enable-threads \ + --disable-zipfs \ ${EXTRA_CONFIGURE_FLAGS} -# For some reason musl isn't link libXau and libxcb. So we hack the Makefile -# to do what we want. -# -# In addition, the wish binary is also failing to link. So we remove it -# from the build and the installation (it shouldn't be needed anyway). -if [ "${CC}" = "musl-clang" ]; then - sed -i 's/-ldl -lpthread /-ldl -lpthread -lXau -lxcb/' tkConfig.sh - sed -i 's/-lpthread $(X11_LIB_SWITCHES) -ldl -lpthread/-lpthread $(X11_LIB_SWITCHES) -ldl -lpthread -lXau -lxcb/' Makefile - sed -i 's/all: binaries libraries doc/all: libraries/' Makefile - sed -i 's/install-binaries: $(TK_STUB_LIB_FILE) $(TK_LIB_FILE) ${WISH_EXE}/install-binaries: $(TK_STUB_LIB_FILE) $(TK_LIB_FILE)/' Makefile +# Remove wish, since we don't need it. +if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then + sed_args=(-i '' -e) +else + sed_args=(-i) fi +sed "${sed_args[@]}" 's/all: binaries libraries/all: libraries/' Makefile +sed "${sed_args[@]}" 's/install-binaries: $(TK_STUB_LIB_FILE) $(TK_LIB_FILE) ${WISH_EXE}/install-binaries: $(TK_STUB_LIB_FILE) $(TK_LIB_FILE)/' Makefile -make -j ${NUM_CPUS} +# We are statically linking libX11, and static libraries do not carry +# information about dependencies. pkg-config --static does, but Tcl/Tk's +# build system apparently is too old for that. So we need to manually +# inform the build process that libX11.a needs libxcb.a and libXau.a. +# Note that the order is significant, for static libraries: X11 requires +# xcb, which requires Xau. +MAKE_VARS=(DYLIB_INSTALL_DIR=@rpath) +if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then + MAKE_VARS+=(X11_LIB_SWITCHES="-lX11 -lxcb -lXau") +fi + +make -j "${NUM_CPUS}" "${MAKE_VARS[@]}" touch wish -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out -make -j ${NUM_CPUS} install-private-headers DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" "${MAKE_VARS[@]}" +make -j "${NUM_CPUS}" install-private-headers DESTDIR="${ROOT}/out" # For some reason libtk*.a have weird permissions. Fix that. -chmod 644 /${ROOT}/out/tools/deps/lib/libtk*.a +if [ -n "${STATIC}" ]; then + chmod 644 "/${ROOT}/out/tools/deps/lib"/libtk*.a +fi -rm ${ROOT}/out/tools/deps/bin/wish* +rm "${ROOT}/out/tools/deps/bin"/wish* diff --git a/cpython-unix/build-uuid.sh b/cpython-unix/build-uuid.sh index a2657f22a..b3f185fd6 100755 --- a/cpython-unix/build-uuid.sh +++ b/cpython-unix/build-uuid.sh @@ -5,18 +5,23 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf libuuid-${UUID_VERSION}.tar.gz -pushd libuuid-${UUID_VERSION} +tar -xf "libuuid-${UUID_VERSION}.tar.gz" +pushd "libuuid-${UUID_VERSION}" -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ +CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" + +# Error by default in Clang 16. +CFLAGS="${CFLAGS} -Wno-error=implicit-function-declaration" + +CFLAGS="${CFLAGS}" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ --disable-shared -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-x11-util-macros.sh b/cpython-unix/build-x11-util-macros.sh index 2ff156563..263cb29e8 100755 --- a/cpython-unix/build-x11-util-macros.sh +++ b/cpython-unix/build-x11-util-macros.sh @@ -5,16 +5,16 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH -tar -xzf util-macros-${X11_UTIL_MACROS_VERSION}.tar.gz -pushd util-macros-${X11_UTIL_MACROS_VERSION} +tar -xzf "util-macros-${X11_UTIL_MACROS_VERSION}.tar.gz" +pushd "util-macros-${X11_UTIL_MACROS_VERSION}" CFLAGS="${EXTRA_TARGET_CFLAGS}" CPPFLAGS="${EXTRA_TARGET_CFLAGS}" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-xcb-proto.sh b/cpython-unix/build-xcb-proto.sh index 21e8c2e4e..ba9f801ff 100755 --- a/cpython-unix/build-xcb-proto.sh +++ b/cpython-unix/build-xcb-proto.sh @@ -5,20 +5,20 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) pkg-config --version export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig -tar -xf xcb-proto-${XCB_PROTO_VERSION}.tar.gz -pushd xcb-proto-${XCB_PROTO_VERSION} +tar -xf "xcb-proto-${XCB_PROTO_VERSION}.tar.xz" +pushd "xcb-proto-${XCB_PROTO_VERSION}" CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-xextproto.sh b/cpython-unix/build-xextproto.sh deleted file mode 100755 index fba2bd8fc..000000000 --- a/cpython-unix/build-xextproto.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -pkg-config --version - -export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH -export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig - -tar -xf xextproto-${XEXTPROTO_VERSION}.tar.gz -pushd xextproto-${XEXTPROTO_VERSION} - -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps - -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-xorgproto.sh b/cpython-unix/build-xorgproto.sh index 97c1aa4f9..5eae50f3f 100755 --- a/cpython-unix/build-xorgproto.sh +++ b/cpython-unix/build-xorgproto.sh @@ -5,20 +5,26 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) pkg-config --version export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig -tar -xf xorgproto-${XORGPROTO_VERSION}.tar.gz -pushd xorgproto-${XORGPROTO_VERSION} +tar -xf "xorgproto-${XORGPROTO_VERSION}.tar.gz" +pushd "xorgproto-${XORGPROTO_VERSION}" + +if [[ "${TARGET_TRIPLE}" = loongarch64* ]]; then + rm -f config.guess.sub config.sub + curl -sSL -o config.guess 'https://git.savannah.gnu.org/cgit/config.git/plain/config.guess' + curl -sSL -o config.sub 'https://git.savannah.gnu.org/cgit/config.git/plain/config.sub' +fi CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-xproto.sh b/cpython-unix/build-xproto.sh deleted file mode 100755 index d126ec01c..000000000 --- a/cpython-unix/build-xproto.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -set -ex - -ROOT=`pwd` - -pkg-config --version - -export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -export PKG_CONFIG_PATH=${TOOLS_PATH}/deps/share/pkgconfig - -tar -xf xproto-${XPROTO_VERSION}.tar.gz -pushd xproto-${XPROTO_VERSION} - -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ - --prefix=/tools/deps - -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out diff --git a/cpython-unix/build-xtrans.sh b/cpython-unix/build-xtrans.sh index 42947389b..7de564ab5 100755 --- a/cpython-unix/build-xtrans.sh +++ b/cpython-unix/build-xtrans.sh @@ -5,20 +5,20 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) pkg-config --version export PATH=/tools/${TOOLCHAIN}/bin:/tools/host/bin:$PATH export PKG_CONFIG_PATH=/tools/deps/share/pkgconfig -tar -xf xtrans-${XTRANS_VERSION}.tar.gz -pushd xtrans-${XTRANS_VERSION} +tar -xf "xtrans-${XTRANS_VERSION}.tar.gz" +pushd "xtrans-${XTRANS_VERSION}" CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps -make -j `nproc` -make -j `nproc` install DESTDIR=${ROOT}/out +make -j "$(nproc)" +make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-xz.sh b/cpython-unix/build-xz.sh index 01e71e9fb..f61ecefd1 100755 --- a/cpython-unix/build-xz.sh +++ b/cpython-unix/build-xz.sh @@ -5,17 +5,26 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf xz-${XZ_VERSION}.tar.gz +tar -xf "xz-${XZ_VERSION}.tar.gz" -pushd xz-${XZ_VERSION} +pushd "xz-${XZ_VERSION}" + +EXTRA_CONFIGURE_FLAGS= + +# musl-clang injects flags that are not used during compilation, +# e.g. -fuse-ld=musl-clang. These raise warnings that can be ignored but +# cause the -Werror check to fail. Skip the check. +if [ "${CC}" = "musl-clang" ]; then + EXTRA_CONFIGURE_FLAGS="${EXTRA_CONFIGURE_FLAGS} SKIP_WERROR_CHECK=yes" +fi CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CCASFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ - --build=${BUILD_TRIPLE} \ - --host=${TARGET_TRIPLE} \ + --build="${BUILD_TRIPLE}" \ + --host="${TARGET_TRIPLE}" \ --prefix=/tools/deps \ --disable-shared \ --disable-xz \ @@ -23,7 +32,8 @@ CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CC --disable-lzmadec \ --disable-lzmainfo \ --disable-lzma-links \ - --disable-scripts + --disable-scripts \ + ${EXTRA_CONFIGURE_FLAGS} -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-zlib.sh b/cpython-unix/build-zlib.sh index eaeca2347..778c30302 100755 --- a/cpython-unix/build-zlib.sh +++ b/cpython-unix/build-zlib.sh @@ -5,14 +5,16 @@ set -ex -ROOT=`pwd` +ROOT=$(pwd) export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH -tar -xf zlib-${ZLIB_VERSION}.tar.gz +tar -xf "zlib-${ZLIB_VERSION}.tar.gz" -pushd zlib-${ZLIB_VERSION} +pushd "zlib-${ZLIB_VERSION}" -CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure --prefix=/tools/deps -make -j ${NUM_CPUS} -make -j ${NUM_CPUS} install DESTDIR=${ROOT}/out +CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" ./configure \ + --prefix=/tools/deps \ + --static +make -j "${NUM_CPUS}" +make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-zstd.sh b/cpython-unix/build-zstd.sh new file mode 100755 index 000000000..5a8e8137f --- /dev/null +++ b/cpython-unix/build-zstd.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +set -ex + +ROOT=$(pwd) + +export PATH=${TOOLS_PATH}/${TOOLCHAIN}/bin:${TOOLS_PATH}/host/bin:$PATH +export PREFIX="/tools/deps" + +tar -xf "zstd-${ZSTD_VERSION}.tar.gz" + +pushd "cpython-source-deps-zstd-${ZSTD_VERSION}/lib" + +if [ "${CC}" = "musl-clang" ]; then + # In order to build the library with intrinsics, we need musl-clang to find + # headers that provide access to the intrinsics, as they are not provided by musl. These are + # part of the include files that are part of clang. But musl-clang eliminates them from the + # default include path. So copy them into place. + for h in ${TOOLS_PATH}/${TOOLCHAIN}/lib/clang/*/include/*intrin.h ${TOOLS_PATH}/${TOOLCHAIN}/lib/clang/*/include/{__wmmintrin_aes.h,__wmmintrin_pclmul.h,emmintrin.h,immintrin.h,mm_malloc.h,arm_neon.h,arm_neon_sve_bridge.h,arm_bf16.h,arm_fp16.h,arm_acle.h,arm_vector_types.h}; do + filename=$(basename "$h") + if [ -f "$h" ]; then + if [ -e "${TOOLS_PATH}/host/include/${filename}" ]; then + echo "warning: ${filename} already exists" + fi + cp "$h" "${TOOLS_PATH}/host/include/" + else + echo "warning: ${filename} not found (skipping)" + fi + done + EXTRA_TARGET_CFLAGS="${EXTRA_TARGET_CFLAGS} -I${TOOLS_PATH}/host/include/" + + # `qsort_r` is only available in musl 1.2.3+ but we use 1.2.2. The zstd source provides a + # fallback implementation, but they do not have a `configure`-style detection of whether + # `qsort_r` is actually available so we patch it to include a check for glibc. + patch -p1 <suffix, ctx->suffixSize, sizeof(U32), + ctx, + (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); +-#elif defined(_GNU_SOURCE) ++#elif defined(_GNU_SOURCE) && defined(__GLIBC__) + qsort_r(ctx->suffix, ctx->suffixSize, sizeof(U32), + (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp), + ctx); +EOF +fi + +CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -DZSTD_MULTITHREAD -O3" LDFLAGS="${EXTRA_TARGET_LDFLAGS}" make -j "${NUM_CPUS}" VERBOSE=1 libzstd.a +make -j "${NUM_CPUS}" install-static DESTDIR="${ROOT}/out" +make -j "${NUM_CPUS}" install-includes DESTDIR="${ROOT}/out" +MT=1 make -j "${NUM_CPUS}" install-pc DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build.Dockerfile b/cpython-unix/build.Dockerfile index e4fef52d1..5a5dcad8a 100644 --- a/cpython-unix/build.Dockerfile +++ b/cpython-unix/build.Dockerfile @@ -1,8 +1,5 @@ {% include 'base.Dockerfile' %} -# libc6-dev:i386 pulls in 32-bit system libraries to enable cross-compiling -# to i386. -# # libffi-dev and zlib1g-dev are present so host Python (during cross-builds) # can build the ctypes and zlib extensions. So comment in build-cpython.sh # for more context. @@ -10,11 +7,12 @@ # Compression packages are needed to extract archives. # # Various other build tools are needed for various building. -RUN apt-get install \ +RUN ulimit -n 10000 && apt-get install \ bzip2 \ + ca-certificates \ + curl \ file \ libc6-dev \ - libc6-dev:i386 \ libffi-dev \ make \ patch \ @@ -23,4 +21,5 @@ RUN apt-get install \ tar \ xz-utils \ unzip \ + zip \ zlib1g-dev diff --git a/cpython-unix/build.cross-loongarch64.Dockerfile b/cpython-unix/build.cross-loongarch64.Dockerfile new file mode 100644 index 000000000..2e78250ef --- /dev/null +++ b/cpython-unix/build.cross-loongarch64.Dockerfile @@ -0,0 +1,65 @@ +# Debian Trixie. +FROM debian@sha256:5e64db7e29879fbb479ab2c6324656c9c0e489423e4885ed7e2f22c5b58a7a9b +LABEL org.opencontainers.image.authors="Gregory Szorc " + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -d /build -s /bin/bash -m build && \ + mkdir /tools && \ + chown -R build:build /build /tools + +ENV HOME=/build \ + SHELL=/bin/bash \ + USER=build \ + LOGNAME=build \ + HOSTNAME=builder \ + DEBIAN_FRONTEND=noninteractive + +CMD ["/bin/bash", "--login"] +WORKDIR '/build' + +RUN for s in debian_trixie debian_trixie-updates; do \ + echo "deb http://snapshot.debian.org/archive/${s%_*}/20240812T212427Z/ ${s#*_} main"; \ + done > /etc/apt/sources.list && \ + for s in debian-security_trixie-security/updates; do \ + echo "deb http://snapshot.debian.org/archive/${s%_*}/20240813T064849Z/ ${s#*_} main"; \ + done >> /etc/apt/sources.list && \ + ( echo 'quiet "true";'; \ + echo 'APT::Get::Assume-Yes "true";'; \ + echo 'APT::Install-Recommends "false";'; \ + echo 'Acquire::Check-Valid-Until "false";'; \ + echo 'Acquire::Retries "5";'; \ + ) > /etc/apt/apt.conf.d/99cpython-portable && \ + rm -f /etc/apt/sources.list.d/* + +RUN apt-get update + +# Host building. +RUN apt-get install \ + bzip2 \ + ca-certificates \ + curl \ + gcc \ + g++ \ + libc6-dev \ + libffi-dev \ + make \ + patch \ + perl \ + pkg-config \ + tar \ + xz-utils \ + unzip \ + zip \ + zlib1g-dev + +RUN apt-get install \ + g++-loongarch64-linux-gnu \ + gcc-loongarch64-linux-gnu \ + libc6-dev-loong64-cross + +RUN cd /tmp && \ + curl -LO https://snapshot.debian.org/archive/debian-ports/20240812T192057Z/pool-loong64/main/libx/libxcrypt/libcrypt-dev_4.4.36-4_loong64.deb && \ + curl -LO https://snapshot.debian.org/archive/debian-ports/20240812T192057Z/pool-loong64/main/libx/libxcrypt/libcrypt1_4.4.36-4_loong64.deb && \ + dpkg -x libcrypt-dev_4.4.36-4_loong64.deb / && \ + dpkg -x libcrypt1_4.4.36-4_loong64.deb / && \ + rm -f /tmp/*.deb diff --git a/cpython-unix/build.cross-riscv64.Dockerfile b/cpython-unix/build.cross-riscv64.Dockerfile new file mode 100644 index 000000000..2e43a1521 --- /dev/null +++ b/cpython-unix/build.cross-riscv64.Dockerfile @@ -0,0 +1,77 @@ +# Debian Buster. +FROM debian@sha256:2a0c1b9175adf759420fe0fbd7f5b449038319171eb76554bb76cbe172b62b42 +LABEL org.opencontainers.image.authors="Gregory Szorc " + +RUN groupadd -g 1000 build && \ + useradd -u 1000 -g 1000 -d /build -s /bin/bash -m build && \ + mkdir /tools && \ + chown -R build:build /build /tools + +ENV HOME=/build \ + SHELL=/bin/bash \ + USER=build \ + LOGNAME=build \ + HOSTNAME=builder \ + DEBIAN_FRONTEND=noninteractive + +CMD ["/bin/bash", "--login"] +WORKDIR '/build' + +RUN for s in debian_buster debian_buster-updates debian-security_buster/updates; do \ + echo "deb http://snapshot.debian.org/archive/${s%_*}/20250109T084424Z/ ${s#*_} main"; \ + done > /etc/apt/sources.list && \ + ( echo 'quiet "true";'; \ + echo 'APT::Get::Assume-Yes "true";'; \ + echo 'APT::Install-Recommends "false";'; \ + echo 'Acquire::Check-Valid-Until "false";'; \ + echo 'Acquire::Retries "5";'; \ + ) > /etc/apt/apt.conf.d/99cpython-portable + +RUN apt-get update + +# Host building. +RUN apt-get install \ + bzip2 \ + gcc \ + g++ \ + libc6-dev \ + libffi-dev \ + make \ + patch \ + perl \ + pkg-config \ + tar \ + xz-utils \ + unzip \ + zip \ + zlib1g-dev + +# Cross-building. +RUN apt-get install \ + g++-aarch64-linux-gnu \ + g++-arm-linux-gnueabi \ + g++-arm-linux-gnueabihf \ + g++-mips-linux-gnu \ + g++-mips64el-linux-gnuabi64 \ + g++-mipsel-linux-gnu \ + g++-powerpc64le-linux-gnu \ + g++-riscv64-linux-gnu \ + g++-s390x-linux-gnu \ + gcc-aarch64-linux-gnu \ + gcc-arm-linux-gnueabi \ + gcc-arm-linux-gnueabihf \ + gcc-mips-linux-gnu \ + gcc-mips64el-linux-gnuabi64 \ + gcc-mipsel-linux-gnu \ + gcc-powerpc64le-linux-gnu \ + gcc-riscv64-linux-gnu \ + gcc-s390x-linux-gnu \ + libc6-dev-arm64-cross \ + libc6-dev-armel-cross \ + libc6-dev-armhf-cross \ + libc6-dev-mips-cross \ + libc6-dev-mips64el-cross \ + libc6-dev-mipsel-cross \ + libc6-dev-ppc64el-cross \ + libc6-dev-riscv64-cross \ + libc6-dev-s390x-cross diff --git a/cpython-unix/build.cross.Dockerfile b/cpython-unix/build.cross.Dockerfile index 83bee5984..3665f9ac2 100644 --- a/cpython-unix/build.cross.Dockerfile +++ b/cpython-unix/build.cross.Dockerfile @@ -1,6 +1,6 @@ # Debian Stretch. -FROM debian@sha256:4b9b2ef8de1f3e9531626e8eb3d19e104e9dfde0a2b0f42b763b38235773f48e -MAINTAINER Gregory Szorc +FROM debian@sha256:cebe6e1c30384958d471467e231f740e8f0fd92cbfd2a435a186e9bada3aee1c +LABEL org.opencontainers.image.authors="Gregory Szorc " RUN groupadd -g 1000 build && \ useradd -u 1000 -g 1000 -d /build -s /bin/bash -m build && \ @@ -17,8 +17,11 @@ ENV HOME=/build \ CMD ["/bin/bash", "--login"] WORKDIR '/build' +# Stretch stopped publishing snapshots in April 2023. Last snapshot +# is 20230423T032533Z. But there are package authentication issues +# with this snapshot. RUN for s in debian_stretch debian_stretch-updates debian-security_stretch/updates; do \ - echo "deb http://snapshot.debian.org/archive/${s%_*}/20210404T083057Z/ ${s#*_} main"; \ + echo "deb http://snapshot.debian.org/archive/${s%_*}/20221105T150728Z/ ${s#*_} main"; \ done > /etc/apt/sources.list && \ ( echo 'quiet "true";'; \ echo 'APT::Get::Assume-Yes "true";'; \ @@ -43,16 +46,26 @@ RUN apt-get install \ tar \ xz-utils \ unzip \ + zip \ zlib1g-dev # Cross-building. RUN apt-get install \ + g++-aarch64-linux-gnu \ + g++-arm-linux-gnueabi \ + g++-arm-linux-gnueabihf \ + g++-mips-linux-gnu \ + g++-mips64el-linux-gnuabi64 \ + g++-mipsel-linux-gnu \ + g++-powerpc64le-linux-gnu \ + g++-s390x-linux-gnu \ gcc-aarch64-linux-gnu \ gcc-arm-linux-gnueabi \ gcc-arm-linux-gnueabihf \ gcc-mips-linux-gnu \ gcc-mips64el-linux-gnuabi64 \ gcc-mipsel-linux-gnu \ + gcc-powerpc64le-linux-gnu \ gcc-s390x-linux-gnu \ libc6-dev-arm64-cross \ libc6-dev-armel-cross \ @@ -60,4 +73,5 @@ RUN apt-get install \ libc6-dev-mips-cross \ libc6-dev-mips64el-cross \ libc6-dev-mipsel-cross \ + libc6-dev-ppc64el-cross \ libc6-dev-s390x-cross diff --git a/cpython-unix/build.debian9.Dockerfile b/cpython-unix/build.debian9.Dockerfile new file mode 100644 index 000000000..1f7c91b6c --- /dev/null +++ b/cpython-unix/build.debian9.Dockerfile @@ -0,0 +1,16 @@ +{% include 'base.debian9.Dockerfile' %} + +RUN ulimit -n 10000 && apt-get install \ + bzip2 \ + file \ + libc6-dev \ + libffi-dev \ + make \ + patch \ + perl \ + pkg-config \ + tar \ + xz-utils \ + unzip \ + zip \ + zlib1g-dev diff --git a/cpython-unix/build.py b/cpython-unix/build.py index 51ec1aa61..eb0e6ae86 100755 --- a/cpython-unix/build.py +++ b/cpython-unix/build.py @@ -14,13 +14,16 @@ import tempfile import docker +import zstandard from pythonbuild.buildenv import build_environment from pythonbuild.cpython import ( + STDLIB_TEST_PACKAGES, derive_setup_local, - parse_config_c, + extension_modules_config, + meets_python_maximum_version, + meets_python_minimum_version, parse_setup_line, - STDLIB_TEST_PACKAGES, ) from pythonbuild.docker import build_docker_image, get_image, write_dockerfiles from pythonbuild.downloads import DOWNLOADS @@ -28,13 +31,16 @@ from pythonbuild.utils import ( add_env_common, add_licenses_to_extension_entry, + clang_toolchain, + create_tar_from_directory, download_entry, - get_targets, get_target_settings, - get_target_support_file, + get_targets, target_needs, validate_python_json, + write_cpython_version, write_package_versions, + write_target_settings, write_triples_makefiles, ) @@ -42,65 +48,81 @@ BUILD = ROOT / "build" DOWNLOADS_PATH = BUILD / "downloads" SUPPORT = ROOT / "cpython-unix" +EXTENSION_MODULES = SUPPORT / "extension-modules.yml" TARGETS_CONFIG = SUPPORT / "targets.yml" +LINUX_ALLOW_SYSTEM_LIBRARIES = { + "c", + "crypt", + "dl", + "m", + "pthread", + "rt", + "util", +} +MACOS_ALLOW_SYSTEM_LIBRARIES = {"dl", "m", "pthread"} +MACOS_ALLOW_FRAMEWORKS = {"CoreFoundation"} -def install_sccache(build_env): - """Attempt to install sccache into the build environment. - - This will attempt to locate a sccache executable and copy it - into the root directory of the build environment. - """ - candidates = [ - # Prefer a binary in the project itself. - ROOT - / "sccache", - ] - - # Look for sccache in $PATH, but only if the build environment - # isn't isolated, as copying binaries into an isolated environment - # may not run. And running sccache in an isolated environment won't - # do anything meaningful unless an external cache is being used. - if not build_env.is_isolated: - for path in os.environ.get("PATH", "").split(":"): - if not path: - continue - - candidates.append(pathlib.Path(path) / "sccache") - - for candidate in candidates: - if candidate.exists(): - build_env.copy_file(candidate) - return - -def add_target_env(env, build_platform, target_triple, build_env): +def add_target_env(env, build_platform, target_triple, build_env, build_options): add_env_common(env) settings = get_target_settings(TARGETS_CONFIG, target_triple) + env["TOOLCHAIN"] = "llvm" env["HOST_CC"] = settings["host_cc"] env["HOST_CXX"] = settings["host_cxx"] env["CC"] = settings["target_cc"] + # We always set CXX, otherwise a build could bypass our toolchain + # accidentally, e.g., on macOS where `g++` links to the system clang. + env["CXX"] = settings["target_cxx"] + + if settings.get("bolt_capable"): + env["BOLT_CAPABLE"] = "1" env["PYBUILD_PLATFORM"] = build_platform env["TOOLS_PATH"] = build_env.tools_path - extra_target_cflags = list(settings.get("target_cflags", [])) + if "debug" in build_options: + extra_target_cflags = ["-O0"] + else: + extra_target_cflags = ["-O3"] + extra_target_cflags += list(settings.get("target_cflags", [])) extra_target_ldflags = list(settings.get("target_ldflags", [])) extra_host_cflags = [] extra_host_ldflags = [] - if build_platform == "linux64": - env["BUILD_TRIPLE"] = "x86_64-unknown-linux-gnu" + # Add compiler-rt for aarch64-musl to resolve missing builtins + if target_triple == "aarch64-unknown-linux-musl": + extra_target_cflags.append("--rtlib=compiler-rt") + extra_target_ldflags.append("--rtlib=compiler-rt") - # TODO should the musl target be normalized? - if target_triple == "x86_64-unknown-linux-musl": - env["TARGET_TRIPLE"] = "x86_64-unknown-linux-gnu" - else: + if build_platform.startswith("linux_"): + machine = platform.machine() + + # arm64 allows building for Linux on a macOS host using Docker + if machine == "aarch64" or machine == "arm64": + env["BUILD_TRIPLE"] = "aarch64-unknown-linux-gnu" env["TARGET_TRIPLE"] = target_triple + elif machine == "x86_64": + env["BUILD_TRIPLE"] = "x86_64-unknown-linux-gnu" + env["TARGET_TRIPLE"] = ( + target_triple.replace("x86_64_v2-", "x86_64-") + .replace("x86_64_v3-", "x86_64-") + .replace("x86_64_v4-", "x86_64-") + ) + else: + raise Exception("unhandled Linux machine value: %s" % machine) + + # This will make x86_64_v2, etc count as cross-compiling. This is + # semantically correct, since the current machine may not support + # instructions on the target machine type. + if env["BUILD_TRIPLE"] != target_triple or target_triple.endswith( + "-unknown-linux-musl" + ): + env["CROSS_COMPILING"] = "1" - if build_platform == "macos": + elif build_platform.startswith("macos_"): machine = platform.machine() if machine == "arm64": @@ -126,6 +148,9 @@ def add_target_env(env, build_platform, target_triple, build_env): env["TARGET_TRIPLE"] = target_triple + if env["BUILD_TRIPLE"] != env["TARGET_TRIPLE"]: + env["CROSS_COMPILING"] = "1" + # We don't have build isolation on macOS. We nerf PATH to prevent # non-system (e.g. Homebrew) executables from being used. env["PATH"] = "/usr/bin:/bin" @@ -151,6 +176,8 @@ def add_target_env(env, build_platform, target_triple, build_env): if not os.path.exists(sdk_path): raise Exception("macOS SDK path %s does not exist" % sdk_path) + env["APPLE_SDK_PATH"] = sdk_path + # Grab the version from the SDK so we can put it in PYTHON.json. sdk_settings_path = pathlib.Path(sdk_path) / "SDKSettings.json" with sdk_settings_path.open("rb") as fh: @@ -178,6 +205,8 @@ def add_target_env(env, build_platform, target_triple, build_env): extra_host_cflags.extend(["-isysroot", host_sdk_path]) extra_host_ldflags.extend(["-isysroot", host_sdk_path]) + else: + raise Exception("unhandled build platform: %s" % build_platform) env["EXTRA_HOST_CFLAGS"] = " ".join(extra_host_cflags) env["EXTRA_HOST_LDFLAGS"] = " ".join(extra_host_ldflags) @@ -194,7 +223,7 @@ def toolchain_archive_path(package_name, host_platform): def install_binutils(platform): - return platform != "macos" + return not platform.startswith("macos_") def simple_build( @@ -204,10 +233,11 @@ def simple_build( entry, host_platform, target_triple, - optimizations, + build_options, dest_archive, extra_archives=None, tools_path="deps", + python_host_version=None, ): archive = download_entry(entry, DOWNLOADS_PATH) @@ -216,25 +246,41 @@ def simple_build( build_env.install_toolchain( BUILD, host_platform, + target_triple, binutils=install_binutils(host_platform), clang=True, musl="musl" in target_triple, + static="static" in build_options, ) for a in extra_archives or []: - build_env.install_artifact_archive(BUILD, a, target_triple, optimizations) + build_env.install_artifact_archive(BUILD, a, target_triple, build_options) + + if python_host_version: + majmin = ".".join(python_host_version.split(".")[0:2]) + build_env.install_toolchain_archive( + BUILD, + f"cpython-{majmin}", + host_platform, + version=python_host_version, + ) build_env.copy_file(archive) build_env.copy_file(SUPPORT / ("build-%s.sh" % entry)) env = { - "TOOLCHAIN": "clang-%s" % host_platform, - "%s_VERSION" % entry.upper().replace("-", "_"): DOWNLOADS[entry]["version"], + "%s_VERSION" % entry.upper().replace("-", "_").replace(".", "_"): DOWNLOADS[ + entry + ]["version"], } - add_target_env(env, host_platform, target_triple, build_env) + if "static" in build_options: + env["STATIC"] = 1 + + add_target_env(env, host_platform, target_triple, build_env, build_options) - if entry == "openssl": + # for OpenSSL, set the OPENSSL_TARGET environment variable + if entry.startswith("openssl-"): settings = get_targets(TARGETS_CONFIG)[target_triple] env["OPENSSL_TARGET"] = settings["openssl_target"] @@ -248,8 +294,6 @@ def build_binutils(client, image, host_platform): archive = download_entry("binutils", DOWNLOADS_PATH) with build_environment(client, image) as build_env: - install_sccache(build_env) - build_env.copy_file(archive) build_env.copy_file(SUPPORT / "build-binutils.sh") @@ -258,7 +302,8 @@ def build_binutils(client, image, host_platform): add_env_common(env) build_env.run( - "build-binutils.sh", environment=env, + "build-binutils.sh", + environment=env, ) build_env.get_tools_archive( @@ -266,127 +311,54 @@ def build_binutils(client, image, host_platform): ) -def build_gcc(client, image, host_platform): - """Build GCC in the Docker image.""" - gcc_archive = download_entry("gcc", DOWNLOADS_PATH) - gmp_archive = download_entry("gmp", DOWNLOADS_PATH) - isl_archive = download_entry("isl", DOWNLOADS_PATH) - mpc_archive = download_entry("mpc", DOWNLOADS_PATH) - mpfr_archive = download_entry("mpfr", DOWNLOADS_PATH) - - with build_environment(client, image) as build_env: - install_sccache(build_env) - - log("copying archives to container...") - for a in (gcc_archive, gmp_archive, isl_archive, mpc_archive, mpfr_archive): - build_env.copy_file(a) - - build_env.copy_file(toolchain_archive_path("binutils", host_platform)) - build_env.copy_file(SUPPORT / "build-gcc.sh") - - env = { - "BINUTILS_VERSION": DOWNLOADS["binutils"]["version"], - "GCC_VERSION": DOWNLOADS["gcc"]["version"], - "GMP_VERSION": DOWNLOADS["gmp"]["version"], - "ISL_VERSION": DOWNLOADS["isl"]["version"], - "MPC_VERSION": DOWNLOADS["mpc"]["version"], - "MPFR_VERSION": DOWNLOADS["mpfr"]["version"], - } - - add_env_common(env) - - build_env.run("build-gcc.sh", environment=env) +def materialize_clang(host_platform: str, target_triple: str): + entry = clang_toolchain(host_platform, target_triple) + tar_zst = download_entry(entry, DOWNLOADS_PATH) + local_filename = "%s-%s-%s.tar" % ( + entry, + DOWNLOADS[entry]["version"], + host_platform, + ) - build_env.get_tools_archive( - toolchain_archive_path("gcc", host_platform), "host" - ) + dctx = zstandard.ZstdDecompressor() + with open(tar_zst, "rb") as ifh: + with open(BUILD / local_filename, "wb") as ofh: + dctx.copy_stream(ifh, ofh) -def build_clang(client, image, host_platform): - if "linux" in host_platform: - cmake_archive = download_entry("cmake-linux-bin", DOWNLOADS_PATH) - ninja_archive = download_entry("ninja-linux-bin", DOWNLOADS_PATH) - elif "macos" in host_platform: - cmake_archive = download_entry("cmake-macos-bin", DOWNLOADS_PATH) - ninja_archive = download_entry("ninja-macos-bin", DOWNLOADS_PATH) - clang_archive = download_entry("clang", DOWNLOADS_PATH) - clang_rt_archive = download_entry("clang-compiler-rt", DOWNLOADS_PATH) - lld_archive = download_entry("lld", DOWNLOADS_PATH) - llvm_archive = download_entry("llvm", DOWNLOADS_PATH) - libcxx_archive = download_entry("libc++", DOWNLOADS_PATH) - libcxxabi_archive = download_entry("libc++abi", DOWNLOADS_PATH) - libunwind_archive = download_entry("libunwind", DOWNLOADS_PATH) +def build_musl(client, image, host_platform: str, target_triple: str, build_options): + static = "static" in build_options + musl = "musl-static" if static else "musl" + musl_archive = download_entry(musl, DOWNLOADS_PATH) with build_environment(client, image) as build_env: - install_sccache(build_env) - - log("copying archives to container...") - for a in ( - cmake_archive, - ninja_archive, - clang_archive, - clang_rt_archive, - lld_archive, - llvm_archive, - libcxx_archive, - libcxxabi_archive, - libunwind_archive, - ): - build_env.copy_file(a) - - tools_path = "clang-%s" % host_platform - build_sh = "build-clang-%s.sh" % host_platform - binutils = install_binutils(host_platform) - gcc = binutils - - env = { - "CLANG_COMPILER_RT_VERSION": DOWNLOADS["clang-compiler-rt"]["version"], - "CLANG_VERSION": DOWNLOADS["clang"]["version"], - "CMAKE_VERSION": DOWNLOADS["cmake-linux-bin"]["version"], - "COMPILER_RT_VERSION": DOWNLOADS["clang-compiler-rt"]["version"], - "GCC_VERSION": DOWNLOADS["gcc"]["version"], - "LIBCXX_VERSION": DOWNLOADS["libc++"]["version"], - "LIBCXXABI_VERSION": DOWNLOADS["libc++abi"]["version"], - "LIBUNWIND_VERSION": DOWNLOADS["libunwind"]["version"], - "LLD_VERSION": DOWNLOADS["lld"]["version"], - "LLVM_VERSION": DOWNLOADS["llvm"]["version"], - } - - add_env_common(env) - - build_env.install_toolchain(BUILD, host_platform, binutils=binutils, gcc=gcc) - - build_env.copy_file(SUPPORT / build_sh) - build_env.run(build_sh, environment=env) - - build_env.get_tools_archive( - toolchain_archive_path("clang", host_platform), tools_path + build_env.install_toolchain( + BUILD, + host_platform, + target_triple, + binutils=True, + clang=True, + static=False, ) - - -def build_musl(client, image, host_platform): - musl_archive = download_entry("musl", DOWNLOADS_PATH) - - with build_environment(client, image) as build_env: - build_env.install_toolchain(BUILD, host_platform, binutils=True, clang=True) build_env.copy_file(musl_archive) build_env.copy_file(SUPPORT / "build-musl.sh") env = { - "MUSL_VERSION": DOWNLOADS["musl"]["version"], - "TOOLCHAIN": "clang-%s" % host_platform, + "MUSL_VERSION": DOWNLOADS[musl]["version"], + "TOOLCHAIN": "llvm", } + if static: + env["STATIC"] = 1 + build_env.run("build-musl.sh", environment=env) - build_env.get_tools_archive( - toolchain_archive_path("musl", host_platform), "host" - ) + build_env.get_tools_archive(toolchain_archive_path(musl, host_platform), "host") def build_libedit( - settings, client, image, host_platform, target_triple, optimizations, dest_archive + settings, client, image, host_platform, target_triple, build_options, dest_archive ): libedit_archive = download_entry("libedit", DOWNLOADS_PATH) @@ -395,98 +367,100 @@ def build_libedit( build_env.install_toolchain( BUILD, host_platform, + target_triple, binutils=install_binutils(host_platform), clang=True, musl="musl" in target_triple, + static="static" in build_options, ) build_env.install_artifact_archive( - BUILD, "ncurses", target_triple, optimizations + BUILD, "ncurses", target_triple, build_options ) build_env.copy_file(libedit_archive) build_env.copy_file(SUPPORT / "build-libedit.sh") env = { - "TOOLCHAIN": "clang-%s" % host_platform, "LIBEDIT_VERSION": DOWNLOADS["libedit"]["version"], } - add_target_env(env, host_platform, target_triple, build_env) + add_target_env(env, host_platform, target_triple, build_env, build_options) build_env.run("build-libedit.sh", environment=env) build_env.get_tools_archive(dest_archive, "deps") -def build_readline( - settings, client, image, host_platform, target_triple, optimizations, dest_archive +def build_cpython_host( + client, + image, + entry, + host_platform: str, + target_triple: str, + build_options: list[str], + dest_archive, + python_source=None, + entry_name=None, ): - readline_archive = download_entry("readline", DOWNLOADS_PATH) - - with build_environment(client, image) as build_env: - if settings.get("needs_toolchain"): - build_env.install_toolchain( - BUILD, - host_platform, - binutils=True, - clang=True, - musl="musl" in target_triple, + """Build binutils in the Docker image.""" + if not python_source: + python_version = entry["version"] + archive = download_entry(entry_name, DOWNLOADS_PATH) + else: + python_version = os.environ["PYBUILD_PYTHON_VERSION"] + archive = DOWNLOADS_PATH / ("Python-%s.tar.xz" % python_version) + print("Compressing %s to %s" % (python_source, archive)) + with archive.open("wb") as fh: + create_tar_from_directory( + fh, python_source, path_prefix="Python-%s" % python_version ) - build_env.install_artifact_archive( - BUILD, "ncurses", target_triple, optimizations + with build_environment(client, image) as build_env: + build_env.install_toolchain( + BUILD, + host_platform, + target_triple, + binutils=install_binutils(host_platform), + clang=True, + static="static" in build_options, ) - build_env.copy_file(readline_archive) - build_env.copy_file(SUPPORT / "build-readline.sh") - - env = { - "TOOLCHAIN": "clang-%s" % host_platform, - "READLINE_VERSION": DOWNLOADS["readline"]["version"], - } - - add_target_env(env, host_platform, target_triple, build_env) - - build_env.run("build-readline.sh", environment=env) - build_env.get_tools_archive(dest_archive, "deps") + build_env.copy_file(archive) -def build_tix( - settings, client, image, host_platform, target_triple, optimizations, dest_archive -): - tcl_archive = download_entry("tcl", DOWNLOADS_PATH) - tk_archive = download_entry("tk", DOWNLOADS_PATH) - tix_archive = download_entry("tix", DOWNLOADS_PATH) + support = { + "build-cpython-host.sh", + } + for s in sorted(support): + build_env.copy_file(SUPPORT / s) - with build_environment(client, image) as build_env: - if settings.get("needs_toolchain"): - build_env.install_toolchain( - BUILD, - host_platform, - binutils=install_binutils(host_platform), - clang=True, - musl="musl" in target_triple, - ) + packages = { + "autoconf", + "m4", + } + for p in sorted(packages): + build_env.install_artifact_archive(BUILD, p, target_triple, build_options) - depends = {"tcl", "tk"} - if host_platform != "macos": - depends |= {"libX11", "xorgproto"} + env = { + "PYTHON_VERSION": python_version, + } - for p in sorted(depends): - build_env.install_artifact_archive(BUILD, p, target_triple, optimizations) + add_target_env(env, host_platform, target_triple, build_env, build_options) - for p in (tcl_archive, tk_archive, tix_archive, SUPPORT / "build-tix.sh"): - build_env.copy_file(p) + # Set environment variables allowing convenient testing for Python + # version ranges. + for v in ("3.10", "3.11", "3.12", "3.13", "3.14", "3.15"): + normal_version = v.replace(".", "_") - env = { - "TOOLCHAIN": "clang-%s" % host_platform, - "TCL_VERSION": DOWNLOADS["tcl"]["version"], - "TIX_VERSION": DOWNLOADS["tix"]["version"], - "TK_VERSION": DOWNLOADS["tk"]["version"], - } + if meets_python_minimum_version(python_version, v): + env[f"PYTHON_MEETS_MINIMUM_VERSION_{normal_version}"] = "1" + if meets_python_maximum_version(python_version, v): + env[f"PYTHON_MEETS_MAXIMUM_VERSION_{normal_version}"] = "1" - add_target_env(env, host_platform, target_triple, build_env) + build_env.run( + "build-cpython-host.sh", + environment=env, + ) - build_env.run("build-tix.sh", environment=env) - build_env.get_tools_archive(dest_archive, "deps") + build_env.get_tools_archive(dest_archive, "host") def python_build_info( @@ -495,11 +469,10 @@ def python_build_info( platform, target_triple, musl, - optimizations, - config_c_in, - setup_dist, - setup_local, - libressl=False, + lto, + static, + extensions, + extra_metadata, ): """Obtain build metadata for the Python distribution.""" @@ -509,36 +482,42 @@ def python_build_info( binary_suffix = "" - if platform == "linux64": - bi["core"][ - "static_lib" - ] = "install/lib/python{version}/config-{version}{binary_suffix}-x86_64-linux-gnu/libpython{version}{binary_suffix}.a".format( - version=version, binary_suffix=binary_suffix + if platform in ("linux_x86_64", "linux_aarch64"): + arch = platform.removeprefix("linux_") + + bi["core"]["static_lib"] = ( + f"install/lib/python{version}/config-{version}{binary_suffix}-{arch}-linux-gnu/libpython{version}{binary_suffix}.a" ) - if not musl: + if not static: bi["core"]["shared_lib"] = "install/lib/libpython%s%s.so.1.0" % ( version, binary_suffix, ) - if optimizations in ("lto", "pgo+lto"): - object_file_format = "llvm-bitcode:%s" % DOWNLOADS["llvm"]["version"] + if lto: + llvm_version = DOWNLOADS[clang_toolchain(platform, target_triple)][ + "version" + ] + if "+" in llvm_version: + llvm_version = llvm_version.split("+")[0] + + object_file_format = f"llvm-bitcode:%{llvm_version}" else: object_file_format = "elf" - elif platform == "macos": - bi["core"][ - "static_lib" - ] = "install/lib/python{version}/config-{version}{binary_suffix}-darwin/libpython{version}{binary_suffix}.a".format( - version=version, binary_suffix=binary_suffix + elif platform.startswith("macos_"): + bi["core"]["static_lib"] = ( + f"install/lib/python{version}/config-{version}{binary_suffix}-darwin/libpython{version}{binary_suffix}.a" ) bi["core"]["shared_lib"] = "install/lib/libpython%s%s.dylib" % ( version, binary_suffix, ) - if optimizations in ("lto", "pgo+lto"): - object_file_format = "llvm-bitcode:%s" % DOWNLOADS["llvm"]["version"] + if lto: + object_file_format = ( + "llvm-bitcode:%s" % DOWNLOADS["llvm-aarch64-macos"]["version"] + ) else: object_file_format = "mach-o" else: @@ -546,6 +525,75 @@ def python_build_info( bi["object_file_format"] = object_file_format + # Determine allowed libraries on Linux + libs = extra_metadata["python_config_vars"].get("LIBS", "").split() + mips = target_triple.split("-")[0] in {"mips", "mipsel"} + linux_allowed_system_libraries = LINUX_ALLOW_SYSTEM_LIBRARIES.copy() + if mips and version == "3.13": + # See https://github.com/astral-sh/python-build-standalone/issues/410 + linux_allowed_system_libraries.add("atomic") + riscv = target_triple.split("-")[0] in {"riscv64"} + if riscv: + # On older GCC versions, RISC-V sub-word atomic operations require a + # helper function found in libatomic. To facilitate this, GCC <15 adds + # "-latomic" to the definition of "-pthread". We think it's generally + # reasonable on RISC-V systems (but not all Linux systems in general) + # to expect a libatomic system library is installed. + # + # Because "-latomic" is implicitly added by "-pthread", it may not be + # found in the LIBS sysconfig variable, but we need to pretend it is so + # that it gets into PYTHON.json (in particular, so that the validation + # script accepts this dependency). + # + # See https://github.com/riscvarchive/riscv-gcc/issues/12 + # https://github.com/riscvarchive/riscv-gcc/issues/337 + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86005 + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104338 + # https://github.com/gcc-mirror/gcc/commit/203f3060dd363361b172f7295f42bb6bf5ac0b3b + linux_allowed_system_libraries.add("atomic") + libs.append("-latomic") + + # Add in core linking annotations. + skip = False + for i, lib in enumerate(libs): + if skip: + skip = False + continue + + if lib.startswith("-l"): + lib = lib[2:] + + if ( + platform in ("linux_x86_64", "linux_aarch64") + and lib not in linux_allowed_system_libraries + ): + raise Exception("unexpected library in LIBS (%s): %s" % (libs, lib)) + elif ( + platform.startswith("macos_") + and lib not in MACOS_ALLOW_SYSTEM_LIBRARIES + ): + raise Exception("unexpected library in LIBS (%s): %s" % (libs, lib)) + + log("adding core system link library: %s" % lib) + bi["core"]["links"].append( + { + "name": lib, + "system": True, + } + ) + elif lib == "-framework": + skip = True + framework = libs[i + 1] + if framework not in MACOS_ALLOW_FRAMEWORKS: + raise Exception( + "unexpected framework in LIBS (%s): %s" % (libs, framework) + ) + + log("adding core link framework: %s" % framework) + bi["core"]["links"].append({"name": framework, "framework": True}) + else: + raise Exception("unknown word in LIBS (%s): %s" % (libs, lib)) + # Object files for the core distribution are found by walking the # build artifacts. core_objs = set() @@ -578,16 +626,12 @@ def python_build_info( libraries.add(libname) - # Extension data is derived by "parsing" the Setup.dist and Setup.local files. - - def process_setup_line(line, variant=None): - d = parse_setup_line(line, variant) + for extension, info in sorted(extensions.items()): + log(f"processing extension module {extension}") + d = parse_setup_line(info["setup_line"], version) if not d: - return - - extension = d["extension"] - log("processing extension %s (variant %s)" % (extension, d["variant"])) + raise Exception(f"Setup line for {extension} failed to parse") objs = [] @@ -609,6 +653,11 @@ def process_setup_line(line, variant=None): links.append({"name": framework, "framework": True}) for libname in sorted(d["links"]): + # Explicitly annotated .a files are statically linked and don't need + # annotations. + if libname.endswith(".a"): + continue + log("adding library %s for extension %s" % (libname, extension)) if libname in libraries: @@ -618,84 +667,29 @@ def process_setup_line(line, variant=None): else: links.append({"name": libname, "system": True}) + if targets := info.get("required-targets"): + required = any(re.match(p, target_triple) for p in targets) + else: + required = False + entry = { - "in_core": False, - "init_fn": "PyInit_%s" % extension, + "in_core": info["in_core"], + "init_fn": info["init_fn"], "links": links, "objs": objs, + "required": required, "variant": d["variant"], } - if libressl: - ignore_keys = {"openssl"} - else: - ignore_keys = {"libressl"} + if info.get("build-mode") == "shared": + shared_dir = extra_metadata["python_config_vars"]["DESTSHARED"].strip("/") + extension_suffix = extra_metadata["python_config_vars"]["EXT_SUFFIX"] + entry["shared_lib"] = "%s/%s%s" % (shared_dir, extension, extension_suffix) - add_licenses_to_extension_entry(entry, ignore_keys=ignore_keys) + add_licenses_to_extension_entry(entry) bi["extensions"].setdefault(extension, []).append(entry) - found_start = False - - for line in setup_dist.splitlines(): - if not found_start: - if line.startswith(b"PYTHONPATH="): - found_start = True - continue - - continue - - process_setup_line(line) - - for line in setup_local.splitlines(): - if line.startswith(b"*static*"): - continue - - if line.startswith(b"*disabled*"): - break - - process_setup_line(line) - - # Extension variants are denoted by the presence of - # Modules/VARIANT--.data files that describe the - # extension. Find those files and process them. - tf = build_env.get_output_archive("python/build/Modules", as_tar=True) - - for ti in tf: - basename = os.path.basename(ti.name) - - if not basename.startswith("VARIANT-") or not basename.endswith(".data"): - continue - - variant = basename[:-5].split("-")[2] - line = tf.extractfile(ti).read().strip() - process_setup_line(line, variant=variant) - - # There are also a setup of built-in extensions defined in config.c.in which - # aren't built using the Setup.* files and are part of the core libpython - # distribution. Define extensions entries for these so downstream consumers - # can register their PyInit_ functions. - for name, init_fn in sorted(config_c_in.items()): - log("adding in-core extension %s" % name) - bi["extensions"].setdefault(name, []).append( - { - "in_core": True, - "init_fn": init_fn, - "links": [], - "objs": [], - "variant": "default", - } - ) - - with get_target_support_file( - SUPPORT, "required-extensions", version, platform, target_triple - ).open("r") as fh: - required_extensions = {l.strip() for l in fh if l.strip()} - - for extension, entries in bi["extensions"].items(): - for entry in entries: - entry["required"] = extension in required_extensions - # Any paths left in modules_objs are not part of any extension and are # instead part of the core distribution. for p in sorted(modules_objs): @@ -711,42 +705,44 @@ def build_cpython( image, host_platform, target_triple, - optimizations, + build_options, dest_archive, - libressl=False, version=None, + python_source=None, ): """Build CPython in a Docker image'""" + parsed_build_options = set(build_options.split("+")) entry_name = "cpython-%s" % version - entry = DOWNLOADS[entry_name] + if not python_source: + entry = DOWNLOADS[entry_name] + python_version = entry["version"] + python_archive = download_entry(entry_name, DOWNLOADS_PATH) + else: + entry = DOWNLOADS.get(entry_name, {}) + python_version = os.environ["PYBUILD_PYTHON_VERSION"] + entry.setdefault("licenses", ["Python-2.0", "CNRI-Python"]) + entry.setdefault("python_tag", "cp" + "".join(version.split("."))) + python_archive = DOWNLOADS_PATH / ("Python-%s.tar.xz" % python_version) + print("Compressing %s to %s" % (python_source, python_archive)) + with python_archive.open("wb") as fh: + create_tar_from_directory( + fh, python_source, path_prefix="Python-%s" % python_version + ) - python_archive = download_entry(entry_name, DOWNLOADS_PATH) setuptools_archive = download_entry("setuptools", DOWNLOADS_PATH) pip_archive = download_entry("pip", DOWNLOADS_PATH) - with get_target_support_file( - SUPPORT, "static-modules", version, host_platform, target_triple - ).open("rb") as fh: - static_modules_lines = [l.rstrip() for l in fh if not l.startswith(b"#")] - - with get_target_support_file( - SUPPORT, "disabled-static-modules", version, host_platform, target_triple - ).open("rb") as fh: - disabled_static_modules = { - l.strip() for l in fh if l.strip() and not l.strip().startswith(b"#") - } + ems = extension_modules_config(EXTENSION_MODULES) setup = derive_setup_local( - static_modules_lines, python_archive, - python_version=entry["version"], - musl="musl" in target_triple, - debug=optimizations == "debug", - disabled=disabled_static_modules, + python_version=python_version, + target_triple=target_triple, + build_options=parsed_build_options, + extension_modules=ems, ) - config_c_in = parse_config_c(setup["config_c_in"].decode("utf-8")) - setup_dist_content = setup["setup_dist"] + enabled_extensions = setup["extensions"] setup_local_content = setup["setup_local"] extra_make_content = setup["make_data"] @@ -755,26 +751,32 @@ def build_cpython( build_env.install_toolchain( BUILD, host_platform, + target_triple, binutils=install_binutils(host_platform), clang=True, musl="musl" in target_triple, + static="static" in build_options, ) packages = target_needs(TARGETS_CONFIG, target_triple) # Toolchain packages are handled specially. packages.discard("binutils") - packages.discard("clang") - packages.discard("gcc") packages.discard("musl") for p in sorted(packages): - build_env.install_artifact_archive(BUILD, p, target_triple, optimizations) + build_env.install_artifact_archive(BUILD, p, target_triple, build_options) + + # Install the host CPython. + build_env.install_toolchain_archive( + BUILD, entry_name, host_platform, version=python_version + ) for p in ( python_archive, setuptools_archive, pip_archive, SUPPORT / "build-cpython.sh", + SUPPORT / "run_tests-13.py", ): build_env.copy_file(p) @@ -782,6 +784,10 @@ def build_cpython( if f.startswith("LICENSE.") and f.endswith(".txt"): build_env.copy_file(ROOT / f) + for f in sorted(os.listdir(SUPPORT)): + if f.endswith(".patch"): + build_env.copy_file(SUPPORT / f) + with tempfile.NamedTemporaryFile("wb") as fh: # In case default file masks cause wonkiness. os.chmod(fh.name, 0o644) @@ -800,61 +806,103 @@ def build_cpython( env = { "PIP_VERSION": DOWNLOADS["pip"]["version"], - "PYTHON_VERSION": entry["version"], - "PYTHON_MAJMIN_VERSION": ".".join(entry["version"].split(".")[0:2]), + "PYTHON_VERSION": python_version, + "PYTHON_MAJMIN_VERSION": ".".join(python_version.split(".")[0:2]), "SETUPTOOLS_VERSION": DOWNLOADS["setuptools"]["version"], "TOOLCHAIN": "clang-%s" % host_platform, } - if optimizations == "debug": + # Set environment variables allowing convenient testing for Python + # version ranges. + for v in ("3.10", "3.11", "3.12", "3.13", "3.14", "3.15"): + normal_version = v.replace(".", "_") + + if meets_python_minimum_version(python_version, v): + env[f"PYTHON_MEETS_MINIMUM_VERSION_{normal_version}"] = "1" + if meets_python_maximum_version(python_version, v): + env[f"PYTHON_MEETS_MAXIMUM_VERSION_{normal_version}"] = "1" + + if "freethreaded" in parsed_build_options: + env["CPYTHON_FREETHREADED"] = "1" + + if "debug" in parsed_build_options: env["CPYTHON_DEBUG"] = "1" - if optimizations in ("pgo", "pgo+lto"): + if "pgo" in parsed_build_options: env["CPYTHON_OPTIMIZED"] = "1" - if optimizations in ("lto", "pgo+lto"): + if "lto" in parsed_build_options: env["CPYTHON_LTO"] = "1" + if "static" in parsed_build_options: + env["CPYTHON_STATIC"] = "1" + + # Extract LLVM major version for JIT configuration + llvm_full_version = DOWNLOADS[clang_toolchain(host_platform, target_triple)][ + "version" + ] + if "+" in llvm_full_version: + llvm_full_version = llvm_full_version.split("+")[0] + # Get major version (e.g., "21" from "21.1.4") + env["LLVM_VERSION"] = llvm_full_version.split(".")[0] - add_target_env(env, host_platform, target_triple, build_env) + add_target_env(env, host_platform, target_triple, build_env, build_options) build_env.run("build-cpython.sh", environment=env) extension_module_loading = ["builtin"] crt_features = [] - if host_platform == "linux64": - if "musl" in target_triple: + if host_platform in ("linux_x86_64", "linux_aarch64"): + if "static" in parsed_build_options: crt_features.append("static") else: extension_module_loading.append("shared-library") - crt_features.append("glibc-dynamic") - glibc_max_version = build_env.get_file("glibc_version.txt").strip() - if not glibc_max_version: - raise Exception("failed to retrieve glibc max symbol version") + if "musl" in target_triple: + crt_features.append("musl-dynamic") - crt_features.append( - "glibc-max-symbol-version:%s" % glibc_max_version.decode("ascii") - ) + musl_version = DOWNLOADS["musl"]["version"] + crt_features.append("musl-version:%s" % musl_version) + + else: + crt_features.append("glibc-dynamic") + + glibc_max_version = build_env.get_file("glibc_version.txt").strip() + if not glibc_max_version: + raise Exception("failed to retrieve glibc max symbol version") + + crt_features.append( + "glibc-max-symbol-version:%s" + % glibc_max_version.decode("ascii") + ) python_symbol_visibility = "global-default" - elif host_platform == "macos": + elif host_platform.startswith("macos_"): python_symbol_visibility = "global-default" extension_module_loading.append("shared-library") crt_features.append("libSystem") else: raise ValueError("unhandled platform: %s" % host_platform) + extra_metadata = json.loads(build_env.get_file("metadata.json")) + + # TODO: Remove `optimizations` in the future, deprecated in favor of + # `build_options` in metadata version 8. + optimizations = build_options.replace("freethreaded+", "") + # Create PYTHON.json file describing this distribution. python_info = { - "version": "7", + "version": "8", "target_triple": target_triple, "optimizations": optimizations, + "build_options": build_options, "python_tag": entry["python_tag"], - "python_version": entry["version"], + "python_version": python_version, "python_stdlib_test_packages": sorted(STDLIB_TEST_PACKAGES), "python_symbol_visibility": python_symbol_visibility, "python_extension_module_loading": extension_module_loading, - "libpython_link_mode": "static" if "musl" in target_triple else "shared", + "libpython_link_mode": ( + "static" if "static" in parsed_build_options else "shared" + ), "crt_features": crt_features, "run_tests": "build/run_tests.py", "build_info": python_build_info( @@ -863,11 +911,10 @@ def build_cpython( host_platform, target_triple, "musl" in target_triple, - optimizations, - config_c_in, - setup_dist_content, - setup_local_content, - libressl=libressl, + "lto" in parsed_build_options, + "static" in parsed_build_options, + enabled_extensions, + extra_metadata, ), "licenses": entry["licenses"], "license_path": "licenses/LICENSE.cpython.txt", @@ -875,11 +922,9 @@ def build_cpython( python_info["tcl_library_path"] = "install/lib" python_info["tcl_library_paths"] = [ - "tcl8", - "tcl8.6", - "thread2.8.5", - "Tix8.4.3", - "tk8.6", + "itcl4.3.5", + "thread3.0.4", + "tk9.0", ] if "-apple" in target_triple: @@ -891,10 +936,9 @@ def build_cpython( ] # Add metadata derived from built distribution. - extra_metadata = build_env.get_file("metadata.json") - python_info.update(json.loads(extra_metadata)) + python_info.update(extra_metadata) - validate_python_json(python_info) + validate_python_json(python_info, extension_modules=ems) with tempfile.NamedTemporaryFile("w") as fh: json.dump(python_info, fh, sort_keys=True, indent=4) @@ -920,12 +964,13 @@ def main(): client = None else: try: - client = docker.from_env() + client = docker.from_env(timeout=600) client.ping() except Exception as e: - print("unable to connect to Docker: %s" % e) + print("unable to connect to Docker: %s" % e, file=sys.stderr) return 1 + # Note these arguments must be synced with `build-main.py` parser = argparse.ArgumentParser() parser.add_argument( "--host-platform", required=True, help="Platform we are building from" @@ -935,12 +980,19 @@ def main(): required=True, help="Host triple that we are building Python for", ) + + # Construct possible options + options = set() + options.update({"debug", "noopt", "pgo", "lto", "pgo+lto"}) + options.update({f"freethreaded+{option}" for option in options}) + options.update({f"{option}+static" for option in options}) parser.add_argument( - "--optimizations", - choices={"debug", "noopt", "pgo", "lto", "pgo+lto"}, - required=True, - help="Optimization profile to use", + "--options", + choices=options, + default="noopt", + help="Build options to apply when compiling Python", ) + parser.add_argument( "--toolchain", action="store_true", @@ -950,6 +1002,16 @@ def main(): "--dest-archive", required=True, help="Path to archive that we are producing" ) parser.add_argument("--docker-image", help="Docker image to use for building") + parser.add_argument( + "--python-source", + default=None, + help="A custom path to CPython source files to use", + ) + parser.add_argument( + "--python-host-version", + default=None, + help="Python X.Y version for host Python installation", + ) parser.add_argument("action") args = parser.parse_args() @@ -958,10 +1020,15 @@ def main(): target_triple = args.target_triple host_platform = args.host_platform - optimizations = args.optimizations + build_options = args.options + python_source = ( + pathlib.Path(args.python_source) if args.python_source != "null" else None + ) dest_archive = pathlib.Path(args.dest_archive) docker_image = args.docker_image + python_host_version = args.python_host_version + settings = get_target_settings(TARGETS_CONFIG, target_triple) if args.action == "dockerfiles": @@ -972,13 +1039,27 @@ def main(): log_name = "image-%s" % action elif args.toolchain: log_name = "%s-%s" % (action, host_platform) + elif args.action.startswith("cpython-") and args.action.endswith("-host"): + log_name = args.action + elif action.startswith("cpython-"): + version = ( + os.environ["PYBUILD_PYTHON_VERSION"] + if python_source + else DOWNLOADS[action]["version"] + ) + log_name = "%s-%s-%s-%s" % ( + action, + version, + target_triple, + build_options, + ) else: entry = DOWNLOADS[action] log_name = "%s-%s-%s-%s" % ( action, entry["version"], target_triple, - optimizations, + build_options, ) log_path = BUILD / "logs" / ("build.%s.log" % log_name) @@ -988,90 +1069,106 @@ def main(): if action == "dockerfiles": write_dockerfiles(SUPPORT, BUILD) elif action == "makefiles": - write_triples_makefiles(get_targets(TARGETS_CONFIG), BUILD, SUPPORT) + targets = get_targets(TARGETS_CONFIG) + write_triples_makefiles(targets, BUILD, SUPPORT) + write_target_settings(targets, BUILD / "targets") write_package_versions(BUILD / "versions") + # Override the DOWNLOADS package entry for CPython for the local build + if python_source: + write_cpython_version( + BUILD / "versions", os.environ["PYBUILD_PYTHON_VERSION"] + ) + elif action.startswith("image-"): image_name = action[6:] image_path = BUILD / ("%s.Dockerfile" % image_name) with image_path.open("rb") as fh: image_data = fh.read() - build_docker_image(client, image_data, BUILD, image_name) + build_docker_image(client, image_data, BUILD, image_name, host_platform) elif action == "binutils": - build_binutils(client, get_image(client, ROOT, BUILD, "gcc"), host_platform) - - elif action == "clang": - build_clang( + build_binutils( client, - get_image(client, ROOT, BUILD, "clang"), - host_platform=host_platform, + get_image(client, ROOT, BUILD, docker_image, host_platform), + host_platform, ) - elif action == "gcc": - build_gcc(client, get_image(client, ROOT, BUILD, "gcc"), host_platform) + elif action == "clang": + materialize_clang(host_platform, target_triple) elif action == "musl": - build_musl(client, get_image(client, ROOT, BUILD, "gcc"), host_platform) + build_musl( + client, + get_image(client, ROOT, BUILD, docker_image, host_platform), + host_platform, + target_triple, + build_options, + ) - elif action == "libedit": - build_libedit( + elif action == "autoconf": + simple_build( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), + action, host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, + tools_path="host", + extra_archives=["m4"], ) - elif action == "readline": - build_readline( + elif action == "libedit": + build_libedit( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, ) elif action in ( "bdb", "bzip2", - "gdbm", - "gettext", - "inputproto", - "kbproto", + "expat", + "libffi-3.3", "libffi", "libpthread-stubs", - "libressl", + "m4", + "mpdecimal", "ncurses", - "openssl", + "openssl-3.5", "patchelf", "sqlite", "tcl", "uuid", "x11-util-macros", - "xextproto", "xorgproto", - "xproto", "xtrans", "xz", "zlib", + "zstd", ): - tools_path = "host" if action == "patchelf" else "deps" + tools_path = "host" if action in ("m4", "patchelf") else "deps" + extra_archives = { + "tcl": {"zlib"}, + }.get(action) simple_build( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), action, host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, + extra_archives=extra_archives, tools_path=tools_path, ) @@ -1079,22 +1176,18 @@ def main(): simple_build( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), action, host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, extra_archives={ - "inputproto", - "kbproto", "libpthread-stubs", "libXau", "libxcb", "x11-util-macros", - "xextproto", "xorgproto", - "xproto", "xtrans", }, ) @@ -1103,54 +1196,45 @@ def main(): simple_build( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), action, host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, - extra_archives={"x11-util-macros", "xproto"}, + extra_archives={"x11-util-macros", "xorgproto"}, ) elif action == "xcb-proto": simple_build( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), action, host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, + python_host_version=python_host_version, ) elif action == "libxcb": simple_build( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), action, host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, - dest_archive=dest_archive, - extra_archives={"libpthread-stubs", "libXau", "xcb-proto", "xproto"}, - ) - - elif action == "tix": - build_tix( - settings, - client, - get_image(client, ROOT, BUILD, docker_image), - host_platform=host_platform, - target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, + extra_archives={"libpthread-stubs", "libXau", "xcb-proto", "xorgproto"}, + python_host_version=python_host_version, ) elif action == "tk": - extra_archives = {"tcl"} - if host_platform != "macos": + extra_archives = {"tcl", "zlib"} + if not host_platform.startswith("macos_"): extra_archives |= { "libX11", "libXau", @@ -1162,26 +1246,52 @@ def main(): simple_build( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), action, host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, extra_archives=extra_archives, + python_host_version=python_host_version, ) - elif action in ("cpython-3.8", "cpython-3.9", "cpython-3.10"): + elif action.startswith("cpython-") and action.endswith("-host"): + entry_name = action[:-5] + if not python_source: + entry = DOWNLOADS[entry_name] + else: + entry = DOWNLOADS.get(entry_name, {}) + build_cpython_host( + client, + get_image(client, ROOT, BUILD, docker_image, host_platform), + entry, + host_platform=host_platform, + target_triple=target_triple, + build_options=build_options, + dest_archive=dest_archive, + python_source=python_source, + entry_name=entry_name, + ) + + elif action in ( + "cpython-3.10", + "cpython-3.11", + "cpython-3.12", + "cpython-3.13", + "cpython-3.14", + "cpython-3.15", + ): build_cpython( settings, client, - get_image(client, ROOT, BUILD, docker_image), + get_image(client, ROOT, BUILD, docker_image, host_platform), host_platform=host_platform, target_triple=target_triple, - optimizations=optimizations, + build_options=build_options, dest_archive=dest_archive, - libressl="PYBUILD_LIBRESSL" in os.environ, version=action.split("-")[1], + python_source=python_source, ) else: diff --git a/cpython-unix/clang.Dockerfile b/cpython-unix/clang.Dockerfile deleted file mode 100644 index 02ea53827..000000000 --- a/cpython-unix/clang.Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -{% include 'base.Dockerfile' %} -RUN apt-get install \ - libc6-dev \ - libc6-dev:i386 \ - patch \ - python3 \ - tar \ - xz-utils \ - unzip \ - zlib1g-dev diff --git a/cpython-unix/disabled-static-modules-3.10.i686-unknown-linux-gnu b/cpython-unix/disabled-static-modules-3.10.i686-unknown-linux-gnu deleted file mode 100644 index dae3e0728..000000000 --- a/cpython-unix/disabled-static-modules-3.10.i686-unknown-linux-gnu +++ /dev/null @@ -1,2 +0,0 @@ -# gdbm package doesn't cross-compile. -_gdbm diff --git a/cpython-unix/disabled-static-modules-3.8.i686-unknown-linux-gnu b/cpython-unix/disabled-static-modules-3.8.i686-unknown-linux-gnu deleted file mode 100644 index dae3e0728..000000000 --- a/cpython-unix/disabled-static-modules-3.8.i686-unknown-linux-gnu +++ /dev/null @@ -1,2 +0,0 @@ -# gdbm package doesn't cross-compile. -_gdbm diff --git a/cpython-unix/disabled-static-modules-3.9.i686-unknown-linux-gnu b/cpython-unix/disabled-static-modules-3.9.i686-unknown-linux-gnu deleted file mode 100644 index dae3e0728..000000000 --- a/cpython-unix/disabled-static-modules-3.9.i686-unknown-linux-gnu +++ /dev/null @@ -1,2 +0,0 @@ -# gdbm package doesn't cross-compile. -_gdbm diff --git a/cpython-unix/disabled-static-modules.3.10.linux64 b/cpython-unix/disabled-static-modules.3.10.linux64 deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpython-unix/disabled-static-modules.3.8.linux64 b/cpython-unix/disabled-static-modules.3.8.linux64 deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpython-unix/disabled-static-modules.3.8.macos b/cpython-unix/disabled-static-modules.3.8.macos deleted file mode 100644 index 6dcb10f13..000000000 --- a/cpython-unix/disabled-static-modules.3.8.macos +++ /dev/null @@ -1,8 +0,0 @@ -# We don't support GDBM because it is GPL v3. -_gdbm -# Not available on macOS. -nis -# Not available on macOS. -ossaudiodev -# Not available on macOS. -spwd diff --git a/cpython-unix/disabled-static-modules.3.9.aarch64-apple-darwin b/cpython-unix/disabled-static-modules.3.9.aarch64-apple-darwin deleted file mode 100644 index 34bdf567f..000000000 --- a/cpython-unix/disabled-static-modules.3.9.aarch64-apple-darwin +++ /dev/null @@ -1,20 +0,0 @@ -# We don't support GDBM because it is GPL v3. -_gdbm -# Not available on macOS. -nis -# Not available on macOS. -ossaudiodev -# Not available on macOS. -spwd - -# Ideally this would only be disabled while cross-compiling. We can't build it -# as a built-in extension via static-modules. And when letting setup.py build -# it, it builds for the host architecture during cross-builds, which upsets -# our validation script when it screens for the target machine type of -# binaries. So just disable it. -_testcapi - -# Similar story as _testcapi. The extension exists to test the limited API, -# which we don't really care about. Statically building it runs into problems -# and cross-compiling emits wrong machine type when built via setup.py. -xxlimited diff --git a/cpython-unix/disabled-static-modules.3.9.aarch64-apple-ios b/cpython-unix/disabled-static-modules.3.9.aarch64-apple-ios deleted file mode 100644 index ed79a2470..000000000 --- a/cpython-unix/disabled-static-modules.3.9.aarch64-apple-ios +++ /dev/null @@ -1,16 +0,0 @@ -# We don't support GDBM because it is GPL v3. -_gdbm -# ncurses not available on iOS. -_curses -_curses_panel -# APIs required by _scproxy not available on iOS. -_scproxy -# tk not available on iOS. -_tkinter -# Not available on iOS. -nis -# Not available on iOS. -ossaudiodev -# readline not available on iOS. -# Not available on iOS. -spwd diff --git a/cpython-unix/disabled-static-modules.3.9.linux64 b/cpython-unix/disabled-static-modules.3.9.linux64 deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpython-unix/disabled-static-modules.3.9.macos b/cpython-unix/disabled-static-modules.3.9.macos deleted file mode 100644 index 6dcb10f13..000000000 --- a/cpython-unix/disabled-static-modules.3.9.macos +++ /dev/null @@ -1,8 +0,0 @@ -# We don't support GDBM because it is GPL v3. -_gdbm -# Not available on macOS. -nis -# Not available on macOS. -ossaudiodev -# Not available on macOS. -spwd diff --git a/cpython-unix/disabled-static-modules.3.9.x86_64-apple-ios b/cpython-unix/disabled-static-modules.3.9.x86_64-apple-ios deleted file mode 100644 index ed79a2470..000000000 --- a/cpython-unix/disabled-static-modules.3.9.x86_64-apple-ios +++ /dev/null @@ -1,16 +0,0 @@ -# We don't support GDBM because it is GPL v3. -_gdbm -# ncurses not available on iOS. -_curses -_curses_panel -# APIs required by _scproxy not available on iOS. -_scproxy -# tk not available on iOS. -_tkinter -# Not available on iOS. -nis -# Not available on iOS. -ossaudiodev -# readline not available on iOS. -# Not available on iOS. -spwd diff --git a/cpython-unix/extension-modules.yml b/cpython-unix/extension-modules.yml new file mode 100644 index 000000000..ab44fcc79 --- /dev/null +++ b/cpython-unix/extension-modules.yml @@ -0,0 +1,1114 @@ +# This file defines metadata for Python extension modules. +# +# Keys are the name of an extension module in a CPython distribution. +# Values are maps with various attributes. See cpython.py for the JSON +# schema definition. + +_abc: + setup-enabled: true + +_ast: + config-c-only: true + +_asyncio: + sources: + - _asynciomodule.c + +_bisect: + sources: + - _bisectmodule.c + +_blake2: + # In 3.14+, Blake2 is provided by Hacl* + sources-conditional: + - sources: + - _blake2/blake2module.c + - _blake2/blake2b_impl.c + - _blake2/blake2s_impl.c + maximum-python-version: "3.13" + - sources: + - blake2module.c + - _hacl/Hacl_Hash_Blake2s.c + - _hacl/Hacl_Hash_Blake2b.c + - _hacl/Lib_Memzero0.c + minimum-python-version: "3.14" + includes-conditional: + - includes: + - Modules/_hacl + - Modules/_hacl/include + - Modules/_hacl/internal + minimum-python-version: "3.14" + defines-conditional: + - define: _BSD_SOURCE + minimum-python-version: "3.14" + - define: _DEFAULT_SOURCE + minimum-python-version: "3.14" + # Disable `explicit_bzero`, it requires glibc 2.25+ + - define: LINUX_NO_EXPLICIT_BZERO + minimum-python-version: "3.14" + +_bz2: + sources: + - _bz2module.c + links: + - bz2 + +_codecs: + setup-enabled: true + required-targets: + - .* + +_codecs_cn: + sources: + - cjkcodecs/_codecs_cn.c + +_codecs_hk: + sources: + - cjkcodecs/_codecs_hk.c + +_codecs_iso2022: + sources: + - cjkcodecs/_codecs_iso2022.c + +_codecs_jp: + sources: + - cjkcodecs/_codecs_jp.c + +_codecs_kr: + sources: + - cjkcodecs/_codecs_kr.c + +_codecs_tw: + sources: + - cjkcodecs/_codecs_tw.c + +_collections: + setup-enabled: true + +_contextvars: + sources: + - _contextvarsmodule.c + config-c-only-conditional: + - config-c-only: true + minimum-python-version: "3.14" + +_crypt: + maximum-python-version: "3.12" + build-mode: shared + sources: + - _cryptmodule.c + links-conditional: + # Linux links against libcrypt. Apple has symbols through a universal framework. + - name: crypt + targets: + - .*-unknown-linux-.* + +_csv: + sources: + - _csv.c + +_ctypes: + sources: + - _ctypes/_ctypes.c + - _ctypes/callbacks.c + - _ctypes/callproc.c + - _ctypes/stgdict.c + - _ctypes/cfield.c + sources-conditional: + - source: _ctypes/darwin/dlfcn_simple.c + targets: + - .*-apple-.* + # Functionality removed in 3.12. + maximum-python-version: "3.11" + - source: _ctypes/malloc_closure.c + targets: + - .*-apple-.* + defines: + - HAVE_FFI_PREP_CIF_VAR=1 + - HAVE_FFI_PREP_CLOSURE_LOC=1 + - HAVE_FFI_CLOSURE_ALLOC=1 + defines-conditional: + - define: MACOSX + targets: + - .*-apple-.* + - define: USING_MALLOC_CLOSURE_DOT_C=1 + targets: + - .*-apple-.* + includes-deps: + - include + includes-conditional: + - path: _ctypes/darwin + targets: + - .*-apple-.* + links: + - ffi + - dl + +_ctypes_test: + build-mode: shared + sources: + - _ctypes/_ctypes_test.c + links: + - m + +_curses: + sources: + - _cursesmodule.c + defines: + - HAVE_NCURSESW=1 + defines-conditional: + - define: _XOPEN_SOURCE_EXTENDED=1 + targets: + - .*-apple-darwin + includes-deps: + - include/ncursesw + links-conditional: + # link against ncursesw on Linux and ncurses on macOS. The macOS library is + # Unicode aware, despite the name implying it isn't. + - name: ncursesw + targets: + - .*-unknown-linux-.* + - name: ncurses + targets: + - .*-apple-darwin + +_curses_panel: + sources: + - _curses_panel.c + defines: + - HAVE_NCURSESW=1 + includes-deps: + - include/ncursesw + links-conditional: + - name: panelw + targets: + - .*-unknown-linux-.* + - name: panel + targets: + - .*-apple-darwin + + - name: ncursesw + targets: + - .*-unknown-linux-.* + - name: ncurses + targets: + - .*-apple-darwin + +_datetime: + sources: + - _datetimemodule.c + setup-enabled-conditional: + - enabled: true + minimum-python-version: "3.14" + +_dbm: + sources: + - _dbmmodule.c + defines-conditional: + - define: HAVE_BERKDB_H + maximum-python-version: "3.10" + targets: + - .*-unknown-linux-.* + - define: DB_DBM_HSEARCH + maximum-python-version: "3.10" + targets: + - .*-unknown-linux-.* + - define: HAVE_NDBM_H + maximum-python-version: "3.10" + targets: + - .*-apple-.* + + # It looks like CPython commit 0a9f69539be27acf1cddf1b58d02a88d02e5008d didn't + # fully implement BDB support in configure. So we add a missing define. + - define: USE_BERKDB + minimum-python-version: "3.11" + targets: + - .*-unknown-linux-.* + - define: USE_NDBM + minimum-python-version: "3.11" + targets: + - .*-apple-.* + includes-deps: + - include + links-conditional: + # macOS ships with an ndbm implementation in libSystem. CPython's setup.py will + # use it unless an ndbm or gdbm_compat library is present. + - name: db + targets: + - .*-unknown-linux-.* + # Allow people who want to avoid the Sleepycat license to remove _dbm entirely. + build-mode: shared + +_decimal: + sources: + - _decimal/_decimal.c + includes-deps: + - include + defines-conditional: + - define: CONFIG_32=1 + targets: + - armv7-.* + - mips-.* + - mipsel-.* + - define: CONFIG_64=1 + targets: + - aarch64-.* + - loongarch64-unknown-linux.* + - ppc64le-unknown-linux.* + - riscv64-unknown-linux.* + - s390x-unknown-linux-.* + - x86_64.* + links: + - mpdec + +_elementtree: + sources: + - _elementtree.c + links: + - expat + +_functools: + setup-enabled: true + +_gdbm: + # Disable GDBM everywhere because it is GPL v3. + disabled-targets: + - .* + # Configs never tested in YAML world since extension disabled globally. See + # VCS history from old static-modules* files for possible config changes if + # we ever need to revive this. + sources: + - _gdbmmodule.c + defines: + - HAVE_NDBM_H + includes-deps: + - include + links: + - gdbm + +_hashlib: + sources: + - _hashopenssl.c + includes-deps: + - include + links: + - crypto + links-conditional: + # Clang built OpenSSL 3.x depends on libatomic. We force linking against + # a static library so there isn't a runtime dependency. + - name: ':libatomic.a' + targets: + - mips-unknown-linux-gnu + - mipsel-unknown-linux-gnu + - x86_64.*-unknown-linux-gnu + +_heapq: + sources: + - _heapqmodule.c + +_hmac: + minimum-python-version: '3.14' + sources: + - hmacmodule.c + - _hacl/Hacl_HMAC.c + - _hacl/Hacl_Streaming_HMAC.c + includes: + - Modules/_hacl/ + - Modules/_hacl/include/ + defines: + - _BSD_SOURCE + - _DEFAULT_SOURCE + +_imp: + config-c-only: true + +_interpchannels: + minimum-python-version: "3.13" + sources: + - _interpchannelsmodule.c + +_interpqueues: + minimum-python-version: "3.13" + sources: + - _interpqueuesmodule.c + +_interpreters: + minimum-python-version: "3.13" + sources: + - _interpretersmodule.c + +_io: + setup-enabled: true + required-targets: + - .* + +_json: + sources: + - _json.c + +_locale: + setup-enabled: true + +_lsprof: + sources: + - _lsprof.c + - rotatingtree.c + +_lzma: + sources: + - _lzmamodule.c + includes-deps: + - include + links: + - lzma + +_math_integer: + minimum-python-version: "3.15" + sources: + - mathintegermodule.c + +_md5: + sources: + - md5module.c + includes: + - Modules/_hacl/include + sources-conditional: + - source: _hacl/Hacl_Hash_MD5.c + minimum-python-version: "3.12" + defines-conditional: + - define: _BSD_SOURCE + minimum-python-version: "3.12" + - define: _DEFAULT_SOURCE + minimum-python-version: "3.12" + +_multibytecodec: + sources: + - cjkcodecs/multibytecodec.c + +_multiprocessing: + # TODO check setup.py logic for semaphore.c and possibly fix missing + # dependency. + sources: + - _multiprocessing/multiprocessing.c + - _multiprocessing/semaphore.c + +_opcode: + sources: + - _opcode.c + setup-enabled-conditional: + - enabled: true + minimum-python-version: "3.14" + +_operator: + setup-enabled: true + +_pickle: + sources: + - _pickle.c + +_posixshmem: + sources: + - _multiprocessing/posixshmem.c + includes: + - Modules/_multiprocessing + links-conditional: + - name: rt + targets: + - .*-unknown-linux-.* + +_posixsubprocess: + sources: + - _posixsubprocess.c + +_queue: + sources: + - _queuemodule.c + +_random: + sources: + - _randommodule.c + + +_remote_debugging: + minimum-python-version: "3.14" + sources-conditional: + - sources: + - _remote_debugging_module.c + maximum-python-version: "3.14" + - sources: + - _remote_debugging/asyncio.c + - _remote_debugging/binary_io_reader.c + - _remote_debugging/binary_io_writer.c + - _remote_debugging/code_objects.c + - _remote_debugging/frame_cache.c + - _remote_debugging/frames.c + - _remote_debugging/module.c + - _remote_debugging/object_reading.c + - _remote_debugging/subprocess.c + - _remote_debugging/threads.c + minimum-python-version: "3.15" + +_scproxy: + # _scproxy is Apple OS only. + # APIs required by _scproxy not available on iOS. + disabled-targets: + - .*-unknown-linux-.* + sources: + - _scproxy.c + frameworks: + - CoreFoundation + - SystemConfiguration + +_sha1: + sources: + - sha1module.c + sources-conditional: + - source: _hacl/Hacl_Hash_SHA1.c + minimum-python-version: "3.12" + includes: + - Modules/_hacl/include + defines-conditional: + - define: _BSD_SOURCE + minimum-python-version: "3.12" + - define: _DEFAULT_SOURCE + minimum-python-version: "3.12" + +# _sha256 refactored and renamed to _sha2 in 3.12 + +_sha256: + maximum-python-version: "3.11" + sources: + - sha256module.c + +_sha2: + minimum-python-version: "3.12" + sources: + - sha2module.c + sources-conditional: + - source: _hacl/Hacl_Hash_SHA2.c + includes: + - Modules/_hacl/include + defines: + - _BSD_SOURCE + - _DEFAULT_SOURCE + +_sha3: + sources-conditional: + # _sha3/sha3module.c -> sha3module.c in 3.12. + - source: _sha3/sha3module.c + maximum-python-version: "3.11" + - source: sha3module.c + minimum-python-version: "3.12" + - source: _hacl/Hacl_Hash_SHA3.c + minimum-python-version: "3.12" + includes: + - Modules/_hacl/include + defines-conditional: + - define: _BSD_SOURCE + minimum-python-version: "3.12" + - define: _DEFAULT_SOURCE + minimum-python-version: "3.12" + +_sha512: + sources: + - sha512module.c + # Refactored into other modules in 3.12. + maximum-python-version: "3.11" + +_signal: + setup-enabled: true + required-targets: + - .* + +_sqlite3: + sources: + - _sqlite/connection.c + - _sqlite/cursor.c + - _sqlite/microprotocols.c + - _sqlite/module.c + - _sqlite/prepare_protocol.c + - _sqlite/row.c + - _sqlite/statement.c + - _sqlite/util.c + sources-conditional: + - source: _sqlite/blob.c + minimum-python-version: "3.11" + - source: _sqlite/cache.c + maximum-python-version: "3.10" + includes-deps: + - include + includes: + - Modules/_sqlite + defines-conditional: + # Require dynamic binaries to load extensions. + # 3.11+ uses opt in. <3.11 uses opt out. + - define: PY_SQLITE_ENABLE_LOAD_EXTENSION=1 + targets: + - .*-apple-darwin + # TODO this should likely be restricted to gnu since musl is statically + # linked. But this would break verification code. So enabled for + # backwards compatibility. + - .*-unknown-linux-.* + links: + - sqlite3 + +_socket: + sources: + - socketmodule.c + +_sre: + setup-enabled: true + +_ssl: + sources: + - _ssl.c + includes-deps: + - include + links: + - ssl + - crypto + links-conditional: + # Clang built OpenSSL 3.x depends on libatomic. We force linking against + # a static library so there isn't a runtime dependency. + - name: ':libatomic.a' + targets: + - mips-unknown-linux-gnu + - mipsel-unknown-linux-gnu + - x86_64.*-unknown-linux-gnu + +_statistics: + sources: + - _statisticsmodule.c + +_stat: + setup-enabled: true + +_string: + config-c-only: true + +_struct: + sources: + - _struct.c + +_suggestions: + setup-enabled: true + minimum-python-version: '3.13' + sources: + - _suggestions.c + +_symtable: + setup-enabled: true + +_sysconfig: + setup-enabled: true + minimum-python-version: '3.13' + sources: + - _sysconfig.c + +_testbuffer: + minimum-python-version: '3.10' + build-mode: shared + sources: + - _testbuffer.c + +_testcapi: + # Must link against public API, which isn't available in static build. + build-mode: shared-or-disabled + sources: + - _testcapimodule.c + sources-conditional: + - source: _testcapi/abstract.c + minimum-python-version: "3.12" + - source: _testcapi/buffer.c + minimum-python-version: "3.12" + - source: _testcapi/bytearray.c + minimum-python-version: "3.12" + maximum-python-version: "3.12" + - source: _testcapi/bytes.c + minimum-python-version: "3.12" + - source: _testcapi/code.c + minimum-python-version: "3.12" + - source: _testcapi/codec.c + minimum-python-version: "3.12" + - source: _testcapi/complex.c + minimum-python-version: "3.12" + - source: _testcapi/config.c + minimum-python-version: "3.14" + - source: _testcapi/datetime.c + minimum-python-version: "3.12" + - source: _testcapi/dict.c + minimum-python-version: "3.12" + - source: _testcapi/docstring.c + minimum-python-version: "3.12" + - source: _testcapi/eval.c + minimum-python-version: "3.12" + maximum-python-version: "3.12" + - source: _testcapi/exceptions.c + minimum-python-version: "3.12" + - source: _testcapi/file.c + minimum-python-version: "3.12" + - source: _testcapi/float.c + minimum-python-version: "3.12" + - source: _testcapi/frame.c + minimum-python-version: "3.14" + - source: _testcapi/function.c + minimum-python-version: "3.14" + - source: _testcapi/gc.c + minimum-python-version: "3.12" + - source: _testcapi/getargs.c + minimum-python-version: "3.12" + - source: _testcapi/hash.c + minimum-python-version: "3.13" + - source: _testcapi/heaptype.c + minimum-python-version: "3.12" + - source: _testcapi/heaptype_relative.c + maximum-python-version: "3.12" + minimum-python-version: "3.12" + - source: _testcapi/immortal.c + minimum-python-version: "3.12" + # import.c was added in 3.12, removed in 3.13, and added again in 3.14. + - source: _testcapi/import.c + minimum-python-version: "3.12" + maximum-python-version: "3.12" + - source: _testcapi/import.c + minimum-python-version: "3.14" + - source: _testcapi/list.c + minimum-python-version: "3.12" + - source: _testcapi/long.c + minimum-python-version: "3.12" + - source: _testcapi/mem.c + minimum-python-version: "3.12" + - source: _testcapi/modsupport.c + minimum-python-version: "3.15" + - source: _testcapi/module.c + minimum-python-version: "3.15" + - source: _testcapi/monitoring.c + minimum-python-version: "3.13" + - source: _testcapi/numbers.c + minimum-python-version: "3.12" + - source: _testcapi/object.c + minimum-python-version: "3.13" + - source: _testcapi/pyatomic.c + minimum-python-version: "3.13" + - source: _testcapi/pyos.c + minimum-python-version: "3.12" + maximum-python-version: "3.12" + - source: _testcapi/pytime.c + minimum-python-version: "3.12" + maximum-python-version: "3.12" + - source: _testcapi/run.c + minimum-python-version: "3.12" + - source: _testcapi/set.c + minimum-python-version: "3.12" + - source: _testcapi/structmember.c + minimum-python-version: "3.12" + - source: _testcapi/sys.c + minimum-python-version: "3.12" + maximum-python-version: "3.12" + - source: _testcapi/time.c + minimum-python-version: "3.13" + - source: _testcapi/tuple.c + minimum-python-version: "3.12" + - source: _testcapi/type.c + minimum-python-version: "3.14" + - source: _testcapi/unicode.c + minimum-python-version: "3.12" + - source: _testcapi/vectorcall.c + minimum-python-version: "3.12" + - source: _testcapi/vectorcall_limited.c + minimum-python-version: "3.12" + maximum-python-version: "3.12" + - source: _testcapi/watchers.c + minimum-python-version: "3.12" + +_testexternalinspection: + minimum-python-version: '3.13' + maximum-python-version: '3.13' + build-mode: shared + sources: + - _testexternalinspection.c + +_testimportmultiple: + minimum-python-version: '3.10' + build-mode: shared + sources: + - _testimportmultiple.c + +_testinternalcapi: + includes: + - Include/internal + sources: + - _testinternalcapi.c + includes-conditional: + - path: _testinternalcapi/parts.h + minimum-python-version: "3.13" + sources-conditional: + - source: _testinternalcapi/pytime.c + minimum-python-version: "3.13" + - source: _testinternalcapi/set.c + minimum-python-version: "3.13" + - source: _testinternalcapi/test_critical_sections.c + minimum-python-version: "3.13" + - source: _testinternalcapi/test_lock.c + minimum-python-version: "3.13" + - source: _testinternalcapi/complex.c + minimum-python-version: "3.14" + - source: _testinternalcapi/interpreter.c + minimum-python-version: "3.15" + - source: _testinternalcapi/tuple.c + minimum-python-version: "3.15" + +_testlimitedcapi: + minimum-python-version: "3.13" + # Must link against public API, which isn't available in static build. + build-mode: shared-or-disabled + sources: + - _testlimitedcapi.c + - _testlimitedcapi/abstract.c + - _testlimitedcapi/bytearray.c + - _testlimitedcapi/bytes.c + - _testlimitedcapi/complex.c + - _testlimitedcapi/dict.c + - _testlimitedcapi/eval.c + - _testlimitedcapi/file.c + - _testlimitedcapi/float.c + - _testlimitedcapi/heaptype_relative.c + - _testlimitedcapi/import.c + - _testlimitedcapi/list.c + - _testlimitedcapi/long.c + - _testlimitedcapi/object.c + - _testlimitedcapi/pyos.c + - _testlimitedcapi/set.c + - _testlimitedcapi/sys.c + - _testlimitedcapi/tuple.c + - _testlimitedcapi/unicode.c + - _testlimitedcapi/vectorcall_limited.c + sources-conditional: + - source: _testlimitedcapi/codec.c + minimum-python-version: "3.14" + - source: _testlimitedcapi/threadstate.c + minimum-python-version: "3.15" + - source: _testlimitedcapi/version.c + minimum-python-version: "3.14" + +_testmultiphase: + minimum-python-version: '3.10' + build-mode: shared + sources: + - _testmultiphase.c + +_testsinglephase: + minimum-python-version: '3.12' + build-mode: shared + sources: + - _testsinglephase.c + +_thread: + setup-enabled: true + required-targets: + - .* + +_tkinter: + sources: + - _tkinter.c + - tkappinit.c + defines: + - WITH_APPINIT + includes-deps: + - include/X11 + build-mode: shared + links: + - tcl9.0 + - tcl9tk9.0 + links-conditional: + - name: X11 + targets: + - .*-unknown-linux-.* + build-mode: static + - name: xcb + targets: + - .*-unknown-linux-.* + build-mode: static + - name: Xau + targets: + - .*-unknown-linux-.* + build-mode: static + +_tokenize: + minimum-python-version: "3.11" + config-c-only: true + +_tracemalloc: + setup-enabled: true + required-targets: + - .* + +_types: + minimum-python-version: "3.14" + setup-enabled: true + sources: + - _typesmodule.c + +_typing: + minimum-python-version: "3.11" + setup-enabled-conditional: + - enabled: true + minimum-python-version: "3.12" + sources: + - _typingmodule.c + +_uuid: + sources: + - _uuidmodule.c + includes-deps: + - include/uuid + links-conditional: + - name: uuid + # iOS doesn't need to link. + targets: + - .*-unknown-linux-.* + - .*-apple-darwin + +_warnings: + config-c-only: true + +_weakref: + setup-enabled: true + required-targets: + - .* + +_xxinterpchannels: + minimum-python-version: '3.12' + maximum-python-version: '3.12' + sources: + - _xxinterpchannelsmodule.c + +_xxsubinterpreters: + minimum-python-version: '3.10' + maximum-python-version: '3.12' + sources: + - _xxsubinterpretersmodule.c + +_xxtestfuzz: + minimum-python-version: '3.10' + sources: + - _xxtestfuzz/_xxtestfuzz.c + - _xxtestfuzz/fuzzer.c + +_zstd: + minimum-python-version: '3.14' + sources: + - _zstd/_zstdmodule.c + - _zstd/zstddict.c + - _zstd/compressor.c + - _zstd/decompressor.c + links: + - zstd + +_zoneinfo: + minimum-python-version: "3.10" + sources: + - _zoneinfo.c + +array: + sources: + - arraymodule.c + +atexit: + setup-enabled: true + +# Modules/Setup comment is ambiguous as to whether this module actually works. +audioop: + maximum-python-version: '3.12' + sources: + - audioop.c + +binascii: + sources: + - binascii.c + +builtins: + config-c-only: true + +cmath: + sources: + - cmathmodule.c + sources-conditional: + - source: _math.c + maximum-python-version: "3.10" + links: + - m + +errno: + setup-enabled: true + +faulthandler: + setup-enabled: true + required-targets: + - .* + +fcntl: + sources: + - fcntlmodule.c + +gc: + config-c-only: true + +grp: + sources: + - grpmodule.c + +itertools: + setup-enabled: true + +marshal: + config-c-only: true + +math: + sources: + - mathmodule.c + sources-conditional: + - source: _math.c + maximum-python-version: "3.10" + links: + - m + +mmap: + sources: + - mmapmodule.c + +nis: + maximum-python-version: "3.12" + disabled-targets: + # NIS is not available on Apple OS. + - aarch64-apple-.* + - x86_64-apple-.* + + # Missing header dependencies on musl. + - .*-unknown-linux-musl + + # On other UNIX platforms, it is globally disabled because it has a dependency + # on libnsl, which isn't part of the Linux Standard Base specification. libnsl + # has a wonky history where it was once part of glibc and core system installs + # but is slowly being phased away from base installations. There are potential + # workarounds to adding nis support. See discussion in + # https://github.com/astral-sh/python-build-standalone/issues/51. + - .* + + sources: + - nismodule.c + links: + - nsl + +ossaudiodev: + maximum-python-version: "3.12" + disabled-targets: + # ossaudiodev not available on Apple OS. + - aarch64-apple-.* + - x86_64-apple-.* + + # Missing header dependencies. + - .*-unknown-linux-musl + sources: + - ossaudiodev.c + +posix: + setup-enabled: true + required-targets: + - .*-unknown-linux-.* + - .*-apple-darwin + +pwd: + setup-enabled: true + +pyexpat: + sources: + - pyexpat.c + links: + - expat + +readline: + sources: + - readline.c + defines: + - USE_LIBEDIT=1 + includes-deps: + - libedit/include + - libedit/include/ncursesw + links: + - edit + links-conditional: + - name: ncursesw + targets: + - .*-unknown-linux-.* + - name: ncurses + targets: + - .*-apple-.* + +resource: + sources: + - resource.c + +select: + sources: + - selectmodule.c + +spwd: + maximum-python-version: "3.12" + sources: + - spwdmodule.c + + # spwd not available on Apple OS. + disabled-targets: + - aarch64-apple-.* + - x86_64-apple-.* + +syslog: + sources: + - syslogmodule.c + +sys: + config-c-only: true + +termios: + sources: + - termios.c + +time: + setup-enabled: true + +unicodedata: + sources: + - unicodedata.c + +xxlimited: + # Example extension. We don't care about it. + disabled-targets: + - .* + +xxlimited_35: + minimum-python-version: '3.10' + # Example extension. We don't care about it. + disabled-targets: + - .* + +xxsubtype: + setup-enabled-conditional: + - enabled: true + maximum-python-version: "3.11" + sources: + - xxsubtype.c + +# xx is a demo extension. So disable globally. +xx: + minimum-python-version: "3.11" + disabled-targets: + - .* + +zlib: + sources: + - zlibmodule.c + includes-deps: + - include + links: + - z diff --git a/cpython-unix/gcc.Dockerfile b/cpython-unix/gcc.Dockerfile index ea6d1030a..f3b76ade2 100644 --- a/cpython-unix/gcc.Dockerfile +++ b/cpython-unix/gcc.Dockerfile @@ -1,5 +1,5 @@ {% include 'base.Dockerfile' %} -RUN apt-get install \ +RUN ulimit -n 10000 && apt-get install \ autoconf \ automake \ bison \ diff --git a/cpython-unix/gcc.debian9.Dockerfile b/cpython-unix/gcc.debian9.Dockerfile new file mode 100644 index 000000000..92d764e82 --- /dev/null +++ b/cpython-unix/gcc.debian9.Dockerfile @@ -0,0 +1,14 @@ +{% include 'base.debian9.Dockerfile' %} +RUN ulimit -n 10000 && apt-get install \ + autoconf \ + automake \ + bison \ + build-essential \ + gawk \ + gcc \ + libtool \ + make \ + tar \ + texinfo \ + xz-utils \ + unzip diff --git a/cpython-unix/patch-always-build-python-for-freeze.patch b/cpython-unix/patch-always-build-python-for-freeze.patch new file mode 100644 index 000000000..55d3cf39b --- /dev/null +++ b/cpython-unix/patch-always-build-python-for-freeze.patch @@ -0,0 +1,36 @@ +diff --git a/configure.ac b/configure.ac +index c62a565eb6..5a5a076f06 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -178,24 +178,13 @@ AC_MSG_CHECKING([for Python interpreter freezing]) + AC_MSG_RESULT([$PYTHON_FOR_FREEZE]) + AC_SUBST([PYTHON_FOR_FREEZE]) + +-AS_VAR_IF([cross_compiling], [yes], +- [ +- dnl external build Python, freezing depends on Programs/_freeze_module.py +- FREEZE_MODULE_BOOTSTRAP='$(PYTHON_FOR_FREEZE) $(srcdir)/Programs/_freeze_module.py' +- FREEZE_MODULE_BOOTSTRAP_DEPS='$(srcdir)/Programs/_freeze_module.py' +- FREEZE_MODULE='$(FREEZE_MODULE_BOOTSTRAP)' +- FREEZE_MODULE_DEPS='$(FREEZE_MODULE_BOOTSTRAP_DEPS)' +- PYTHON_FOR_BUILD_DEPS='' +- ], +- [ +- dnl internal build tools also depend on Programs/_freeze_module and _bootstrap_python. +- FREEZE_MODULE_BOOTSTRAP='./Programs/_freeze_module' +- FREEZE_MODULE_BOOTSTRAP_DEPS="Programs/_freeze_module" +- FREEZE_MODULE='$(PYTHON_FOR_FREEZE) $(srcdir)/Programs/_freeze_module.py' +- FREEZE_MODULE_DEPS="_bootstrap_python \$(srcdir)/Programs/_freeze_module.py" +- PYTHON_FOR_BUILD_DEPS='$(BUILDPYTHON)' +- ] +-) ++dnl external build Python, freezing depends on Programs/_freeze_module.py ++FREEZE_MODULE_BOOTSTRAP='$(PYTHON_FOR_FREEZE) $(srcdir)/Programs/_freeze_module.py' ++FREEZE_MODULE_BOOTSTRAP_DEPS='$(srcdir)/Programs/_freeze_module.py' ++FREEZE_MODULE='$(FREEZE_MODULE_BOOTSTRAP)' ++FREEZE_MODULE_DEPS='$(FREEZE_MODULE_BOOTSTRAP_DEPS)' ++PYTHON_FOR_BUILD_DEPS='' ++ + AC_SUBST([FREEZE_MODULE_BOOTSTRAP]) + AC_SUBST([FREEZE_MODULE_BOOTSTRAP_DEPS]) + AC_SUBST([FREEZE_MODULE]) diff --git a/cpython-unix/patch-apple-cross-3.12.patch b/cpython-unix/patch-apple-cross-3.12.patch new file mode 100644 index 000000000..3729fe1c5 --- /dev/null +++ b/cpython-unix/patch-apple-cross-3.12.patch @@ -0,0 +1,85 @@ +diff --git a/configure.ac b/configure.ac +index c62a565eb6..7e5d34632c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -545,6 +545,15 @@ then + *-*-cygwin*) + ac_sys_system=Cygwin + ;; ++ *-apple-ios*) ++ ac_sys_system=iOS ++ ;; ++ *-apple-tvos*) ++ ac_sys_system=tvOS ++ ;; ++ *-apple-watchos*) ++ ac_sys_system=watchOS ++ ;; + *-*-vxworks*) + ac_sys_system=VxWorks + ;; +@@ -600,6 +609,19 @@ if test "$cross_compiling" = yes; then + *-*-cygwin*) + _host_cpu= + ;; ++ *-*-darwin*) ++ _host_cpu= ++ ;; ++ *-apple-*) ++ case "$host_cpu" in ++ arm*) ++ _host_cpu=arm ++ ;; ++ *) ++ _host_cpu=$host_cpu ++ ;; ++ esac ++ ;; + *-*-vxworks*) + _host_cpu=$host_cpu + ;; +@@ -614,6 +636,23 @@ if test "$cross_compiling" = yes; then + _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" + fi + ++# The _PYTHON_HOST_PLATFORM environment variable is used to ++# override the platform name in distutils and sysconfig when ++# cross-compiling. On Apple, the platform name expansion logic ++# is non-trivial, including renaming MACHDEP=darwin to macosx ++# and including the deployment target (or current OS version if ++# not set). Here we always force an override based on the target ++# triple. We do this in all build configurations because historically ++# the automatic resolution has been brittle. ++case "$host" in ++aarch64-apple-darwin*) ++ _PYTHON_HOST_PLATFORM="macosx-${MACOSX_DEPLOYMENT_TARGET}-arm64" ++ ;; ++x86_64-apple-darwin*) ++ _PYTHON_HOST_PLATFORM="macosx-${MACOSX_DEPLOYMENT_TARGET}-x86_64" ++ ;; ++esac ++ + # Some systems cannot stand _XOPEN_SOURCE being defined at all; they + # disable features if it is defined, without any means to access these + # features as extensions. For these systems, we skip the definition of +@@ -1507,7 +1546,7 @@ if test $enable_shared = "yes"; then + BLDLIBRARY='-Wl,+b,$(LIBDIR) -L. -lpython$(LDVERSION)' + RUNSHARED=SHLIB_PATH=`pwd`${SHLIB_PATH:+:${SHLIB_PATH}} + ;; +- Darwin*) ++ Darwin*|iOS*|tvOS*|watchOS*) + LDLIBRARY='libpython$(LDVERSION).dylib' + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} +@@ -3173,6 +3203,11 @@ then + Linux*|GNU*|QNX*|VxWorks*|Haiku*) + LDSHARED='$(CC) -shared' + LDCXXSHARED='$(CXX) -shared';; ++ iOS*|tvOS*|watchOS*) ++ LDSHARED='$(CC) -bundle -undefined dynamic_lookup' ++ LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' ++ BLDSHARED="$LDSHARED" ++ ;; + FreeBSD*) + if [[ "`$CC -dM -E - > conftest.c <=6) && defined(_MIPSEL) +diff --git a/configure.ac b/configure.ac +index aa515da..b5bad6e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -779,6 +779,20 @@ cat >> conftest.c <=6) && defined(_MIPSEL) diff --git a/cpython-unix/patch-configure-bolt-apply-flags-128514.patch b/cpython-unix/patch-configure-bolt-apply-flags-128514.patch new file mode 100644 index 000000000..e9d864321 --- /dev/null +++ b/cpython-unix/patch-configure-bolt-apply-flags-128514.patch @@ -0,0 +1,14 @@ +diff --git a/configure.ac b/configure.ac +index ee034e5a962..f1a69b7d4a7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2183,8 +2183,9 @@ then + [m4_normalize(" + -update-debug-sections + -reorder-blocks=ext-tsp + -reorder-functions=cdsort + -split-functions ++ -split-strategy=cdsplit + -icf=1 + -inline-all + -split-eh diff --git a/cpython-unix/patch-configure-bolt-icf-safe.patch b/cpython-unix/patch-configure-bolt-icf-safe.patch new file mode 100644 index 000000000..1cc41adcb --- /dev/null +++ b/cpython-unix/patch-configure-bolt-icf-safe.patch @@ -0,0 +1,84 @@ +From 91fc5ae4a5a66a03931f8cd383abd2aa062bb0e9 Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Sat, 24 May 2025 19:04:09 -0400 +Subject: [PATCH 1/1] Use only safe identical code folding with BOLT + +"Identical code folding" (ICF) is the feature of an optimizer to find that two +functions have the same code and that they can therefore be deduplicated +in the binary. While this is usually safe, it can cause observable +behavior differences if the program relies on the fact that the two +functions have different addresses. + +CPython relies on this in (at least) Objects/typeobject.c, which defines +two functions wrap_binaryfunc() and wrap_binaryfunc_l() with the same +implementation, and stores their addresses in the slotdefs array. If +these two functions have the same address, update_one_slot() in that +file will fill in slots it shouldn't, causing, for instances, +classes defined in Python that inherit from some built-in types to +misbehave. + +As of LLVM 20 (llvm/llvm-project#116275), BOLT has a "safe ICF" mode, +where it looks to see if there are any uses of a function symbol outside +function calls (e.g., relocations in data sections) and skips ICF on +such functions. The intent is that this avoids observable behavior +differences but still saves storage as much as possible. + +This version is about two months old at the time of writing. To support +older LLVM versions, we have to turn off ICF entirely. + +This problem was previously noticed for Windows/MSVC in #53093 (and +again in #24098), where the default behavior of PGO is to enable ICF +(which they expand to "identical COMDAT folding") and we had to turn it +off. +--- + configure | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- + configure.ac | 25 ++++++++++++++++++++++++- + 2 files changed, 73 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 8d939f07505..25737e3f9d6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2129,6 +2129,29 @@ if test "$Py_BOLT" = 'true' ; then + else + AC_MSG_ERROR([merge-fdata is required for a --enable-bolt build but could not be found.]) + fi ++ ++ py_bolt_icf_flag="-icf=safe" ++ AC_CACHE_CHECK( ++ [whether ${LLVM_BOLT} supports safe identical code folding], ++ [py_cv_bolt_icf_safe], ++ [ ++ saved_cflags="$CFLAGS" ++ saved_ldflags="$LDFLAGS" ++ CFLAGS="$CFLAGS_NODIST" ++ LDFLAGS="$LDFLAGS_NODIST" ++ AC_LINK_IFELSE( ++ [AC_LANG_PROGRAM([[]], [[]])], ++ [py_cv_bolt_icf_safe=no ++ ${LLVM_BOLT} -icf=safe -o conftest.bolt conftest$EXEEXT >&AS_MESSAGE_LOG_FD 2>&1 dnl ++ && py_cv_bolt_icf_safe=yes], ++ [AC_MSG_FAILURE([could not compile empty test program])]) ++ CFLAGS="$saved_cflags" ++ LDFLAGS="$saved_ldflags" ++ ] ++ ) ++ if test "$py_cv_bolt_icf_safe" = no; then ++ py_bolt_icf_flag="" ++ fi + fi + + dnl Enable BOLT of libpython if built. +@@ -2184,7 +2207,7 @@ then + -reorder-blocks=ext-tsp + -reorder-functions=cdsort + -split-functions + -split-strategy=cdsplit +- -icf=1 ++ ${py_bolt_icf_flag} + -inline-all + -split-eh + -reorder-functions-use-hot-size +-- +2.39.5 (Apple Git-154) + diff --git a/cpython-unix/patch-configure-bolt-remove-use-gnu-stack.patch b/cpython-unix/patch-configure-bolt-remove-use-gnu-stack.patch new file mode 100644 index 000000000..7cd091380 --- /dev/null +++ b/cpython-unix/patch-configure-bolt-remove-use-gnu-stack.patch @@ -0,0 +1,12 @@ +diff --git a/configure.ac b/configure.ac +index a1f4a567095..03264f87d4c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2205,7 +2205,6 @@ then + -inline-ap + -indirect-call-promotion=all + -dyno-stats +- -use-gnu-stack + -frame-opt=hot + ")] + ) diff --git a/cpython-unix/patch-configure-bolt-skip-funcs-3.15.patch b/cpython-unix/patch-configure-bolt-skip-funcs-3.15.patch new file mode 100644 index 000000000..df3d5a2b2 --- /dev/null +++ b/cpython-unix/patch-configure-bolt-skip-funcs-3.15.patch @@ -0,0 +1,18 @@ +diff --git a/configure.ac b/configure.ac +index a059a07bec2..92a7ff8d54c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2167,11 +2167,8 @@ then + [m4_normalize(" + [-update-debug-sections] + +- dnl At least LLVM 19.x doesn't support computed gotos in PIC compiled code. +- dnl Exclude functions containing computed gotos. +- dnl TODO this may be fixed in LLVM 20.x via https://github.com/llvm/llvm-project/pull/120267. +- dnl GCC's LTO creates .lto_priv.0 clones of these functions. +- [-skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1,sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1] ++ dnl LLVM on at least 20.1.0 crashes on this symbol. Work around. ++ [-skip-funcs=RC4_options/1] + ")] + ) + fi diff --git a/cpython-unix/patch-configure-bolt-skip-funcs.patch b/cpython-unix/patch-configure-bolt-skip-funcs.patch new file mode 100644 index 000000000..afc8dd95f --- /dev/null +++ b/cpython-unix/patch-configure-bolt-skip-funcs.patch @@ -0,0 +1,17 @@ +diff --git a/configure.ac b/configure.ac +index 3fcb18922c5..2df614a3063 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2227,10 +2227,8 @@ then + [m4_normalize(" + [-update-debug-sections] + +- dnl At least LLVM 19.x doesn't support computed gotos in PIC compiled code. +- dnl Exclude functions containing computed gotos. +- dnl TODO this may be fixed in LLVM 20.x via https://github.com/llvm/llvm-project/pull/120267. +- [-skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1] ++ dnl LLVM on at least 20.1.0 crashes on this symbol. Work around. ++ [-skip-funcs=RC4_options/1] + ")] + ) + fi diff --git a/cpython-unix/patch-configure-crypt-no-modify-libs.patch b/cpython-unix/patch-configure-crypt-no-modify-libs.patch new file mode 100644 index 000000000..acb80763e --- /dev/null +++ b/cpython-unix/patch-configure-crypt-no-modify-libs.patch @@ -0,0 +1,21 @@ +diff --git a/configure.ac b/configure.ac +index ac3be3850a..b6e2144783 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -4057,6 +4057,8 @@ AC_CHECK_FUNCS(setpgrp, + + # We search for both crypt and crypt_r as one or the other may be defined + # This gets us our -lcrypt in LIBS when required on the target platform. ++# Save/restore LIBS to avoid linking libpython with libcrypt. ++LIBS_SAVE=$LIBS + AC_SEARCH_LIBS(crypt, crypt) + AC_SEARCH_LIBS(crypt_r, crypt) + +@@ -4071,6 +4073,7 @@ char *r = crypt_r("", "", &d); + [AC_DEFINE(HAVE_CRYPT_R, 1, [Define if you have the crypt_r() function.])], + []) + ) ++LIBS=$LIBS_SAVE + + AC_CHECK_FUNCS(clock_gettime, [], [ + AC_CHECK_LIB(rt, clock_gettime, [ diff --git a/cpython-unix/patch-configure-disable-stdlib-mod-3.12.patch b/cpython-unix/patch-configure-disable-stdlib-mod-3.12.patch new file mode 100644 index 000000000..675c31071 --- /dev/null +++ b/cpython-unix/patch-configure-disable-stdlib-mod-3.12.patch @@ -0,0 +1,26 @@ +diff --git a/configure.ac b/configure.ac +index a284a118f02..7e536c41fda 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -7859,11 +7859,7 @@ AC_DEFUN([PY_STDLIB_MOD], [ + m4_pushdef([modcond], [MODULE_]m4_toupper([$1]))dnl + m4_pushdef([modstate], [py_cv_module_$1])dnl + dnl Check if module has been disabled by PY_STDLIB_MOD_SET_NA() +- AS_IF([test "$modstate" != "n/a"], [ +- AS_IF([m4_ifblank([$2], [true], [$2])], +- [AS_IF([m4_ifblank([$3], [true], [$3])], [modstate=yes], [modstate=missing])], +- [modstate=disabled]) +- ]) ++ AS_IF([test "$modstate" != "n/a"], [modstate=disabled]) + _MODULE_BLOCK_ADD(modcond[_STATE], [$modstate]) + AS_VAR_IF([modstate], [yes], [ + m4_ifblank([$4], [], [_MODULE_BLOCK_ADD([MODULE_]m4_toupper([$1])[_CFLAGS], [$4])]) +@@ -7883,7 +7879,7 @@ AC_DEFUN([PY_STDLIB_MOD_SIMPLE], [ + m4_pushdef([modcond], [MODULE_]m4_toupper([$1]))dnl + m4_pushdef([modstate], [py_cv_module_$1])dnl + dnl Check if module has been disabled by PY_STDLIB_MOD_SET_NA() +- AS_IF([test "$modstate" != "n/a"], [modstate=yes]) ++ AS_IF([test "$modstate" != "n/a"], [modstate=disabled]) + AM_CONDITIONAL(modcond, [test "$modstate" = yes]) + _MODULE_BLOCK_ADD(modcond[_STATE], [$modstate]) + AS_VAR_IF([modstate], [yes], [ diff --git a/cpython-unix/patch-configure-disable-stdlib-mod.patch b/cpython-unix/patch-configure-disable-stdlib-mod.patch new file mode 100644 index 000000000..637c9d23a --- /dev/null +++ b/cpython-unix/patch-configure-disable-stdlib-mod.patch @@ -0,0 +1,30 @@ +diff --git a/configure.ac b/configure.ac +index ab5e1de6fa..cd177e84f5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -6878,13 +6878,7 @@ dnl sets MODULE_$NAME_CFLAGS and MODULE_$NAME_LDFLAGS + AC_DEFUN([PY_STDLIB_MOD], [ + AC_MSG_CHECKING([for stdlib extension module $1]) + m4_pushdef([modcond], [MODULE_]m4_toupper([$1]))dnl +- m4_pushdef([modstate], [py_cv_module_$1])dnl +- dnl Check if module has been disabled by PY_STDLIB_MOD_SET_NA() +- AS_IF([test "$modstate" != "n/a"], [ +- AS_IF(m4_ifblank([$2], [true], [$2]), +- [AS_IF([m4_ifblank([$3], [true], [$3])], [modstate=yes], [modstate=missing])], +- [modstate=disabled]) +- ]) ++ m4_pushdef([modstate], [disabled])dnl + _MODULE_BLOCK_ADD(modcond[_STATE], [$modstate]) + AS_VAR_IF([modstate], [yes], [ + m4_ifblank([$4], [], [_MODULE_BLOCK_ADD([MODULE_]m4_toupper([$1])[_CFLAGS], [$4])]) +@@ -6902,9 +6896,7 @@ dnl PY_STDLIB_MOD_SIMPLE([NAME], [CFLAGS], [LDFLAGS]) + dnl cflags and ldflags are optional + AC_DEFUN([PY_STDLIB_MOD_SIMPLE], [ + m4_pushdef([modcond], [MODULE_]m4_toupper([$1]))dnl +- m4_pushdef([modstate], [py_cv_module_$1])dnl +- dnl Check if module has been disabled by PY_STDLIB_MOD_SET_NA() +- AS_IF([test "$modstate" != "n/a"], [modstate=yes]) ++ m4_pushdef([modstate], [disabled])dnl + AM_CONDITIONAL(modcond, [test "$modstate" = yes]) + _MODULE_BLOCK_ADD(modcond[_STATE], [$modstate]) + AS_VAR_IF([modstate], [yes], [ diff --git a/cpython-unix/patch-configure-remove-libtool-cruft.patch b/cpython-unix/patch-configure-remove-libtool-cruft.patch new file mode 100644 index 000000000..05b0d4c6b --- /dev/null +++ b/cpython-unix/patch-configure-remove-libtool-cruft.patch @@ -0,0 +1,77 @@ +diff --git a/configure.ac b/configure.ac +index 956334a865..d5f4aed476 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2970,72 +2970,6 @@ case $ac_sys_system/$ac_sys_release in + ;; + esac + +- +-AC_SUBST(LIBTOOL_CRUFT) +-case $ac_sys_system/$ac_sys_release in +- Darwin/@<:@01567@:>@\..*) +- LIBTOOL_CRUFT="-framework System -lcc_dynamic" +- if test "${enable_universalsdk}"; then +- : +- else +- LIBTOOL_CRUFT="${LIBTOOL_CRUFT} -arch_only `/usr/bin/arch`" +- fi +- LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' +- LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; +- Darwin/*) +- gcc_version=`gcc -dumpversion` +- if test ${gcc_version} '<' 4.0 +- then +- LIBTOOL_CRUFT="-lcc_dynamic" +- else +- LIBTOOL_CRUFT="" +- fi +- AC_RUN_IFELSE([AC_LANG_SOURCE([[ +- #include +- int main(int argc, char*argv[]) +- { +- if (sizeof(long) == 4) { +- return 0; +- } else { +- return 1; +- } +- } +- ]])],[ac_osx_32bit=yes],[ac_osx_32bit=no],[ac_osx_32bit=yes]) +- +- if test "${ac_osx_32bit}" = "yes"; then +- case `/usr/bin/arch` in +- i386) +- MACOSX_DEFAULT_ARCH="i386" +- ;; +- ppc) +- MACOSX_DEFAULT_ARCH="ppc" +- ;; +- *) +- AC_MSG_ERROR([Unexpected output of 'arch' on macOS]) +- ;; +- esac +- else +- case `/usr/bin/arch` in +- i386) +- MACOSX_DEFAULT_ARCH="x86_64" +- ;; +- ppc) +- MACOSX_DEFAULT_ARCH="ppc64" +- ;; +- arm64) +- MACOSX_DEFAULT_ARCH="arm64" +- ;; +- *) +- AC_MSG_ERROR([Unexpected output of 'arch' on macOS]) +- ;; +- esac +- +- fi +- +- LIBTOOL_CRUFT=$LIBTOOL_CRUFT" -lSystem -lSystemStubs -arch_only ${MACOSX_DEFAULT_ARCH}" +- LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' +- LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; +-esac + AC_MSG_CHECKING(for --enable-framework) + if test "$enable_framework" + then diff --git a/cpython-unix/patch-cpython-configure-target-triple-musl-3.10.patch b/cpython-unix/patch-cpython-configure-target-triple-musl-3.10.patch new file mode 100644 index 000000000..a391278b6 --- /dev/null +++ b/cpython-unix/patch-cpython-configure-target-triple-musl-3.10.patch @@ -0,0 +1,31 @@ +From d7a70fe9ffa0e4e173bae545cdcba62dc7a2a1f1 Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Sat, 25 Oct 2025 18:49:45 -0400 +Subject: [PATCH 1/1] LOCAL: configure.ac: Fix musl detection +Forwarded: not-needed + +Newer versions of Python rework this code significantly, so this does +not need to be upstreamed. +--- + configure.ac | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/configure.ac b/configure.ac +index ac3be3850a9..9c7c02ea6de 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -866,6 +866,11 @@ EOF + + if $CPP $CPPFLAGS conftest.c >conftest.out 2>/dev/null; then + PLATFORM_TRIPLET=`grep -v '^#' conftest.out | grep -v '^ *$' | tr -d ' '` ++ case "$CC" in ++ musl-*) ++ PLATFORM_TRIPLET=`echo "$PLATFORM_TRIPLET" | sed 's/linux-gnu/linux-musl/'` ++ ;; ++ esac + AC_MSG_RESULT([$PLATFORM_TRIPLET]) + else + AC_MSG_RESULT([none]) +-- +2.39.5 (Apple Git-154) + diff --git a/cpython-unix/patch-cpython-configure-target-triple-musl-3.12.patch b/cpython-unix/patch-cpython-configure-target-triple-musl-3.12.patch new file mode 100644 index 000000000..145f536a7 --- /dev/null +++ b/cpython-unix/patch-cpython-configure-target-triple-musl-3.12.patch @@ -0,0 +1,36 @@ +From 851c49bafb1fe11c02eafe4850e952cafec548b7 Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Sat, 25 Oct 2025 18:49:45 -0400 +Subject: [PATCH 1/1] LOCAL: configure.ac: Fix musl detection +Forwarded: not-needed + +We set HOST_CC to regular (glibc) clang and CC to musl-clang. +AC_CANONICAL_HOST sets build_os based on the output of config.guess, +which looks at HOST_CC in preference to CC, and therefore misreports the +target triple as gnu. Directly checking CC works for us. + +Newer versions of Python rework this code significantly to not rely on +build_os and do musl detection internally, so this does not need to be +upstreamed. +--- + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 1a02d19f1b2..ee743c11aa5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1108,8 +1108,8 @@ EOF + + if $CPP $CPPFLAGS conftest.c >conftest.out 2>/dev/null; then + PLATFORM_TRIPLET=`grep -v '^#' conftest.out | grep -v '^ *$' | tr -d ' '` +- case "$build_os" in +- linux-musl*) ++ case "$CC" in ++ musl-*) + PLATFORM_TRIPLET=`echo "$PLATFORM_TRIPLET" | sed 's/linux-gnu/linux-musl/'` + ;; + esac +-- +2.39.5 (Apple Git-154) + diff --git a/cpython-unix/patch-cpython-redhat-cert-file.patch b/cpython-unix/patch-cpython-redhat-cert-file.patch new file mode 100644 index 000000000..38e1126e4 --- /dev/null +++ b/cpython-unix/patch-cpython-redhat-cert-file.patch @@ -0,0 +1,30 @@ +diff --git a/Lib/ssl.py b/Lib/ssl.py +index 42ebb8ed384..c15c0ec940f 100644 +--- a/Lib/ssl.py ++++ b/Lib/ssl.py +@@ -423,6 +423,8 @@ class SSLContext(_SSLContext): + """An SSLContext holds various SSL-related configuration options and + data, such as certificates and possibly a private key.""" + _windows_cert_stores = ("CA", "ROOT") ++ _FALLBACK_CERT_FILE = "/etc/pki/tls/cert.pem" # RHEL 8 and below, Fedora 33 and below ++ _FALLBACK_CERT_DIR = "/etc/pki/tls/certs" # RHEL 8 and below, Fedora 33 and below + + sslsocket_class = None # SSLSocket is assigned later. + sslobject_class = None # SSLObject is assigned later. +@@ -531,6 +533,16 @@ def load_default_certs(self, purpose=Purpose.SERVER_AUTH): + if sys.platform == "win32": + for storename in self._windows_cert_stores: + self._load_windows_store_certs(storename, purpose) ++ elif sys.platform == "linux": ++ _def_paths = _ssl.get_default_verify_paths() ++ if (_def_paths[0] not in os.environ and ++ not os.path.isfile(_def_paths[1]) and ++ os.path.isfile(self._FALLBACK_CERT_FILE)): ++ self.load_verify_locations(cafile=self._FALLBACK_CERT_FILE) ++ if (_def_paths[2] not in os.environ and ++ not os.path.isdir(_def_paths[3]) and ++ os.path.isdir(self._FALLBACK_CERT_DIR)): ++ self.load_verify_locations(capath=self._FALLBACK_CERT_DIR) + self.set_default_verify_paths() + + if hasattr(_SSLContext, 'minimum_version'): diff --git a/cpython-unix/patch-cross-readelf.patch b/cpython-unix/patch-cross-readelf.patch new file mode 100644 index 000000000..b1f64b4bb --- /dev/null +++ b/cpython-unix/patch-cross-readelf.patch @@ -0,0 +1,20 @@ +diff --git a/configure.ac b/configure.ac +index c62a565eb6..7e5d34632c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1626,15 +1665,6 @@ then + fi + + AC_CHECK_TOOLS([READELF], [readelf], [:]) +-if test "$cross_compiling" = yes; then +- case "$READELF" in +- readelf|:) +- AC_MSG_ERROR([readelf for the host is required for cross builds]) +- ;; +- esac +-fi +-AC_SUBST(READELF) +- + + case $MACHDEP in + hp*|HP*) diff --git a/cpython-unix/patch-ctypes-callproc-legacy.patch b/cpython-unix/patch-ctypes-callproc-legacy.patch new file mode 100644 index 000000000..89b5e2db1 --- /dev/null +++ b/cpython-unix/patch-ctypes-callproc-legacy.patch @@ -0,0 +1,34 @@ +diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c +index b0f1e0bd04..80e81fe65c 100644 +--- a/Modules/_ctypes/callproc.c ++++ b/Modules/_ctypes/callproc.c +@@ -1450,29 +1450,8 @@ copy_com_pointer(PyObject *self, PyObject *args) + } + #else + #ifdef __APPLE__ +-#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH + #define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH_RUNTIME \ + __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +-#else +-// Support the deprecated case of compiling on an older macOS version +-static void *libsystem_b_handle; +-static bool (*_dyld_shared_cache_contains_path)(const char *path); +- +-__attribute__((constructor)) void load_dyld_shared_cache_contains_path(void) { +- libsystem_b_handle = dlopen("/usr/lib/libSystem.B.dylib", RTLD_LAZY); +- if (libsystem_b_handle != NULL) { +- _dyld_shared_cache_contains_path = dlsym(libsystem_b_handle, "_dyld_shared_cache_contains_path"); +- } +-} +- +-__attribute__((destructor)) void unload_dyld_shared_cache_contains_path(void) { +- if (libsystem_b_handle != NULL) { +- dlclose(libsystem_b_handle); +- } +-} +-#define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH_RUNTIME \ +- _dyld_shared_cache_contains_path != NULL +-#endif + + static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args) + { diff --git a/cpython-unix/patch-ctypes-callproc.patch b/cpython-unix/patch-ctypes-callproc.patch new file mode 100644 index 000000000..7053330d4 --- /dev/null +++ b/cpython-unix/patch-ctypes-callproc.patch @@ -0,0 +1,40 @@ +diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c +index f42ff08f58..e0a698b6b2 100644 +--- a/Modules/_ctypes/callproc.c ++++ b/Modules/_ctypes/callproc.c +@@ -1446,34 +1446,8 @@ copy_com_pointer(PyObject *self, PyObject *args) + } + #else + #ifdef __APPLE__ +-#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH +-# ifdef HAVE_BUILTIN_AVAILABLE +-# define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH_RUNTIME \ +- __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +-# else +-# define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH_RUNTIME \ +- (_dyld_shared_cache_contains_path != NULL) +-# endif +-#else +-// Support the deprecated case of compiling on an older macOS version +-static void *libsystem_b_handle; +-static bool (*_dyld_shared_cache_contains_path)(const char *path); +- +-__attribute__((constructor)) void load_dyld_shared_cache_contains_path(void) { +- libsystem_b_handle = dlopen("/usr/lib/libSystem.B.dylib", RTLD_LAZY); +- if (libsystem_b_handle != NULL) { +- _dyld_shared_cache_contains_path = dlsym(libsystem_b_handle, "_dyld_shared_cache_contains_path"); +- } +-} +- +-__attribute__((destructor)) void unload_dyld_shared_cache_contains_path(void) { +- if (libsystem_b_handle != NULL) { +- dlclose(libsystem_b_handle); +- } +-} + #define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH_RUNTIME \ +- _dyld_shared_cache_contains_path != NULL +-#endif ++ __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) + + static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args) + { diff --git a/cpython-unix/patch-ctypes-static-binary.patch b/cpython-unix/patch-ctypes-static-binary.patch new file mode 100644 index 000000000..a44258b27 --- /dev/null +++ b/cpython-unix/patch-ctypes-static-binary.patch @@ -0,0 +1,15 @@ +diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py +--- a/Lib/ctypes/__init__.py ++++ b/Lib/ctypes/__init__.py +@@ -441,7 +441,10 @@ if _os.name == "nt": + elif _sys.platform == "cygwin": + pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) + else: +- pythonapi = PyDLL(None) ++ try: ++ pythonapi = PyDLL(None) ++ except OSError: ++ pythonapi = None + + + if _os.name == "nt": diff --git a/cpython-unix/patch-dont-clear-runshared-13.patch b/cpython-unix/patch-dont-clear-runshared-13.patch new file mode 100644 index 000000000..7dcba0844 --- /dev/null +++ b/cpython-unix/patch-dont-clear-runshared-13.patch @@ -0,0 +1,14 @@ +diff -u 13-a/configure.ac 13-b/configure.ac +--- 13-a/configure.ac 2024-05-08 05:21:00.000000000 -0400 ++++ 13-b/configure.ac 2024-05-19 12:44:04.530770938 -0400 +@@ -1564,10 +1564,6 @@ + fi + AC_MSG_RESULT([$LDLIBRARY]) + +-if test "$cross_compiling" = yes; then +- RUNSHARED= +-fi +- + AC_MSG_CHECKING([HOSTRUNNER]) + AC_ARG_VAR([HOSTRUNNER], [Program to run CPython for the host platform]) + if test -z "$HOSTRUNNER" diff --git a/cpython-unix/patch-dont-clear-runshared-14.patch b/cpython-unix/patch-dont-clear-runshared-14.patch new file mode 100644 index 000000000..e08626602 --- /dev/null +++ b/cpython-unix/patch-dont-clear-runshared-14.patch @@ -0,0 +1,15 @@ +diff --git a/configure.ac b/configure.ac +index bd0221481c5..f2fb52c1efc 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1605,10 +1605,6 @@ else # shared is disabled + fi + AC_MSG_RESULT([$LDLIBRARY]) + +-if test "$cross_compiling" = yes; then +- RUNSHARED= +-fi +- + # HOSTRUNNER - Program to run CPython for the host platform + AC_MSG_CHECKING([HOSTRUNNER]) + if test -z "$HOSTRUNNER" diff --git a/cpython-unix/patch-dont-clear-runshared-legacy.patch b/cpython-unix/patch-dont-clear-runshared-legacy.patch new file mode 100644 index 000000000..2a7d878f8 --- /dev/null +++ b/cpython-unix/patch-dont-clear-runshared-legacy.patch @@ -0,0 +1,15 @@ +diff --git a/configure.ac b/configure.ac +index cc69015b10..0ca05f346a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1192,10 +1192,6 @@ else # shared is disabled + esac + fi + +-if test "$cross_compiling" = yes; then +- RUNSHARED= +-fi +- + AC_MSG_RESULT($LDLIBRARY) + + AC_SUBST(AR) diff --git a/cpython-unix/patch-dont-clear-runshared.patch b/cpython-unix/patch-dont-clear-runshared.patch new file mode 100644 index 000000000..44b825a22 --- /dev/null +++ b/cpython-unix/patch-dont-clear-runshared.patch @@ -0,0 +1,15 @@ +diff --git a/configure.ac b/configure.ac +index 90008bcae1..f75e518856 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1528,10 +1528,6 @@ else # shared is disabled + esac + fi + +-if test "$cross_compiling" = yes; then +- RUNSHARED= +-fi +- + AC_ARG_VAR([HOSTRUNNER], [Program to run CPython for the host platform]) + if test -z "$HOSTRUNNER" + then diff --git a/cpython-unix/patch-getpath-use-base_executable-for-executable_dir-314.patch b/cpython-unix/patch-getpath-use-base_executable-for-executable_dir-314.patch new file mode 100644 index 000000000..581a91a40 --- /dev/null +++ b/cpython-unix/patch-getpath-use-base_executable-for-executable_dir-314.patch @@ -0,0 +1,14 @@ +diff --git a/Modules/getpath.py b/Modules/getpath.py +index ceb605a75c8..164d708ffca 100644 +--- a/Modules/getpath.py ++++ b/Modules/getpath.py +@@ -411,6 +411,9 @@ def search_up(prefix, *landmarks, test=isfile): + if isfile(candidate): + base_executable = candidate + break ++ if base_executable and isfile(base_executable): ++ # Update the executable directory to be based on the resolved base executable ++ executable_dir = real_executable_dir = dirname(base_executable) + # home key found; stop iterating over lines + break + diff --git a/cpython-unix/patch-getpath-use-base_executable-for-executable_dir.patch b/cpython-unix/patch-getpath-use-base_executable-for-executable_dir.patch new file mode 100644 index 000000000..e6c740afe --- /dev/null +++ b/cpython-unix/patch-getpath-use-base_executable-for-executable_dir.patch @@ -0,0 +1,15 @@ +diff --git a/Modules/getpath.py b/Modules/getpath.py +index 1f1bfcb4f64..ff5b18cc385 100644 +--- a/Modules/getpath.py ++++ b/Modules/getpath.py +@@ -398,6 +398,9 @@ def search_up(prefix, *landmarks, test=isfile): + if isfile(candidate): + base_executable = candidate + break ++ if base_executable and isfile(base_executable): ++ # Update the executable directory to be based on the resolved base executable ++ executable_dir = real_executable_dir = dirname(base_executable) + break + else: + venv_prefix = None + diff --git a/cpython-unix/patch-jit-cflags-313.patch b/cpython-unix/patch-jit-cflags-313.patch new file mode 100644 index 000000000..d82d1eed7 --- /dev/null +++ b/cpython-unix/patch-jit-cflags-313.patch @@ -0,0 +1,80 @@ +diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py +index 50b5d923a35..4a71476026f 100644 +--- a/Tools/jit/_targets.py ++++ b/Tools/jit/_targets.py +@@ -10,6 +10,7 @@ + import sys + import tempfile + import typing ++import shlex + + import _llvm + import _schema +@@ -44,6 +45,17 @@ class _Target(typing.Generic[_S, _R]): + stable: bool = False + debug: bool = False + verbose: bool = False ++ cflags: str = "" ++ known_symbols: dict[str, int] = dataclasses.field(default_factory=dict) ++ ++ def _get_nop(self) -> bytes: ++ if re.fullmatch(r"aarch64-.*", self.triple): ++ nop = b"\x1f\x20\x03\xD5" ++ elif re.fullmatch(r"x86_64-.*|i686.*", self.triple): ++ nop = b"\x90" ++ else: ++ raise ValueError(f"NOP not defined for {self.triple}") ++ return nop + + def _compute_digest(self, out: pathlib.Path) -> str: + hasher = hashlib.sha256() +@@ -114,6 +126,7 @@ async def _compile( + return _stencils.StencilGroup() + o = tempdir / f"{opname}.o" + args = [ ++ *shlex.split(self.cflags), + f"--target={self.triple}", + "-DPy_BUILD_CORE_MODULE", + "-D_DEBUG" if self.debug else "-DNDEBUG", +diff --git a/Tools/jit/build.py b/Tools/jit/build.py +index 4a23c6f0afa..618b53804db 100644 +--- a/Tools/jit/build.py ++++ b/Tools/jit/build.py +@@ -22,7 +22,11 @@ + parser.add_argument( + "-v", "--verbose", action="store_true", help="echo commands as they are run" + ) ++ parser.add_argument( ++ "--with-cflags", help="additional flags to pass to the compiler", default="" ++ ) + args = parser.parse_args() + args.target.debug = args.debug + args.target.verbose = args.verbose ++ args.target.cflags = args.with_cflags + args.target.build(pathlib.Path.cwd(), comment=comment, force=args.force) +diff --git a/configure b/configure +index 1cd1f690f7b..7fb6c4adfea 100755 +--- a/configure ++++ b/configure +@@ -8326,7 +8326,7 @@ then : + + else $as_nop + as_fn_append CFLAGS_NODIST " $jit_flags" +- REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host" ++ REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host --with-cflags=\"\$(CONFIGURE_CFLAGS)\"" + JIT_STENCILS_H="jit_stencils.h" + if test "x$Py_DEBUG" = xtrue + then : +diff --git a/configure.ac b/configure.ac +index 3fcb18922c5..616999a96b2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1846,7 +1846,7 @@ AS_VAR_IF([jit_flags], + [], + [AS_VAR_APPEND([CFLAGS_NODIST], [" $jit_flags"]) + AS_VAR_SET([REGEN_JIT_COMMAND], +- ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host"]) ++ ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host --with-cflags=\"\$(CONFIGURE_CFLAGS)\""]) + AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"]) + AS_VAR_IF([Py_DEBUG], + [true], diff --git a/cpython-unix/patch-jit-llvm-version-3.13.patch b/cpython-unix/patch-jit-llvm-version-3.13.patch new file mode 100644 index 000000000..f8f6fd0a7 --- /dev/null +++ b/cpython-unix/patch-jit-llvm-version-3.13.patch @@ -0,0 +1,12 @@ +diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py +--- a/Tools/jit/_llvm.py ++++ b/Tools/jit/_llvm.py +@@ -8,7 +8,7 @@ + import subprocess + import typing + +-_LLVM_VERSION = 18 ++_LLVM_VERSION = 22 + _LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+") + + _P = typing.ParamSpec("_P") diff --git a/cpython-unix/patch-jit-llvm-version-3.14.patch b/cpython-unix/patch-jit-llvm-version-3.14.patch new file mode 100644 index 000000000..48d5e63bd --- /dev/null +++ b/cpython-unix/patch-jit-llvm-version-3.14.patch @@ -0,0 +1,12 @@ +diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py +--- a/Tools/jit/_llvm.py ++++ b/Tools/jit/_llvm.py +@@ -8,7 +8,7 @@ + import subprocess + import typing + +-_LLVM_VERSION = 19 ++_LLVM_VERSION = 22 + _LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+") + + _P = typing.ParamSpec("_P") diff --git a/cpython-unix/patch-macos-link-extension-modules-13.patch b/cpython-unix/patch-macos-link-extension-modules-13.patch new file mode 100644 index 000000000..75b0d7815 --- /dev/null +++ b/cpython-unix/patch-macos-link-extension-modules-13.patch @@ -0,0 +1,12 @@ +diff -u 13-a/Makefile.pre.in 13-b/Makefile.pre.in +--- 13-a/Makefile.pre.in 2024-05-08 05:21:00.000000000 -0400 ++++ 13-b/Makefile.pre.in 2024-05-19 07:55:45.091521909 -0400 +@@ -903,7 +903,7 @@ + $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ + + libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) +- $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ ++ $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + + + libpython$(VERSION).sl: $(LIBRARY_OBJS) diff --git a/cpython-unix/patch-macos-link-extension-modules.patch b/cpython-unix/patch-macos-link-extension-modules.patch new file mode 100644 index 000000000..7273c5295 --- /dev/null +++ b/cpython-unix/patch-macos-link-extension-modules.patch @@ -0,0 +1,12 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -628,7 +628,7 @@ libpython3.so: libpython$(LDVERSION).so + $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ + + libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) +- $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ ++ $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + + + libpython$(VERSION).sl: $(LIBRARY_OBJS) diff --git a/cpython-unix/patch-makesetup-deduplicate-objs.patch b/cpython-unix/patch-makesetup-deduplicate-objs.patch new file mode 100644 index 000000000..95706aea8 --- /dev/null +++ b/cpython-unix/patch-makesetup-deduplicate-objs.patch @@ -0,0 +1,14 @@ +diff --git a/Modules/makesetup b/Modules/makesetup +index 1a767838c9..9f6d2f4396 100755 +--- a/Modules/makesetup ++++ b/Modules/makesetup +@@ -253,6 +253,9 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | + done + done + ++ # Deduplicate OBJS. ++ OBJS=$(echo $OBJS | tr ' ' '\n' | sort -u | xargs) ++ + case $SHAREDMODS in + '') ;; + *) DEFS="SHAREDMODS=$SHAREDMODS$NL$DEFS";; diff --git a/cpython-unix/patch-pgo-file-pool-3.11.patch b/cpython-unix/patch-pgo-file-pool-3.11.patch new file mode 100644 index 000000000..8a3e6bd2e --- /dev/null +++ b/cpython-unix/patch-pgo-file-pool-3.11.patch @@ -0,0 +1,31 @@ +Commit ID: 06a31572dd8267a1806209367de6758237851086 +Change ID: sxvospwszzwyqqtollwuxpnvrytuszuv +Author : Gregory Szorc (2026-03-21 10:19:41) +Committer: Gregory Szorc (2026-03-21 11:30:28) + + Use %Nm filename pattern for PGO + + %p expands to the process ID. While unlikely, PID collisions can occur, + leading to loss of data. + + The %Nm syntax creates a pool of files of size N and the PGO instrumentation + code picks a file (based on PID % N). If there is a conflict, merging mode + is used, which utilizes file locking to update the file in place so data + loss cannot occur. + + See the source code at + https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/profile/InstrProfilingFile.c. + +diff --git a/configure.ac b/configure.ac +index bd851bb626..b138499d1b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1479,7 +1479,7 @@ + PGO_PROF_GEN_FLAG="-fprofile-instr-generate" + PGO_PROF_USE_FLAG="-fprofile-instr-use=code.profclangd" + LLVM_PROF_MERGER="${LLVM_PROFDATA} merge -output=code.profclangd *.profclangr" +- LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"code-%p.profclangr\"" ++ LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"code-%128m.profclangr\"" + LLVM_MERGED_DATA_FILE=code.profclangd + if test $LLVM_PROF_FOUND = not-found + then diff --git a/cpython-unix/patch-pgo-file-pool.patch b/cpython-unix/patch-pgo-file-pool.patch new file mode 100644 index 000000000..8539c51fe --- /dev/null +++ b/cpython-unix/patch-pgo-file-pool.patch @@ -0,0 +1,33 @@ +Commit ID: 1604e0b7c531078d4d707ac6ca639c39b98d521a +Change ID: lzlmtwqoqqunpttzsskyysxzvvlmsnyx +Author : Gregory Szorc (2026-03-21 10:19:41) +Committer: Gregory Szorc (2026-03-21 10:47:53) + + Use %Nm filename pattern for PGO + + %p expands to the process ID. While unlikely, PID collisions can occur, + leading to loss of data. + + The %Nm syntax creates a pool of files of size N and the PGO instrumentation + code picks a file (based on PID % N). If there is a conflict, merging mode + is used, which utilizes file locking to update the file in place so data + loss cannot occur. + + See the source code at + https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/profile/InstrProfilingFile.c. + +diff --git a/configure.ac b/configure.ac +index 82cf6f938a..13db5547e1 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2062,8 +2062,8 @@ + -output=\"\$(shell pwd)/code.profclangd\" + \"\$(shell pwd)\"/*.profclangr + ") +- LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"\$(shell pwd)/code-%p.profclangr\"" +- LLVM_MERGED_DATA_FILE="\$(shell pwd)/code.profclangd" ++ LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"\$(shell pwd)/code-%128m.profclangr\"" ++ LLVM_MERGED_DATA_FILE="\$(shell pwd)/code.profclangd" + if test $LLVM_PROF_FOUND = not-found + then + LLVM_PROF_ERR=yes diff --git a/cpython-unix/patch-pgo-make-targets.patch b/cpython-unix/patch-pgo-make-targets.patch new file mode 100644 index 000000000..a591866f6 --- /dev/null +++ b/cpython-unix/patch-pgo-make-targets.patch @@ -0,0 +1,24 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +index b356f6293e..e584dede51 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -642,12 +642,16 @@ run_profile_task: + build_all_merge_profile: + $(LLVM_PROF_MERGER) + +-# Compile Python binary with profile guided optimization. +-# To force re-running of the profile task, remove the profile-run-stamp file. +-profile-opt: profile-run-stamp ++profile-rebuild-stamp: profile-run-stamp + @echo "Rebuilding with profile guided optimizations:" + -rm -f profile-clean-stamp + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST)" ++ touch $@ ++ ++# Compile Python binary with profile guided optimization. ++# To force re-running of the profile task, remove the profile-run-stamp file. ++.PHONY: profile-opt ++profile-opt: profile-rebuild-stamp + + # Compile and run with gcov + .PHONY=coverage coverage-lcov coverage-report diff --git a/cpython-unix/patch-pgo-print-statistics-3.11.patch b/cpython-unix/patch-pgo-print-statistics-3.11.patch new file mode 100644 index 000000000..f31acac68 --- /dev/null +++ b/cpython-unix/patch-pgo-print-statistics-3.11.patch @@ -0,0 +1,54 @@ +Commit ID: 909b38686377da571cede31fdeb1904adbf63976 +Change ID: sustussslnyzvxnlryomvvsnqlmznlto +Author : Gregory Szorc (2026-03-21 10:11:11) +Committer: Gregory Szorc (2026-03-21 15:34:47) + + Print PGO statistics + + This can be useful for debugging PGO behavior. + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index fa99dd86c4..53044b22a2 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -47,9 +47,11 @@ + GITBRANCH= @GITBRANCH@ + PGO_PROF_GEN_FLAG=@PGO_PROF_GEN_FLAG@ + PGO_PROF_USE_FLAG=@PGO_PROF_USE_FLAG@ ++LLVM_PROFDATA=@LLVM_PROFDATA@ + LLVM_PROF_MERGER=@LLVM_PROF_MERGER@ + LLVM_PROF_FILE=@LLVM_PROF_FILE@ + LLVM_PROF_ERR=@LLVM_PROF_ERR@ ++LLVM_MERGED_DATA_FILE=@LLVM_MERGED_DATA_FILE@ + DTRACE= @DTRACE@ + DFLAGS= @DFLAGS@ + DTRACE_HEADERS= @DTRACE_HEADERS@ +@@ -508,6 +510,8 @@ + # Next, run the profile task to generate the profile information. + $(MAKE) run_profile_task + $(MAKE) build_all_merge_profile ++ # Show details of profile coverage to aid PGO debugging. ++ $(LLVM_PROFDATA) show --detailed-summary --topn=500 $(LLVM_MERGED_DATA_FILE) + # Remove profile generation binary since we are done with it. + $(MAKE) clean-retain-profile + # This is an expensive target to build and it does not have proper +diff --git a/configure.ac b/configure.ac +index ac3be3850a..bd851bb626 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1451,6 +1451,7 @@ + AC_SUBST(LLVM_PROF_FILE) + AC_SUBST(LLVM_PROF_ERR) + AC_SUBST(LLVM_PROFDATA) ++AC_SUBST([LLVM_MERGED_DATA_FILE]) + AC_PATH_TOOL(LLVM_PROFDATA, llvm-profdata, '', ${llvm_path}) + AC_SUBST(LLVM_PROF_FOUND) + if test -n "${LLVM_PROFDATA}" -a -x "${LLVM_PROFDATA}" +@@ -1479,6 +1480,7 @@ + PGO_PROF_USE_FLAG="-fprofile-instr-use=code.profclangd" + LLVM_PROF_MERGER="${LLVM_PROFDATA} merge -output=code.profclangd *.profclangr" + LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"code-%p.profclangr\"" ++ LLVM_MERGED_DATA_FILE=code.profclangd + if test $LLVM_PROF_FOUND = not-found + then + LLVM_PROF_ERR=yes diff --git a/cpython-unix/patch-pgo-print-statistics.patch b/cpython-unix/patch-pgo-print-statistics.patch new file mode 100644 index 000000000..610578c8f --- /dev/null +++ b/cpython-unix/patch-pgo-print-statistics.patch @@ -0,0 +1,54 @@ +Commit ID: c4fe7216d13cc238d74b58b636adc166a7a779f1 +Change ID: nsupxmoltwsmtrxtwxznkqopuwyyrwnk +Author : Gregory Szorc (2026-03-21 10:11:11) +Committer: Gregory Szorc (2026-03-21 15:31:30) + + Print PGO statistics + + This can be useful for debugging PGO behavior. + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 80a1b590c2..2e61cd1019 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -49,9 +49,11 @@ + GITBRANCH= @GITBRANCH@ + PGO_PROF_GEN_FLAG=@PGO_PROF_GEN_FLAG@ + PGO_PROF_USE_FLAG=@PGO_PROF_USE_FLAG@ ++LLVM_PROFDATA=@LLVM_PROFDATA@ + LLVM_PROF_MERGER=@LLVM_PROF_MERGER@ + LLVM_PROF_FILE=@LLVM_PROF_FILE@ + LLVM_PROF_ERR=@LLVM_PROF_ERR@ ++LLVM_MERGED_DATA_FILE=@LLVM_MERGED_DATA_FILE@ + DTRACE= @DTRACE@ + DFLAGS= @DFLAGS@ + DTRACE_HEADERS= @DTRACE_HEADERS@ +@@ -858,6 +860,8 @@ + @ # FIXME: can't run for a cross build + $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) + $(LLVM_PROF_MERGER) ++ # Show details of profile coverage to aid PGO debugging. ++ $(LLVM_PROFDATA) show --detailed-summary --topn=500 $(LLVM_MERGED_DATA_FILE) + # Remove profile generation binary since we are done with it. + $(MAKE) clean-retain-profile + # This is an expensive target to build and it does not have proper +diff --git a/configure.ac b/configure.ac +index a1f4a56709..82cf6f938a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2029,6 +2029,7 @@ + AC_SUBST([LLVM_PROF_FILE]) + AC_SUBST([LLVM_PROF_ERR]) + AC_SUBST([LLVM_PROFDATA]) ++AC_SUBST([LLVM_MERGED_DATA_FILE]) + AC_PATH_TOOL([LLVM_PROFDATA], [llvm-profdata], [''], [${llvm_path}]) + AC_SUBST([LLVM_PROF_FOUND]) + if test -n "${LLVM_PROFDATA}" -a -x "${LLVM_PROFDATA}" +@@ -2062,6 +2063,7 @@ + \"\$(shell pwd)\"/*.profclangr + ") + LLVM_PROF_FILE="LLVM_PROFILE_FILE=\"\$(shell pwd)/code-%p.profclangr\"" ++ LLVM_MERGED_DATA_FILE="\$(shell pwd)/code.profclangd" + if test $LLVM_PROF_FOUND = not-found + then + LLVM_PROF_ERR=yes diff --git a/cpython-unix/patch-posixmodule-remove-system.patch b/cpython-unix/patch-posixmodule-remove-system.patch new file mode 100644 index 000000000..8bb8974cf --- /dev/null +++ b/cpython-unix/patch-posixmodule-remove-system.patch @@ -0,0 +1,18 @@ +diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c +index 12f72f525f..4503c5fc60 100644 +--- a/Modules/posixmodule.c ++++ b/Modules/posixmodule.c +@@ -326,6 +326,13 @@ corresponding Unix manual entries for more information on calls."); + # endif /* _MSC_VER */ + #endif /* ! __WATCOMC__ || __QNX__ */ + ++#if __APPLE__ ++#include ++#if TARGET_OS_IPHONE ++# undef HAVE_SYSTEM ++#endif ++#endif ++ + _Py_IDENTIFIER(__fspath__); + + /*[clinic input] diff --git a/cpython-unix/patch-pwd-remove-conditional.patch b/cpython-unix/patch-pwd-remove-conditional.patch new file mode 100644 index 000000000..df6dc4985 --- /dev/null +++ b/cpython-unix/patch-pwd-remove-conditional.patch @@ -0,0 +1,10 @@ +diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in +index e3e9b96b06..5fb3ab9b56 100644 +--- a/Modules/Setup.bootstrap.in ++++ b/Modules/Setup.bootstrap.in +@@ -32,4 +32,4 @@ _stat _stat.c + _symtable symtablemodule.c + + # for systems without $HOME env, used by site._getuserbase() +-@MODULE_PWD_TRUE@pwd pwdmodule.c ++pwd pwdmodule.c diff --git a/cpython-unix/patch-python-3.14-asyncio-static.patch b/cpython-unix/patch-python-3.14-asyncio-static.patch new file mode 100644 index 000000000..5806200cd --- /dev/null +++ b/cpython-unix/patch-python-3.14-asyncio-static.patch @@ -0,0 +1,71 @@ +From 805dec280697c8f6ee3707d015563430cc704cb7 Mon Sep 17 00:00:00 2001 +From: Kumar Aditya +Date: Wed, 16 Jul 2025 22:09:08 +0530 +Subject: [PATCH] gh-136669: build `_asyncio` as static module (#136670) + +`_asyncio` is now built as a static module so that thread states can be accessed directly via registers and avoids the overhead of function call. +--- + .../Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst | 1 + + Modules/Setup.stdlib.in | 7 ++++++- + Modules/_remote_debugging_module.c | 6 +++--- + 3 files changed, 10 insertions(+), 4 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst + +diff --git a/Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst b/Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst +new file mode 100644 +index 00000000000..0d93397ff35 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst +@@ -0,0 +1 @@ ++:mod:`!_asyncio` is now statically linked for improved performance. +diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in +index 905ea4aa2e5..7f4c4a80673 100644 +--- a/Modules/Setup.stdlib.in ++++ b/Modules/Setup.stdlib.in +@@ -32,7 +32,6 @@ + ############################################################################ + # Modules that should always be present (POSIX and Windows): + @MODULE_ARRAY_TRUE@array arraymodule.c +-@MODULE__ASYNCIO_TRUE@_asyncio _asynciomodule.c + @MODULE__BISECT_TRUE@_bisect _bisectmodule.c + @MODULE__CSV_TRUE@_csv _csv.c + @MODULE__HEAPQ_TRUE@_heapq _heapqmodule.c +@@ -190,3 +189,9 @@ + # Limited API template modules; must be built as shared modules. + @MODULE_XXLIMITED_TRUE@xxlimited xxlimited.c + @MODULE_XXLIMITED_35_TRUE@xxlimited_35 xxlimited_35.c ++ ++ ++# for performance ++*static* ++ ++@MODULE__ASYNCIO_TRUE@_asyncio _asynciomodule.c +diff --git a/Modules/_remote_debugging_module.c b/Modules/_remote_debugging_module.c +index a26e6820f55..a8c9b077a09 100644 +--- a/Modules/_remote_debugging_module.c ++++ b/Modules/_remote_debugging_module.c +@@ -826,7 +826,7 @@ _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle) + } + #elif defined(__linux__) + // On Linux, search for asyncio debug in executable or DLL +- address = search_linux_map_for_section(handle, "AsyncioDebug", "_asyncio.cpython", NULL); ++ address = search_linux_map_for_section(handle, "AsyncioDebug", "python", NULL); + if (address == 0) { + // Error out: 'python' substring covers both executable and DLL + PyObject *exc = PyErr_GetRaisedException(); +@@ -835,10 +835,10 @@ _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle) + } + #elif defined(__APPLE__) && TARGET_OS_OSX + // On macOS, try libpython first, then fall back to python +- address = search_map_for_section(handle, "AsyncioDebug", "_asyncio.cpython", NULL); ++ address = search_map_for_section(handle, "AsyncioDebug", "libpython", NULL); + if (address == 0) { + PyErr_Clear(); +- address = search_map_for_section(handle, "AsyncioDebug", "_asyncio.cpython", NULL); ++ address = search_map_for_section(handle, "AsyncioDebug", "python", NULL); + } + if (address == 0) { + // Error out: 'python' substring covers both executable and DLL +-- +2.50.1 (Apple Git-155) + diff --git a/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter-3.10.patch b/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter-3.10.patch new file mode 100644 index 000000000..33bd662af --- /dev/null +++ b/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter-3.10.patch @@ -0,0 +1,93 @@ +From 579a7cf9498ccfa656dd720a5db8dd6e04e97150 Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Sat, 19 Apr 2025 11:13:40 -0400 +Subject: [PATCH 1/1] configure: add --enable-static-libpython-for-interpreter + +This option changes the behavior of --enable-shared to continue to build +the libpython3.x.so shared library, but not use it for linking the +python3 interpreter executable. Instead, the executable is linked +directly against the libpython .o files as it would be with +--disable-shared [in newer versions of Python]. + +There are two benefits of this change. First, libpython uses +thread-local storage, which is noticeably slower when used in a loaded +module instead of in the main program, because the main program can take +advantage of constant offsets from the thread state pointer but loaded +modules have to dynamically call a function __tls_get_addr() to +potentially allocate their thread-local storage area. (There is another +thread-local storage model for dynamic libraries which mitigates most of +this performance hit, but it comes at the cost of preventing +dlopen("libpython3.x.so"), which is a use case we want to preserve.) + +Second, this improves the user experience around relocatable Python a +little bit, in that we don't need to use an $ORIGIN-relative path to +locate libpython3.x.so, which has some mild benefits around musl (which +does not support $ORIGIN-relative DT_NEEDED, only $ORIGIN-relative +DT_RPATH/DT_RUNPATH), users who want to make the interpreter setuid or +setcap (which prevents processing $ORIGIN), etc. +--- + Makefile.pre.in | 4 +++- + configure.ac | 18 ++++++++++++++++++ + 2 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index fa99dd86c41..84c00a5c071 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -458,6 +458,8 @@ LIBRARY_OBJS= \ + $(LIBRARY_OBJS_OMIT_FROZEN) \ + Python/frozen.o + ++LINK_PYTHON_OBJS=@LINK_PYTHON_OBJS@ ++ + ########################################################################## + # DTrace + +@@ -586,7 +588,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c + + # Build the interpreter + $(BUILDPYTHON): Programs/python.o $(LIBRARY_DEPS) +- $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) ++ $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) + + platform: $(BUILDPYTHON) pybuilddir.txt + $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform +diff --git a/configure.ac b/configure.ac +index ac3be3850a9..a07003a24ed 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1093,6 +1093,17 @@ then + fi + AC_MSG_RESULT($enable_shared) + ++AC_MSG_CHECKING([for --enable-static-libpython-for-interpreter]) ++AC_ARG_ENABLE([static-libpython-for-interpreter], ++ AS_HELP_STRING([--enable-static-libpython-for-interpreter], ++ [even with --enable-shared, statically link libpython into the interpreter (default is to use the shared library)])) ++ ++if test -z "$enable_static_libpython_for_interpreter" ++then ++ enable_static_libpython_for_interpreter="no" ++fi ++AC_MSG_RESULT([$enable_static_libpython_for_interpreter]) ++ + AC_MSG_CHECKING(for --enable-profiling) + AC_ARG_ENABLE(profiling, + AS_HELP_STRING([--enable-profiling], [enable C-level code profiling with gprof (default is no)])) +@@ -1198,6 +1209,13 @@ fi + + AC_MSG_RESULT($LDLIBRARY) + ++if test "$enable_static_libpython_for_interpreter" = "yes"; then ++ LINK_PYTHON_OBJS='$(LIBRARY_OBJS)' ++else ++ LINK_PYTHON_OBJS='$(BLDLIBRARY)' ++fi ++AC_SUBST(LINK_PYTHON_OBJS) ++ + AC_SUBST(AR) + AC_CHECK_TOOLS(AR, ar aal, ar) + +-- +2.39.5 (Apple Git-154) + diff --git a/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter-3.11.patch b/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter-3.11.patch new file mode 100644 index 000000000..4a9a76515 --- /dev/null +++ b/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter-3.11.patch @@ -0,0 +1,69 @@ +From a5182aec2c0597adb8a01298af120809fcf3187b Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Sat, 19 Apr 2025 11:13:40 -0400 +Subject: [PATCH 1/1] configure: add --enable-static-libpython-for-interpreter + +This option changes the behavior of --enable-shared to continue to build +the libpython3.x.so shared library, but not use it for linking the +python3 interpreter executable. Instead, the executable is linked +directly against the libpython .o files as it would be with +--disable-shared. + +There are two benefits of this change. First, libpython uses +thread-local storage, which is noticeably slower when used in a loaded +module instead of in the main program, because the main program can take +advantage of constant offsets from the thread state pointer but loaded +modules have to dynamically call a function __tls_get_addr() to +potentially allocate their thread-local storage area. (There is another +thread-local storage model for dynamic libraries which mitigates most of +this performance hit, but it comes at the cost of preventing +dlopen("libpython3.x.so"), which is a use case we want to preserve.) + +Second, this improves the user experience around relocatable Python a +little bit, in that we don't need to use an $ORIGIN-relative path to +locate libpython3.x.so, which has some mild benefits around musl (which +does not support $ORIGIN-relative DT_NEEDED, only $ORIGIN-relative +DT_RPATH/DT_RUNPATH), users who want to make the interpreter setuid or +setcap (which prevents processing $ORIGIN), etc. +--- + configure.ac | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index ab5e1de6fab..6783c36da4d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1419,6 +1419,17 @@ fi], + [AC_MSG_RESULT(yes)]) + AC_SUBST(STATIC_LIBPYTHON) + ++AC_MSG_CHECKING([for --enable-static-libpython-for-interpreter]) ++AC_ARG_ENABLE([static-libpython-for-interpreter], ++ AS_HELP_STRING([--enable-static-libpython-for-interpreter], ++ [even with --enable-shared, statically link libpython into the interpreter (default is to use the shared library)])) ++ ++if test -z "$enable_static_libpython_for_interpreter" ++then ++ enable_static_libpython_for_interpreter="no" ++fi ++AC_MSG_RESULT([$enable_static_libpython_for_interpreter]) ++ + AC_MSG_CHECKING(for --enable-profiling) + AC_ARG_ENABLE(profiling, + AS_HELP_STRING([--enable-profiling], [enable C-level code profiling with gprof (default is no)])) +@@ -1563,7 +1574,11 @@ if test "$PY_ENABLE_SHARED" = 1 || test "$enable_framework" ; then + LIBRARY_DEPS="\$(LIBRARY) $LIBRARY_DEPS" + fi + # Link Python program to the shared library +- LINK_PYTHON_OBJS='$(BLDLIBRARY)' ++ if test "$enable_static_libpython_for_interpreter" = "yes"; then ++ LINK_PYTHON_OBJS='$(LIBRARY_OBJS)' ++ else ++ LINK_PYTHON_OBJS='$(BLDLIBRARY)' ++ fi + else + if test "$STATIC_LIBPYTHON" = 0; then + # Build Python needs object files but don't need to build +-- +2.39.5 (Apple Git-154) + diff --git a/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter.patch b/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter.patch new file mode 100644 index 000000000..b8d23b52e --- /dev/null +++ b/cpython-unix/patch-python-configure-add-enable-static-libpython-for-interpreter.patch @@ -0,0 +1,86 @@ +From 439f6e6bb62482a98fb6765d723cedea12f3b10f Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Sat, 19 Apr 2025 11:13:40 -0400 +Subject: [PATCH 1/1] configure: add --enable-static-libpython-for-interpreter + +This option changes the behavior of --enable-shared to continue to build +the libpython3.x.so shared library, but not use it for linking the +python3 interpreter executable. Instead, the executable is linked +directly against the libpython .o files as it would be with +--disable-shared. + +There are two benefits of this change. First, libpython uses +thread-local storage, which is noticeably slower when used in a loaded +module instead of in the main program, because the main program can take +advantage of constant offsets from the thread state pointer but loaded +modules have to dynamically call a function __tls_get_addr() to +potentially allocate their thread-local storage area. (There is another +thread-local storage model for dynamic libraries which mitigates most of +this performance hit, but it comes at the cost of preventing +dlopen("libpython3.x.so"), which is a use case we want to preserve.) + +Second, this improves the user experience around relocatable Python a +little bit, in that we don't need to use an $ORIGIN-relative path to +locate libpython3.x.so, which has some mild benefits around musl (which +does not support $ORIGIN-relative DT_NEEDED, only $ORIGIN-relative +DT_RPATH/DT_RUNPATH), users who want to make the interpreter setuid or +setcap (which prevents processing $ORIGIN), etc. +--- + configure.ac | 24 +++++++++++++++++++++--- + 1 file changed, 21 insertions(+), 3 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 004797b5233..a3a5ac1cdce 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1502,6 +1502,17 @@ fi], + [AC_MSG_RESULT([yes])]) + AC_SUBST([STATIC_LIBPYTHON]) + ++AC_MSG_CHECKING([for --enable-static-libpython-for-interpreter]) ++AC_ARG_ENABLE([static-libpython-for-interpreter], ++ AS_HELP_STRING([--enable-static-libpython-for-interpreter], ++ [even with --enable-shared, statically link libpython into the interpreter (default is to use the shared library)])) ++ ++if test -z "$enable_static_libpython_for_interpreter" ++then ++ enable_static_libpython_for_interpreter="no" ++fi ++AC_MSG_RESULT([$enable_static_libpython_for_interpreter]) ++ + AC_MSG_CHECKING([for --enable-profiling]) + AC_ARG_ENABLE([profiling], + AS_HELP_STRING([--enable-profiling], [enable C-level code profiling with gprof (default is no)])) +@@ -1660,7 +1671,11 @@ if test "$PY_ENABLE_SHARED" = 1 || test "$enable_framework" ; then + LIBRARY_DEPS="\$(LIBRARY) $LIBRARY_DEPS" + fi + # Link Python program to the shared library +- LINK_PYTHON_OBJS='$(BLDLIBRARY)' ++ if test "$enable_static_libpython_for_interpreter" = "yes"; then ++ LINK_PYTHON_OBJS='$(LIBRARY_OBJS)' ++ else ++ LINK_PYTHON_OBJS='$(BLDLIBRARY)' ++ fi + else + if test "$STATIC_LIBPYTHON" = 0; then + # Build Python needs object files but don't need to build +@@ -2166,11 +2181,14 @@ if test "$Py_BOLT" = 'true' ; then + fi + fi + +-dnl Enable BOLT of libpython if built. ++dnl Enable BOLT of libpython if built and used by the python3 binary. ++dnl (If it is built but not used, we cannot profile it.) + AC_SUBST([BOLT_BINARIES]) + BOLT_BINARIES='$(BUILDPYTHON)' + AS_VAR_IF([enable_shared], [yes], [ +- BOLT_BINARIES="${BOLT_BINARIES} \$(INSTSONAME)" ++ AS_VAR_IF([enable_static_libpython_for_interpreter], [no], [ ++ BOLT_BINARIES="${BOLT_BINARIES} \$(INSTSONAME)" ++ ]) + ]) + + AC_ARG_VAR( +-- +2.39.5 (Apple Git-154) + diff --git a/cpython-unix/patch-python-configure-hacl-no-simd.patch b/cpython-unix/patch-python-configure-hacl-no-simd.patch new file mode 100644 index 000000000..125aea33f --- /dev/null +++ b/cpython-unix/patch-python-configure-hacl-no-simd.patch @@ -0,0 +1,24 @@ +diff --git a/configure.ac b/configure.ac +index a7b2f62579b..06c0c0c0da0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -7897,8 +7897,7 @@ AC_SUBST([LIBHACL_LDFLAGS]) + # The SIMD files use aligned_alloc, which is not available on older versions of + # Android. + # The *mmintrin.h headers are x86-family-specific, so can't be used on WASI. +-if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "WASI" || \ +- { test -n "$ANDROID_API_LEVEL" && test "$ANDROID_API_LEVEL" -ge 28; } ++if false + then + dnl This can be extended here to detect e.g. Power8, which HACL* should also support. + AX_CHECK_COMPILE_FLAG([-msse -msse2 -msse3 -msse4.1 -msse4.2],[ +@@ -7930,8 +7929,7 @@ AC_SUBST([LIBHACL_BLAKE2_SIMD128_OBJS]) + # Although AVX support is not guaranteed on Android + # (https://developer.android.com/ndk/guides/abis#86-64), this is safe because we do a + # runtime CPUID check. +-if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "WASI" || \ +- { test -n "$ANDROID_API_LEVEL" && test "$ANDROID_API_LEVEL" -ge 28; } ++if false + then + AX_CHECK_COMPILE_FLAG([-mavx2],[ + [LIBHACL_SIMD256_FLAGS="-mavx2"] diff --git a/cpython-unix/patch-python-getpath-backport-3.11.patch b/cpython-unix/patch-python-getpath-backport-3.11.patch new file mode 100644 index 000000000..9f79b3619 --- /dev/null +++ b/cpython-unix/patch-python-getpath-backport-3.11.patch @@ -0,0 +1,109 @@ +From fe34b57349c57df0f1443f622985b872807ad1a0 Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Tue, 16 Dec 2025 09:33:06 -0500 +Subject: [PATCH 1/1] Backport relevant parts of 3.14 getpath.c to 3.11 +Forwarded: not-needed + +--- + Modules/getpath.c | 42 ++++++++++++++++-------------------------- + configure.ac | 2 +- + pyconfig.h.in | 3 +++ + 3 files changed, 20 insertions(+), 27 deletions(-) + +diff --git a/Modules/getpath.c b/Modules/getpath.c +index 61d654065fd..7457f70109f 100644 +--- a/Modules/getpath.c ++++ b/Modules/getpath.c +@@ -18,6 +18,10 @@ + # include + #endif + ++#ifdef HAVE_DLFCN_H ++# include ++#endif ++ + /* Reference the precompiled getpath.py */ + #include "../Python/frozen_modules/getpath.h" + +@@ -762,39 +766,25 @@ progname_to_dict(PyObject *dict, const char *key) + static int + library_to_dict(PyObject *dict, const char *key) + { ++/* macOS framework builds do not link against a libpython dynamic library, but ++ instead link against a macOS Framework. */ ++#if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK) ++ + #ifdef MS_WINDOWS + extern HMODULE PyWin_DLLhModule; + if (PyWin_DLLhModule) { + return winmodule_to_dict(dict, key, PyWin_DLLhModule); + } +-#elif defined(WITH_NEXT_FRAMEWORK) +- static char modPath[MAXPATHLEN + 1]; +- static int modPathInitialized = -1; +- if (modPathInitialized < 0) { +- modPathInitialized = 0; +- +- /* On Mac OS X we have a special case if we're running from a framework. +- This is because the python home should be set relative to the library, +- which is in the framework, not relative to the executable, which may +- be outside of the framework. Except when we're in the build +- directory... */ +- NSSymbol symbol = NSLookupAndBindSymbol("_Py_Initialize"); +- if (symbol != NULL) { +- NSModule pythonModule = NSModuleForSymbol(symbol); +- if (pythonModule != NULL) { +- /* Use dylib functions to find out where the framework was loaded from */ +- const char *path = NSLibraryNameForModule(pythonModule); +- if (path) { +- strncpy(modPath, path, MAXPATHLEN); +- modPathInitialized = 1; +- } +- } +- } +- } +- if (modPathInitialized > 0) { +- return decode_to_dict(dict, key, modPath); ++#endif ++ ++#if HAVE_DLADDR ++ Dl_info libpython_info; ++ if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) { ++ return decode_to_dict(dict, key, libpython_info.dli_fname); + } + #endif ++#endif ++ + return PyDict_SetItemString(dict, key, Py_None) == 0; + } + +diff --git a/configure.ac b/configure.ac +index 7b4000fa9c3..e1c537fd153 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -4594,7 +4594,7 @@ fi + # checks for library functions + AC_CHECK_FUNCS([ \ + accept4 alarm bind_textdomain_codeset chmod chown clock close_range confstr \ +- copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ ++ copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ + faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ + fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + gai_strerror getegid getentropy geteuid getgid getgrgid getgrgid_r \ +diff --git a/pyconfig.h.in b/pyconfig.h.in +index a8c35bba448..422a1cd0878 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -281,6 +281,9 @@ + /* Define if you have the 'dirfd' function or macro. */ + #undef HAVE_DIRFD + ++/* Define to 1 if you have the 'dladdr' function. */ ++#undef HAVE_DLADDR ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_DLFCN_H + +-- +2.50.1 (Apple Git-155) + diff --git a/cpython-unix/patch-python-getpath-backport-3.12.patch b/cpython-unix/patch-python-getpath-backport-3.12.patch new file mode 100644 index 000000000..79085f6f1 --- /dev/null +++ b/cpython-unix/patch-python-getpath-backport-3.12.patch @@ -0,0 +1,110 @@ +From 3cf84081c92fe6ea1edc24aa579b34a0934b3c2d Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Tue, 16 Dec 2025 09:32:12 -0500 +Subject: [PATCH 1/1] Backport relevant parts of 3.14 getpath.c to 3.12 +Forwarded: not-needed + +--- + Modules/getpath.c | 42 +++++++++++++++--------------------------- + configure.ac | 2 +- + pyconfig.h.in | 3 +++ + 3 files changed, 19 insertions(+), 28 deletions(-) + +diff --git a/Modules/getpath.c b/Modules/getpath.c +index 0a310000751..9cea2d7bd20 100644 +--- a/Modules/getpath.c ++++ b/Modules/getpath.c +@@ -18,6 +18,10 @@ + # include + #endif + ++#ifdef HAVE_DLFCN_H ++# include ++#endif ++ + /* Reference the precompiled getpath.py */ + #include "../Python/frozen_modules/getpath.h" + +@@ -752,41 +756,25 @@ progname_to_dict(PyObject *dict, const char *key) + static int + library_to_dict(PyObject *dict, const char *key) + { ++/* macOS framework builds do not link against a libpython dynamic library, but ++ instead link against a macOS Framework. */ ++#if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK) ++ + #ifdef MS_WINDOWS +-#ifdef Py_ENABLE_SHARED + extern HMODULE PyWin_DLLhModule; + if (PyWin_DLLhModule) { + return winmodule_to_dict(dict, key, PyWin_DLLhModule); + } + #endif +-#elif defined(WITH_NEXT_FRAMEWORK) +- static char modPath[MAXPATHLEN + 1]; +- static int modPathInitialized = -1; +- if (modPathInitialized < 0) { +- modPathInitialized = 0; +- +- /* On Mac OS X we have a special case if we're running from a framework. +- This is because the python home should be set relative to the library, +- which is in the framework, not relative to the executable, which may +- be outside of the framework. Except when we're in the build +- directory... */ +- NSSymbol symbol = NSLookupAndBindSymbol("_Py_Initialize"); +- if (symbol != NULL) { +- NSModule pythonModule = NSModuleForSymbol(symbol); +- if (pythonModule != NULL) { +- /* Use dylib functions to find out where the framework was loaded from */ +- const char *path = NSLibraryNameForModule(pythonModule); +- if (path) { +- strncpy(modPath, path, MAXPATHLEN); +- modPathInitialized = 1; +- } +- } +- } +- } +- if (modPathInitialized > 0) { +- return decode_to_dict(dict, key, modPath); ++ ++#if HAVE_DLADDR ++ Dl_info libpython_info; ++ if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) { ++ return decode_to_dict(dict, key, libpython_info.dli_fname); + } + #endif ++#endif ++ + return PyDict_SetItemString(dict, key, Py_None) == 0; + } + +diff --git a/configure.ac b/configure.ac +index 1a02d19f1b2..1177525f88f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -4920,7 +4920,7 @@ fi + # checks for library functions + AC_CHECK_FUNCS([ \ + accept4 alarm bind_textdomain_codeset chmod chown clock close_range confstr \ +- copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ ++ copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ + faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ + fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + gai_strerror getegid getentropy geteuid getgid getgrgid getgrgid_r \ +diff --git a/pyconfig.h.in b/pyconfig.h.in +index df4d29fe549..f2f09a7ec8c 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -280,6 +280,9 @@ + /* Define if you have the 'dirfd' function or macro. */ + #undef HAVE_DIRFD + ++/* Define to 1 if you have the 'dladdr' function. */ ++#undef HAVE_DLADDR ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_DLFCN_H + +-- +2.50.1 (Apple Git-155) + diff --git a/cpython-unix/patch-python-getpath-backport-3.13.patch b/cpython-unix/patch-python-getpath-backport-3.13.patch new file mode 100644 index 000000000..d6c853deb --- /dev/null +++ b/cpython-unix/patch-python-getpath-backport-3.13.patch @@ -0,0 +1,109 @@ +From 3342daa091b0a8e6cf15fdaaa2c6fc2f9dcc8a60 Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Tue, 16 Dec 2025 09:29:55 -0500 +Subject: [PATCH 1/1] Backport relevant parts of 3.14 getpath.c to 3.13 +Forwarded: not-needed + +--- + Modules/getpath.c | 38 +++++++++++++++----------------------- + configure.ac | 2 +- + pyconfig.h.in | 3 +++ + 3 files changed, 19 insertions(+), 24 deletions(-) + +diff --git a/Modules/getpath.c b/Modules/getpath.c +index d0128b20fae..50612432027 100644 +--- a/Modules/getpath.c ++++ b/Modules/getpath.c +@@ -17,10 +17,13 @@ + #endif + + #ifdef __APPLE__ +-# include + # include + #endif + ++#ifdef HAVE_DLFCN_H ++# include ++#endif ++ + /* Reference the precompiled getpath.py */ + #include "Python/frozen_modules/getpath.h" + +@@ -803,36 +806,25 @@ progname_to_dict(PyObject *dict, const char *key) + static int + library_to_dict(PyObject *dict, const char *key) + { ++/* macOS framework builds do not link against a libpython dynamic library, but ++ instead link against a macOS Framework. */ ++#if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK) ++ + #ifdef MS_WINDOWS +-#ifdef Py_ENABLE_SHARED + extern HMODULE PyWin_DLLhModule; + if (PyWin_DLLhModule) { + return winmodule_to_dict(dict, key, PyWin_DLLhModule); + } + #endif +-#elif defined(WITH_NEXT_FRAMEWORK) +- static char modPath[MAXPATHLEN + 1]; +- static int modPathInitialized = -1; +- if (modPathInitialized < 0) { +- modPathInitialized = 0; +- +- /* On Mac OS X we have a special case if we're running from a framework. +- This is because the python home should be set relative to the library, +- which is in the framework, not relative to the executable, which may +- be outside of the framework. Except when we're in the build +- directory... */ +- Dl_info pythonInfo; +- if (dladdr(&Py_Initialize, &pythonInfo)) { +- if (pythonInfo.dli_fname) { +- strncpy(modPath, pythonInfo.dli_fname, MAXPATHLEN); +- modPathInitialized = 1; +- } +- } +- } +- if (modPathInitialized > 0) { +- return decode_to_dict(dict, key, modPath); ++ ++#if HAVE_DLADDR ++ Dl_info libpython_info; ++ if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) { ++ return decode_to_dict(dict, key, libpython_info.dli_fname); + } + #endif ++#endif ++ + return PyDict_SetItemString(dict, key, Py_None) == 0; + } + +diff --git a/configure.ac b/configure.ac +index 94776540d1b..fbc6cfd0de4 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -5217,7 +5217,7 @@ fi + # checks for library functions + AC_CHECK_FUNCS([ \ + accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ +- copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ ++ copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ + faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ + fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ +diff --git a/pyconfig.h.in b/pyconfig.h.in +index e18a6426b06..10b0cc1dafd 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -284,6 +284,9 @@ + /* Define if you have the 'dirfd' function or macro. */ + #undef HAVE_DIRFD + ++/* Define to 1 if you have the 'dladdr' function. */ ++#undef HAVE_DLADDR ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_DLFCN_H + +-- +2.50.1 (Apple Git-155) + diff --git a/cpython-unix/patch-python-getpath-library.patch b/cpython-unix/patch-python-getpath-library.patch new file mode 100644 index 000000000..5c500f386 --- /dev/null +++ b/cpython-unix/patch-python-getpath-library.patch @@ -0,0 +1,151 @@ +From 60d6a76dcee2a5647d69874d9b5c24f701a6722d Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Mon, 1 Dec 2025 14:11:43 -0500 +Subject: [PATCH 1/1] getpath: Fix library detection and canonicalize paths on + Linux +Forwarded: no + +The code in getpath.py to look for the stdlib relative to the Python +library did not work in the common layout where libpython itself is in +the lib/ directory; it added an extra lib/ segment. It is also equally +applicable and useful when statically linking libpython into bin/python; +in both cases, we want to go up a directory and then look into +lib/python3.x/. Add an extra dirname() call in getpath.py, and +unconditionally attempt to fill in the "library" variable in getpath.c, +even on builds that are statically linking libpython. + +Also, we want to use the realpath'd version of the library's path to +locate the standard library, particularly in the case where the library +is a symlink to an executable statically linking libpython. On macOS +dyld, this is done automatically. On glibc and musl, we often get +relative paths and they are not canonicalized, so instead, use +/proc/self/maps to find the file where libpython is coming from. + +(We could instead use the origin, which is canonicalized, but there is +no safe API on glibc to read it and no API at all on musl. Note that and +glibc also uses procfs to do so; see discussion at +https://sourceware.org/bugzilla/show_bug.cgi?id=25263) + +Finally, switch the target address for lookups to the current function's +return address. This avoids issues on some build configurations and +platforms where the addresses Python library functions are behind a +layer of indirection like the PLT. (See also the BUGS section of Linux +man-pages' dladdr(3).) +--- + Modules/getpath.c | 62 +++++++++++++++++++++++++++++++++++++++++----- + Modules/getpath.py | 4 +-- + 2 files changed, 58 insertions(+), 8 deletions(-) + +diff --git a/Modules/getpath.c b/Modules/getpath.c +index 1e75993480a..347c21e7387 100644 +--- a/Modules/getpath.c ++++ b/Modules/getpath.c +@@ -802,14 +802,19 @@ progname_to_dict(PyObject *dict, const char *key) + } + + ++static void ++fclose_cleanup(FILE **pf) { ++ if (*pf) { ++ fclose(*pf); ++ *pf = NULL; ++ } ++} ++ ++ + /* Add the runtime library's path to the dict */ + static int + library_to_dict(PyObject *dict, const char *key) + { +-/* macOS framework builds do not link against a libpython dynamic library, but +- instead link against a macOS Framework. */ +-#if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK) +- + #ifdef MS_WINDOWS + extern HMODULE PyWin_DLLhModule; + if (PyWin_DLLhModule) { +@@ -817,12 +822,57 @@ library_to_dict(PyObject *dict, const char *key) + } + #endif + ++ const void *target = __builtin_extract_return_addr( ++ __builtin_return_address(0) ++ ); ++ ++#ifdef __linux__ ++ /* Linux libcs do not reliably report the realpath in dladdr dli_fname and ++ * sometimes return relative paths, especially if the returned object is ++ * the main program itself. However, /proc/self/maps will give absolute ++ * realpaths (from the kernel, for the same reason that /proc/self/exe is ++ * canonical), so try to parse and look it up there. (dyld seems to ++ * reliably report the canonical path, so doing this matches the behavior ++ * on macOS.) */ ++ ++ __attribute__((cleanup(fclose_cleanup))) ++ FILE *maps = fopen("/proc/self/maps", "r"); ++ if (maps != NULL) { ++ /* See implementation in fs/proc/task_mmu.c for spacing. The pathname ++ * is the last field and has any \n characters escaped, so we can read ++ * until \n. Note that the filename may have " (deleted)" appended; ++ * we don't bother to handle that specially as the only user of this ++ * value calls dirname() anyway. ++ * TODO(geofft): Consider using PROCMAP_QUERY if supported. ++ */ ++ uintptr_t low, high; ++ char rest[PATH_MAX + 1]; ++ while (fscanf(maps, "%lx-%lx %*s %*s %*s %*s", &low, &high) == 2) { ++ if (fgets(rest, PATH_MAX + 1, maps) == NULL) { ++ break; ++ } ++ if (strlen(rest) >= PATH_MAX) { ++ // If the line is too long our parsing will be out of sync. ++ break; ++ } ++ ++ if (low <= (uintptr_t)target && (uintptr_t)target < high) { ++ // Skip past padding spaces in the filename. ++ const char *filename = rest + strspn(rest, " "); ++ if (filename[0] == '/') { ++ return decode_to_dict(dict, key, filename); ++ } ++ break; ++ } ++ } ++ } ++#endif ++ + #if HAVE_DLADDR + Dl_info libpython_info; +- if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) { ++ if (dladdr(target, &libpython_info) && libpython_info.dli_fname) { + return decode_to_dict(dict, key, libpython_info.dli_fname); + } +-#endif + #endif + + return PyDict_SetItemString(dict, key, Py_None) == 0; +diff --git a/Modules/getpath.py b/Modules/getpath.py +index b89d7427e3f..8c431e53be2 100644 +--- a/Modules/getpath.py ++++ b/Modules/getpath.py +@@ -436,7 +436,7 @@ def search_up(prefix, *landmarks, test=isfile): + + if not executable_dir and os_name == 'darwin' and library: + # QUIRK: macOS checks adjacent to its library early +- library_dir = dirname(library) ++ library_dir = dirname(dirname(library)) + if any(isfile(joinpath(library_dir, p)) for p in STDLIB_LANDMARKS): + # Exceptions here should abort the whole process (to match + # previous behavior) +@@ -570,7 +570,7 @@ def search_up(prefix, *landmarks, test=isfile): + + # First try to detect prefix by looking alongside our runtime library, if known + if library and not prefix: +- library_dir = dirname(library) ++ library_dir = dirname(dirname(library)) + if ZIP_LANDMARK: + if os_name == 'nt': + # QUIRK: Windows does not search up for ZIP file +-- +2.50.1 (Apple Git-155) + diff --git a/cpython-unix/patch-python-link-modules-3.10.patch b/cpython-unix/patch-python-link-modules-3.10.patch new file mode 100644 index 000000000..58247aebd --- /dev/null +++ b/cpython-unix/patch-python-link-modules-3.10.patch @@ -0,0 +1,12 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -563,7 +563,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c + + # Build the interpreter + $(BUILDPYTHON): Programs/python.o $(LIBRARY_DEPS) +- $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) ++ $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(SYSLIBS) + + platform: $(BUILDPYTHON) pybuilddir.txt + $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform diff --git a/cpython-unix/patch-python-link-modules-3.11.patch b/cpython-unix/patch-python-link-modules-3.11.patch new file mode 100644 index 000000000..8bc7aee63 --- /dev/null +++ b/cpython-unix/patch-python-link-modules-3.11.patch @@ -0,0 +1,13 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +index b356f6293e..89fddd4d4e 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -702,7 +702,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c + + # Build the interpreter + $(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) +- $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) ++ $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(SYSLIBS) + + platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt + $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform diff --git a/cpython-unix/patch-python-link-modules-3.15.patch b/cpython-unix/patch-python-link-modules-3.15.patch new file mode 100644 index 000000000..5225bb6f9 --- /dev/null +++ b/cpython-unix/patch-python-link-modules-3.15.patch @@ -0,0 +1,13 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 120a6add385..4d8abc5256a 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -990,7 +990,7 @@ clinic-tests: check-clean-src $(srcdir)/Lib/test/clinic.test.c + + # Build the interpreter + $(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) +- $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) ++ $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(SYSLIBS) + + platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt + $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform diff --git a/cpython-unix/patch-python-relative-build-details.patch b/cpython-unix/patch-python-relative-build-details.patch new file mode 100644 index 000000000..ad2a65334 --- /dev/null +++ b/cpython-unix/patch-python-relative-build-details.patch @@ -0,0 +1,53 @@ +From 5bb9be38eae4afe6246691a7affe0c7681f45ca2 Mon Sep 17 00:00:00 2001 +From: Geoffrey Thomas +Date: Mon, 6 Oct 2025 18:07:47 -0400 +Subject: [PATCH 1/1] Makefile: Generate relative paths for build-details.json + +--- + Makefile.pre.in | 2 +- + Tools/build/generate-build-details.py | 5 ++++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 764eef5be3e..4dbbf8ad8bc 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -1004,7 +1004,7 @@ pybuilddir.txt: $(PYTHON_FOR_BUILD_DEPS) + fi + + build-details.json: pybuilddir.txt +- $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/generate-build-details.py `cat pybuilddir.txt`/build-details.json ++ $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/generate-build-details.py --relative-paths --config-file-path $(LIBDEST)/build-details.json `cat pybuilddir.txt`/build-details.json + + # Build static library + $(LIBRARY): $(LIBRARY_OBJS) +diff --git a/Tools/build/generate-build-details.py b/Tools/build/generate-build-details.py +index ed9ab2844d2..8d086ce3b32 100644 +--- a/Tools/build/generate-build-details.py ++++ b/Tools/build/generate-build-details.py +@@ -131,11 +131,12 @@ def generate_data(schema_version: str) -> collections.defaultdict[str, Any]: + + + def make_paths_relative(data: dict[str, Any], config_path: str | None = None) -> None: ++ base_prefix = data['base_prefix'] ++ + # Make base_prefix relative to the config_path directory + if config_path: + data['base_prefix'] = relative_path(data['base_prefix'], + os.path.dirname(config_path)) +- base_prefix = data['base_prefix'] + + # Update path values to make them relative to base_prefix + PATH_KEYS = ( +@@ -203,6 +204,8 @@ def main() -> None: + if args.relative_paths: + make_paths_relative(data, args.config_file_path) + ++ print(f"generate-build-details debug: {sysconfig=} {sysconfig.get_platform()=}", file=sys.stderr) ++ + json_output = json.dumps(data, indent=2) + with open(args.location, 'w', encoding='utf-8') as f: + f.write(json_output) +-- +2.39.5 (Apple Git-154) + diff --git a/cpython-unix/patch-readline-libedit-completer-delims.patch b/cpython-unix/patch-readline-libedit-completer-delims.patch new file mode 100644 index 000000000..0a52a649e --- /dev/null +++ b/cpython-unix/patch-readline-libedit-completer-delims.patch @@ -0,0 +1,72 @@ +diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py +index 835280f2281..6c2726d3209 100644 +--- a/Lib/test/test_readline.py ++++ b/Lib/test/test_readline.py +@@ -5,6 +5,7 @@ + import os + import sys + import tempfile ++import textwrap + import unittest + from test.support import verbose + from test.support.import_helper import import_module +@@ -163,6 +164,25 @@ def test_auto_history_disabled(self): + # end, so don't expect it in the output. + self.assertIn(b"History length: 0", output) + ++ def test_set_complete_delims(self): ++ script = textwrap.dedent(""" ++ import readline ++ def complete(text, state): ++ if state == 0 and text == "$": ++ return "$complete" ++ return None ++ if "libedit" in getattr(readline, "__doc__", ""): ++ readline.parse_and_bind(r'bind "\\t" rl_complete') ++ else: ++ readline.parse_and_bind(r'"\\t": complete') ++ readline.set_completer_delims(" \\t\\n") ++ readline.set_completer(complete) ++ print(input()) ++ """) ++ ++ output = run_pty(script, input=b"$\t\n") ++ self.assertIn(b"$complete", output) ++ + def test_nonascii(self): + loc = locale.setlocale(locale.LC_CTYPE, None) + if loc in ('C', 'POSIX'): +diff --git a/Modules/readline.c b/Modules/readline.c +index 8c7f526d418..1e13a0e6e06 100644 +--- a/Modules/readline.c ++++ b/Modules/readline.c +@@ -572,6 +572,13 @@ readline_set_completer_delims(PyObject *module, PyObject *string) + if (break_chars) { + free(completer_word_break_characters); + completer_word_break_characters = break_chars; ++#ifdef WITH_EDITLINE ++ rl_basic_word_break_characters = break_chars; ++#else ++ if (using_libedit_emulation) { ++ rl_basic_word_break_characters = break_chars; ++ } ++#endif + rl_completer_word_break_characters = break_chars; + Py_RETURN_NONE; + } +@@ -1260,6 +1267,15 @@ setup_readline(readlinestate *mod_state) + completer_word_break_characters = + strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?"); + /* All nonalphanums except '.' */ ++#ifdef WITH_EDITLINE ++ // libedit uses rl_basic_word_break_characters instead of ++ // rl_completer_word_break_characters as complete delimiter ++ rl_basic_word_break_characters = completer_word_break_characters; ++#else ++ if (using_libedit_emulation) { ++ rl_basic_word_break_characters = completer_word_break_characters; ++ } ++#endif + rl_completer_word_break_characters = completer_word_break_characters; + + mod_state->begidx = PyLong_FromLong(0L); diff --git a/cpython-unix/patch-readline-libedit-completions.patch b/cpython-unix/patch-readline-libedit-completions.patch new file mode 100644 index 000000000..b4c83939b --- /dev/null +++ b/cpython-unix/patch-readline-libedit-completions.patch @@ -0,0 +1,52 @@ +diff --git a/Modules/readline.c b/Modules/readline.c +index 27b89de7279..8c7f526d418 100644 +--- a/Modules/readline.c ++++ b/Modules/readline.c +@@ -440,7 +440,7 @@ readline_set_completion_display_matches_hook_impl(PyObject *module, + default completion display. */ + rl_completion_display_matches_hook = + readlinestate_global->completion_display_matches_hook ? +-#if defined(_RL_FUNCTION_TYPEDEF) ++#if defined(HAVE_RL_COMPDISP_FUNC_T) + (rl_compdisp_func_t *)on_completion_display_matches_hook : 0; + #else + (VFunction *)on_completion_display_matches_hook : 0; +diff --git a/configure.ac b/configure.ac +index e1cbb7c7fbe..629b7b76c3c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -5918,6 +5918,20 @@ if test "$py_cv_lib_readline" = yes; then + AC_CHECK_LIB($LIBREADLINE, append_history, + AC_DEFINE(HAVE_RL_APPEND_HISTORY, 1, + [Define if readline supports append_history]),,$READLINE_LIBS) ++ ++ # in readline as well as newer editline (April 2023) ++ AC_CHECK_TYPE([rl_compdisp_func_t], ++ [AC_DEFINE([HAVE_RL_COMPDISP_FUNC_T], [1], ++ [Define if readline supports rl_compdisp_func_t])], ++ [], ++ [ ++#include /* Must be first for Gnu Readline */ ++#ifdef WITH_EDITLINE ++# include ++#else ++# include ++#endif ++ ]) + fi + + # End of readline checks: restore LIBS +diff --git a/pyconfig.h.in b/pyconfig.h.in +index 0536047f573..94d02e14c44 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -968,6 +968,9 @@ + /* Define if you can turn off readline's signal handling. */ + #undef HAVE_RL_CATCH_SIGNAL + ++/* Define if readline supports rl_compdisp_func_t */ ++#undef HAVE_RL_COMPDISP_FUNC_T ++ + /* Define if you have readline 2.2 */ + #undef HAVE_RL_COMPLETION_APPEND_CHARACTER + diff --git a/cpython-unix/patch-sem-clockwait-weak-3.10.patch b/cpython-unix/patch-sem-clockwait-weak-3.10.patch new file mode 100644 index 000000000..af3813168 --- /dev/null +++ b/cpython-unix/patch-sem-clockwait-weak-3.10.patch @@ -0,0 +1,113 @@ +--- a/Python/thread_pthread.h ++++ b/Python/thread_pthread.h +@@ -87,6 +87,18 @@ + #endif + #endif + ++/* When building against glibc headers older than 2.30, configure cannot ++ * detect sem_clockwait. Declare it as a weak symbol so it ++ * resolves to NULL on old glibc and to the real function on glibc 2.30+. ++ * This enables monotonic clock waits at runtime when available, preventing ++ * hangs when the system clock jumps backward (e.g., NTP sync). */ ++#if defined(__linux__) && !defined(HAVE_SEM_CLOCKWAIT) ++#include ++__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t, ++ const struct timespec *); ++#define HAVE_SEM_CLOCKWAIT 1 ++#define _Py_SEM_CLOCKWAIT_WEAK 1 ++#endif + + /* Whether or not to use semaphores directly rather than emulating them with + * mutexes and condition variables: +@@ -443,9 +455,7 @@ + sem_t *thelock = (sem_t *)lock; + int status, error = 0; + struct timespec ts; +-#ifndef HAVE_SEM_CLOCKWAIT + _PyTime_t deadline = 0; +-#endif + + (void) error; /* silence unused-but-set-variable warning */ + dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n", +@@ -455,29 +465,35 @@ + Py_FatalError("Timeout larger than PY_TIMEOUT_MAX"); + } + +- if (microseconds > 0) { +-#ifdef HAVE_SEM_CLOCKWAIT +- monotonic_abs_timeout(microseconds, &ts); ++#ifdef _Py_SEM_CLOCKWAIT_WEAK ++ int use_clockwait = (sem_clockwait != NULL); + #else +- MICROSECONDS_TO_TIMESPEC(microseconds, ts); ++ int use_clockwait = 1; ++#endif + +- if (!intr_flag) { +- /* cannot overflow thanks to (microseconds > PY_TIMEOUT_MAX) +- check done above */ +- _PyTime_t timeout = _PyTime_FromNanoseconds(microseconds * 1000); +- deadline = _PyTime_GetMonotonicClock() + timeout; ++ if (microseconds > 0) { ++ if (use_clockwait) { ++ monotonic_abs_timeout(microseconds, &ts); ++ } else { ++ MICROSECONDS_TO_TIMESPEC(microseconds, ts); ++ ++ if (!intr_flag) { ++ /* cannot overflow thanks to (microseconds > PY_TIMEOUT_MAX) ++ check done above */ ++ _PyTime_t timeout = _PyTime_FromNanoseconds(microseconds * 1000); ++ deadline = _PyTime_GetMonotonicClock() + timeout; ++ } + } +-#endif + } + + while (1) { + if (microseconds > 0) { +-#ifdef HAVE_SEM_CLOCKWAIT +- status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, +- &ts)); +-#else +- status = fix_status(sem_timedwait(thelock, &ts)); +-#endif ++ if (use_clockwait) { ++ status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, ++ &ts)); ++ } else { ++ status = fix_status(sem_timedwait(thelock, &ts)); ++ } + } + else if (microseconds == 0) { + status = fix_status(sem_trywait(thelock)); +@@ -494,8 +510,7 @@ + + // sem_clockwait() uses an absolute timeout, there is no need + // to recompute the relative timeout. +-#ifndef HAVE_SEM_CLOCKWAIT +- if (microseconds > 0) { ++ if (!use_clockwait && microseconds > 0) { + /* wait interrupted by a signal (EINTR): recompute the timeout */ + _PyTime_t dt = deadline - _PyTime_GetMonotonicClock(); + if (dt < 0) { +@@ -516,18 +531,13 @@ + microseconds = 0; + } + } +-#endif + } + + /* Don't check the status if we're stopping because of an interrupt. */ + if (!(intr_flag && status == EINTR)) { + if (microseconds > 0) { + if (status != ETIMEDOUT) { +-#ifdef HAVE_SEM_CLOCKWAIT +- CHECK_STATUS("sem_clockwait"); +-#else +- CHECK_STATUS("sem_timedwait"); +-#endif ++ CHECK_STATUS(use_clockwait ? "sem_clockwait" : "sem_timedwait"); + } + } + else if (microseconds == 0) { diff --git a/cpython-unix/patch-sem-clockwait-weak-3.11.patch b/cpython-unix/patch-sem-clockwait-weak-3.11.patch new file mode 100644 index 000000000..6951fb4d3 --- /dev/null +++ b/cpython-unix/patch-sem-clockwait-weak-3.11.patch @@ -0,0 +1,103 @@ +--- a/Python/thread_pthread.h ++++ b/Python/thread_pthread.h +@@ -89,6 +89,18 @@ + #endif + #endif + ++/* When building against glibc headers older than 2.30, configure cannot ++ * detect sem_clockwait. Declare it as a weak symbol so it ++ * resolves to NULL on old glibc and to the real function on glibc 2.30+. ++ * This enables monotonic clock waits at runtime when available, preventing ++ * hangs when the system clock jumps backward (e.g., NTP sync). */ ++#if defined(__linux__) && !defined(HAVE_SEM_CLOCKWAIT) ++#include ++__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t, ++ const struct timespec *); ++#define HAVE_SEM_CLOCKWAIT 1 ++#define _Py_SEM_CLOCKWAIT_WEAK 1 ++#endif + + /* Whether or not to use semaphores directly rather than emulating them with + * mutexes and condition variables: +@@ -463,32 +475,32 @@ + timeout = _PyTime_FromNanoseconds(-1); + } + +-#ifdef HAVE_SEM_CLOCKWAIT +- struct timespec abs_timeout; +- // Local scope for deadline +- { +- _PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout); +- _PyTime_AsTimespec_clamp(deadline, &abs_timeout); +- } ++#ifdef _Py_SEM_CLOCKWAIT_WEAK ++ int use_clockwait = (sem_clockwait != NULL); + #else ++ int use_clockwait = 1; ++#endif ++ struct timespec abs_timeout; + _PyTime_t deadline = 0; +- if (timeout > 0 && !intr_flag) { ++ if (use_clockwait) { ++ _PyTime_t dl = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout); ++ _PyTime_AsTimespec_clamp(dl, &abs_timeout); ++ } else if (timeout > 0 && !intr_flag) { + deadline = _PyDeadline_Init(timeout); + } +-#endif + + while (1) { + if (timeout > 0) { +-#ifdef HAVE_SEM_CLOCKWAIT +- status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, +- &abs_timeout)); +-#else +- _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(), +- timeout); +- struct timespec ts; +- _PyTime_AsTimespec_clamp(abs_time, &ts); +- status = fix_status(sem_timedwait(thelock, &ts)); +-#endif ++ if (use_clockwait) { ++ status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, ++ &abs_timeout)); ++ } else { ++ _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(), ++ timeout); ++ struct timespec ts; ++ _PyTime_AsTimespec_clamp(abs_time, &ts); ++ status = fix_status(sem_timedwait(thelock, &ts)); ++ } + } + else if (timeout == 0) { + status = fix_status(sem_trywait(thelock)); +@@ -505,8 +517,7 @@ + + // sem_clockwait() uses an absolute timeout, there is no need + // to recompute the relative timeout. +-#ifndef HAVE_SEM_CLOCKWAIT +- if (timeout > 0) { ++ if (!use_clockwait && timeout > 0) { + /* wait interrupted by a signal (EINTR): recompute the timeout */ + timeout = _PyDeadline_Get(deadline); + if (timeout < 0) { +@@ -514,18 +525,13 @@ + break; + } + } +-#endif + } + + /* Don't check the status if we're stopping because of an interrupt. */ + if (!(intr_flag && status == EINTR)) { + if (timeout > 0) { + if (status != ETIMEDOUT) { +-#ifdef HAVE_SEM_CLOCKWAIT +- CHECK_STATUS("sem_clockwait"); +-#else +- CHECK_STATUS("sem_timedwait"); +-#endif ++ CHECK_STATUS(use_clockwait ? "sem_clockwait" : "sem_timedwait"); + } + } + else if (timeout == 0) { diff --git a/cpython-unix/patch-sem-clockwait-weak-3.12.patch b/cpython-unix/patch-sem-clockwait-weak-3.12.patch new file mode 100644 index 000000000..b6cb6566a --- /dev/null +++ b/cpython-unix/patch-sem-clockwait-weak-3.12.patch @@ -0,0 +1,104 @@ +--- a/Python/thread_pthread.h ++++ b/Python/thread_pthread.h +@@ -96,6 +96,19 @@ + #undef HAVE_SEM_CLOCKWAIT + #endif + ++/* When building against glibc headers older than 2.30, configure cannot ++ * detect sem_clockwait. Declare it as a weak symbol so it ++ * resolves to NULL on old glibc and to the real function on glibc 2.30+. ++ * This enables monotonic clock waits at runtime when available, preventing ++ * hangs when the system clock jumps backward (e.g., NTP sync). */ ++#if defined(__linux__) && !defined(HAVE_SEM_CLOCKWAIT) ++#include ++__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t, ++ const struct timespec *); ++#define HAVE_SEM_CLOCKWAIT 1 ++#define _Py_SEM_CLOCKWAIT_WEAK 1 ++#endif ++ + /* Whether or not to use semaphores directly rather than emulating them with + * mutexes and condition variables: + */ +@@ -456,32 +469,32 @@ + timeout = _PyTime_FromNanoseconds(-1); + } + +-#ifdef HAVE_SEM_CLOCKWAIT +- struct timespec abs_timeout; +- // Local scope for deadline +- { +- _PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout); +- _PyTime_AsTimespec_clamp(deadline, &abs_timeout); +- } ++#ifdef _Py_SEM_CLOCKWAIT_WEAK ++ int use_clockwait = (sem_clockwait != NULL); + #else ++ int use_clockwait = 1; ++#endif ++ struct timespec abs_timeout; + _PyTime_t deadline = 0; +- if (timeout > 0 && !intr_flag) { ++ if (use_clockwait) { ++ _PyTime_t dl = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout); ++ _PyTime_AsTimespec_clamp(dl, &abs_timeout); ++ } else if (timeout > 0 && !intr_flag) { + deadline = _PyDeadline_Init(timeout); + } +-#endif + + while (1) { + if (timeout > 0) { +-#ifdef HAVE_SEM_CLOCKWAIT +- status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, +- &abs_timeout)); +-#else +- _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(), +- timeout); +- struct timespec ts; +- _PyTime_AsTimespec_clamp(abs_time, &ts); +- status = fix_status(sem_timedwait(thelock, &ts)); +-#endif ++ if (use_clockwait) { ++ status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, ++ &abs_timeout)); ++ } else { ++ _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(), ++ timeout); ++ struct timespec ts; ++ _PyTime_AsTimespec_clamp(abs_time, &ts); ++ status = fix_status(sem_timedwait(thelock, &ts)); ++ } + } + else if (timeout == 0) { + status = fix_status(sem_trywait(thelock)); +@@ -498,8 +511,7 @@ + + // sem_clockwait() uses an absolute timeout, there is no need + // to recompute the relative timeout. +-#ifndef HAVE_SEM_CLOCKWAIT +- if (timeout > 0) { ++ if (!use_clockwait && timeout > 0) { + /* wait interrupted by a signal (EINTR): recompute the timeout */ + timeout = _PyDeadline_Get(deadline); + if (timeout < 0) { +@@ -507,18 +519,13 @@ + break; + } + } +-#endif + } + + /* Don't check the status if we're stopping because of an interrupt. */ + if (!(intr_flag && status == EINTR)) { + if (timeout > 0) { + if (status != ETIMEDOUT) { +-#ifdef HAVE_SEM_CLOCKWAIT +- CHECK_STATUS("sem_clockwait"); +-#else +- CHECK_STATUS("sem_timedwait"); +-#endif ++ CHECK_STATUS(use_clockwait ? "sem_clockwait" : "sem_timedwait"); + } + } + else if (timeout == 0) { diff --git a/cpython-unix/patch-sem-clockwait-weak-3.13.patch b/cpython-unix/patch-sem-clockwait-weak-3.13.patch new file mode 100644 index 000000000..7d313dfbe --- /dev/null +++ b/cpython-unix/patch-sem-clockwait-weak-3.13.patch @@ -0,0 +1,113 @@ +--- a/Python/thread_pthread.h ++++ b/Python/thread_pthread.h +@@ -100,6 +100,19 @@ + #undef HAVE_SEM_CLOCKWAIT + #endif + ++/* When building against glibc headers older than 2.30, configure cannot ++ * detect sem_clockwait. Declare it as a weak symbol so it ++ * resolves to NULL on old glibc and to the real function on glibc 2.30+. ++ * This enables monotonic clock waits at runtime when available, preventing ++ * hangs when the system clock jumps backward (e.g., NTP sync). */ ++#if defined(__linux__) && !defined(HAVE_SEM_CLOCKWAIT) ++#include ++__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t, ++ const struct timespec *); ++#define HAVE_SEM_CLOCKWAIT 1 ++#define _Py_SEM_CLOCKWAIT_WEAK 1 ++#endif ++ + /* Whether or not to use semaphores directly rather than emulating them with + * mutexes and condition variables: + */ +@@ -516,38 +529,38 @@ + timeout = -1; + } + +-#ifdef HAVE_SEM_CLOCKWAIT ++#ifdef _Py_SEM_CLOCKWAIT_WEAK ++ int use_clockwait = (sem_clockwait != NULL); ++#else ++ int use_clockwait = 1; ++#endif + struct timespec abs_timeout; +- // Local scope for deadline +- { ++ PyTime_t deadline = 0; ++ if (use_clockwait) { + PyTime_t now; + // silently ignore error: cannot report error to the caller + (void)PyTime_MonotonicRaw(&now); +- PyTime_t deadline = _PyTime_Add(now, timeout); +- _PyTime_AsTimespec_clamp(deadline, &abs_timeout); +- } +-#else +- PyTime_t deadline = 0; +- if (timeout > 0 && !intr_flag) { ++ PyTime_t dl = _PyTime_Add(now, timeout); ++ _PyTime_AsTimespec_clamp(dl, &abs_timeout); ++ } else if (timeout > 0 && !intr_flag) { + deadline = _PyDeadline_Init(timeout); + } +-#endif + + while (1) { + if (timeout > 0) { +-#ifdef HAVE_SEM_CLOCKWAIT +- status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, +- &abs_timeout)); +-#else +- PyTime_t now; +- // silently ignore error: cannot report error to the caller +- (void)PyTime_TimeRaw(&now); +- PyTime_t abs_time = _PyTime_Add(now, timeout); ++ if (use_clockwait) { ++ status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, ++ &abs_timeout)); ++ } else { ++ PyTime_t now; ++ // silently ignore error: cannot report error to the caller ++ (void)PyTime_TimeRaw(&now); ++ PyTime_t abs_time = _PyTime_Add(now, timeout); + +- struct timespec ts; +- _PyTime_AsTimespec_clamp(abs_time, &ts); +- status = fix_status(sem_timedwait(thelock, &ts)); +-#endif ++ struct timespec ts; ++ _PyTime_AsTimespec_clamp(abs_time, &ts); ++ status = fix_status(sem_timedwait(thelock, &ts)); ++ } + } + else if (timeout == 0) { + status = fix_status(sem_trywait(thelock)); +@@ -564,8 +577,7 @@ + + // sem_clockwait() uses an absolute timeout, there is no need + // to recompute the relative timeout. +-#ifndef HAVE_SEM_CLOCKWAIT +- if (timeout > 0) { ++ if (!use_clockwait && timeout > 0) { + /* wait interrupted by a signal (EINTR): recompute the timeout */ + timeout = _PyDeadline_Get(deadline); + if (timeout < 0) { +@@ -573,18 +585,13 @@ + break; + } + } +-#endif + } + + /* Don't check the status if we're stopping because of an interrupt. */ + if (!(intr_flag && status == EINTR)) { + if (timeout > 0) { + if (status != ETIMEDOUT) { +-#ifdef HAVE_SEM_CLOCKWAIT +- CHECK_STATUS("sem_clockwait"); +-#else +- CHECK_STATUS("sem_timedwait"); +-#endif ++ CHECK_STATUS(use_clockwait ? "sem_clockwait" : "sem_timedwait"); + } + } + else if (timeout == 0) { diff --git a/cpython-unix/patch-sem-clockwait-weak-3.15.patch b/cpython-unix/patch-sem-clockwait-weak-3.15.patch new file mode 100644 index 000000000..113f7abaf --- /dev/null +++ b/cpython-unix/patch-sem-clockwait-weak-3.15.patch @@ -0,0 +1,51 @@ +--- a/Python/parking_lot.c ++++ b/Python/parking_lot.c +@@ -10,7 +10,21 @@ + + #include + ++/* When building against glibc headers older than 2.30, configure cannot ++ * detect sem_clockwait. Declare it as a weak symbol so it ++ * resolves to NULL on old glibc and to the real function on glibc 2.30+. ++ * This enables monotonic clock waits at runtime when available, preventing ++ * hangs when the system clock jumps backward (e.g., NTP sync). */ ++#if defined(__linux__) && defined(_Py_USE_SEMAPHORES) && \ ++ !defined(HAVE_SEM_CLOCKWAIT) && !defined(_Py_THREAD_SANITIZER) ++#include ++__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t, ++ const struct timespec *); ++#define HAVE_SEM_CLOCKWAIT 1 ++#define _Py_SEM_CLOCKWAIT_WEAK 1 ++#endif + ++ + typedef struct { + // The mutex protects the waiter queue and the num_waiters counter. + _PyRawMutex mutex; +@@ -150,6 +164,9 @@ + struct timespec ts; + + #if defined(CLOCK_MONOTONIC) && defined(HAVE_SEM_CLOCKWAIT) && !defined(_Py_THREAD_SANITIZER) ++#ifdef _Py_SEM_CLOCKWAIT_WEAK ++ if (sem_clockwait != NULL) { ++#endif + PyTime_t now; + // silently ignore error: cannot report error to the caller + (void)PyTime_MonotonicRaw(&now); +@@ -157,6 +174,16 @@ + _PyTime_AsTimespec_clamp(deadline, &ts); + + err = sem_clockwait(&sema->platform_sem, CLOCK_MONOTONIC, &ts); ++#ifdef _Py_SEM_CLOCKWAIT_WEAK ++ } else { ++ PyTime_t now; ++ (void)PyTime_TimeRaw(&now); ++ PyTime_t deadline = _PyTime_Add(now, timeout); ++ _PyTime_AsTimespec_clamp(deadline, &ts); ++ ++ err = sem_timedwait(&sema->platform_sem, &ts); ++ } ++#endif + #else + PyTime_t now; + // silently ignore error: cannot report error to the caller diff --git a/cpython-unix/patch-test-embed-prevent-segfault.patch b/cpython-unix/patch-test-embed-prevent-segfault.patch new file mode 100644 index 000000000..d05e2b00b --- /dev/null +++ b/cpython-unix/patch-test-embed-prevent-segfault.patch @@ -0,0 +1,12 @@ +diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py +index 13713cf37b8..40ee4837bfe 100644 +--- a/Lib/test/test_embed.py ++++ b/Lib/test/test_embed.py +@@ -1615,6 +1615,7 @@ def test_getpath_abspath_win32(self): + for (_, expected), result in zip(CASES, results): + self.assertEqual(result, expected) + ++ @unittest.skipIf(support.check_bolt_optimized, "segfaults on BOLT instrumented binaries") + def test_global_pathconfig(self): + # Test C API functions getting the path configuration: + # diff --git a/cpython-unix/patch-testinternalcapi-interpreter-extern.patch b/cpython-unix/patch-testinternalcapi-interpreter-extern.patch new file mode 100644 index 000000000..4ddfc75c4 --- /dev/null +++ b/cpython-unix/patch-testinternalcapi-interpreter-extern.patch @@ -0,0 +1,13 @@ +diff --git a/Modules/_testinternalcapi/interpreter.c b/Modules/_testinternalcapi/interpreter.c +index 2cd23fa3c58..653332f7073 100644 +--- a/Modules/_testinternalcapi/interpreter.c ++++ b/Modules/_testinternalcapi/interpreter.c +@@ -21,7 +21,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) + } + #endif + +-_PyJitEntryFuncPtr _Py_jit_entry; ++extern _PyJitEntryFuncPtr _Py_jit_entry; + + #if _Py_TAIL_CALL_INTERP + #include "test_targets.h" diff --git a/cpython-unix/patch-tkinter-3.10.patch b/cpython-unix/patch-tkinter-3.10.patch new file mode 100644 index 000000000..dc95cbf71 --- /dev/null +++ b/cpython-unix/patch-tkinter-3.10.patch @@ -0,0 +1,143 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 2a3e65b..2572277 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -115,6 +115,7 @@ Copyright (C) 1994 Steen Lumholt. + #ifdef MS_WINDOWS + #include + #define WAIT_FOR_STDIN ++#endif + + static PyObject * + _get_tcl_lib_path() +@@ -132,6 +133,7 @@ _get_tcl_lib_path() + return NULL; + } + ++#ifdef MS_WINDOWS + /* Check expected location for an installed Python first */ + tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tcl_library_path == NULL) { +@@ -169,11 +171,34 @@ _get_tcl_lib_path() + tcl_library_path = NULL; + #endif + } ++#else ++ /* Check expected location for an installed Python first */ ++ PyObject *suffix = PyUnicode_FromString("/lib/tcl" TCL_VERSION); ++ if (suffix == NULL) { ++ Py_DECREF(prefix); ++ return NULL; ++ } ++ tcl_library_path = PyUnicode_Concat(prefix, suffix); ++ Py_DECREF(suffix); ++ Py_DECREF(prefix); ++ if (tcl_library_path == NULL) { ++ return NULL; ++ } ++ stat_return_value = _Py_stat(tcl_library_path, &stat_buf); ++ if (stat_return_value == -2) { ++ return NULL; ++ } ++ if (stat_return_value == -1) { ++ /* install location doesn't exist, reset errno and leave Tcl ++ to its own devices */ ++ errno = 0; ++ tcl_library_path = NULL; ++ } ++#endif + already_checked = 1; + } + return tcl_library_path; + } +-#endif /* MS_WINDOWS */ + + /* The threading situation is complicated. Tcl is not thread-safe, except + when configured with --enable-threads. +@@ -709,6 +731,9 @@ Tkapp_New(const char *screenName, const char *className, + { + TkappObject *v; + char *argv0; ++#ifndef MS_WINDOWS ++ int tcl_library_env_set = 0; ++#endif + + v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type); + if (v == NULL) +@@ -839,9 +864,41 @@ Tkapp_New(const char *screenName, const char *className, + } + } + } ++#else ++ { ++ const char *env_val = getenv("TCL_LIBRARY"); ++ if (!env_val || !env_val[0]) { ++ PyObject *str_path; ++ PyObject *utf8_path; ++ ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ return NULL; ++ } ++ if (str_path != NULL) { ++ utf8_path = PyUnicode_AsUTF8String(str_path); ++ if (utf8_path == NULL) { ++ return NULL; ++ } ++ Tcl_SetVar(v->interp, ++ "tcl_library", ++ PyBytes_AS_STRING(utf8_path), ++ TCL_GLOBAL_ONLY); ++ setenv("TCL_LIBRARY", PyBytes_AS_STRING(utf8_path), 1); ++ tcl_library_env_set = 1; ++ Py_DECREF(utf8_path); ++ } ++ } ++ } + #endif + +- if (Tcl_AppInit(v->interp) != TCL_OK) { ++ int app_rc = Tcl_AppInit(v->interp); ++#ifndef MS_WINDOWS ++ if (tcl_library_env_set) { ++ unsetenv("TCL_LIBRARY"); ++ } ++#endif ++ if (app_rc != TCL_OK) { + PyObject *result = Tkinter_Error(v); + #ifdef TKINTER_PROTECT_LOADTK + if (wantTk) { +@@ -3628,7 +3685,33 @@ PyInit__tkinter(void) + PyMem_Free(wcs_path); + } + #else ++ int set_var = 0; ++ PyObject *str_path; ++ char *path; ++ const char *env_val = getenv("TCL_LIBRARY"); ++ ++ if (!env_val || !env_val[0]) { ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ if (str_path != NULL) { ++ path = PyUnicode_AsUTF8(str_path); ++ if (path == NULL) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ setenv("TCL_LIBRARY", path, 1); ++ set_var = 1; ++ } ++ } ++ + Tcl_FindExecutable(PyBytes_AS_STRING(cexe)); ++ ++ if (set_var) { ++ unsetenv("TCL_LIBRARY"); ++ } + #endif /* MS_WINDOWS */ + } + Py_XDECREF(cexe); diff --git a/cpython-unix/patch-tkinter-3.11.patch b/cpython-unix/patch-tkinter-3.11.patch new file mode 100644 index 000000000..540900a6c --- /dev/null +++ b/cpython-unix/patch-tkinter-3.11.patch @@ -0,0 +1,154 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 005036d..1dd202d 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -28,9 +28,7 @@ Copyright (C) 1994 Steen Lumholt. + + #include "Python.h" + #include +-#ifdef MS_WINDOWS +-# include "pycore_fileutils.h" // _Py_stat() +-#endif ++#include "pycore_fileutils.h" // _Py_stat() + + #ifdef MS_WINDOWS + #include +@@ -123,6 +121,7 @@ Copyright (C) 1994 Steen Lumholt. + #ifdef MS_WINDOWS + #include + #define WAIT_FOR_STDIN ++#endif + + static PyObject * + _get_tcl_lib_path() +@@ -140,6 +139,7 @@ _get_tcl_lib_path() + return NULL; + } + ++#ifdef MS_WINDOWS + /* Check expected location for an installed Python first */ + tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tcl_library_path == NULL) { +@@ -177,11 +177,34 @@ _get_tcl_lib_path() + tcl_library_path = NULL; + #endif + } ++#else ++ /* Check expected location for an installed Python first */ ++ PyObject *suffix = PyUnicode_FromString("/lib/tcl" TCL_VERSION); ++ if (suffix == NULL) { ++ Py_DECREF(prefix); ++ return NULL; ++ } ++ tcl_library_path = PyUnicode_Concat(prefix, suffix); ++ Py_DECREF(suffix); ++ Py_DECREF(prefix); ++ if (tcl_library_path == NULL) { ++ return NULL; ++ } ++ stat_return_value = _Py_stat(tcl_library_path, &stat_buf); ++ if (stat_return_value == -2) { ++ return NULL; ++ } ++ if (stat_return_value == -1) { ++ /* install location doesn't exist, reset errno and leave Tcl ++ to its own devices */ ++ errno = 0; ++ tcl_library_path = NULL; ++ } ++#endif + already_checked = 1; + } + return tcl_library_path; + } +-#endif /* MS_WINDOWS */ + + /* The threading situation is complicated. Tcl is not thread-safe, except + when configured with --enable-threads. +@@ -574,6 +594,9 @@ Tkapp_New(const char *screenName, const char *className, + { + TkappObject *v; + char *argv0; ++#ifndef MS_WINDOWS ++ int tcl_library_env_set = 0; ++#endif + + v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type); + if (v == NULL) +@@ -704,9 +727,41 @@ Tkapp_New(const char *screenName, const char *className, + } + } + } ++#else ++ { ++ const char *env_val = getenv("TCL_LIBRARY"); ++ if (!env_val || !env_val[0]) { ++ PyObject *str_path; ++ PyObject *utf8_path; ++ ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ return NULL; ++ } ++ if (str_path != NULL) { ++ utf8_path = PyUnicode_AsUTF8String(str_path); ++ if (utf8_path == NULL) { ++ return NULL; ++ } ++ Tcl_SetVar(v->interp, ++ "tcl_library", ++ PyBytes_AS_STRING(utf8_path), ++ TCL_GLOBAL_ONLY); ++ setenv("TCL_LIBRARY", PyBytes_AS_STRING(utf8_path), 1); ++ tcl_library_env_set = 1; ++ Py_DECREF(utf8_path); ++ } ++ } ++ } + #endif + +- if (Tcl_AppInit(v->interp) != TCL_OK) { ++ int app_rc = Tcl_AppInit(v->interp); ++#ifndef MS_WINDOWS ++ if (tcl_library_env_set) { ++ unsetenv("TCL_LIBRARY"); ++ } ++#endif ++ if (app_rc != TCL_OK) { + PyObject *result = Tkinter_Error(v); + #ifdef TKINTER_PROTECT_LOADTK + if (wantTk) { +@@ -3428,7 +3483,33 @@ PyInit__tkinter(void) + PyMem_Free(wcs_path); + } + #else ++ int set_var = 0; ++ PyObject *str_path; ++ char *path; ++ const char *env_val = getenv("TCL_LIBRARY"); ++ ++ if (!env_val || !env_val[0]) { ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ if (str_path != NULL) { ++ path = PyUnicode_AsUTF8(str_path); ++ if (path == NULL) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ setenv("TCL_LIBRARY", path, 1); ++ set_var = 1; ++ } ++ } ++ + Tcl_FindExecutable(PyBytes_AS_STRING(cexe)); ++ ++ if (set_var) { ++ unsetenv("TCL_LIBRARY"); ++ } + #endif /* MS_WINDOWS */ + } + Py_XDECREF(cexe); diff --git a/cpython-unix/patch-tkinter-3.12.patch b/cpython-unix/patch-tkinter-3.12.patch new file mode 100644 index 000000000..4b943df67 --- /dev/null +++ b/cpython-unix/patch-tkinter-3.12.patch @@ -0,0 +1,154 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 60f66a5..dcd10a6 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -28,9 +28,7 @@ Copyright (C) 1994 Steen Lumholt. + + #include "Python.h" + #include +-#ifdef MS_WINDOWS +-# include "pycore_fileutils.h" // _Py_stat() +-#endif ++#include "pycore_fileutils.h" // _Py_stat() + + #include "pycore_long.h" + #include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString() +@@ -135,6 +133,7 @@ typedef int Tcl_Size; + #ifdef MS_WINDOWS + #include + #define WAIT_FOR_STDIN ++#endif + + static PyObject * + _get_tcl_lib_path(void) +@@ -152,6 +151,7 @@ _get_tcl_lib_path(void) + return NULL; + } + ++#ifdef MS_WINDOWS + /* Check expected location for an installed Python first */ + tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tcl_library_path == NULL) { +@@ -191,11 +191,34 @@ _get_tcl_lib_path(void) + tcl_library_path = NULL; + #endif + } ++#else ++ /* Check expected location for an installed Python first */ ++ PyObject *suffix = PyUnicode_FromString("/lib/tcl" TCL_VERSION); ++ if (suffix == NULL) { ++ Py_DECREF(prefix); ++ return NULL; ++ } ++ tcl_library_path = PyUnicode_Concat(prefix, suffix); ++ Py_DECREF(suffix); ++ Py_DECREF(prefix); ++ if (tcl_library_path == NULL) { ++ return NULL; ++ } ++ stat_return_value = _Py_stat(tcl_library_path, &stat_buf); ++ if (stat_return_value == -2) { ++ return NULL; ++ } ++ if (stat_return_value == -1) { ++ /* install location doesn't exist, reset errno and leave Tcl ++ to its own devices */ ++ errno = 0; ++ tcl_library_path = NULL; ++ } ++#endif + already_checked = 1; + } + return tcl_library_path; + } +-#endif /* MS_WINDOWS */ + + /* The threading situation is complicated. Tcl is not thread-safe, except + when configured with --enable-threads. +@@ -582,6 +602,9 @@ Tkapp_New(const char *screenName, const char *className, + { + TkappObject *v; + char *argv0; ++#ifndef MS_WINDOWS ++ int tcl_library_env_set = 0; ++#endif + + v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type); + if (v == NULL) +@@ -733,9 +756,41 @@ Tkapp_New(const char *screenName, const char *className, + } + } + } ++#else ++ { ++ const char *env_val = getenv("TCL_LIBRARY"); ++ if (!env_val || !env_val[0]) { ++ PyObject *str_path; ++ PyObject *utf8_path; ++ ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ return NULL; ++ } ++ if (str_path != NULL) { ++ utf8_path = PyUnicode_AsUTF8String(str_path); ++ if (utf8_path == NULL) { ++ return NULL; ++ } ++ Tcl_SetVar(v->interp, ++ "tcl_library", ++ PyBytes_AS_STRING(utf8_path), ++ TCL_GLOBAL_ONLY); ++ setenv("TCL_LIBRARY", PyBytes_AS_STRING(utf8_path), 1); ++ tcl_library_env_set = 1; ++ Py_DECREF(utf8_path); ++ } ++ } ++ } + #endif + +- if (Tcl_AppInit(v->interp) != TCL_OK) { ++ int app_rc = Tcl_AppInit(v->interp); ++#ifndef MS_WINDOWS ++ if (tcl_library_env_set) { ++ unsetenv("TCL_LIBRARY"); ++ } ++#endif ++ if (app_rc != TCL_OK) { + PyObject *result = Tkinter_Error(v); + Py_DECREF((PyObject *)v); + return (TkappObject *)result; +@@ -3548,7 +3603,33 @@ PyInit__tkinter(void) + PyMem_Free(wcs_path); + } + #else ++ int set_var = 0; ++ PyObject *str_path; ++ char *path; ++ const char *env_val = getenv("TCL_LIBRARY"); ++ ++ if (!env_val || !env_val[0]) { ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ if (str_path != NULL) { ++ path = PyUnicode_AsUTF8(str_path); ++ if (path == NULL) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ setenv("TCL_LIBRARY", path, 1); ++ set_var = 1; ++ } ++ } ++ + Tcl_FindExecutable(PyBytes_AS_STRING(cexe)); ++ ++ if (set_var) { ++ unsetenv("TCL_LIBRARY"); ++ } + #endif /* MS_WINDOWS */ + } + Py_XDECREF(cexe); diff --git a/cpython-unix/patch-tkinter-3.13.patch b/cpython-unix/patch-tkinter-3.13.patch new file mode 100644 index 000000000..70ce47676 --- /dev/null +++ b/cpython-unix/patch-tkinter-3.13.patch @@ -0,0 +1,155 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 38e6afd..2b69877 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -26,9 +26,8 @@ Copyright (C) 1994 Steen Lumholt. + #endif + + #include "Python.h" +-#ifdef MS_WINDOWS +-# include "pycore_fileutils.h" // _Py_stat() +-#endif ++ ++#include "pycore_fileutils.h" // _Py_stat() + + #include "pycore_long.h" // _PyLong_IsNegative() + #include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString() +@@ -133,6 +132,7 @@ typedef int Tcl_Size; + #ifdef MS_WINDOWS + #include + #define WAIT_FOR_STDIN ++#endif + + static PyObject * + _get_tcl_lib_path(void) +@@ -150,6 +150,7 @@ _get_tcl_lib_path(void) + return NULL; + } + ++#ifdef MS_WINDOWS + /* Check expected location for an installed Python first */ + tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tcl_library_path == NULL) { +@@ -189,11 +190,34 @@ _get_tcl_lib_path(void) + tcl_library_path = NULL; + #endif + } ++#else ++ /* Check expected location for an installed Python first */ ++ PyObject *suffix = PyUnicode_FromString("/lib/tcl" TCL_VERSION); ++ if (suffix == NULL) { ++ Py_DECREF(prefix); ++ return NULL; ++ } ++ tcl_library_path = PyUnicode_Concat(prefix, suffix); ++ Py_DECREF(suffix); ++ Py_DECREF(prefix); ++ if (tcl_library_path == NULL) { ++ return NULL; ++ } ++ stat_return_value = _Py_stat(tcl_library_path, &stat_buf); ++ if (stat_return_value == -2) { ++ return NULL; ++ } ++ if (stat_return_value == -1) { ++ /* install location doesn't exist, reset errno and leave Tcl ++ to its own devices */ ++ errno = 0; ++ tcl_library_path = NULL; ++ } ++#endif + already_checked = 1; + } + return tcl_library_path; + } +-#endif /* MS_WINDOWS */ + + /* The threading situation is complicated. Tcl is not thread-safe, except + when configured with --enable-threads. +@@ -579,6 +600,9 @@ Tkapp_New(const char *screenName, const char *className, + { + TkappObject *v; + char *argv0; ++#ifndef MS_WINDOWS ++ int tcl_library_env_set = 0; ++#endif + + v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type); + if (v == NULL) +@@ -733,9 +757,41 @@ Tkapp_New(const char *screenName, const char *className, + } + } + } ++#else ++ { ++ const char *env_val = getenv("TCL_LIBRARY"); ++ if (!env_val || !env_val[0]) { ++ PyObject *str_path; ++ PyObject *utf8_path; ++ ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ return NULL; ++ } ++ if (str_path != NULL) { ++ utf8_path = PyUnicode_AsUTF8String(str_path); ++ if (utf8_path == NULL) { ++ return NULL; ++ } ++ Tcl_SetVar(v->interp, ++ "tcl_library", ++ PyBytes_AS_STRING(utf8_path), ++ TCL_GLOBAL_ONLY); ++ setenv("TCL_LIBRARY", PyBytes_AS_STRING(utf8_path), 1); ++ tcl_library_env_set = 1; ++ Py_DECREF(utf8_path); ++ } ++ } ++ } + #endif + +- if (Tcl_AppInit(v->interp) != TCL_OK) { ++ int app_rc = Tcl_AppInit(v->interp); ++#ifndef MS_WINDOWS ++ if (tcl_library_env_set) { ++ unsetenv("TCL_LIBRARY"); ++ } ++#endif ++ if (app_rc != TCL_OK) { + PyObject *result = Tkinter_Error(v); + Py_DECREF((PyObject *)v); + return (TkappObject *)result; +@@ -3532,7 +3588,33 @@ PyInit__tkinter(void) + PyMem_Free(wcs_path); + } + #else ++ int set_var = 0; ++ PyObject *str_path; ++ char *path; ++ const char *env_val = getenv("TCL_LIBRARY"); ++ ++ if (!env_val || !env_val[0]) { ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ if (str_path != NULL) { ++ path = PyUnicode_AsUTF8(str_path); ++ if (path == NULL) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ setenv("TCL_LIBRARY", path, 1); ++ set_var = 1; ++ } ++ } ++ + Tcl_FindExecutable(PyBytes_AS_STRING(cexe)); ++ ++ if (set_var) { ++ unsetenv("TCL_LIBRARY"); ++ } + #endif /* MS_WINDOWS */ + } + Py_XDECREF(cexe); diff --git a/cpython-unix/patch-tkinter-3.14.patch b/cpython-unix/patch-tkinter-3.14.patch new file mode 100644 index 000000000..292bf40fc --- /dev/null +++ b/cpython-unix/patch-tkinter-3.14.patch @@ -0,0 +1,143 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 08fb961..ab9f222 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -134,6 +134,7 @@ typedef int Tcl_Size; + #ifdef MS_WINDOWS + #include + #define WAIT_FOR_STDIN ++#endif + + static PyObject * + _get_tcl_lib_path(void) +@@ -151,6 +152,7 @@ _get_tcl_lib_path(void) + return NULL; + } + ++#ifdef MS_WINDOWS + /* Check expected location for an installed Python first */ + tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tcl_library_path == NULL) { +@@ -190,11 +192,34 @@ _get_tcl_lib_path(void) + tcl_library_path = NULL; + #endif + } ++#else ++ /* Check expected location for an installed Python first */ ++ PyObject *suffix = PyUnicode_FromString("/lib/tcl" TCL_VERSION); ++ if (suffix == NULL) { ++ Py_DECREF(prefix); ++ return NULL; ++ } ++ tcl_library_path = PyUnicode_Concat(prefix, suffix); ++ Py_DECREF(suffix); ++ Py_DECREF(prefix); ++ if (tcl_library_path == NULL) { ++ return NULL; ++ } ++ stat_return_value = _Py_stat(tcl_library_path, &stat_buf); ++ if (stat_return_value == -2) { ++ return NULL; ++ } ++ if (stat_return_value == -1) { ++ /* install location doesn't exist, reset errno and leave Tcl ++ to its own devices */ ++ errno = 0; ++ tcl_library_path = NULL; ++ } ++#endif + already_checked = 1; + } + return tcl_library_path; + } +-#endif /* MS_WINDOWS */ + + /* The threading situation is complicated. Tcl is not thread-safe, except + when configured with --enable-threads. +@@ -592,6 +614,9 @@ Tkapp_New(const char *screenName, const char *className, + { + TkappObject *v; + char *argv0; ++#ifndef MS_WINDOWS ++ int tcl_library_env_set = 0; ++#endif + + v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type); + if (v == NULL) +@@ -747,9 +772,41 @@ Tkapp_New(const char *screenName, const char *className, + } + } + } ++#else ++ { ++ const char *env_val = getenv("TCL_LIBRARY"); ++ if (!env_val || !env_val[0]) { ++ PyObject *str_path; ++ PyObject *utf8_path; ++ ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ return NULL; ++ } ++ if (str_path != NULL) { ++ utf8_path = PyUnicode_AsUTF8String(str_path); ++ if (utf8_path == NULL) { ++ return NULL; ++ } ++ Tcl_SetVar(v->interp, ++ "tcl_library", ++ PyBytes_AS_STRING(utf8_path), ++ TCL_GLOBAL_ONLY); ++ setenv("TCL_LIBRARY", PyBytes_AS_STRING(utf8_path), 1); ++ tcl_library_env_set = 1; ++ Py_DECREF(utf8_path); ++ } ++ } ++ } + #endif + +- if (Tcl_AppInit(v->interp) != TCL_OK) { ++ int app_rc = Tcl_AppInit(v->interp); ++#ifndef MS_WINDOWS ++ if (tcl_library_env_set) { ++ unsetenv("TCL_LIBRARY"); ++ } ++#endif ++ if (app_rc != TCL_OK) { + PyObject *result = Tkinter_Error(v); + Py_DECREF((PyObject *)v); + return (TkappObject *)result; +@@ -3590,7 +3647,33 @@ PyInit__tkinter(void) + PyMem_Free(wcs_path); + } + #else ++ int set_var = 0; ++ PyObject *str_path; ++ char *path; ++ const char *env_val = getenv("TCL_LIBRARY"); ++ ++ if (!env_val || !env_val[0]) { ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ if (str_path != NULL) { ++ path = PyUnicode_AsUTF8(str_path); ++ if (path == NULL) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ setenv("TCL_LIBRARY", path, 1); ++ set_var = 1; ++ } ++ } ++ + Tcl_FindExecutable(PyBytes_AS_STRING(cexe)); ++ ++ if (set_var) { ++ unsetenv("TCL_LIBRARY"); ++ } + #endif /* MS_WINDOWS */ + } + Py_XDECREF(cexe); diff --git a/cpython-unix/patch-tkinter-backport-tcl-9-310.patch b/cpython-unix/patch-tkinter-backport-tcl-9-310.patch new file mode 100644 index 000000000..fc9c5e316 --- /dev/null +++ b/cpython-unix/patch-tkinter-backport-tcl-9-310.patch @@ -0,0 +1,313 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 2a3e65b6c97..d601a2b7c2a 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -55,9 +55,24 @@ Copyright (C) 1994 Steen Lumholt. + #if TK_HEX_VERSION >= 0x08050208 && TK_HEX_VERSION < 0x08060000 || \ + TK_HEX_VERSION >= 0x08060200 + #define HAVE_LIBTOMMATH ++#ifndef TCL_WITH_EXTERNAL_TOMMATH ++#define TCL_NO_TOMMATH_H ++#endif + #include + #endif + ++#if defined(TCL_WITH_EXTERNAL_TOMMATH) || (TK_HEX_VERSION >= 0x08070000) ++#define USE_DEPRECATED_TOMMATH_API 0 ++#else ++#define USE_DEPRECATED_TOMMATH_API 1 ++#endif ++ ++// As suggested by https://core.tcl-lang.org/tcl/wiki?name=Migrating+C+extensions+to+Tcl+9 ++#ifndef TCL_SIZE_MAX ++typedef int Tcl_Size; ++#define TCL_SIZE_MAX INT_MAX ++#endif ++ + #if !(defined(MS_WINDOWS) || defined(__CYGWIN__)) + #define HAVE_CREATEFILEHANDLER + #endif +@@ -308,6 +323,7 @@ typedef struct { + const Tcl_ObjType *ListType; + const Tcl_ObjType *ProcBodyType; + const Tcl_ObjType *StringType; ++ const Tcl_ObjType *UTF32StringType; + } TkappObject; + + #define Tkapp_Interp(v) (((TkappObject *) (v))->interp) +@@ -488,7 +504,7 @@ unicodeFromTclString(const char *s) + static PyObject * + unicodeFromTclObj(Tcl_Obj *value) + { +- int len; ++ Tcl_Size len; + #if USE_TCL_UNICODE + int byteorder = NATIVE_BYTEORDER; + const Tcl_UniChar *u = Tcl_GetUnicodeFromObj(value, &len); +@@ -510,7 +526,7 @@ unicodeFromTclObj(Tcl_Obj *value) + static PyObject * + Split(const char *list) + { +- int argc; ++ Tcl_Size argc; + const char **argv; + PyObject *v; + +@@ -612,7 +628,7 @@ SplitObj(PyObject *arg) + return result; + } + else if (PyUnicode_Check(arg)) { +- int argc; ++ Tcl_Size argc; + const char **argv; + const char *list = PyUnicode_AsUTF8(arg); + +@@ -627,7 +643,7 @@ SplitObj(PyObject *arg) + /* Fall through, returning arg. */ + } + else if (PyBytes_Check(arg)) { +- int argc; ++ Tcl_Size argc; + const char **argv; + const char *list = PyBytes_AS_STRING(arg); + +@@ -655,6 +671,10 @@ class _tkinter.tktimertoken "TkttObject *" "&Tktt_Type_spec" + + /**** Tkapp Object ****/ + ++#if TK_MAJOR_VERSION >= 9 ++int Tcl_AppInit(Tcl_Interp *); ++#endif ++ + #ifndef WITH_APPINIT + int + Tcl_AppInit(Tcl_Interp *interp) +@@ -736,15 +756,41 @@ Tkapp_New(const char *screenName, const char *className, + } + + v->OldBooleanType = Tcl_GetObjType("boolean"); +- v->BooleanType = Tcl_GetObjType("booleanString"); +- v->ByteArrayType = Tcl_GetObjType("bytearray"); ++ { ++ Tcl_Obj *value; ++ int boolValue; ++ ++ /* Tcl 8.5 "booleanString" type is not registered ++ and is renamed to "boolean" in Tcl 9.0. ++ Based on approach suggested at ++ https://core.tcl-lang.org/tcl/info/3bb3bcf2da5b */ ++ value = Tcl_NewStringObj("true", -1); ++ Tcl_GetBooleanFromObj(NULL, value, &boolValue); ++ v->BooleanType = value->typePtr; ++ Tcl_DecrRefCount(value); ++ ++ // "bytearray" type is not registered in Tcl 9.0 ++ value = Tcl_NewByteArrayObj(NULL, 0); ++ v->ByteArrayType = value->typePtr; ++ Tcl_DecrRefCount(value); ++ } + v->DoubleType = Tcl_GetObjType("double"); ++ /* TIP 484 suggests retrieving the "int" type without Tcl_GetObjType("int") ++ since it is no longer registered in Tcl 9.0. But even though Tcl 8.7 ++ only uses the "wideInt" type on platforms with 32-bit long, it still has ++ a registered "int" type, which FromObj() should recognize just in case. */ + v->IntType = Tcl_GetObjType("int"); ++ if (v->IntType == NULL) { ++ Tcl_Obj *value = Tcl_NewIntObj(0); ++ v->IntType = value->typePtr; ++ Tcl_DecrRefCount(value); ++ } + v->WideIntType = Tcl_GetObjType("wideInt"); + v->BignumType = Tcl_GetObjType("bignum"); + v->ListType = Tcl_GetObjType("list"); + v->ProcBodyType = Tcl_GetObjType("procbody"); + v->StringType = Tcl_GetObjType("string"); ++ v->UTF32StringType = Tcl_GetObjType("utf32string"); + + /* Delete the 'exit' command, which can screw things up */ + Tcl_DeleteCommand(v->interp, "exit"); +@@ -1229,20 +1275,33 @@ static PyObject* + fromBignumObj(TkappObject *tkapp, Tcl_Obj *value) + { + mp_int bigValue; ++ mp_err err; ++#if USE_DEPRECATED_TOMMATH_API + unsigned long numBytes; ++#else ++ size_t numBytes; ++#endif + unsigned char *bytes; + PyObject *res; + + if (Tcl_GetBignumFromObj(Tkapp_Interp(tkapp), value, &bigValue) != TCL_OK) + return Tkinter_Error(tkapp); ++#if USE_DEPRECATED_TOMMATH_API + numBytes = mp_unsigned_bin_size(&bigValue); ++#else ++ numBytes = mp_ubin_size(&bigValue); ++#endif + bytes = PyMem_Malloc(numBytes); + if (bytes == NULL) { + mp_clear(&bigValue); + return PyErr_NoMemory(); + } +- if (mp_to_unsigned_bin_n(&bigValue, bytes, +- &numBytes) != MP_OKAY) { ++#if USE_DEPRECATED_TOMMATH_API ++ err = mp_to_unsigned_bin_n(&bigValue, bytes, &numBytes); ++#else ++ err = mp_to_ubin(&bigValue, bytes, numBytes, NULL); ++#endif ++ if (err != MP_OKAY) { + mp_clear(&bigValue); + PyMem_Free(bytes); + return PyErr_NoMemory(); +@@ -1277,7 +1336,7 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + } + + if (value->typePtr == tkapp->ByteArrayType) { +- int size; ++ Tcl_Size size; + char *data = (char*)Tcl_GetByteArrayFromObj(value, &size); + return PyBytes_FromStringAndSize(data, size); + } +@@ -1286,14 +1345,6 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + return PyFloat_FromDouble(value->internalRep.doubleValue); + } + +- if (value->typePtr == tkapp->IntType) { +- long longValue; +- if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK) +- return PyLong_FromLong(longValue); +- /* If there is an error in the long conversion, +- fall through to wideInt handling. */ +- } +- + if (value->typePtr == tkapp->IntType || + value->typePtr == tkapp->WideIntType) { + result = fromWideIntObj(tkapp, value); +@@ -1313,8 +1364,8 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + #endif + + if (value->typePtr == tkapp->ListType) { +- int size; +- int i, status; ++ Tcl_Size i, size; ++ int status; + PyObject *elem; + Tcl_Obj *tcl_elem; + +@@ -1340,23 +1391,12 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + return result; + } + +- if (value->typePtr == tkapp->ProcBodyType) { +- /* fall through: return tcl object. */ +- } +- +- if (value->typePtr == tkapp->StringType) { ++ if (value->typePtr == tkapp->StringType || ++ value->typePtr == tkapp->UTF32StringType) ++ { + return unicodeFromTclObj(value); + } + +-#if TK_HEX_VERSION >= 0x08050000 +- if (tkapp->BooleanType == NULL && +- strcmp(value->typePtr->name, "booleanString") == 0) { +- /* booleanString type is not registered in Tcl */ +- tkapp->BooleanType = value->typePtr; +- return fromBoolean(tkapp, value); +- } +-#endif +- + #ifdef HAVE_LIBTOMMATH + if (tkapp->BignumType == NULL && + strcmp(value->typePtr->name, "bignum") == 0) { +@@ -1382,10 +1422,10 @@ typedef struct Tkapp_CallEvent { + Tcl_Condition *done; + } Tkapp_CallEvent; + +-void +-Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, int objc) ++static void ++Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, Tcl_Size objc) + { +- int i; ++ Tcl_Size i; + for (i = 0; i < objc; i++) + Tcl_DecrRefCount(objv[i]); + if (objv != objStore) +@@ -1396,7 +1436,7 @@ Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, int objc) + interpreter thread, which may or may not be the calling thread. */ + + static Tcl_Obj** +-Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc) ++Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, Tcl_Size *pobjc) + { + Tcl_Obj **objv = objStore; + Py_ssize_t objc = 0, i; +@@ -1444,10 +1484,10 @@ Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc) + Tcl_IncrRefCount(objv[i]); + } + } +- *pobjc = (int)objc; ++ *pobjc = (Tcl_Size)objc; + return objv; + finally: +- Tkapp_CallDeallocArgs(objv, objStore, (int)objc); ++ Tkapp_CallDeallocArgs(objv, objStore, (Tcl_Size)objc); + return NULL; + } + +@@ -1490,7 +1530,7 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags) + { + Tcl_Obj *objStore[ARGSZ]; + Tcl_Obj **objv; +- int objc; ++ Tcl_Size objc; + int i; + ENTER_PYTHON + objv = Tkapp_CallArgs(e->args, objStore, &objc); +@@ -1541,7 +1581,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) + { + Tcl_Obj *objStore[ARGSZ]; + Tcl_Obj **objv = NULL; +- int objc, i; ++ Tcl_Size objc; + PyObject *res = NULL; + TkappObject *self = (TkappObject*)selfptr; + int flags = TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL; +@@ -1587,6 +1627,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) + else + { + ++ int i; + objv = Tkapp_CallArgs(args, objStore, &objc); + if (!objv) + return NULL; +@@ -2276,13 +2317,12 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg) + /*[clinic end generated code: output=13b51d34386d36fb input=2b2e13351e3c0b53]*/ + { + char *list; +- int argc; ++ Tcl_Size argc, i; + const char **argv; + PyObject *v; +- int i; + + if (PyTclObject_Check(arg)) { +- int objc; ++ Tcl_Size objc; + Tcl_Obj **objv; + if (Tcl_ListObjGetElements(Tkapp_Interp(self), + ((PyTclObject*)arg)->value, +@@ -2365,7 +2405,7 @@ _tkinter_tkapp_split(TkappObject *self, PyObject *arg) + + if (PyTclObject_Check(arg)) { + Tcl_Obj *value = ((PyTclObject*)arg)->value; +- int objc; ++ Tcl_Size objc; + Tcl_Obj **objv; + int i; + if (Tcl_ListObjGetElements(Tkapp_Interp(self), value, diff --git a/cpython-unix/patch-tkinter-backport-tcl-9-311.patch b/cpython-unix/patch-tkinter-backport-tcl-9-311.patch new file mode 100644 index 000000000..819948d96 --- /dev/null +++ b/cpython-unix/patch-tkinter-backport-tcl-9-311.patch @@ -0,0 +1,235 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 005036d3ff2..2bc963a4025 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -58,6 +58,9 @@ Copyright (C) 1994 Steen Lumholt. + #error "Tk older than 8.5.12 not supported" + #endif + ++#ifndef TCL_WITH_EXTERNAL_TOMMATH ++#define TCL_NO_TOMMATH_H ++#endif + #include + + #if defined(TCL_WITH_EXTERNAL_TOMMATH) || (TK_HEX_VERSION >= 0x08070000) +@@ -66,6 +69,12 @@ Copyright (C) 1994 Steen Lumholt. + #define USE_DEPRECATED_TOMMATH_API 1 + #endif + ++// As suggested by https://core.tcl-lang.org/tcl/wiki?name=Migrating+C+extensions+to+Tcl+9 ++#ifndef TCL_SIZE_MAX ++typedef int Tcl_Size; ++#define TCL_SIZE_MAX INT_MAX ++#endif ++ + #if !(defined(MS_WINDOWS) || defined(__CYGWIN__)) + #define HAVE_CREATEFILEHANDLER + #endif +@@ -316,6 +325,7 @@ typedef struct { + const Tcl_ObjType *ListType; + const Tcl_ObjType *ProcBodyType; + const Tcl_ObjType *StringType; ++ const Tcl_ObjType *UTF32StringType; + } TkappObject; + + #define Tkapp_Interp(v) (((TkappObject *) (v))->interp) +@@ -492,7 +502,7 @@ unicodeFromTclString(const char *s) + static PyObject * + unicodeFromTclObj(Tcl_Obj *value) + { +- int len; ++ Tcl_Size len; + #if USE_TCL_UNICODE + int byteorder = NATIVE_BYTEORDER; + const Tcl_UniChar *u = Tcl_GetUnicodeFromObj(value, &len); +@@ -520,6 +530,10 @@ class _tkinter.tktimertoken "TkttObject *" "&Tktt_Type_spec" + + /**** Tkapp Object ****/ + ++#if TK_MAJOR_VERSION >= 9 ++int Tcl_AppInit(Tcl_Interp *); ++#endif ++ + #ifndef WITH_APPINIT + int + Tcl_AppInit(Tcl_Interp *interp) +@@ -601,15 +615,41 @@ Tkapp_New(const char *screenName, const char *className, + } + + v->OldBooleanType = Tcl_GetObjType("boolean"); +- v->BooleanType = Tcl_GetObjType("booleanString"); +- v->ByteArrayType = Tcl_GetObjType("bytearray"); ++ { ++ Tcl_Obj *value; ++ int boolValue; ++ ++ /* Tcl 8.5 "booleanString" type is not registered ++ and is renamed to "boolean" in Tcl 9.0. ++ Based on approach suggested at ++ https://core.tcl-lang.org/tcl/info/3bb3bcf2da5b */ ++ value = Tcl_NewStringObj("true", -1); ++ Tcl_GetBooleanFromObj(NULL, value, &boolValue); ++ v->BooleanType = value->typePtr; ++ Tcl_DecrRefCount(value); ++ ++ // "bytearray" type is not registered in Tcl 9.0 ++ value = Tcl_NewByteArrayObj(NULL, 0); ++ v->ByteArrayType = value->typePtr; ++ Tcl_DecrRefCount(value); ++ } + v->DoubleType = Tcl_GetObjType("double"); ++ /* TIP 484 suggests retrieving the "int" type without Tcl_GetObjType("int") ++ since it is no longer registered in Tcl 9.0. But even though Tcl 8.7 ++ only uses the "wideInt" type on platforms with 32-bit long, it still has ++ a registered "int" type, which FromObj() should recognize just in case. */ + v->IntType = Tcl_GetObjType("int"); ++ if (v->IntType == NULL) { ++ Tcl_Obj *value = Tcl_NewIntObj(0); ++ v->IntType = value->typePtr; ++ Tcl_DecrRefCount(value); ++ } + v->WideIntType = Tcl_GetObjType("wideInt"); + v->BignumType = Tcl_GetObjType("bignum"); + v->ListType = Tcl_GetObjType("list"); + v->ProcBodyType = Tcl_GetObjType("procbody"); + v->StringType = Tcl_GetObjType("string"); ++ v->UTF32StringType = Tcl_GetObjType("utf32string"); + + /* Delete the 'exit' command, which can screw things up */ + Tcl_DeleteCommand(v->interp, "exit"); +@@ -1150,7 +1190,7 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + } + + if (value->typePtr == tkapp->ByteArrayType) { +- int size; ++ Tcl_Size size; + char *data = (char*)Tcl_GetByteArrayFromObj(value, &size); + return PyBytes_FromStringAndSize(data, size); + } +@@ -1159,14 +1199,6 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + return PyFloat_FromDouble(value->internalRep.doubleValue); + } + +- if (value->typePtr == tkapp->IntType) { +- long longValue; +- if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK) +- return PyLong_FromLong(longValue); +- /* If there is an error in the long conversion, +- fall through to wideInt handling. */ +- } +- + if (value->typePtr == tkapp->IntType || + value->typePtr == tkapp->WideIntType) { + result = fromWideIntObj(tkapp, value); +@@ -1184,8 +1216,8 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + } + + if (value->typePtr == tkapp->ListType) { +- int size; +- int i, status; ++ Tcl_Size i, size; ++ int status; + PyObject *elem; + Tcl_Obj *tcl_elem; + +@@ -1211,21 +1243,12 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) + return result; + } + +- if (value->typePtr == tkapp->ProcBodyType) { +- /* fall through: return tcl object. */ +- } +- +- if (value->typePtr == tkapp->StringType) { ++ if (value->typePtr == tkapp->StringType || ++ value->typePtr == tkapp->UTF32StringType) ++ { + return unicodeFromTclObj(value); + } + +- if (tkapp->BooleanType == NULL && +- strcmp(value->typePtr->name, "booleanString") == 0) { +- /* booleanString type is not registered in Tcl */ +- tkapp->BooleanType = value->typePtr; +- return fromBoolean(tkapp, value); +- } +- + if (tkapp->BignumType == NULL && + strcmp(value->typePtr->name, "bignum") == 0) { + /* bignum type is not registered in Tcl */ +@@ -1250,9 +1273,9 @@ typedef struct Tkapp_CallEvent { + } Tkapp_CallEvent; + + static void +-Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, int objc) ++Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, Tcl_Size objc) + { +- int i; ++ Tcl_Size i; + for (i = 0; i < objc; i++) + Tcl_DecrRefCount(objv[i]); + if (objv != objStore) +@@ -1263,7 +1286,7 @@ Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, int objc) + interpreter thread, which may or may not be the calling thread. */ + + static Tcl_Obj** +-Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc) ++Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, Tcl_Size *pobjc) + { + Tcl_Obj **objv = objStore; + Py_ssize_t objc = 0, i; +@@ -1311,10 +1334,10 @@ Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc) + Tcl_IncrRefCount(objv[i]); + } + } +- *pobjc = (int)objc; ++ *pobjc = (Tcl_Size)objc; + return objv; + finally: +- Tkapp_CallDeallocArgs(objv, objStore, (int)objc); ++ Tkapp_CallDeallocArgs(objv, objStore, (Tcl_Size)objc); + return NULL; + } + +@@ -1357,7 +1380,7 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags) + { + Tcl_Obj *objStore[ARGSZ]; + Tcl_Obj **objv; +- int objc; ++ Tcl_Size objc; + int i; + ENTER_PYTHON + objv = Tkapp_CallArgs(e->args, objStore, &objc); +@@ -1408,7 +1431,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) + { + Tcl_Obj *objStore[ARGSZ]; + Tcl_Obj **objv = NULL; +- int objc, i; ++ Tcl_Size objc; + PyObject *res = NULL; + TkappObject *self = (TkappObject*)selfptr; + int flags = TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL; +@@ -1454,6 +1477,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) + else + { + ++ int i; + objv = Tkapp_CallArgs(args, objStore, &objc); + if (!objv) + return NULL; +@@ -2139,13 +2163,12 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg) + /*[clinic end generated code: output=13b51d34386d36fb input=2b2e13351e3c0b53]*/ + { + char *list; +- int argc; ++ Tcl_Size argc, i; + const char **argv; + PyObject *v; +- int i; + + if (PyTclObject_Check(arg)) { +- int objc; ++ Tcl_Size objc; + Tcl_Obj **objv; + if (Tcl_ListObjGetElements(Tkapp_Interp(self), + ((PyTclObject*)arg)->value, diff --git a/cpython-unix/patch-write-python-for-build-3.12.patch b/cpython-unix/patch-write-python-for-build-3.12.patch new file mode 100644 index 000000000..b25927f66 --- /dev/null +++ b/cpython-unix/patch-write-python-for-build-3.12.patch @@ -0,0 +1,17 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 09ceccda1d..91e08cf28f 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -2769,6 +2769,12 @@ update-config: + + Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h + ++write-python-for-build: ++ echo "#!/bin/sh" > python-for-build ++ echo "set -e" >> python-for-build ++ echo "exec env $(PYTHON_FOR_BUILD) \$$@" >> python-for-build ++ chmod +x python-for-build ++ + ########################################################################## + # Module dependencies and platform-specific files + diff --git a/cpython-unix/patch-write-python-for-build.patch b/cpython-unix/patch-write-python-for-build.patch new file mode 100644 index 000000000..17cbc6895 --- /dev/null +++ b/cpython-unix/patch-write-python-for-build.patch @@ -0,0 +1,17 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +index f128444b98..d2013a2987 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -1930,6 +1930,12 @@ patchcheck: @DEF_MAKE_RULE@ + + Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h + ++write-python-for-build: ++ echo "#!/bin/sh" > python-for-build ++ echo "set -e" >> python-for-build ++ echo "exec env $(PYTHON_FOR_BUILD) \$$@" >> python-for-build ++ chmod +x python-for-build ++ + # Declare targets that aren't real files + .PHONY: all build_all sharedmods check-clean-src oldsharedmods test quicktest + .PHONY: install altinstall oldsharedinstall bininstall altbininstall diff --git a/cpython-unix/required-extensions.3.10.linux64 b/cpython-unix/required-extensions.3.10.linux64 deleted file mode 100644 index 5e419ea73..000000000 --- a/cpython-unix/required-extensions.3.10.linux64 +++ /dev/null @@ -1,8 +0,0 @@ -_codecs -_io -_signal -_thread -_tracemalloc -_weakref -faulthandler -posix diff --git a/cpython-unix/required-extensions.3.8.linux64 b/cpython-unix/required-extensions.3.8.linux64 deleted file mode 100644 index 5e419ea73..000000000 --- a/cpython-unix/required-extensions.3.8.linux64 +++ /dev/null @@ -1,8 +0,0 @@ -_codecs -_io -_signal -_thread -_tracemalloc -_weakref -faulthandler -posix diff --git a/cpython-unix/required-extensions.3.8.macos b/cpython-unix/required-extensions.3.8.macos deleted file mode 100644 index 5e419ea73..000000000 --- a/cpython-unix/required-extensions.3.8.macos +++ /dev/null @@ -1,8 +0,0 @@ -_codecs -_io -_signal -_thread -_tracemalloc -_weakref -faulthandler -posix diff --git a/cpython-unix/required-extensions.3.9.linux64 b/cpython-unix/required-extensions.3.9.linux64 deleted file mode 100644 index 5e419ea73..000000000 --- a/cpython-unix/required-extensions.3.9.linux64 +++ /dev/null @@ -1,8 +0,0 @@ -_codecs -_io -_signal -_thread -_tracemalloc -_weakref -faulthandler -posix diff --git a/cpython-unix/required-extensions.3.9.macos b/cpython-unix/required-extensions.3.9.macos deleted file mode 100644 index 5e419ea73..000000000 --- a/cpython-unix/required-extensions.3.9.macos +++ /dev/null @@ -1,8 +0,0 @@ -_codecs -_io -_signal -_thread -_tracemalloc -_weakref -faulthandler -posix diff --git a/cpython-unix/run_tests-13.py b/cpython-unix/run_tests-13.py new file mode 100644 index 000000000..a78210cc8 --- /dev/null +++ b/cpython-unix/run_tests-13.py @@ -0,0 +1,30 @@ +""" +Run Python's test suite. + +As of Python 3.13, this script is no longer included in Python itself. +Instead, use: + + $ python -m test --slow-ci + +""" + +import os +import sys + + +def main(regrtest_args): + args = [ + sys.executable, + "-m", + "test", + "--slow-ci", + ] + + args.extend(regrtest_args) + print(" ".join(args)) + + os.execv(sys.executable, args) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/cpython-unix/static-modules.3.10.i686-unknown-linux-gnu b/cpython-unix/static-modules.3.10.i686-unknown-linux-gnu deleted file mode 100644 index 2b5e93307..000000000 --- a/cpython-unix/static-modules.3.10.i686-unknown-linux-gnu +++ /dev/null @@ -1,42 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_32=1 -DPPRO=1 -DASM=1 -IModules/_decimal/libmpdec -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.10.linux64 b/cpython-unix/static-modules.3.10.linux64 deleted file mode 100644 index 3886cbda4..000000000 --- a/cpython-unix/static-modules.3.10.linux64 +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_64=1 -DASM=1 -IModules/_decimal/libmpdec -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.8.i686-unknown-linux-gnu b/cpython-unix/static-modules.3.8.i686-unknown-linux-gnu deleted file mode 100644 index 4c1ca62eb..000000000 --- a/cpython-unix/static-modules.3.8.i686-unknown-linux-gnu +++ /dev/null @@ -1,35 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/memory.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_32=1 -DPPRO=1 -DASM=1 -IModules/_decimal/libmpdec -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.8.linux64 b/cpython-unix/static-modules.3.8.linux64 deleted file mode 100644 index ba7515f85..000000000 --- a/cpython-unix/static-modules.3.8.linux64 +++ /dev/null @@ -1,36 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/memory.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_64=1 -DASM=1 -IModules/_decimal/libmpdec -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.8.macos b/cpython-unix/static-modules.3.8.macos deleted file mode 100644 index 2dbbc562c..000000000 --- a/cpython-unix/static-modules.3.8.macos +++ /dev/null @@ -1,35 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -# We link against the system ncurses on macOS for simplicity. There is no ncursesw -# but it is Unicode aware. -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -D_XOPEN_SOURCE_EXTENDED=1 -lncurses -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -D_XOPEN_SOURCE_EXTENDED=1 -lpanel -lncurses -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/darwin/dlfcn_simple.c _ctypes/malloc_closure.c _ctypes/stgdict.c _ctypes/cfield.c -DMACOSX -DUSING_MALLOC_CLOSURE_DOT_C=1 -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I_ctypes/darwin -lffi -ldl -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/memory.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DUNIVERSAL=1 -IModules/_decimal/libmpdec -# macOS ships with an ndbm implementation in libSystem. CPython's setup.py will -# use it unless an ndbl or gdbm_compat library is present. -_dbm _dbmmodule.c -DHAVE_NDBM_H -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_hashlib _hashopenssl.c -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -_queue _queuemodule.c -_scproxy _scproxy.c -framework SystemConfiguration -framework CoreFoundation -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -lsqlite3 -_ssl _ssl.c -lssl -lcrypto -# CoreFoundation isn't a directory dependency but is a dependency of libtcl and libtk. -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -framework AppKit -framework ApplicationServices -framework Carbon -framework CoreFoundation -framework CoreServices -framework CoreGraphics -framework IOKit -ltcl8.6 -ltk8.6 -_uuid _uuidmodule.c -luuid -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -readline readline.c -ledit -lncurses diff --git a/cpython-unix/static-modules.3.9.aarch64-apple-ios b/cpython-unix/static-modules.3.9.aarch64-apple-ios deleted file mode 100644 index e59a1ecb7..000000000 --- a/cpython-unix/static-modules.3.9.aarch64-apple-ios +++ /dev/null @@ -1,25 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/darwin/dlfcn_simple.c _ctypes/malloc_closure.c _ctypes/stgdict.c _ctypes/cfield.c -DMACOSX -DUSING_MALLOC_CLOSURE_DOT_C=1 -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I_ctypes/darwin -lffi -ldl -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DUNIVERSAL=1 -IModules/_decimal/libmpdec -# macOS ships with an ndbm implementation in libSystem. CPython's setup.py will -# use it unless an ndbl or gdbm_compat library is present. -_dbm _dbmmodule.c -DHAVE_NDBM_H -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_hashlib _hashopenssl.c -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -llzma -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -lsqlite3 -_ssl _ssl.c -lssl -lcrypto -_uuid _uuidmodule.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat diff --git a/cpython-unix/static-modules.3.9.aarch64-unknown-linux-gnu b/cpython-unix/static-modules.3.9.aarch64-unknown-linux-gnu deleted file mode 100644 index e6fd98f25..000000000 --- a/cpython-unix/static-modules.3.9.aarch64-unknown-linux-gnu +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_64=1 -DANSI=1 -DHAVE_UINT128_T=1 -IModules/_decimal/libmpdec -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.armv7-unknown-linux-gnueabi b/cpython-unix/static-modules.3.9.armv7-unknown-linux-gnueabi deleted file mode 100644 index 380c68ccc..000000000 --- a/cpython-unix/static-modules.3.9.armv7-unknown-linux-gnueabi +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_32=1 -DANSI=1 -IModules/_decimal/libmpdec -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.armv7-unknown-linux-gnueabihf b/cpython-unix/static-modules.3.9.armv7-unknown-linux-gnueabihf deleted file mode 100644 index 380c68ccc..000000000 --- a/cpython-unix/static-modules.3.9.armv7-unknown-linux-gnueabihf +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_32=1 -DANSI=1 -IModules/_decimal/libmpdec -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.i686-unknown-linux-gnu b/cpython-unix/static-modules.3.9.i686-unknown-linux-gnu deleted file mode 100644 index a6533c275..000000000 --- a/cpython-unix/static-modules.3.9.i686-unknown-linux-gnu +++ /dev/null @@ -1,44 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_32=1 -DPPRO=1 -DASM=1 -IModules/_decimal/libmpdec -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# Cannot build _gdbm due to use of bdb. -#_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.linux64 b/cpython-unix/static-modules.3.9.linux64 deleted file mode 100644 index 0f5858d52..000000000 --- a/cpython-unix/static-modules.3.9.linux64 +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_64=1 -DASM=1 -IModules/_decimal/libmpdec -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.macos b/cpython-unix/static-modules.3.9.macos deleted file mode 100644 index 9997ba12e..000000000 --- a/cpython-unix/static-modules.3.9.macos +++ /dev/null @@ -1,42 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -# We link against the system ncurses on macOS for simplicity. There is no ncursesw -# but it is Unicode aware. -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -D_XOPEN_SOURCE_EXTENDED=1 -lncurses -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -D_XOPEN_SOURCE_EXTENDED=1 -lpanel -lncurses -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/darwin/dlfcn_simple.c _ctypes/malloc_closure.c _ctypes/stgdict.c _ctypes/cfield.c -DMACOSX -DUSING_MALLOC_CLOSURE_DOT_C=1 -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I_ctypes/darwin -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DUNIVERSAL=1 -IModules/_decimal/libmpdec -# macOS ships with an ndbm implementation in libSystem. CPython's setup.py will -# use it unless an ndbl or gdbm_compat library is present. -_dbm _dbmmodule.c -DHAVE_NDBM_H -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_hashlib _hashopenssl.c -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -_queue _queuemodule.c -_scproxy _scproxy.c -framework SystemConfiguration -framework CoreFoundation -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -lsqlite3 -_ssl _ssl.c -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -# CoreFoundation isn't a directory dependency but is a dependency of libtcl and libtk. -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -framework AppKit -framework ApplicationServices -framework Carbon -framework CoreFoundation -framework CoreServices -framework CoreGraphics -framework IOKit -ltcl8.6 -ltk8.6 -_uuid _uuidmodule.c -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -readline readline.c -ledit -lncurses diff --git a/cpython-unix/static-modules.3.9.mips-unknown-linux-gnu b/cpython-unix/static-modules.3.9.mips-unknown-linux-gnu deleted file mode 100644 index 380c68ccc..000000000 --- a/cpython-unix/static-modules.3.9.mips-unknown-linux-gnu +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_32=1 -DANSI=1 -IModules/_decimal/libmpdec -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.mipsel-unknown-linux-gnu b/cpython-unix/static-modules.3.9.mipsel-unknown-linux-gnu deleted file mode 100644 index 380c68ccc..000000000 --- a/cpython-unix/static-modules.3.9.mipsel-unknown-linux-gnu +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_32=1 -DANSI=1 -IModules/_decimal/libmpdec -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.s390x-unknown-linux-gnu b/cpython-unix/static-modules.3.9.s390x-unknown-linux-gnu deleted file mode 100644 index e6fd98f25..000000000 --- a/cpython-unix/static-modules.3.9.s390x-unknown-linux-gnu +++ /dev/null @@ -1,43 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -lcrypt -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I/tools/deps/include -L/tools/deps/lib -lffi -ldl -_ctypes_test _ctypes/_ctypes_test.c -lm -_curses _cursesmodule.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lncursesw -_curses_panel _curses_panel.c -DHAVE_NCURSESW=1 -I/tools/deps/include/ncursesw -L/tools/deps/lib -lpanelw -lncursesw -_dbm _dbmmodule.c -DHAVE_BERKDB_H -DDB_DBM_HSEARCH -I/tools/deps/include -L/tools/deps/lib -ldb -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DCONFIG_64=1 -DANSI=1 -DHAVE_UINT128_T=1 -IModules/_decimal/libmpdec -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_gdbm _gdbmmodule.c -DHAVE_NDBM_H -I/tools/deps/include -L/tools/deps/lib -lgdbm -_hashlib _hashopenssl.c -I/tools/deps/include -L/tools/deps/lib -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I/tools/deps/include -L/tools/deps/lib -llzma -# TODO check setup.py logic for semaphore.c and possibly fix missing -# dependency. -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -lrt -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -I/tools/deps/include -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -L/tools/deps/lib -lsqlite3 -_ssl _ssl.c -I/tools/deps/include -lssl -lcrypto -_testbuffer _testbuffer.c -_testimportmultiple _testimportmultiple.c -_testinternalcapi _testinternalcapi.c -DPy_BUILD_CORE_MODULE -_testmultiphase _testmultiphase.c -_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT -I/tools/deps/include/X11 -L/tools/deps/lib -ltcl8.6 -ltk8.6 -lX11 -lxcb -lXau -_uuid _uuidmodule.c -I/tools/deps/include/uuid -luuid -_xxsubinterpreters _xxsubinterpretersmodule.c -_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c -ossaudiodev ossaudiodev.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -# readline variant needs to come first because libreadline is in /tools/deps and is -# picked up by build. We /could/ make libedit first. But since we employ a hack to -# coerce use of libedit on Linux, it seems prudent for the build system to pick -# up readline. -readline VARIANT=readline readline.c -I/tools/deps/include -I/tools/deps/include/ncursesw -L/tools/deps/lib -lreadline -lncursesw -readline VARIANT=libedit readline-libedit.c -DUSE_LIBEDIT=1 -I/tools/deps/libedit/include -I/tools/deps/libedit/include/ncursesw -L/tools/deps/libedit/lib -ledit -lncursesw diff --git a/cpython-unix/static-modules.3.9.x86_64-apple-ios b/cpython-unix/static-modules.3.9.x86_64-apple-ios deleted file mode 100644 index e59a1ecb7..000000000 --- a/cpython-unix/static-modules.3.9.x86_64-apple-ios +++ /dev/null @@ -1,25 +0,0 @@ -# Setup.dist doesn't have entries for all modules. This file defines -# what's missing. The content here is reconstructed from logic in -# setup.py and what was observed to execute in a normal build via setup.py. -# We should audit this every time we upgrade CPython. - -_bz2 _bz2module.c -lbz2 -_crypt _cryptmodule.c -_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/darwin/dlfcn_simple.c _ctypes/malloc_closure.c _ctypes/stgdict.c _ctypes/cfield.c -DMACOSX -DUSING_MALLOC_CLOSURE_DOT_C=1 -DHAVE_FFI_PREP_CIF_VAR=1 -DHAVE_FFI_PREP_CLOSURE_LOC=1 -DHAVE_FFI_CLOSURE_ALLOC=1 -I_ctypes/darwin -lffi -ldl -_decimal _decimal/_decimal.c _decimal/libmpdec/basearith.c _decimal/libmpdec/constants.c _decimal/libmpdec/context.c _decimal/libmpdec/convolute.c _decimal/libmpdec/crt.c _decimal/libmpdec/difradix2.c _decimal/libmpdec/fnt.c _decimal/libmpdec/fourstep.c _decimal/libmpdec/io.c _decimal/libmpdec/mpalloc.c _decimal/libmpdec/mpdecimal.c _decimal/libmpdec/numbertheory.c _decimal/libmpdec/sixstep.c _decimal/libmpdec/transpose.c -DUNIVERSAL=1 -IModules/_decimal/libmpdec -# macOS ships with an ndbm implementation in libSystem. CPython's setup.py will -# use it unless an ndbl or gdbm_compat library is present. -_dbm _dbmmodule.c -DHAVE_NDBM_H -_elementtree _elementtree.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat -_hashlib _hashopenssl.c -lssl -lcrypto -_json _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -llzma -_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_posixshmem _multiprocessing/posixshmem.c -IModules/_multiprocessing -_queue _queuemodule.c -_sqlite3 _sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c _sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c -IModules/_sqlite -DMODULE_NAME=\"sqlite3\" -DSQLITE_OMIT_LOAD_EXTENSION=1 -lsqlite3 -_ssl _ssl.c -lssl -lcrypto -_uuid _uuidmodule.c -pyexpat pyexpat.c expat/xmlparse.c expat/xmlrole.c expat/xmltok.c -DHAVE_EXPAT_CONFIG_H=1 -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI -IModules/expat diff --git a/cpython-unix/targets.yml b/cpython-unix/targets.yml index 2eed59243..588c2db79 100644 --- a/cpython-unix/targets.yml +++ b/cpython-unix/targets.yml @@ -25,6 +25,10 @@ # target_cc # Path to C compiler to use for building binaries targeting the # target architecture. + +# target_cxx +# Path to C++ compiler to use for building binaries targeting the +# target architecture. # # target_cflags # List of extra compiler flags to use when building for the target. @@ -45,6 +49,9 @@ # # openssl_target # Name of OpenSSL platform build target. +# +# bolt_capable +# Whether the target is capable of performing BOLT optimizations. --- @@ -54,13 +61,20 @@ # 11.0+. aarch64-apple-darwin: host_platforms: - - macos + - macos_arm64 + - macos_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ target_cc: clang + target_cxx: clang++ apple_sdk_platform: macosx target_cflags: - '-arch' @@ -76,339 +90,465 @@ aarch64-apple-darwin: # to work with the macOS SDKs out of the box. We work around # this by undoing the -Werror=undef-prefix in that commit. - '-Wno-undef-prefix' + - '-fvisibility=hidden' target_ldflags: - '-arch' - 'arm64' - '-mmacosx-version-min=11.0' needs: + - autoconf - bzip2 + - expat - libffi + - m4 + - mpdecimal + - openssl-3.5 - sqlite - tcl - tk - - tix - uuid - xz - - zlib + - zstd openssl_target: darwin64-arm64-cc -aarch64-apple-ios: +aarch64-unknown-linux-gnu: host_platforms: - - macos + - linux_x86_64 + - linux_aarch64 + - macos_arm64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' + docker_image_suffix: .debian9 needs_toolchain: true - apple_sdk_platform: iphoneos host_cc: clang host_cxx: clang++ target_cc: clang + target_cxx: clang++ target_cflags: - - '-arch' - - 'arm64' - - '-mios-version-min=12.3' - # Suppress extremely verbose warnings we see with LLVM 10. - - '-Wno-nullability-completeness' - - '-Wno-expansion-to-defined' - # LLVM 11 contains commit https://reviews.llvm.org/D83250, - # which enables -Werror for undef-prefix=TARGET_OS_. - # However, the macOS SDK has headers that reference deprecated - # TARGET_OS defines, like TARGET_OS_EMBEDDED. So LLVM 11 refuses - # to work with the macOS SDKs out of the box. We work around - # this by undoing the -Werror=undef-prefix in that commit. - - '-Wno-undef-prefix' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + # Needed to prevent BOLT from crashing. + - '-fdebug-default-version=4' target_ldflags: - - '-arch' - - 'arm64' - - '-mios-version-min=12.3' + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf + - bdb + - binutils - bzip2 + - expat + - libedit - libffi + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - ncurses + - openssl-3.5 + - patchelf - sqlite + - tcl + - tk + - uuid + - xorgproto - xz - zlib - openssl_target: ios64-cross + - zstd + openssl_target: linux-aarch64 + # Blocked on: + # BOLT-ERROR: Cannot relax adr in non-simple function + # trampoline_code_table/1. Use --strict option to override + # See https://github.com/llvm/llvm-project/issues/146541 + # bolt_capable: true -aarch64-unknown-linux-gnu: +armv7-unknown-linux-gnueabi: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ - target_cc: /usr/bin/aarch64-linux-gnu-gcc + target_cc: /usr/bin/arm-linux-gnueabi-gcc + target_cxx: /usr/bin/arm-linux-gnueabi-g++ + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gdbm + - expat - libedit - libffi - libX11 - libXau - libxcb + - m4 + - mpdecimal - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib - openssl_target: linux-aarch64 + - zstd + openssl_target: linux-armv4 -arm64-apple-tvos: +armv7-unknown-linux-gnueabihf: host_platforms: - - macos + - linux_x86_64 pythons_supported: - - '3.9' - needs_toolchain: true - apple_sdk_platform: appletvos - host_cc: clang - host_cxx: clang++ - target_cc: clang - target_cflags: - - '-arch' - - 'arm64' - - '-mappletvos-version-min=12.3' - # Suppress extremely verbose warnings we see with LLVM 10. - - '-Wno-nullability-completeness' - - '-Wno-expansion-to-defined' - # LLVM 11 contains commit https://reviews.llvm.org/D83250, - # which enables -Werror for undef-prefix=TARGET_OS_. - # However, the macOS SDK has headers that reference deprecated - # TARGET_OS defines, like TARGET_OS_EMBEDDED. So LLVM 11 refuses - # to work with the macOS SDKs out of the box. We work around - # this by undoing the -Werror=undef-prefix in that commit. - - '-Wno-undef-prefix' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' + docker_image_suffix: .cross + host_cc: /usr/bin/x86_64-linux-gnu-gcc + host_cxx: /usr/bin/x86_64-linux-gnu-g++ + target_cc: /usr/bin/arm-linux-gnueabihf-gcc + target_cxx: /usr/bin/arm-linux-gnueabihf-g++ target_ldflags: - - '-arch' - - 'arm64' - - '-mappletvos-version-min=12.3' + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf + - bdb + - binutils - bzip2 + - expat + - libedit + - libffi + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - ncurses + - openssl-3.5 + - patchelf - sqlite + - tcl + - tk + - uuid + - xorgproto - xz - zlib - openssl_target: todo + - zstd + openssl_target: linux-armv4 -armv7-unknown-linux-gnueabi: +loongarch64-unknown-linux-gnu: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.9' - docker_image_suffix: .cross + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + docker_image_suffix: .cross-loongarch64 host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ - target_cc: /usr/bin/arm-linux-gnueabi-gcc + target_cc: /usr/bin/loongarch64-linux-gnu-gcc + target_cxx: /usr/bin/loongarch64-linux-gnu-g++ + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gdbm + - expat - libedit - libffi - libX11 - libXau - libxcb + - m4 + - mpdecimal - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib - openssl_target: linux-armv4 + - zstd + openssl_target: linux64-loongarch64 -armv7-unknown-linux-gnueabihf: +mips-unknown-linux-gnu: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ - target_cc: /usr/bin/arm-linux-gnueabihf-gcc + target_cc: /usr/bin/mips-linux-gnu-gcc + target_cxx: /usr/bin/mips-linux-gnu-g++ + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gdbm + - expat - libedit - libffi - libX11 - libXau - libxcb + - m4 + - mpdecimal - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib - openssl_target: linux-armv4 + - zstd + openssl_target: linux-mips32 -i686-unknown-linux-gnu: +mipsel-unknown-linux-gnu: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.8' - - '3.9' - '3.10' - needs_toolchain: true - host_cc: clang - host_cxx: clang++ - target_cc: clang - target_cflags: - - '-m32' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' + docker_image_suffix: .cross + host_cc: /usr/bin/x86_64-linux-gnu-gcc + host_cxx: /usr/bin/x86_64-linux-gnu-g++ + target_cc: /usr/bin/mipsel-linux-gnu-gcc + target_cxx: /usr/bin/mipsel-linux-gnu-g++ target_ldflags: - - '-m32' + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gcc - # TODO cross-compiling has issues. - # - gdbm + - expat - libedit - libffi - libX11 - libXau - libxcb + - m4 + - mpdecimal - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib - openssl_target: linux-x86-clang + - zstd + openssl_target: linux-mips32 -mips-unknown-linux-gnu: +ppc64le-unknown-linux-gnu: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ - target_cc: /usr/bin/mips-linux-gnu-gcc + target_cc: /usr/bin/powerpc64le-linux-gnu-gcc + target_cxx: /usr/bin/powerpc64le-linux-gnu-g++ + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gdbm + - expat - libedit - libffi - libX11 - libXau - libxcb + - m4 + - mpdecimal - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib - openssl_target: linux-mips32 + - zstd + openssl_target: linux-ppc64le -mipsel-unknown-linux-gnu: +riscv64-unknown-linux-gnu: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.9' - docker_image_suffix: .cross + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' + docker_image_suffix: .cross-riscv64 host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ - target_cc: /usr/bin/mipsel-linux-gnu-gcc + target_cc: /usr/bin/riscv64-linux-gnu-gcc + target_cxx: /usr/bin/riscv64-linux-gnu-g++ + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gdbm + - expat - libedit - libffi - libX11 - libXau - libxcb + - m4 + - mpdecimal - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib - openssl_target: linux-mips32 + - zstd + openssl_target: linux64-riscv64 s390x-unknown-linux-gnu: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/s390x-linux-gnu-gcc + target_cxx: /usr/bin/s390x-linux-gnu-g++ + target_cflags: + # set the minimum compatibility level to z10 (released 2008) + - '-march=z10' + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gdbm + - expat - libedit - libffi - libX11 - libXau - libxcb + - m4 + - mpdecimal - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib + - zstd openssl_target: linux64-s390x -thumb7k-apple-watchos: +# Intel macOS. +# +# We target compatibility with macOS 10.15+ for compatibility with older Apple +# machines. +x86_64-apple-darwin: host_platforms: - - macos + - macos_arm64 + - macos_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true - apple_sdk_platform: watchos + apple_sdk_platform: macosx host_cc: clang host_cxx: clang++ target_cc: clang + target_cxx: clang++ target_cflags: - '-arch' - - 'armv7k' - - '-mwatchos-version-min-7.0' + - 'x86_64' + - '-mmacosx-version-min=10.15' # Suppress extremely verbose warnings we see with LLVM 10. - '-Wno-nullability-completeness' - '-Wno-expansion-to-defined' @@ -419,234 +559,491 @@ thumb7k-apple-watchos: # to work with the macOS SDKs out of the box. We work around # this by undoing the -Werror=undef-prefix in that commit. - '-Wno-undef-prefix' + - '-fvisibility=hidden' target_ldflags: - '-arch' - - 'armv7k' - - '-mwatchos-version-min-7.0' + - 'x86_64' + - '-mmacosx-version-min=10.15' needs: + - autoconf - bzip2 + - expat + - libffi + - m4 + - mpdecimal + - openssl-3.5 - sqlite + - tcl + - tk + - uuid - xz - - zlib - openssl_target: todo + - zstd + openssl_target: darwin64-x86_64-cc -# Intel macOS. -# -# We target compatibility with macOS 10.9+ for compatibility with older Apple -# machines. -x86_64-apple-darwin: +x86_64-unknown-linux-gnu: host_platforms: - - macos + - linux_x86_64 pythons_supported: - - '3.8' - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true - apple_sdk_platform: macosx host_cc: clang host_cxx: clang++ target_cc: clang + target_cxx: clang++ target_cflags: - - '-arch' - - 'x86_64' - - '-mmacosx-version-min=10.9' - # Suppress extremely verbose warnings we see with LLVM 10. - - '-Wno-nullability-completeness' - - '-Wno-expansion-to-defined' - # LLVM 11 contains commit https://reviews.llvm.org/D83250, - # which enables -Werror for undef-prefix=TARGET_OS_. - # However, the macOS SDK has headers that reference deprecated - # TARGET_OS defines, like TARGET_OS_EMBEDDED. So LLVM 11 refuses - # to work with the macOS SDKs out of the box. We work around - # this by undoing the -Werror=undef-prefix in that commit. - - '-Wno-undef-prefix' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + # Needed to prevent BOLT from crashing. + - '-fdebug-default-version=4' target_ldflags: - - '-arch' - - 'x86_64' - - '-mmacosx-version-min=10.9' + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf + - bdb + - binutils - bzip2 + - expat + - libedit - libffi + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - ncurses + - openssl-3.5 + - patchelf - sqlite - tcl - tk - - tix - uuid + - xorgproto - xz - zlib - openssl_target: darwin64-x86_64-cc + - zstd + openssl_target: linux-x86_64 + bolt_capable: true -x86_64-apple-ios: +x86_64_v2-unknown-linux-gnu: host_platforms: - - macos + - linux_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true - apple_sdk_platform: iphonesimulator host_cc: clang host_cxx: clang++ target_cc: clang + target_cxx: clang++ target_cflags: - - '-arch' - - 'x86_64' - - '-mios-simulator-version-min=12.3' - # Suppress extremely verbose warnings we see with LLVM 10. - - '-Wno-nullability-completeness' - - '-Wno-expansion-to-defined' - # LLVM 11 contains commit https://reviews.llvm.org/D83250, - # which enables -Werror for undef-prefix=TARGET_OS_. - # However, the macOS SDK has headers that reference deprecated - # TARGET_OS defines, like TARGET_OS_EMBEDDED. So LLVM 11 refuses - # to work with the macOS SDKs out of the box. We work around - # this by undoing the -Werror=undef-prefix in that commit. - - '-Wno-undef-prefix' + - '-march=x86-64-v2' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + # Needed to prevent BOLT from crashing. + - '-fdebug-default-version=4' target_ldflags: - - '-arch' - - 'x86_64' - - '-mios-simulator-version-min=12.3' + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf + - bdb + - binutils - bzip2 + - expat + - libedit - libffi + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - ncurses + - openssl-3.5 + - patchelf - sqlite + - tcl + - tk + - uuid + - xorgproto - xz - zlib - openssl_target: darwin64-x86_64-cc + - zstd + openssl_target: linux-x86_64 + bolt_capable: true -x86_64-apple-tvos: +x86_64_v3-unknown-linux-gnu: host_platforms: - - macos + - linux_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true - apple_sdk_platform: appletvsimulator host_cc: clang host_cxx: clang++ target_cc: clang + target_cxx: clang++ target_cflags: - - '-arch' - - 'x86_64' - - '-mappletvsimulator-version-min=12.3' - # Suppress extremely verbose warnings we see with LLVM 10. - - '-Wno-nullability-completeness' - - '-Wno-expansion-to-defined' - # LLVM 11 contains commit https://reviews.llvm.org/D83250, - # which enables -Werror for undef-prefix=TARGET_OS_. - # However, the macOS SDK has headers that reference deprecated - # TARGET_OS defines, like TARGET_OS_EMBEDDED. So LLVM 11 refuses - # to work with the macOS SDKs out of the box. We work around - # this by undoing the -Werror=undef-prefix in that commit. - - '-Wno-undef-prefix' + - '-march=x86-64-v3' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + # Needed to prevent BOLT from crashing. + - '-fdebug-default-version=4' target_ldflags: - - '-arch' - - 'x86_64' - - '-mappletvsimulator-version-min=12.3' + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf + - bdb + - binutils - bzip2 + - expat + - libedit + - libffi + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - ncurses + - openssl-3.5 + - patchelf - sqlite + - tcl + - tk + - uuid + - xorgproto - xz - openssl_target: todo + - zlib + - zstd + openssl_target: linux-x86_64 + bolt_capable: true -x86_64-apple-watchos: +x86_64_v4-unknown-linux-gnu: host_platforms: - - macos + - linux_x86_64 pythons_supported: - - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true - apple_sdk_platform: watchsimulator host_cc: clang host_cxx: clang++ target_cc: clang + target_cxx: clang++ target_cflags: - - '-arch' - - 'x86_64' - - '-mwatchsimulator-version-min=7.0' - # Suppress extremely verbose warnings we see with LLVM 10. - - '-Wno-nullability-completeness' - - '-Wno-expansion-to-defined' - # LLVM 11 contains commit https://reviews.llvm.org/D83250, - # which enables -Werror for undef-prefix=TARGET_OS_. - # However, the macOS SDK has headers that reference deprecated - # TARGET_OS defines, like TARGET_OS_EMBEDDED. So LLVM 11 refuses - # to work with the macOS SDKs out of the box. We work around - # this by undoing the -Werror=undef-prefix in that commit. - - '-Wno-undef-prefix' + - '-march=x86-64-v4' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + # Needed to prevent BOLT from crashing. + - '-fdebug-default-version=4' target_ldflags: - - '-arch' - - 'x86_64' - - '-mwatchsimulator-version-min=7.0' + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf + - bdb + - binutils - bzip2 + - expat + - libedit + - libffi + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - ncurses + - openssl-3.5 + - patchelf - sqlite + - tcl + - tk + - uuid + - xorgproto - xz - zlib - openssl_target: todo + - zstd + openssl_target: linux-x86_64 + bolt_capable: true -x86_64-unknown-linux-gnu: +x86_64-unknown-linux-musl: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.8' - - '3.9' - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ - target_cc: clang + target_cc: musl-clang + target_cxx: clang++ # TODO: Explore a musl-clang++ shim? + target_cflags: + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gcc - - gdbm + - expat - libedit - - libffi + - libffi-3.3 - libX11 - libXau - libxcb + - m4 + - mpdecimal + - musl - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib + - zstd openssl_target: linux-x86_64 -x86_64-unknown-linux-musl: +x86_64_v2-unknown-linux-musl: host_platforms: - - linux64 + - linux_x86_64 pythons_supported: - - '3.8' - - '3.9' - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ target_cc: musl-clang + target_cxx: clang++ # TODO: Explore a musl-clang++ shim? + target_cflags: + - '-march=x86-64-v2' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' needs: + - autoconf - bdb - binutils - bzip2 - - gcc - - gdbm + - expat - libedit - - libffi + - libffi-3.3 - libX11 - libXau - libxcb + - m4 + - mpdecimal - musl - ncurses + - openssl-3.5 - patchelf - - readline - sqlite - tcl - tk - - tix - uuid - xorgproto - xz - zlib - openssl_target: unsupported + - zstd + openssl_target: linux-x86_64 + +x86_64_v3-unknown-linux-musl: + host_platforms: + - linux_x86_64 + pythons_supported: + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' + needs_toolchain: true + host_cc: clang + host_cxx: clang++ + target_cc: musl-clang + target_cxx: clang++ # TODO: Explore a musl-clang++ shim? + target_cflags: + - '-march=x86-64-v3' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' + needs: + - autoconf + - bdb + - binutils + - bzip2 + - expat + - libedit + - libffi-3.3 + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - musl + - ncurses + - openssl-3.5 + - patchelf + - sqlite + - tcl + - tk + - uuid + - xorgproto + - xz + - zlib + - zstd + openssl_target: linux-x86_64 + +x86_64_v4-unknown-linux-musl: + host_platforms: + - linux_x86_64 + pythons_supported: + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' + needs_toolchain: true + host_cc: clang + host_cxx: clang++ + target_cc: musl-clang + target_cxx: clang++ # TODO: Explore a musl-clang++ shim? + target_cflags: + - '-march=x86-64-v4' + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' + needs: + - autoconf + - bdb + - binutils + - bzip2 + - expat + - libedit + - libffi-3.3 + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - musl + - ncurses + - openssl-3.5 + - patchelf + - sqlite + - tcl + - tk + - uuid + - xorgproto + - xz + - zlib + - zstd + openssl_target: linux-x86_64 + +aarch64-unknown-linux-musl: + host_platforms: + - linux_x86_64 + - linux_aarch64 + - macos_arm64 + pythons_supported: + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + - '3.15' + needs_toolchain: true + docker_image_suffix: .debian9 + needs_toolchain: true + host_cc: clang + host_cxx: clang++ + target_cc: musl-clang + target_cxx: clang++ + target_cflags: + - '-fvisibility=hidden' + # Enable frame pointers + - '-fno-omit-frame-pointer' + - '-mno-omit-leaf-frame-pointer' + target_ldflags: + # Hardening + - '-Wl,-z,noexecstack' + needs: + - autoconf + - bdb + - binutils + - bzip2 + - expat + - libedit + - libffi-3.3 + - libX11 + - libXau + - libxcb + - m4 + - mpdecimal + - musl + - ncurses + - openssl-3.5 + - patchelf + - sqlite + - tcl + - tk + - uuid + - xorgproto + - xz + - zlib + - zstd + openssl_target: linux-aarch64 diff --git a/cpython-unix/xcb.Dockerfile b/cpython-unix/xcb.Dockerfile deleted file mode 100644 index 0480ecae0..000000000 --- a/cpython-unix/xcb.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -{% include 'build.Dockerfile' %} -RUN apt-get install \ - python diff --git a/cpython-unix/xcb.cross.Dockerfile b/cpython-unix/xcb.cross.Dockerfile deleted file mode 100644 index cc003ff2a..000000000 --- a/cpython-unix/xcb.cross.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -{% include 'build.cross.Dockerfile' %} -RUN apt-get install \ - python diff --git a/cpython-windows/build.py b/cpython-windows/build.py index 26ea132cd..f93a59a50 100644 --- a/cpython-windows/build.py +++ b/cpython-windows/build.py @@ -7,6 +7,7 @@ import concurrent.futures import io import json +import multiprocessing import os import pathlib import re @@ -15,16 +16,20 @@ import sys import tempfile import zipfile -import multiprocessing +from pythonbuild.cpython import ( + STDLIB_TEST_PACKAGES, + meets_python_maximum_version, + meets_python_minimum_version, + parse_config_c, +) from pythonbuild.downloads import DOWNLOADS -from pythonbuild.cpython import parse_config_c, STDLIB_TEST_PACKAGES from pythonbuild.utils import ( + compress_python_archive, create_tar_from_directory, download_entry, extract_tar_to_directory, extract_zip_to_directory, - compress_python_archive, normalize_tar_archive, release_tag_from_git, validate_python_json, @@ -50,8 +55,7 @@ }, "_bz2": {}, "_ctypes": { - "shared_depends": ["libffi-7"], - "static_depends_no_project": ["libffi"], + "shared_depends": ["libffi-8"], }, "_decimal": {}, "_elementtree": {}, @@ -61,24 +65,39 @@ }, "_lzma": { "ignore_additional_depends": {"$(OutDir)liblzma$(PyDebugExt).lib"}, - "static_depends": ["liblzma"], }, - "_msi": {}, + "_msi": { + # Removed in 3.13. + "ignore_missing": True, + }, "_overlapped": {}, "_multiprocessing": {}, + "_remote_debugging": { + # Added in 3.14 + "ignore_missing": True + }, "_socket": {}, - "_sqlite3": {"shared_depends": ["sqlite3"], "static_depends": ["sqlite3"]}, + "_sqlite3": {"shared_depends": ["sqlite3"]}, # See the one-off calls to copy_link_to_lib() and elsewhere to hack up # project files. "_ssl": { "shared_depends_amd64": ["libcrypto-1_1-x64", "libssl-1_1-x64"], "shared_depends_win32": ["libcrypto-1_1", "libssl-1_1"], - "static_depends_no_project": ["libcrypto_static", "libssl_static"], }, - "_tkinter": {"ignore_static": True, "shared_depends": ["tcl86t", "tk86t"],}, + "_tkinter": { + "shared_depends": ["tcl86t", "tk86t"], + }, "_queue": {}, "_uuid": {"ignore_missing": True}, + "_wmi": { + # Introduced in 3.12. + "ignore_missing": True, + }, "_zoneinfo": {"ignore_missing": True}, + "_zstd": { + # Added in 3.14 + "ignore_missing": True + }, "pyexpat": {}, "select": {}, "unicodedata": {}, @@ -103,9 +122,10 @@ "_lzma": ["xz"], "_sqlite3": ["sqlite"], "_ssl": ["openssl"], - "_tkinter": ["tcl", "tk", "tix"], + "_tkinter": ["tcl-8612", "tk-8612", "tix"], "_uuid": ["uuid"], "zlib": ["zlib"], + "_zstd": ["zstd"], } @@ -149,7 +169,10 @@ "test_pprint", "test_re", "test_set", + # Renamed to test_sqlite3 in 3.11. We keep both names as we just look for + # test presence in this set. "test_sqlite", + "test_sqlite3", "test_statistics", "test_struct", "test_tabnanny", @@ -213,15 +236,25 @@ def find_vswhere(): return vswhere -def find_vs_path(path): +def find_vs_path(path, msvc_version): vswhere = find_vswhere() + if msvc_version == "2019": + version = "[16,17)" + elif msvc_version == "2022": + version = "[17,18)" + elif msvc_version == "2026": + version = "[18,19)" + else: + raise ValueError(f"unsupported Visual Studio version: {msvc_version}") + p = subprocess.check_output( [ str(vswhere), + "-utf8", # Visual Studio 2019. "-version", - "[16,17)", + version, "-property", "installationPath", "-products", @@ -229,7 +262,6 @@ def find_vs_path(path): ] ) - # Strictly speaking the output may not be UTF-8. p = pathlib.Path(p.strip().decode("utf-8")) p = p / path @@ -241,49 +273,18 @@ def find_vs_path(path): return p -def find_msbuild(): - return find_vs_path(pathlib.Path("MSBuild") / "Current" / "Bin" / "MSBuild.exe") - - -def find_vcvarsall_path(): - """Find path to vcvarsall.bat""" - return find_vs_path(pathlib.Path("VC") / "Auxiliary" / "Build" / "vcvarsall.bat") - - -def find_vctools_path(): - vswhere = find_vswhere() - - p = subprocess.check_output( - [ - str(vswhere), - "-latest", - "-products", - "*", - "-requires", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", - "installationPath", - ] +def find_msbuild(msvc_version): + return find_vs_path( + pathlib.Path("MSBuild") / "Current" / "Bin" / "MSBuild.exe", msvc_version ) - # Strictly speaking the output may not be UTF-8. - p = pathlib.Path(p.strip().decode("utf-8")) - version_path = ( - p / "VC" / "Auxiliary" / "Build" / "Microsoft.VCToolsVersion.default.txt" +def find_vcvarsall_path(msvc_version): + """Find path to vcvarsall.bat""" + return find_vs_path( + pathlib.Path("VC") / "Auxiliary" / "Build" / "vcvarsall.bat", msvc_version ) - with version_path.open("r", encoding="utf-8") as fh: - tools_version = fh.read().strip() - - tools_path = p / "VC" / "Tools" / "MSVC" / tools_version / "bin" / "Hostx64" / "x64" - - if not tools_path.exists(): - print("%s does not exist" % tools_path) - sys.exit(1) - - return tools_path - class NoSearchStringError(Exception): """Represents a missing search string when replacing content in a file.""" @@ -310,374 +311,7 @@ def static_replace_in_file(p: pathlib.Path, search, replace): fh.write(data) -def add_to_config_c(source_path: pathlib.Path, extension: str, init_fn: str): - """Add an extension to PC/config.c""" - - config_c_path = source_path / "PC" / "config.c" - - lines = [] - - with config_c_path.open("r", encoding="utf8") as fh: - for line in fh: - line = line.rstrip() - - # Insert the init function declaration before the _inittab struct. - if line.startswith("struct _inittab"): - log("adding %s declaration to config.c" % init_fn) - lines.append("extern PyObject* %s(void);" % init_fn) - - # Insert the extension in the _inittab struct. - if line.lstrip().startswith("/* Sentinel */"): - log("marking %s as a built-in extension module" % extension) - lines.append('{"%s", %s},' % (extension, init_fn)) - - lines.append(line) - - with config_c_path.open("w", encoding="utf8") as fh: - fh.write("\n".join(lines)) - - -def remove_from_extension_modules(source_path: pathlib.Path, extension: str): - """Remove an extension from the set of extension/external modules. - - Call this when an extension will be compiled into libpython instead of - compiled as a standalone extension. - """ - - RE_EXTENSION_MODULES = re.compile('<(Extension|External)Modules Include="([^"]+)"') - - pcbuild_proj_path = source_path / "PCbuild" / "pcbuild.proj" - - lines = [] - - with pcbuild_proj_path.open("r", encoding="utf8") as fh: - for line in fh: - line = line.rstrip() - - m = RE_EXTENSION_MODULES.search(line) - - if m: - modules = [m for m in m.group(2).split(";") if m != extension] - - # Ignore line if new value is empty. - if not modules: - continue - - line = line.replace(m.group(2), ";".join(modules)) - - lines.append(line) - - with pcbuild_proj_path.open("w", encoding="utf8") as fh: - fh.write("\n".join(lines)) - - -def make_project_static_library(source_path: pathlib.Path, project: str): - """Turn a project file into a static library.""" - - proj_path = source_path / "PCbuild" / ("%s.vcxproj" % project) - lines = [] - - found_config_type = False - found_target_ext = False - - with proj_path.open("r", encoding="utf8") as fh: - for line in fh: - line = line.rstrip() - - # Change the project configuration to a static library. - if "DynamicLibrary" in line: - log("changing %s to a static library" % project) - found_config_type = True - line = line.replace("DynamicLibrary", "StaticLibrary") - - elif "StaticLibrary" in line: - log("%s is already a static library" % project) - return - - # Change the output file name from .pyd to .lib because it is no - # longer an extension. - if ".pyd" in line: - log("changing output of %s to a .lib" % project) - found_target_ext = True - line = line.replace(".pyd", ".lib") - - lines.append(line) - - if not found_config_type: - log("failed to adjust config type for %s" % project) - sys.exit(1) - - if not found_target_ext: - log("failed to adjust target extension for %s" % project) - sys.exit(1) - - with proj_path.open("w", encoding="utf8") as fh: - fh.write("\n".join(lines)) - - -def convert_to_static_library( - source_path: pathlib.Path, - extension: str, - entry: dict, - honor_allow_missing_preprocessor: bool, -): - """Converts an extension to a static library.""" - - proj_path = source_path / "PCbuild" / ("%s.vcxproj" % extension) - - if not proj_path.exists() and entry.get("ignore_missing"): - return False - - # Make the extension's project emit a static library so we can link - # against libpython. - make_project_static_library(source_path, extension) - - # And do the same thing for its dependencies. - for project in entry.get("static_depends", []): - make_project_static_library(source_path, project) - - copy_link_to_lib(proj_path) - - lines = [] - - RE_PREPROCESSOR_DEFINITIONS = re.compile( - "]*>([^<]+)" - ) - - found_preprocessor = False - itemgroup_line = None - itemdefinitiongroup_line = None - - with proj_path.open("r", encoding="utf8") as fh: - for i, line in enumerate(fh): - line = line.rstrip() - - # Add Py_BUILD_CORE_BUILTIN to preprocessor definitions so linkage - # data is correct. - m = RE_PREPROCESSOR_DEFINITIONS.search(line) - - # But don't do it if it is an annotation for an individual source file. - if m and " entry. - if "" in line and not itemgroup_line: - itemgroup_line = i - - # Find the first entry. - if "" in line and not itemdefinitiongroup_line: - itemdefinitiongroup_line = i - - lines.append(line) - - if not found_preprocessor: - if honor_allow_missing_preprocessor and entry.get("allow_missing_preprocessor"): - log("not adjusting preprocessor definitions for %s" % extension) - else: - log("introducing to %s" % extension) - lines[itemgroup_line:itemgroup_line] = [ - " ", - " ", - " Py_BUILD_CORE_BUILTIN;%(PreprocessorDefinitions)", - " ", - " ", - ] - - itemdefinitiongroup_line = itemgroup_line + 1 - - if "static_depends" in entry: - if not itemdefinitiongroup_line: - log("unable to find for %s" % extension) - sys.exit(1) - - log("changing %s to automatically link library dependencies" % extension) - lines[itemdefinitiongroup_line + 1 : itemdefinitiongroup_line + 1] = [ - " ", - " true", - " ", - ] - - # Ensure the extension project doesn't depend on pythoncore: as a built-in - # extension, pythoncore will depend on it. - - # This logic is a bit hacky. Ideally we'd parse the file as XML and operate - # in the XML domain. But that is more work. The goal here is to strip the - # ... containing the - # {pythoncore ID}. This could leave an item . - # That should be fine. - start_line, end_line = None, None - for i, line in enumerate(lines): - if "{cf7ac3d1-e2df-41d2-bea6-1e2556cdea26}" in line: - for j in range(i, 0, -1): - if "" in lines[j]: - end_line = j - break - - break - - if start_line is not None and end_line is not None: - log("stripping pythoncore dependency from %s" % extension) - for line in lines[start_line : end_line + 1]: - log(line) - - lines = lines[:start_line] + lines[end_line + 1 :] - - with proj_path.open("w", encoding="utf8") as fh: - fh.write("\n".join(lines)) - - # Tell pythoncore to link against the static .lib. - RE_ADDITIONAL_DEPENDENCIES = re.compile( - "([^<]+)" - ) - - pythoncore_path = source_path / "PCbuild" / "pythoncore.vcxproj" - lines = [] - - with pythoncore_path.open("r", encoding="utf8") as fh: - for line in fh: - line = line.rstrip() - - m = RE_ADDITIONAL_DEPENDENCIES.search(line) - - if m: - log("changing pythoncore to link against %s.lib" % extension) - # TODO we shouldn't need this with static linking if the - # project is configured to link library dependencies. - # But removing it results in unresolved external symbols - # when linking the python project. There /might/ be a - # visibility issue with the PyMODINIT_FUNC macro. - line = line.replace( - m.group(1), r"$(OutDir)%s.lib;%s" % (extension, m.group(1)) - ) - - lines.append(line) - - with pythoncore_path.open("w", encoding="utf8") as fh: - fh.write("\n".join(lines)) - - # Change pythoncore to depend on the extension project. - - # pcbuild.proj is the file that matters for msbuild. And order within - # matters. We remove the extension from the "ExtensionModules" set of - # projects. Then we re-add the project to before "pythoncore." - remove_from_extension_modules(source_path, extension) - - pcbuild_proj_path = source_path / "PCbuild" / "pcbuild.proj" - - with pcbuild_proj_path.open("r", encoding="utf8") as fh: - data = fh.read() - - data = data.replace( - '', - ' \n ' - % extension, - ) - - with pcbuild_proj_path.open("w", encoding="utf8") as fh: - fh.write(data) - - # We don't technically need to modify the solution since msbuild doesn't - # use it. But it enables debugging inside Visual Studio, which is - # convenient. - RE_PROJECT = re.compile( - 'Project\("\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942\}"\) = "([^"]+)", "[^"]+", "{([^\}]+)\}"' - ) - - pcbuild_sln_path = source_path / "PCbuild" / "pcbuild.sln" - lines = [] - - extension_id = None - pythoncore_line = None - - with pcbuild_sln_path.open("r", encoding="utf8") as fh: - # First pass buffers the file, finds the ID of the extension project, - # and finds where the pythoncore project is defined. - for i, line in enumerate(fh): - line = line.rstrip() - - m = RE_PROJECT.search(line) - - if m and m.group(1) == extension: - extension_id = m.group(2) - - if m and m.group(1) == "pythoncore": - pythoncore_line = i - - lines.append(line) - - # Not all projects are in the solution(!!!). Since we don't use the - # solution for building, that's fine to ignore. - if not extension_id: - log("failed to find project %s in solution" % extension) - - if not pythoncore_line: - log("failed to find pythoncore project in solution") - - if extension_id and pythoncore_line: - log("making pythoncore depend on %s" % extension) - - needs_section = ( - not lines[pythoncore_line + 1].lstrip().startswith("ProjectSection") - ) - offset = 1 if needs_section else 2 - - lines.insert( - pythoncore_line + offset, "\t\t{%s} = {%s}" % (extension_id, extension_id) - ) - - if needs_section: - lines.insert( - pythoncore_line + 1, - "\tProjectSection(ProjectDependencies) = postProject", - ) - lines.insert(pythoncore_line + 3, "\tEndProjectSection") - - with pcbuild_sln_path.open("w", encoding="utf8") as fh: - fh.write("\n".join(lines)) - - return True - - -def copy_link_to_lib(p: pathlib.Path): - """Copy the contents of a section to a section.""" - - lines = [] - copy_lines = [] - copy_active = False - - with p.open("r", encoding="utf8") as fh: - for line in fh: - line = line.rstrip() - - lines.append(line) - - if "" in line: - copy_active = True - continue - - elif "" in line: - copy_active = False - - log("duplicating section in %s" % p) - lines.append(" ") - lines.extend(copy_lines) - lines.append(" ") - - if copy_active: - copy_lines.append(line) - - with p.open("w", encoding="utf8") as fh: - fh.write("\n".join(lines)) - - -OPENSSL_PROPS_REMOVE_RULES = b""" +OPENSSL_PROPS_REMOVE_RULES_LEGACY = b""" <_SSLDLL Include="$(opensslOutDir)\libcrypto$(_DLLSuffix).dll" /> <_SSLDLL Include="$(opensslOutDir)\libcrypto$(_DLLSuffix).pdb" /> @@ -692,6 +326,25 @@ def copy_link_to_lib(p: pathlib.Path): """ +OPENSSL_PROPS_REMOVE_RULES = b""" + + <_SSLDLL Include="$(opensslOutDir)\libcrypto$(_DLLSuffix).dll" /> + <_SSLDLL Include="$(opensslOutDir)\libcrypto$(_DLLSuffix).pdb" /> + <_SSLDLL Include="$(opensslOutDir)\libssl$(_DLLSuffix).dll" /> + <_SSLDLL Include="$(opensslOutDir)\libssl$(_DLLSuffix).pdb" /> + + + + + + + +""" + LIBFFI_PROPS_REMOVE_RULES = b""" @@ -700,7 +353,11 @@ def copy_link_to_lib(p: pathlib.Path): def hack_props( - td: pathlib.Path, pcbuild_path: pathlib.Path, arch: str, static: bool, + td: pathlib.Path, + pcbuild_path: pathlib.Path, + arch: str, + python_version: str, + zlib_entry: str, ): # TODO can we pass props into msbuild.exe? @@ -710,15 +367,25 @@ def hack_props( bzip2_version = DOWNLOADS["bzip2"]["version"] sqlite_version = DOWNLOADS["sqlite"]["version"] xz_version = DOWNLOADS["xz"]["version"] - zlib_version = DOWNLOADS["zlib"]["version"] - tcltk_commit = DOWNLOADS["tk-windows-bin"]["git_commit"] + zlib_version = DOWNLOADS[zlib_entry]["version"] + zstd_version = DOWNLOADS["zstd"]["version"] + + mpdecimal_version = DOWNLOADS["mpdecimal"]["version"] + + if meets_python_minimum_version(python_version, "3.14") or arch == "arm64": + tcltk_commit = DOWNLOADS["tk-windows-bin"]["git_commit"] + else: + tcltk_commit = DOWNLOADS["tk-windows-bin-8612"]["git_commit"] sqlite_path = td / ("sqlite-autoconf-%s" % sqlite_version) bzip2_path = td / ("bzip2-%s" % bzip2_version) libffi_path = td / "libffi" tcltk_path = td / ("cpython-bin-deps-%s" % tcltk_commit) xz_path = td / ("xz-%s" % xz_version) - zlib_path = td / ("zlib-%s" % zlib_version) + zlib_prefix = "cpython-source-deps-" if zlib_entry == "zlib-ng" else "" + zlib_path = td / ("%s%s-%s" % (zlib_prefix, zlib_entry, zlib_version)) + zstd_path = td / ("cpython-source-deps-zstd-%s" % zstd_version) + mpdecimal_path = td / ("mpdecimal-%s" % mpdecimal_version) openssl_root = td / "openssl" / arch openssl_libs_path = openssl_root / "lib" @@ -731,29 +398,43 @@ def hack_props( for line in fh: line = line.rstrip() - if b"" in line: + # The syntax of these lines changed in 3.10+. 3.10 backport commit + # 3139ea33ed84190e079d6ff4859baccdad778dae. Once we drop support for + # Python 3.9 we can pass these via properties instead of editing the + # properties file. + if b"%s\\" % bzip2_path - elif b"" in line: + elif b"%s\\" % libffi_path - elif b"" in line: + elif b"%s\\" % xz_path - elif b"" in line: + elif b"%s" % openssl_include_path ) - elif b"" in line: + elif b"%s\\" % openssl_libs_path - elif b"" in line: + elif b"%s\\" % sqlite_path - elif b"" in line: + elif b"%s\\" % zlib_path + # On 3.14+, it's zlib-ng and the name changed + elif b"%s\\" % zlib_path + + elif b"%s\\" % zstd_path + + elif b"%s\\" % mpdecimal_path + lines.append(line) with python_props_path.open("wb") as fh: @@ -761,81 +442,98 @@ def hack_props( tcltkprops_path = pcbuild_path / "tcltk.props" - static_replace_in_file( - tcltkprops_path, - br"$(ExternalsDir)tcltk-$(TclMajorVersion).$(TclMinorVersion).$(TclPatchLevel).$(TclRevision)\$(ArchName)\", - br"%s\$(ArchName)\" % tcltk_path, - ) + # Later versions of 3.10 and 3.11 enabled support for defining paths via properties. + # See CPython commit 3139ea33ed84190e079d6ff4859baccdad778dae. + # Once we drop support for CPython 3.9 we can replace this with passing properties. + try: + static_replace_in_file( + tcltkprops_path, + rb"""$(ExternalsDir)tcltk-$(TclVersion)\$(ArchName)\""", + rb"%s\$(ArchName)\" % tcltk_path, + ) + except NoSearchStringError: + static_replace_in_file( + tcltkprops_path, + rb"$(ExternalsDir)tcltk-$(TclMajorVersion).$(TclMinorVersion).$(TclPatchLevel).$(TclRevision)\$(ArchName)\", + rb"%s\$(ArchName)\" % tcltk_path, + ) # We want to statically link against OpenSSL. This requires using our own # OpenSSL build. This requires some hacking of various files. openssl_props = pcbuild_path / "openssl.props" - if static: - # We don't need the install rules to copy the libcrypto and libssl DLLs. + if arch == "amd64": + suffix = b"-x64" + elif arch == "win32": + suffix = b"" + elif arch == "arm64": + suffix = b"" + else: + raise Exception("unhandled architecture: %s" % arch) + + try: + # CPython 3.11+ builds with OpenSSL 3.x by default. static_replace_in_file( openssl_props, - OPENSSL_PROPS_REMOVE_RULES.strip().replace(b"\n", b"\r\n"), - b"", + b"<_DLLSuffix>-3", + b"<_DLLSuffix>-3%s" % suffix, ) - - # We need to copy linking settings for dynamic libraries to static libraries. - copy_link_to_lib(pcbuild_path / "libffi.props") - copy_link_to_lib(pcbuild_path / "openssl.props") - - # We should look against the static library variants. + except NoSearchStringError: static_replace_in_file( openssl_props, - b"libcrypto.lib;libssl.lib;", - b"libcrypto_static.lib;libssl_static.lib;", + b"<_DLLSuffix>-1_1", + b"<_DLLSuffix>-1_1%s" % suffix, ) - else: - if arch == "amd64": - suffix = b"x64" - elif arch == "win32": - suffix = None - else: - raise Exception("unhandled architecture: %s" % arch) - - if suffix: - static_replace_in_file( - openssl_props, - b"<_DLLSuffix>-1_1", - b"<_DLLSuffix>-1_1-%s" % suffix, - ) libffi_props = pcbuild_path / "libffi.props" - if static: - # For some reason the built .lib doesn't have the -7 suffix in - # static build mode. This is possibly a side-effect of CPython's - # libffi build script not officially supporting static-only builds. + # Always use libffi-8 / 3.4.2. (Python < 3.11 use libffi-7 by default.) + try: static_replace_in_file( libffi_props, - b"libffi-7.lib;%(AdditionalDependencies)", - b"libffi.lib;%(AdditionalDependencies)", + rb"""<_LIBFFIDLL Include="$(libffiOutDir)\libffi-7.dll" />""", + rb"""<_LIBFFIDLL Include="$(libffiOutDir)\libffi-8.dll" />""", ) - static_replace_in_file( - libffi_props, LIBFFI_PROPS_REMOVE_RULES.strip().replace(b"\n", b"\r\n"), b"" + libffi_props, + rb"libffi-7.lib;%(AdditionalDependencies)", + rb"libffi-8.lib;%(AdditionalDependencies)", ) + except NoSearchStringError: + pass def hack_project_files( td: pathlib.Path, cpython_source_path: pathlib.Path, build_directory: str, - static: bool, - honor_allow_missing_preprocessor: bool, + python_version: str, + zlib_entry: str, + arch: str, ): """Hacks Visual Studio project files to work with our build.""" pcbuild_path = cpython_source_path / "PCbuild" hack_props( - td, pcbuild_path, build_directory, static=static, + td, + pcbuild_path, + build_directory, + python_version, + zlib_entry, ) + # `--include-tcltk` is forced off on arm64, undo that + # See https://github.com/python/cpython/pull/132650 + try: + static_replace_in_file( + cpython_source_path / "PC" / "layout" / "main.py", + rb'if ns.arch in ("arm32", "arm64"):', + rb'if ns.arch == "arm32":', + ) + except NoSearchStringError: + pass + # Our SQLite directory is named weirdly. This throws off version detection # in the project file. Replace the parsing logic with a static string. sqlite3_version = DOWNLOADS["sqlite"]["actual_version"].encode("ascii") @@ -843,112 +541,143 @@ def hack_project_files( sqlite3_path = pcbuild_path / "sqlite3.vcxproj" static_replace_in_file( sqlite3_path, - br"<_SqliteVersion>$([System.Text.RegularExpressions.Regex]::Match(`$(sqlite3Dir)`, `((\d+)\.(\d+)\.(\d+)\.(\d+))\\?$`).Groups)", - br"<_SqliteVersion>%s" % sqlite3_version, + rb"<_SqliteVersion>$([System.Text.RegularExpressions.Regex]::Match(`$(sqlite3Dir)`, `((\d+)\.(\d+)\.(\d+)\.(\d+))\\?$`).Groups)", + rb"<_SqliteVersion>%s" % sqlite3_version, ) static_replace_in_file( sqlite3_path, - br"$(_SqliteVersion.Split(`;`)[1])", - br"%s" % sqlite3_version, + rb"$(_SqliteVersion.Split(`;`)[1])", + rb"%s" % sqlite3_version, ) static_replace_in_file( sqlite3_path, - br"$(_SqliteVersion.Split(`;`)[2])", - br"%s" % sqlite3_version_parts[0], + rb"$(_SqliteVersion.Split(`;`)[2])", + rb"%s" % sqlite3_version_parts[0], ) static_replace_in_file( sqlite3_path, - br"$(_SqliteVersion.Split(`;`)[3])", - br"%s" % sqlite3_version_parts[1], + rb"$(_SqliteVersion.Split(`;`)[3])", + rb"%s" % sqlite3_version_parts[1], ) static_replace_in_file( sqlite3_path, - br"$(_SqliteVersion.Split(`;`)[4])", - br"%s" % sqlite3_version_parts[2], + rb"$(_SqliteVersion.Split(`;`)[4])", + rb"%s" % sqlite3_version_parts[2], ) static_replace_in_file( sqlite3_path, - br"$(_SqliteVersion.Split(`;`)[5])", - br"%s" % sqlite3_version_parts[3], + rb"$(_SqliteVersion.Split(`;`)[5])", + rb"%s" % sqlite3_version_parts[3], ) - # Our version of the xz sources is newer than what's in cpython-source-deps - # and the xz sources changed the path to config.h. Hack the project file - # accordingly. - liblzma_path = pcbuild_path / "liblzma.vcxproj" - static_replace_in_file( - liblzma_path, - br"$(lzmaDir)windows;$(lzmaDir)src/liblzma/common;", - br"$(lzmaDir)windows\vs2017;$(lzmaDir)src/liblzma/common;", - ) - static_replace_in_file( - liblzma_path, - br'', - br'', + # Please try to keep these in sync with cpython-unix/build-sqlite.sh + sqlite_build_flags = { + b"SQLITE_ENABLE_DBSTAT_VTAB", + b"SQLITE_ENABLE_FTS3", + b"SQLITE_ENABLE_FTS3_PARENTHESIS", + b"SQLITE_ENABLE_FTS4", + b"SQLITE_ENABLE_FTS5", + b"SQLITE_ENABLE_GEOPOLY", + b"SQLITE_ENABLE_RTREE", + } + with sqlite3_path.open("rb") as fh: + data = fh.read() + sqlite_preprocessor_regex = ( + rb"(SQLITE_ENABLE.*)" ) - - # Our custom OpenSSL build has applink.c in a different location - # from the binary OpenSSL distribution. Update it. - ssl_proj = pcbuild_path / "_ssl.vcxproj" - static_replace_in_file( - ssl_proj, - br'', - br'', + m = re.search(sqlite_preprocessor_regex, data) + if m is None: + raise NoSearchStringError( + "search string (%s) not in %s" % (sqlite_preprocessor_regex, sqlite3_path) + ) + current_flags = set(m.group(1).split(b";")) + data = ( + data[: m.start(1)] + + b";".join(sqlite_build_flags - current_flags) + + b";" + + data[m.start(1) :] ) + with sqlite3_path.open("wb") as fh: + fh.write(data) - pythoncore_proj = pcbuild_path / "pythoncore.vcxproj" - - if static: - for extension, entry in sorted(CONVERT_TO_BUILTIN_EXTENSIONS.items()): - if entry.get("ignore_static"): - log("ignoring extension %s in static builds" % extension) - continue - - init_fn = entry.get("init", "PyInit_%s" % extension) - - if convert_to_static_library( - cpython_source_path, extension, entry, honor_allow_missing_preprocessor - ): - add_to_config_c(cpython_source_path, extension, init_fn) - - # pythoncore.vcxproj produces libpython. Typically pythonXY.dll. We change - # it to produce a static library. - pyproject_props = pcbuild_path / "pyproject.props" - - # Need to replace Py_ENABLE_SHARED with Py_NO_ENABLE_SHARED so symbol - # visibility is proper. - - # Replacing it in the global properties file has the most bang for our buck. - if static: + # Our version of the xz sources may be newer than what's in cpython-source-deps. + # The source files and locations may have changed. Hack the project file + # accordingly. + # + # CPython updates xz occasionally. When these changes make it into a release + # these modification to the project file are not needed. + # The most recent change was an update to version 5.8.1: + # https://github.com/python/cpython/pull/141022 + try: + liblzma_path = pcbuild_path / "liblzma.vcxproj" static_replace_in_file( - pyproject_props, - b"WIN32;", - b"Py_NO_ENABLE_SHARED;WIN32;", + liblzma_path, + rb"$(lzmaDir)windows/vs2019;$(lzmaDir)src/liblzma/common;", + rb"$(lzmaDir)windows;$(lzmaDir)src/liblzma/common;", ) - static_replace_in_file( - pythoncore_proj, b"Py_ENABLE_SHARED", b"Py_NO_ENABLE_SHARED" + liblzma_path, + b'\r\n \r\n', + b'\r\n ', ) - - # Disable whole program optimization because it interferes with the format - # of object files and makes it so we can't easily consume their symbols. - # TODO this /might/ be OK once we figure out symbol exporting issues. - if static: static_replace_in_file( - pyproject_props, - b"true", - b"false", + liblzma_path, + b'\r\n \r\n', + b'\r\n ', + ) + static_replace_in_file( + liblzma_path, + b'', + b'\r\n ', ) - - # Make libpython a static library. - if static: static_replace_in_file( - pythoncore_proj, - b"DynamicLibrary", - b"StaticLibrary", + liblzma_path, + rb'', + rb'', ) + except NoSearchStringError: + pass - copy_link_to_lib(pythoncore_proj) + # Our logic for rewriting extension projects gets confused by _sqlite.vcxproj not + # having a `` line in 3.10+. So adjust that. + try: + static_replace_in_file( + pcbuild_path / "_sqlite3.vcxproj", + rb"$(sqlite3Dir);%(AdditionalIncludeDirectories)", + b"$(sqlite3Dir);%(AdditionalIncludeDirectories)\r\n %(PreprocessorDefinitions)", + ) + except NoSearchStringError: + pass + + # Our custom OpenSSL build has applink.c in a different location from the + # binary OpenSSL distribution. This is no longer relevant for 3.12+ per + # https://github.com/python/cpython/pull/131839, so we allow it to fail.swe + try: + ssl_proj = pcbuild_path / "_ssl.vcxproj" + static_replace_in_file( + ssl_proj, + rb'', + rb'', + ) + except NoSearchStringError: + pass + + # Python 3.12+ uses the the pre-built tk-windows-bin 8.6.12 which doesn't + # have a standalone zlib DLL, so we remove references to it. For Python + # 3.14+, we're using tk-windows-bin 8.6.14 which includes a prebuilt zlib + # DLL, so we skip this patch there. + # On arm64, we use the new version of tk-windows-bin for all versions. + if meets_python_minimum_version(python_version, "3.12") and ( + meets_python_maximum_version(python_version, "3.13") or arch == "arm64" + ): + try: + static_replace_in_file( + pcbuild_path / "_tkinter.vcxproj", + rb'<_TclTkDLL Include="$(tcltkdir)\bin\$(tclZlibDllName)" />', + rb"", + ) + except NoSearchStringError: + pass # We don't need to produce python_uwp.exe and its *w variant. Or the # python3.dll, pyshellext, or pylauncher. @@ -962,10 +691,6 @@ def hack_project_files( b'', b"", ) - if static: - static_replace_in_file( - pcbuild_proj, b'', b"" - ) static_replace_in_file( pcbuild_proj, b'', @@ -978,323 +703,19 @@ def hack_project_files( # Ditto for freeze_importlib, which isn't needed since we don't modify # the frozen importlib baked into the source distribution ( # Python/importlib.h and Python/importlib_external.h). - static_replace_in_file( - pcbuild_proj, - b"""""", - b"", - ) - - # Switch to the static version of the run-time library. - if static: - static_replace_in_file( - pcbuild_path / "pyproject.props", - b"MultiThreadedDLL", - b"MultiThreaded", - ) - static_replace_in_file( - pcbuild_path / "pyproject.props", - b"MultiThreadedDebugDLL", - b"MultiThreadedDebug", - ) - - -PYPORT_EXPORT_SEARCH_39 = b""" -#if defined(__CYGWIN__) -# define HAVE_DECLSPEC_DLL -#endif - -#include "exports.h" - -/* only get special linkage if built as shared or platform is Cygwin */ -#if defined(Py_ENABLE_SHARED) || defined(__CYGWIN__) -# if defined(HAVE_DECLSPEC_DLL) -# if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) -# define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE -# define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE - /* module init functions inside the core need no external linkage */ - /* except for Cygwin to handle embedding */ -# if defined(__CYGWIN__) -# define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* -# else /* __CYGWIN__ */ -# define PyMODINIT_FUNC PyObject* -# endif /* __CYGWIN__ */ -# else /* Py_BUILD_CORE */ - /* Building an extension module, or an embedded situation */ - /* public Python functions and data are imported */ - /* Under Cygwin, auto-import functions to prevent compilation */ - /* failures similar to those described at the bottom of 4.1: */ - /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ -# if !defined(__CYGWIN__) -# define PyAPI_FUNC(RTYPE) Py_IMPORTED_SYMBOL RTYPE -# endif /* !__CYGWIN__ */ -# define PyAPI_DATA(RTYPE) extern Py_IMPORTED_SYMBOL RTYPE - /* module init functions outside the core must be exported */ -# if defined(__cplusplus) -# define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject* -# else /* __cplusplus */ -# define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* -# endif /* __cplusplus */ -# endif /* Py_BUILD_CORE */ -# endif /* HAVE_DECLSPEC_DLL */ -#endif /* Py_ENABLE_SHARED */ - -/* If no external linkage macros defined by now, create defaults */ -#ifndef PyAPI_FUNC -# define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE -#endif -#ifndef PyAPI_DATA -# define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE -#endif -#ifndef PyMODINIT_FUNC -# if defined(__cplusplus) -# define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject* -# else /* __cplusplus */ -# define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* -# endif /* __cplusplus */ -#endif -""" - -PYPORT_EXPORT_SEARCH_38 = b""" -#if defined(__CYGWIN__) -# define HAVE_DECLSPEC_DLL -#endif - -/* only get special linkage if built as shared or platform is Cygwin */ -#if defined(Py_ENABLE_SHARED) || defined(__CYGWIN__) -# if defined(HAVE_DECLSPEC_DLL) -# if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) -# define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE -# define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE - /* module init functions inside the core need no external linkage */ - /* except for Cygwin to handle embedding */ -# if defined(__CYGWIN__) -# define PyMODINIT_FUNC __declspec(dllexport) PyObject* -# else /* __CYGWIN__ */ -# define PyMODINIT_FUNC PyObject* -# endif /* __CYGWIN__ */ -# else /* Py_BUILD_CORE */ - /* Building an extension module, or an embedded situation */ - /* public Python functions and data are imported */ - /* Under Cygwin, auto-import functions to prevent compilation */ - /* failures similar to those described at the bottom of 4.1: */ - /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ -# if !defined(__CYGWIN__) -# define PyAPI_FUNC(RTYPE) __declspec(dllimport) RTYPE -# endif /* !__CYGWIN__ */ -# define PyAPI_DATA(RTYPE) extern __declspec(dllimport) RTYPE - /* module init functions outside the core must be exported */ -# if defined(__cplusplus) -# define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject* -# else /* __cplusplus */ -# define PyMODINIT_FUNC __declspec(dllexport) PyObject* -# endif /* __cplusplus */ -# endif /* Py_BUILD_CORE */ -# endif /* HAVE_DECLSPEC_DLL */ -#endif /* Py_ENABLE_SHARED */ - -/* If no external linkage macros defined by now, create defaults */ -#ifndef PyAPI_FUNC -# define PyAPI_FUNC(RTYPE) RTYPE -#endif -#ifndef PyAPI_DATA -# define PyAPI_DATA(RTYPE) extern RTYPE -#endif -#ifndef PyMODINIT_FUNC -# if defined(__cplusplus) -# define PyMODINIT_FUNC extern "C" PyObject* -# else /* __cplusplus */ -# define PyMODINIT_FUNC PyObject* -# endif /* __cplusplus */ -#endif -""" - -PYPORT_EXPORT_SEARCH_37 = b""" -#if defined(__CYGWIN__) -# define HAVE_DECLSPEC_DLL -#endif - -/* only get special linkage if built as shared or platform is Cygwin */ -#if defined(Py_ENABLE_SHARED) || defined(__CYGWIN__) -# if defined(HAVE_DECLSPEC_DLL) -# if defined(Py_BUILD_CORE) || defined(Py_BUILD_CORE_BUILTIN) -# define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE -# define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE - /* module init functions inside the core need no external linkage */ - /* except for Cygwin to handle embedding */ -# if defined(__CYGWIN__) -# define PyMODINIT_FUNC __declspec(dllexport) PyObject* -# else /* __CYGWIN__ */ -# define PyMODINIT_FUNC PyObject* -# endif /* __CYGWIN__ */ -# else /* Py_BUILD_CORE */ - /* Building an extension module, or an embedded situation */ - /* public Python functions and data are imported */ - /* Under Cygwin, auto-import functions to prevent compilation */ - /* failures similar to those described at the bottom of 4.1: */ - /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ -# if !defined(__CYGWIN__) -# define PyAPI_FUNC(RTYPE) __declspec(dllimport) RTYPE -# endif /* !__CYGWIN__ */ -# define PyAPI_DATA(RTYPE) extern __declspec(dllimport) RTYPE - /* module init functions outside the core must be exported */ -# if defined(__cplusplus) -# define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject* -# else /* __cplusplus */ -# define PyMODINIT_FUNC __declspec(dllexport) PyObject* -# endif /* __cplusplus */ -# endif /* Py_BUILD_CORE */ -# endif /* HAVE_DECLSPEC_DLL */ -#endif /* Py_ENABLE_SHARED */ - -/* If no external linkage macros defined by now, create defaults */ -#ifndef PyAPI_FUNC -# define PyAPI_FUNC(RTYPE) RTYPE -#endif -#ifndef PyAPI_DATA -# define PyAPI_DATA(RTYPE) extern RTYPE -#endif -#ifndef PyMODINIT_FUNC -# if defined(__cplusplus) -# define PyMODINIT_FUNC extern "C" PyObject* -# else /* __cplusplus */ -# define PyMODINIT_FUNC PyObject* -# endif /* __cplusplus */ -#endif -""" - -PYPORT_EXPORT_REPLACE_NEW = b""" -#include "exports.h" -#define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE -#define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE -#define PyMODINIT_FUNC __declspec(dllexport) PyObject* -""" - -PYPORT_EXPORT_REPLACE_OLD = b""" -#define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE -#define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE -#define PyMODINIT_FUNC __declspec(dllexport) PyObject* -""" - -CTYPES_INIT_REPLACE = b""" -if _os.name == "nt": - pythonapi = PyDLL("python dll", None, _sys.dllhandle) -elif _sys.platform == "cygwin": - pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) -else: - pythonapi = PyDLL(None) -""" - - -def hack_source_files(source_path: pathlib.Path, static: bool): - """Apply source modifications to make things work.""" - - # The PyAPI_FUNC, PyAPI_DATA, and PyMODINIT_FUNC macros define symbol - # visibility. By default, pyport.h looks at Py_ENABLE_SHARED, __CYGWIN__, - # Py_BUILD_CORE, Py_BUILD_CORE_BUILTIN, etc to determine what the macros - # should be. The logic assumes that Python is being built in a certain - # manner - notably that extensions are standalone dynamic libraries. - # - # We force the use of __declspec(dllexport) in all cases to ensure that - # API symbols are exported. This annotation becomes embedded within the - # object file. When that object file is linked, the symbol is exported - # from the final binary. For statically linked binaries, this behavior - # may not be needed. However, by exporting the symbols we allow downstream - # consumers of the object files to produce a binary that can be - # dynamically linked. This is a useful property to have. - if static: - pyport_h = source_path / "Include" / "pyport.h" - try: - static_replace_in_file( - pyport_h, PYPORT_EXPORT_SEARCH_39, PYPORT_EXPORT_REPLACE_NEW - ) - except NoSearchStringError: - try: - static_replace_in_file( - pyport_h, PYPORT_EXPORT_SEARCH_38, PYPORT_EXPORT_REPLACE_OLD - ) - except NoSearchStringError: - static_replace_in_file( - pyport_h, PYPORT_EXPORT_SEARCH_37, PYPORT_EXPORT_REPLACE_OLD - ) - - # Modules/_winapi.c and Modules/overlapped.c both define an - # ``OverlappedType`` symbol. We rename one to make the symbol conflict - # go away. - # TODO send this patch upstream. - if static: - overlapped_c = source_path / "Modules" / "overlapped.c" - static_replace_in_file(overlapped_c, b"OverlappedType", b"OOverlappedType") - - # Modules/ctypes/callbacks.c has lines like the following: - # #ifndef Py_NO_ENABLE_SHARED - # BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvRes) - # We currently define Py_ENABLE_SHARED. And I /think/ this check should - # also check against Py_BUILD_CORE_BUILTIN because Py_BUILD_CORE_BUILTIN - # with Py_ENABLE_SHARED is theoretically a valid configuration. - # TODO send this patch upstream. - if static: - callbacks_c = source_path / "Modules" / "_ctypes" / "callbacks.c" - static_replace_in_file( - callbacks_c, - b"#ifndef Py_NO_ENABLE_SHARED\nBOOL WINAPI DllMain(", - b"#if !defined(Py_NO_ENABLE_SHARED) && !defined(Py_BUILD_CORE_BUILTIN)\nBOOL WINAPI DllMain(", - ) - - # Lib/ctypes/__init__.py needs to populate the Python API version. On - # Windows, it assumes a ``pythonXY`` is available. On Cygwin, a - # ``libpythonXY`` DLL. The former assumes that ``sys.dllhandle`` is - # available. And ``sys.dllhandle`` is only populated if ``MS_COREDLL`` - # (a deprecated symbol) is defined. And ``MS_COREDLL`` is not defined - # if ``Py_NO_ENABLE_SHARED`` is defined. The gist of it is that ctypes - # assumes that Python on Windows will use a Python DLL. # - # The ``pythonapi`` handle obtained in ``ctypes/__init__.py`` needs to - # expose a handle on the Python API. If we have a static library, that - # handle should be the current binary. So all the fancy logic to find - # the DLL can be simplified. - # - # But, ``PyDLL(None)`` doesn't work out of the box because this is - # translated into a call to ``LoadLibrary(NULL)``. Unlike ``dlopen()``, - # ``LoadLibrary()`` won't accept a NULL value. So, we need a way to - # get an ``HMODULE`` for the current executable. Arguably the best way - # to do this is with ``GetModuleHandleEx()`` using the following C code: - # - # HMODULE hModule = NULL; - # GetModuleHandleEx( - # GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - # (LPCSTR)SYMBOL_IN_CURRENT_MODULE, - # &hModule); - # - # The ``ctypes`` module has handles on function pointers in the current - # binary. One would think we'd be able to use ``ctypes.cast()`` + - # ``ctypes.addressof()`` to get a pointer to a symbol in the current - # executable. But the addresses appear to be to heap allocated PyObject - # instances, which won't work. - # - # An ideal solution would be to expose the ``HMODULE`` of the current - # module. We /should/ be able to change the behavior of ``sys.dllhandle`` - # to facilitate this. But this is a bit more work. Our hack is to instead - # use ``sys.executable`` with ``LoadLibrary()``. This should hopefully be - # "good enough." - # - # TODO improve the logic upstream - if static: - ctypes_init = source_path / "Lib" / "ctypes" / "__init__.py" + # But Python 3.11 refactored the frozen module project handling and if + # we attempt to disable this project there we get a build failure due to + # a missing /Python/frozen_modules/getpath.h file. So we skip this on + # newer Python. + try: static_replace_in_file( - ctypes_init, - CTYPES_INIT_REPLACE.strip(), - b"pythonapi = PyDLL(_sys.executable)", - ) - - # Producing statically linked binaries invalidates assumptions in the - # layout tool. Update the tool accordingly. - layout_main = source_path / "PC" / "layout" / "main.py" - - # We no longer have a pythonXX.dll file. - if static: - static_replace_in_file( - layout_main, b" yield from in_build(PYTHON_DLL_NAME)\n", b"" + pcbuild_proj, + b"""""", + b"", ) + except NoSearchStringError: + pass def run_msbuild( @@ -1302,8 +723,9 @@ def run_msbuild( pcbuild_path: pathlib.Path, configuration: str, platform: str, - static: bool, python_version: str, + windows_sdk_version: str, + freethreaded: bool, ): args = [ str(msbuild), @@ -1316,14 +738,23 @@ def run_msbuild( "/verbosity:normal", "/property:IncludeExternals=true", "/property:IncludeSSL=true", - # TODO support tkinter in static builds. - "/property:IncludeTkinter=%s" % ("false" if static else "true"), - # TODO support test extensions in static builds. - "/property:IncludeTests=%s" % ("false" if static else "true"), + "/property:IncludeTkinter=true", + "/property:IncludeTests=true", "/property:OverrideVersion=%s" % python_version, "/property:IncludeCTypes=true", + # We pin the Windows 10 SDK version to make builds more deterministic. + # This can also work around known incompatibilities with the Windows 11 + # SDK as of at least CPython 3.9.7. + f"/property:DefaultWindowsSDKVersion={windows_sdk_version}", ] + if freethreaded: + args.append("/property:DisableGil=true") + + # Build tail-calling Python for 3.15+ + if python_version.startswith("3.15") and platform == "x64": + args.append("/property:UseTailCallInterp=true") + exec_and_log(args, str(pcbuild_path), os.environ) @@ -1331,23 +762,22 @@ def build_openssl_for_arch( perl_path, arch: str, openssl_archive, + openssl_version: str, nasm_archive, build_root: pathlib.Path, - profile: str, *, jom_archive, ): - openssl_version = DOWNLOADS["openssl"]["version"] nasm_version = DOWNLOADS["nasm-windows-bin"]["version"] log("extracting %s to %s" % (openssl_archive, build_root)) extract_tar_to_directory(openssl_archive, build_root) log("extracting %s to %s" % (nasm_archive, build_root)) - extract_tar_to_directory(nasm_archive, build_root) + extract_zip_to_directory(nasm_archive, build_root) log("extracting %s to %s" % (jom_archive, build_root)) extract_zip_to_directory(jom_archive, build_root / "jom") - nasm_path = build_root / ("cpython-bin-deps-nasm-%s" % nasm_version) + nasm_path = build_root / ("nasm-%s" % nasm_version) jom_path = build_root / "jom" env = dict(os.environ) @@ -1359,12 +789,11 @@ def build_openssl_for_arch( # uplink.c tries to find the OPENSSL_Applink function exported from the current # executable. However, it is exported from _ssl[_d].pyd in shared builds. So # update its sounce to look for it from there. - if "shared" in profile: - static_replace_in_file( - source_root / "ms" / "uplink.c", - b"((h = GetModuleHandle(NULL)) == NULL)", - b'((h = GetModuleHandleA("_ssl.pyd")) == NULL) if ((h = GetModuleHandleA("_ssl_d.pyd")) == NULL) if ((h = GetModuleHandle(NULL)) == NULL)', - ) + static_replace_in_file( + source_root / "ms" / "uplink.c", + b"((h = GetModuleHandle(NULL)) == NULL)", + b'((h = GetModuleHandleA("_ssl.pyd")) == NULL) if ((h = GetModuleHandleA("_ssl_d.pyd")) == NULL) if ((h = GetModuleHandle(NULL)) == NULL)', + ) if arch == "x86": configure = "VC-WIN32" @@ -1372,9 +801,11 @@ def build_openssl_for_arch( elif arch == "amd64": configure = "VC-WIN64A" prefix = "64" + elif arch == "arm64": + configure = "VC-WIN64-ARM" + prefix = "arm64" else: - print("invalid architecture: %s" % arch) - sys.exit(1) + raise Exception("unhandled architecture: %s" % arch) # The official CPython OpenSSL builds hack ms/uplink.c to change the # ``GetModuleHandle(NULL)`` invocation to load things from _ssl.pyd @@ -1397,12 +828,12 @@ def build_openssl_for_arch( "--prefix=/%s" % prefix, ], source_root, - {**env, "CFLAGS": env.get("CFLAGS", "") + " /FS",}, + { + **env, + "CFLAGS": env.get("CFLAGS", "") + " /FS", + }, ) - if "static" in profile: - static_replace_in_file(source_root / "Makefile", b"/MD", b"/MT") - # exec_and_log(["nmake"], source_root, env) exec_and_log( [str(jom_path / "jom"), "/J", str(multiprocessing.cpu_count())], @@ -1422,14 +853,25 @@ def build_openssl_for_arch( log("copying %s to %s" % (source, dest)) shutil.copyfile(source, dest) + # Copy `applink.c` to the include directory. + source_applink = source_root / "ms" / "applink.c" + dest_applink = install_root / "include" / "openssl" / "applink.c" + log("copying %s to %s" % (source_applink, dest_applink)) + shutil.copyfile(source_applink, dest_applink) + def build_openssl( - perl_path: pathlib.Path, arch: str, profile: str, dest_archive: pathlib.Path + entry: str, + perl_path: pathlib.Path, + arch: str, + dest_archive: pathlib.Path, ): """Build OpenSSL from sources using the Perl executable specified.""" + openssl_version = DOWNLOADS[entry]["version"] + # First ensure the dependencies are in place. - openssl_archive = download_entry("openssl", BUILD) + openssl_archive = download_entry(entry, BUILD) nasm_archive = download_entry("nasm-windows-bin", BUILD) jom_archive = download_entry("jom-windows-bin", BUILD) @@ -1438,6 +880,7 @@ def build_openssl( root_32 = td / "x86" root_64 = td / "x64" + root_arm64 = td / "arm64" if arch == "x86": root_32.mkdir() @@ -1445,9 +888,9 @@ def build_openssl( perl_path, "x86", openssl_archive, + openssl_version, nasm_archive, root_32, - profile, jom_archive=jom_archive, ) elif arch == "amd64": @@ -1456,18 +899,33 @@ def build_openssl( perl_path, "amd64", openssl_archive, + openssl_version, nasm_archive, root_64, - profile, + jom_archive=jom_archive, + ) + elif arch == "arm64": + root_arm64.mkdir() + build_openssl_for_arch( + perl_path, + "arm64", + openssl_archive, + openssl_version, + nasm_archive, + root_arm64, jom_archive=jom_archive, ) else: - raise ValueError("unhandled arch: %s" % arch) + raise Exception("unhandled architecture: %s" % arch) install = td / "out" if arch == "x86": shutil.copytree(root_32 / "install" / "32", install / "openssl" / "win32") + elif arch == "arm64": + shutil.copytree( + root_arm64 / "install" / "arm64", install / "openssl" / "arm64" + ) else: shutil.copytree(root_64 / "install" / "64", install / "openssl" / "amd64") @@ -1479,8 +937,8 @@ def build_libffi( python: str, arch: str, sh_exe: pathlib.Path, + msvc_version: str, dest_archive: pathlib.Path, - static: bool, ): with tempfile.TemporaryDirectory(prefix="libffi-build-") as td: td = pathlib.Path(td) @@ -1511,7 +969,7 @@ def build_libffi( "-c", "core.autocrlf=input", "checkout", - "ed22026f39b37f892ded95d7b30e77dfb5126334", + "16fad4855b3d8c03b5910e405ff3a04395b39a98", ], cwd=ffi_source_path, check=True, @@ -1525,39 +983,27 @@ def build_libffi( prepare_libffi = ( td / ("Python-%s" % python_entry["version"]) - / "PCBuild" + / "PCbuild" / "prepare_libffi.bat" ) - if static: - # We replace FFI_BUILDING_DLL with FFI_BUILDING so - # declspec(dllexport) isn't used. - # We add USE_STATIC_RTL to force static linking of the crt. - static_replace_in_file( - prepare_libffi, - b"CPPFLAGS='-DFFI_BUILDING_DLL'", - b"CPPFLAGS='-DFFI_BUILDING -DUSE_STATIC_RTL'", - ) - - # We also need to tell configure to only build a static library. - static_replace_in_file( - prepare_libffi, - b"--build=$BUILD --host=$HOST;", - b"--build=$BUILD --host=$HOST --disable-shared;", - ) - env = dict(os.environ) env["LIBFFI_SOURCE"] = str(ffi_source_path) - env["VCVARSALL"] = str(find_vcvarsall_path()) + env["VCVARSALL"] = str(find_vcvarsall_path(msvc_version)) env["SH"] = str(sh_exe) args = [str(prepare_libffi), "-pdb"] if arch == "x86": args.append("-x86") artifacts_path = ffi_source_path / "i686-pc-cygwin" - else: + elif arch == "arm64": + args.append("-arm64") + artifacts_path = ffi_source_path / "aarch64-w64-cygwin" + elif arch == "amd64": args.append("-x64") artifacts_path = ffi_source_path / "x86_64-w64-cygwin" + else: + raise Exception("unhandled architecture: %s" % arch) subprocess.run(args, env=env, check=True) @@ -1588,7 +1034,9 @@ def collect_python_build_artifacts( python_majmin: str, arch: str, config: str, - static: bool, + openssl_entry: str, + zlib_entry: str, + freethreaded: bool, ): """Collect build artifacts from Python. @@ -1622,6 +1070,8 @@ def collect_python_build_artifacts( # We don't care about build artifacts for the python executable. "python", "pythonw", + # Used to bootstrap interpreter. + "_freeze_module", # Don't care about venvlauncher executable. "venvlauncher", "venvwlauncher", @@ -1629,17 +1079,21 @@ def collect_python_build_artifacts( "_ctypes_test", "_testbuffer", "_testcapi", + "_testclinic_limited", + "_testclinic", "_testconsole", "_testembed", "_testimportmultiple", "_testinternalcapi", + "_testlimitedcapi", "_testmultiphase", + "_testsinglephase", + "xxlimited_35", "xxlimited", } other_projects = {"pythoncore"} - if not static: - other_projects.add("python3dll") + other_projects.add("python3dll") # Projects providing dependencies. depends_projects = set() @@ -1650,9 +1104,6 @@ def collect_python_build_artifacts( dirs = {p for p in os.listdir(intermediates_path)} for extension, entry in CONVERT_TO_BUILTIN_EXTENSIONS.items(): - if static and entry.get("ignore_static"): - continue - if extension not in dirs: if entry.get("ignore_missing"): continue @@ -1661,14 +1112,14 @@ def collect_python_build_artifacts( sys.exit(1) extension_projects.add(extension) - if static: - depends_projects |= set(entry.get("static_depends", [])) - if not static: - depends_projects |= { - "liblzma", - "sqlite3", - } + depends_projects |= { + "liblzma", + "sqlite3", + } + + if zlib_entry == "zlib-ng": + depends_projects |= {"zlib-ng"} known_projects = ( ignore_projects | other_projects | depends_projects | extension_projects @@ -1714,6 +1165,22 @@ def find_additional_dependencies(project: pathlib.Path): return set() + if arch == "amd64": + abi_platform = "win_amd64" + elif arch == "win32": + abi_platform = "win32" + elif arch == "arm64": + abi_platform = "win_arm64" + else: + raise Exception("unhandled architecture: %s" % arch) + + if freethreaded: + abi_tag = ".cp%st-%s" % (python_majmin, abi_platform) + lib_suffix = "t" + else: + abi_tag = "" + lib_suffix = "" + # Copy object files for core sources into their own directory. core_dir = out_dir / "build" / "core" core_dir.mkdir(parents=True) @@ -1731,38 +1198,30 @@ def find_additional_dependencies(project: pathlib.Path): res["inittab_source"] = "build/core/config.c" res["inittab_cflags"] = ["-DNDEBUG", "-DPy_BUILD_CORE"] - if static: - exts = ("lib",) - else: - exts = ("lib", "exp") + exts = ("lib", "exp") for ext in exts: - source = outputs_path / ("python%s.%s" % (python_majmin, ext)) - dest = core_dir / ("python%s.%s" % (python_majmin, ext)) + source = outputs_path / ("python%s%s.%s" % (python_majmin, lib_suffix, ext)) + dest = core_dir / ("python%s%s.%s" % (python_majmin, lib_suffix, ext)) log("copying %s" % source) shutil.copyfile(source, dest) - if static: - res["core"]["static_lib"] = "build/core/python%s.lib" % python_majmin - else: - res["core"]["shared_lib"] = "install/python%s.dll" % python_majmin + res["core"]["shared_lib"] = "install/python%s%s.dll" % (python_majmin, lib_suffix) # We hack up pythoncore.vcxproj and the list in it when this function # runs isn't totally accurate. We hardcode the list from the CPython # distribution. # TODO pull from unaltered file res["core"]["links"] = [ - {"name": "shlwapi", "system": True}, {"name": "version", "system": True}, {"name": "ws2_32", "system": True}, - # pathcch is Windows 8+ only. Python 3.9 dropped support for Windows 7. - # So this dependency is technically incorrect on Python 3.8. - {"name": "pathcch", "system": True}, # In addition to the ones explicitly in the project, there are some # implicit link libraries not present. We list those as well. {"name": "Ole32", "system": True}, {"name": "OleAut32", "system": True}, {"name": "User32", "system": True}, + # Presence of pathcch drops support for Windows 7. + {"name": "pathcch", "system": True}, ] # Copy files for extensions into their own directories. @@ -1790,35 +1249,17 @@ def find_additional_dependencies(project: pathlib.Path): for obj in process_project(ext, dest_dir): entry["objs"].append("build/extensions/%s/%s" % (ext, obj)) - if static: - for lib in CONVERT_TO_BUILTIN_EXTENSIONS.get(ext, {}).get( - "static_depends", [] - ): - entry["links"].append( - {"name": lib, "path_static": "build/lib/%s.lib" % lib} - ) - else: - for lib in CONVERT_TO_BUILTIN_EXTENSIONS.get(ext, {}).get( - "shared_depends", [] - ): - entry["links"].append( - {"name": lib, "path_dynamic": "install/DLLs/%s.dll" % lib} - ) - - for lib in CONVERT_TO_BUILTIN_EXTENSIONS.get(ext, {}).get( - "shared_depends_%s" % arch, [] - ): - entry["links"].append( - {"name": lib, "path_dynamic": "install/DLLs/%s.dll" % lib} - ) + for lib in CONVERT_TO_BUILTIN_EXTENSIONS.get(ext, {}).get("shared_depends", []): + entry["links"].append( + {"name": lib, "path_dynamic": "install/DLLs/%s.dll" % lib} + ) - if static: - for lib in CONVERT_TO_BUILTIN_EXTENSIONS.get(ext, {}).get( - "static_depends_no_project", [] - ): - entry["links"].append( - {"name": lib, "path_static": "build/lib/%s.lib" % lib} - ) + for lib in CONVERT_TO_BUILTIN_EXTENSIONS.get(ext, {}).get( + "shared_depends_%s" % arch, [] + ): + entry["links"].append( + {"name": lib, "path_dynamic": "install/DLLs/%s.dll" % lib} + ) if ext in EXTENSION_TO_LIBRARY_DOWNLOADS_ENTRY: licenses = set() @@ -1826,6 +1267,16 @@ def find_additional_dependencies(project: pathlib.Path): license_public_domain = False for name in EXTENSION_TO_LIBRARY_DOWNLOADS_ENTRY[ext]: + if name == "openssl": + name = openssl_entry + + if name == "zlib": + name = zlib_entry + + # On 3.14+ and aarch64, we use the latest tcl/tk version + if ext == "_tkinter" and (python_majmin == "314" or arch == "arm64"): + name = name.replace("-8612", "") + download_entry = DOWNLOADS[name] # This will raise if no license metadata defined. This is @@ -1842,18 +1293,15 @@ def find_additional_dependencies(project: pathlib.Path): res["extensions"][ext] = [entry] # Copy the extension static library. - ext_static = outputs_path / ("%s.lib" % ext) - dest = dest_dir / ("%s.lib" % ext) + ext_static = outputs_path / ("%s%s.lib" % (ext, abi_tag)) + dest = dest_dir / ("%s%s.lib" % (ext, abi_tag)) log("copying static extension %s" % ext_static) shutil.copyfile(ext_static, dest) - if static: - res["extensions"][ext][0]["static_lib"] = "build/extensions/%s/%s.lib" % ( - ext, - ext, - ) - else: - res["extensions"][ext][0]["shared_lib"] = "install/DLLs/%s.pyd" % ext + res["extensions"][ext][0]["shared_lib"] = "install/DLLs/%s%s.pyd" % ( + ext, + abi_tag, + ) lib_dir = out_dir / "build" / "lib" lib_dir.mkdir() @@ -1879,34 +1327,73 @@ def build_cpython( python_entry_name: str, target_triple: str, arch: str, - profile, + build_options: str, + msvc_version: str, + windows_sdk_version: str, openssl_archive, libffi_archive, -): - static = "static" in profile - pgo = "-pgo" in profile + openssl_entry: str, +) -> pathlib.Path: + parsed_build_options = set(build_options.split("+")) + pgo = "pgo" in parsed_build_options + freethreaded = "freethreaded" in parsed_build_options - msbuild = find_msbuild() + msbuild = find_msbuild(msvc_version) log("found MSBuild at %s" % msbuild) # The python.props file keys off MSBUILD, so it needs to be set. os.environ["MSBUILD"] = str(msbuild) + python_archive = download_entry(python_entry_name, BUILD) + entry = DOWNLOADS[python_entry_name] + python_version = entry["version"] + + zlib_entry = ( + "zlib-ng" if meets_python_minimum_version(python_version, "3.14") else "zlib" + ) + bzip2_archive = download_entry("bzip2", BUILD) sqlite_archive = download_entry("sqlite", BUILD) + xz_archive = download_entry("xz", BUILD) + zlib_archive = download_entry(zlib_entry, BUILD) + + setuptools_wheel = download_entry("setuptools", BUILD) + pip_wheel = download_entry("pip", BUILD) + + # On CPython 3.14+, we use the latest tcl/tk version which has additional + # runtime dependencies, so we are conservative and use the old version + # elsewhere. The old version isn't built for arm64, so we use the new + # version there too + tk_bin_entry = ( + "tk-windows-bin" + if meets_python_minimum_version(python_version, "3.14") or arch == "arm64" + else "tk-windows-bin-8612" + ) tk_bin_archive = download_entry( - "tk-windows-bin", BUILD, local_name="tk-windows-bin.tar.gz" + tk_bin_entry, BUILD, local_name="tk-windows-bin.tar.gz" ) - xz_archive = download_entry("xz", BUILD) - zlib_archive = download_entry("zlib", BUILD) - - python_archive = download_entry(python_entry_name, BUILD) - entry = DOWNLOADS[python_entry_name] - python_version = entry["version"] + # On CPython 3.14+, zstd is included + if meets_python_minimum_version(python_version, "3.14"): + zstd_archive = download_entry("zstd", BUILD) + else: + zstd_archive = None - setuptools_archive = download_entry("setuptools", BUILD) - pip_archive = download_entry("pip", BUILD) + # CPython 3.13+ no longer uses a bundled `mpdecimal` version so we build it + if meets_python_minimum_version(python_version, "3.13"): + mpdecimal_archive = download_entry("mpdecimal", BUILD) + else: + # TODO: Consider using the built mpdecimal for earlier versions as well, + # as we do for Unix builds. + mpdecimal_archive = None + + if freethreaded: + (major, minor, _) = python_version.split(".") + python_exe = f"python{major}.{minor}t.exe" + pythonw_exe = f"pythonw{major}.{minor}t.exe" + else: + python_exe = "python.exe" + pythonw_exe = "pythonw.exe" if arch == "amd64": build_platform = "x64" @@ -1914,10 +1401,16 @@ def build_cpython( elif arch == "x86": build_platform = "win32" build_directory = "win32" + elif arch == "arm64": + build_platform = "arm64" + build_directory = "arm64" else: - raise ValueError("unhandled arch: %s" % arch) + raise Exception("unhandled architecture: %s" % arch) - with tempfile.TemporaryDirectory(prefix="python-build-") as td: + tempdir_opts = ( + {"ignore_cleanup_errors": True} if sys.version_info >= (3, 12) else {} + ) + with tempfile.TemporaryDirectory(prefix="python-build-", **tempdir_opts) as td: td = pathlib.Path(td) with concurrent.futures.ThreadPoolExecutor(10) as e: @@ -1925,41 +1418,64 @@ def build_cpython( for a in ( python_archive, bzip2_archive, + mpdecimal_archive, openssl_archive, - pip_archive, - setuptools_archive, sqlite_archive, tk_bin_archive, xz_archive, zlib_archive, + zstd_archive, ): + if a is None: + continue + log("extracting %s to %s" % (a, td)) fs.append(e.submit(extract_tar_to_directory, a, td)) for f in fs: f.result() + # Copy the config.h file used by upstream CPython for xz 5.8.1 + # https://github.com/python/cpython-source-deps/blob/665d407bd6bc941944db2152e4b5dca388ea586e/windows/config.h + xz_version = DOWNLOADS["xz"]["version"] + xz_path = td / ("xz-%s" % xz_version) + config_src = SUPPORT / "xz-support" / "config.h" + config_dest = xz_path / "windows" / "config.h" + log(f"copying {config_src} to {config_dest}") + shutil.copyfile(config_src, config_dest) + extract_tar_to_directory(libffi_archive, td) # We need all the OpenSSL library files in the same directory to appease # install rules. - if not static: - openssl_arch = {"amd64": "amd64", "x86": "win32"}[arch] - openssl_root = td / "openssl" / openssl_arch - openssl_bin_path = openssl_root / "bin" - openssl_lib_path = openssl_root / "lib" - - for f in sorted(os.listdir(openssl_bin_path)): - if not f.startswith("lib"): - continue + openssl_arch = {"amd64": "amd64", "x86": "win32", "arm64": "arm64"}[arch] + openssl_root = td / "openssl" / openssl_arch + openssl_bin_path = openssl_root / "bin" + openssl_lib_path = openssl_root / "lib" + + for f in sorted(os.listdir(openssl_bin_path)): + if not f.startswith("lib"): + continue + + source = openssl_bin_path / f + dest = openssl_lib_path / f + log("copying %s to %s" % (source, dest)) + shutil.copyfile(source, dest) - source = openssl_bin_path / f - dest = openssl_lib_path / f - log("copying %s to %s" % (source, dest)) - shutil.copyfile(source, dest) + # Delete the tk nmake helper, it's not needed and links msvc + if tk_bin_entry == "tk-windows-bin": + tcltk_commit: str = DOWNLOADS[tk_bin_entry]["git_commit"] + tcltk_path = td / ("cpython-bin-deps-%s" % tcltk_commit) + ( + tcltk_path + / build_directory + / "lib" + / "nmake" + / "x86_64-w64-mingw32-nmakehlp.exe" + ).unlink() cpython_source_path = td / ("Python-%s" % python_version) - pcbuild_path = cpython_source_path / "PCBuild" + pcbuild_path = cpython_source_path / "PCbuild" out_dir = td / "out" @@ -1978,10 +1494,10 @@ def build_cpython( td, cpython_source_path, build_directory, - static=static, - honor_allow_missing_preprocessor=python_entry_name == "cpython-3.8", + python_version=python_version, + zlib_entry=zlib_entry, + arch=arch, ) - hack_source_files(cpython_source_path, static=static) if pgo: run_msbuild( @@ -1989,8 +1505,9 @@ def build_cpython( pcbuild_path, configuration="PGInstrument", platform=build_platform, - static=static, python_version=python_version, + windows_sdk_version=windows_sdk_version, + freethreaded=freethreaded, ) # build-windows.py sets some environment variables which cause the @@ -2001,7 +1518,8 @@ def build_cpython( p for p in env["PATH"].split(";") if p != str(BUILD / "venv" / "bin") ] env["PATH"] = ";".join(paths) - del env["PYTHONPATH"] + if "PYTHONPATH" in env: + del env["PYTHONPATH"] env["PYTHONHOME"] = str(cpython_source_path) @@ -2010,7 +1528,7 @@ def build_cpython( # test execution. We work around this by invoking the test harness # separately for each test. instrumented_python = ( - pcbuild_path / build_directory / "instrumented" / "python.exe" + pcbuild_path / build_directory / "instrumented" / python_exe ) tests = subprocess.run( @@ -2054,8 +1572,9 @@ def build_cpython( pcbuild_path, configuration="PGUpdate", platform=build_platform, - static=static, python_version=python_version, + windows_sdk_version=windows_sdk_version, + freethreaded=freethreaded, ) artifact_config = "PGUpdate" @@ -2065,8 +1584,9 @@ def build_cpython( pcbuild_path, configuration="Release", platform=build_platform, - static=static, python_version=python_version, + windows_sdk_version=windows_sdk_version, + freethreaded=freethreaded, ) artifact_config = "Release" @@ -2094,34 +1614,82 @@ def build_cpython( "--temp", str(layout_tmp), "--include-dev", - "--include-distutils", "--include-symbols", "--include-tests", "--include-venv", ] - if static: - args.append("--flat-dlls") - else: - args.extend(["--include-idle", "--include-stable", "--include-tcltk"]) + if freethreaded: + args.append("--include-freethreaded") - exec_and_log( - args, pcbuild_path, os.environ, - ) + # CPython 3.12 removed distutils. + if not meets_python_minimum_version(python_version, "3.12"): + args.append("--include-distutils") + + args.extend(["--include-idle", "--include-stable", "--include-tcltk"]) - # Install setuptools and pip. exec_and_log( - [str(install_dir / "python.exe"), "setup.py", "install"], - str(td / ("setuptools-%s" % DOWNLOADS["setuptools"]["version"])), + args, + pcbuild_path, os.environ, ) + # We install pip by using pip to install itself. This leverages a feature + # where Python can automatically recognize wheel/zip files on sys.path and + # import their contents. According to + # https://github.com/pypa/pip/issues/11146 running pip from a wheel is not + # supported. But it has historically worked and is simple. So do this until + # it stops working and we need to switch to running pip from the filesystem. + pip_env = dict(os.environ) + pip_env["PYTHONPATH"] = str(pip_wheel) + + # Install pip and setuptools. exec_and_log( - [str(install_dir / "python.exe"), "setup.py", "install"], - str(td / ("pip-%s" % DOWNLOADS["pip"]["version"])), - os.environ, + [ + str(install_dir / python_exe), + "-m", + "pip", + "install", + "--no-cache-dir", + "--no-index", + str(pip_wheel), + ], + td, + pip_env, ) + # Setuptools is only installed for Python 3.11 and older, for parity with + # `ensurepip` and `venv`: https://github.com/python/cpython/pull/101039 + if meets_python_maximum_version(python_version, "3.11"): + exec_and_log( + [ + str(install_dir / python_exe), + "-m", + "pip", + "install", + "--no-cache-dir", + "--no-index", + str(setuptools_wheel), + ], + td, + pip_env, + ) + + # The executables in the Scripts/ directory don't work because they reference + # python.dll in the wrong path. You can run these via e.g. `python.exe -m pip`. + # So just delete them for now. + for filename in sorted(os.listdir(install_dir / "Scripts")): + assert filename.startswith("pip") and filename.endswith(".exe") + p = install_dir / "Scripts" / filename + log("removing non-functional executable: %s" % p) + os.unlink(p) + + # But this leaves the Scripts directory empty, which we don't want. So + # create a placeholder file to ensure the directory is created on archive + # extract. + with (install_dir / "Scripts" / ".empty").open("ab"): + pass + # Now copy the build artifacts into the output directory. build_info = collect_python_build_artifacts( pcbuild_path, @@ -2129,7 +1697,9 @@ def build_cpython( "".join(entry["version"].split(".")[0:2]), build_directory, artifact_config, - static=static, + openssl_entry=openssl_entry, + zlib_entry=zlib_entry, + freethreaded=freethreaded, ) for ext, init_fn in sorted(builtin_extensions.items()): @@ -2153,29 +1723,45 @@ def build_cpython( for record in entries: record["required"] = extension in REQUIRED_EXTENSIONS - # Copy libffi static library as a one-off. - if static: - source = td / "libffi" / "libffi.lib" - dest = out_dir / "python" / "build" / "lib" / "libffi.lib" - log("copying %s to %s" % (source, dest)) - shutil.copyfile(source, dest) - # Copy OpenSSL libraries as a one-off. for lib in ("crypto", "ssl"): - if static: - name = "lib%s_static.lib" % lib - else: - name = "lib%s.lib" % lib + name = "lib%s.lib" % lib source = td / "openssl" / build_directory / "lib" / name dest = out_dir / "python" / "build" / "lib" / name log("copying %s to %s" % (source, dest)) shutil.copyfile(source, dest) - shutil.copyfile( - cpython_source_path / "Tools" / "scripts" / "run_tests.py", - out_dir / "python" / "build" / "run_tests.py", - ) + # Create a `python.exe` copy when an alternative executable is built, e.g., when + # free-threading is enabled the name is `python3.13t.exe`. + canonical_python_exe = install_dir / "python.exe" + if not canonical_python_exe.exists(): + shutil.copy2( + install_dir / python_exe, + canonical_python_exe, + ) + + # Create a `pythonw.exe` copy when an alternative executable is built, e.g., when + # free-threading is enabled the name is `pythonw3.13t.exe`. + canonical_pythonw_exe = install_dir / "pythonw.exe" + if not canonical_pythonw_exe.exists(): + shutil.copy2( + install_dir / pythonw_exe, + canonical_pythonw_exe, + ) + + # CPython 3.13 removed `run_tests.py`, we provide a compatibility script + # for now. + if meets_python_minimum_version(python_version, "3.13"): + shutil.copyfile( + SUPPORT / "run_tests-13.py", + out_dir / "python" / "build" / "run_tests.py", + ) + else: + shutil.copyfile( + cpython_source_path / "Tools" / "scripts" / "run_tests.py", + out_dir / "python" / "build" / "run_tests.py", + ) licenses_dir = out_dir / "python" / "licenses" licenses_dir.mkdir() @@ -2183,38 +1769,31 @@ def build_cpython( if f.startswith("LICENSE.") and f.endswith(".txt"): shutil.copyfile(ROOT / f, licenses_dir / f) - extension_module_loading = ["builtin"] - - # Static builds do not support loading extension modules, since Python - # symbols are not exported. - if not static: - extension_module_loading.append("shared-library") + extension_module_loading = ["builtin", "shared-library"] # Patches to CPython above (search for __declspec) always force # __declspec(dllexport), even for static distributions. python_symbol_visibility = "dllexport" - if static: - crt_features = ["static"] - else: - crt_features = ["vcruntime:140"] + crt_features = ["vcruntime:140"] - if "pgo" in profile: + if pgo: optimizations = "pgo" else: optimizations = "noopt" # Create PYTHON.json file describing this distribution. python_info = { - "version": "7", + "version": "8", "target_triple": target_triple, "optimizations": optimizations, + "build_options": build_options, "python_tag": entry["python_tag"], "python_version": python_version, "python_symbol_visibility": python_symbol_visibility, "python_stdlib_test_packages": sorted(STDLIB_TEST_PACKAGES), "python_extension_module_loading": extension_module_loading, - "libpython_link_mode": "static" if static else "shared", + "libpython_link_mode": "shared", "crt_features": crt_features, "build_info": build_info, "licenses": entry["licenses"], @@ -2223,13 +1802,12 @@ def build_cpython( } # Collect information from running Python script. - python_exe = out_dir / "python" / "install" / "python.exe" metadata_path = td / "metadata.json" env = dict(os.environ) env["ROOT"] = str(out_dir / "python") subprocess.run( [ - str(python_exe), + str(canonical_python_exe), str(SUPPORT / "generate_metadata.py"), str(metadata_path), ], @@ -2242,24 +1820,28 @@ def build_cpython( python_info.update(metadata) - if not static: - python_info["tcl_library_path"] = "install/tcl" - python_info["tcl_library_paths"] = [ - "dde1.4", - "reg1.3", - "tcl8.6", - "tk8.6", - "tcl8", - "tix8.4.3", - ] + python_info["tcl_library_path"] = "install/tcl" + python_info["tcl_library_paths"] = [ + "dde1.4", + "reg1.3", + "tcl8.6", + "tk8.6", + "tcl8", + "tix8.4.3", + ] - validate_python_json(python_info) + validate_python_json(python_info, extension_modules=None) with (out_dir / "python" / "PYTHON.json").open("w", encoding="utf8") as fh: json.dump(python_info, fh, sort_keys=True, indent=4) dest_path = BUILD / ( - "cpython-%s-%s-%s.tar" % (entry["version"], target_triple, profile,) + "cpython-%s-%s-%s.tar" + % ( + entry["version"], + target_triple, + build_options, + ) ) data = io.BytesIO() @@ -2288,29 +1870,47 @@ def fetch_strawberry_perl() -> pathlib.Path: return strawberryperl -def main(): +def main() -> None: BUILD.mkdir(exist_ok=True) parser = argparse.ArgumentParser() + parser.add_argument( + "--vs", + choices={"2019", "2022", "2026"}, + default="2022", + help="Visual Studio version to use", + ) parser.add_argument( "--python", - choices={"cpython-3.8", "cpython-3.9"}, - default="cpython-3.9", + choices={ + "cpython-3.10", + "cpython-3.11", + "cpython-3.12", + "cpython-3.13", + "cpython-3.14", + "cpython-3.15", + }, + default="cpython-3.11", help="Python distribution to build", ) + optimizations = {"noopt", "pgo"} parser.add_argument( - "--profile", - choices={"static-noopt", "shared-noopt", "shared-pgo"}, - default="static-noopt", - help="How to compile Python", + "--options", + choices=optimizations.union({f"freethreaded+{o}" for o in optimizations}), + default="noopt", + help="Build options to apply when compiling Python", + ) + parser.add_argument( + "--sh", required=True, help="Path to sh.exe in a cygwin or mingw installation" + ) + parser.add_argument( + "--windows-sdk-version", + default="10.0.26100.0", + help="Windows SDK version to build with", ) - parser.add_argument("--sh", help="Path to sh.exe in a cygwin or mingw installation") args = parser.parse_args() - - if args.python in ("cpython-3.8", "cpython-3.9") and not args.sh: - print("--sh required when building Python 3.8+") - return 1 + build_options = args.options log_path = BUILD / "build.log" @@ -2320,27 +1920,46 @@ def main(): if os.environ.get("Platform") == "x86": target_triple = "i686-pc-windows-msvc" arch = "x86" - else: + elif os.environ.get("Platform") == "arm64": + target_triple = "aarch64-pc-windows-msvc" + arch = "arm64" + elif os.environ.get("Platform") == "x64": target_triple = "x86_64-pc-windows-msvc" arch = "amd64" + else: + raise Exception("unhandled architecture: %s" % os.environ.get("Platform")) # TODO need better dependency checking. - openssl_archive = BUILD / ("openssl-%s-%s.tar" % (target_triple, args.profile)) + + # CPython 3.11+ have native support for OpenSSL 3.x. We anticipate this + # will change in a future minor release once OpenSSL 1.1 goes out of support. + # But who knows. + if args.python == "cpython-3.10": + openssl_entry = "openssl-1.1" + else: + openssl_entry = "openssl-3.5" + + openssl_archive = BUILD / ( + "%s-%s-%s.tar" % (openssl_entry, target_triple, build_options) + ) if not openssl_archive.exists(): perl_path = fetch_strawberry_perl() / "perl" / "bin" / "perl.exe" LOG_PREFIX[0] = "openssl" build_openssl( - perl_path, arch, profile=args.profile, dest_archive=openssl_archive + openssl_entry, + perl_path, + arch, + dest_archive=openssl_archive, ) - libffi_archive = BUILD / ("libffi-%s-%s.tar" % (target_triple, args.profile)) + libffi_archive = BUILD / ("libffi-%s-%s.tar" % (target_triple, build_options)) if not libffi_archive.exists(): build_libffi( args.python, arch, pathlib.Path(args.sh), + args.vs, libffi_archive, - "static" in args.profile, ) LOG_PREFIX[0] = "cpython" @@ -2348,9 +1967,12 @@ def main(): args.python, target_triple, arch, - profile=args.profile, + build_options=build_options, + msvc_version=args.vs, + windows_sdk_version=args.windows_sdk_version, openssl_archive=openssl_archive, libffi_archive=libffi_archive, + openssl_entry=openssl_entry, ) if "PYBUILD_RELEASE_TAG" in os.environ: @@ -2358,8 +1980,12 @@ def main(): else: release_tag = release_tag_from_git() + # Create, e.g., `cpython-3.10.13+20240224-x86_64-pc-windows-msvc-pgo.tar.zst`. + DIST.mkdir(exist_ok=True) compress_python_archive( - tar_path, DIST, "%s-%s" % (tar_path.stem, release_tag), + tar_path, + DIST, + "%s-%s" % (tar_path.stem, release_tag), ) diff --git a/cpython-windows/generate_metadata.py b/cpython-windows/generate_metadata.py index 5e5a583ea..c6341b64e 100644 --- a/cpython-windows/generate_metadata.py +++ b/cpython-windows/generate_metadata.py @@ -26,7 +26,7 @@ ).decode("ascii"), "python_paths": {}, "python_paths_abstract": sysconfig.get_paths(expand=False), - "python_exe": "install/python.exe", + "python_exe": f"install/{os.path.basename(sys.executable)}", "python_major_minor_version": sysconfig.get_python_version(), "python_config_vars": {k: str(v) for k, v in sysconfig.get_config_vars().items()}, } diff --git a/cpython-windows/run_tests-13.py b/cpython-windows/run_tests-13.py new file mode 100644 index 000000000..f3c165fbc --- /dev/null +++ b/cpython-windows/run_tests-13.py @@ -0,0 +1,30 @@ +""" +Run Python's test suite. + +As of Python 3.13, this script is no longer included in Python itself. +Instead, use: + + $ python -m test --slow-ci + +""" + +import sys +from subprocess import call + + +def main(regrtest_args): + args = [ + sys.executable, + "-m", + "test", + "--slow-ci", + ] + + args.extend(regrtest_args) + print(" ".join(args)) + + sys.exit(call(args)) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/cpython-windows/xz-support/README b/cpython-windows/xz-support/README new file mode 100644 index 000000000..12b027338 --- /dev/null +++ b/cpython-windows/xz-support/README @@ -0,0 +1,8 @@ +The upstream xz sources requires cmake to build on windows. +This can be avoided by extracting a config.h file extracted from the CMake's +results, as is done by CPython. +This file may need to be updated when upgrading the xz version. +The file in this directory is taken from the xz branch of +https://github.com/python/cpython-source-deps. +Specifically: +https://github.com/python/cpython-source-deps/blob/665d407bd6bc941944db2152e4b5dca388ea586e/windows/config.h \ No newline at end of file diff --git a/cpython-windows/xz-support/config.h b/cpython-windows/xz-support/config.h new file mode 100644 index 000000000..81ddf6b7f --- /dev/null +++ b/cpython-windows/xz-support/config.h @@ -0,0 +1,67 @@ +/* Configuration extracted from CMake'd project files. + +This is used by CPython, and is not part of the regular xz release. +*/ + +#define HAVE_CHECK_CRC32 1 +#define HAVE_CHECK_CRC64 1 +#define HAVE_CHECK_SHA256 1 + +#define HAVE_DECODERS 1 +#define HAVE_DECODER_ARM 1 +#define HAVE_DECODER_ARM64 1 +#define HAVE_DECODER_ARMTHUMB 1 +#define HAVE_DECODER_DELTA 1 +#define HAVE_DECODER_IA64 1 +#define HAVE_DECODER_POWERPC 1 +#define HAVE_DECODER_LZMA1 1 +#define HAVE_DECODER_LZMA2 1 +#define HAVE_DECODER_SPARC 1 +#define HAVE_DECODER_X86 1 + +#define HAVE_ENCODERS 1 +#define HAVE_ENCODER_ARM 1 +#define HAVE_ENCODER_ARM64 1 +#define HAVE_ENCODER_ARMTHUMB 1 +#define HAVE_ENCODER_DELTA 1 +#define HAVE_ENCODER_IA64 1 +#define HAVE_ENCODER_POWERPC 1 +#define HAVE_ENCODER_LZMA1 1 +#define HAVE_ENCODER_LZMA2 1 +#define HAVE_ENCODER_SPARC 1 +#define HAVE_ENCODER_X86 1 + +#if defined(_M_ARM64) + +#undef HAVE_IMMINTRIN_H +#undef HAVE_USABLE_CLMUL + +#else + +#define HAVE_IMMINTRIN_H 1 +#define HAVE_USABLE_CLMUL 1 +#define HAVE__MM_MOVEMASK_EPI8 1 +#define TUKLIB_FAST_UNALIGNED_ACCESS 1 + +#endif + +#define HAVE___BUILTIN_ASSUME_ALIGNED 1 +#define HAVE__BOOL 1 + +#define HAVE_INTTYPES_H 1 +#define HAVE_MF_BT2 1 +#define HAVE_MF_BT3 1 +#define HAVE_MF_BT4 1 +#define HAVE_MF_HC3 1 +#define HAVE_MF_HC4 1 +#define HAVE_STDBOOL_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_VISIBILITY 0 + +#define MYTHREAD_VISTA 1 + +#define PACKAGE_BUGREPORT "xz@tukaani.org" +#define PACKAGE_NAME "XZ Utils" +#define PACKAGE_URL "https://tukaani.org/xz/" + +#define TUKLIB_SYMBOL_PREFIX lzma_ diff --git a/docs/building.rst b/docs/building.rst index 3fe77492b..947871a83 100644 --- a/docs/building.rst +++ b/docs/building.rst @@ -4,145 +4,104 @@ Building ======== -Linux -===== +A Python distribution can be built on a Linux, macOS or Windows host. +Regardless of the operating system, `uv `_ must be installed. +Additional operating system requirements are needed and outlined in the following sections. -The host system must be 64-bit. A Python 3.5+ interpreter must be -available. The execution environment must have access to a Docker -daemon (all build operations are performed in Docker containers for -isolation from the host system). +Regardless of the host, to build a Python distribution:: + + $ uv run --no-dev build.py + +On Linux and macOS, ``./build.py`` can also be used; it uses ``uv run --no-dev`` via its shebang. + +To build a different version of Python:: + + $ uv run --no-dev build.py --python cpython-3.14 -To build a Python distribution for Linux x64:: +Various build options can be specified:: - $ ./build-linux.py - # With profile-guided optimizations (generated code should be faster): - $ ./build-linux.py --optimizations pgo + # With profile-guided optimizations (generated code should be faster) + $ uv run --no-dev build.py --options pgo # Produce a debug build. - $ ./build-linux.py --optimizations debug + $ uv run --no-dev build.py --options debug + # Produce a free-threaded build without extra optimizations + $ uv run --no-dev build.py --options freethreaded+noopt -You can also build another version of Python. e.g.:: +Different platforms support different build options. +``uv run --no-dev build.py --help`` will show the available build options and other usage information. - $ ./build-linux.py --python cpython-3.8 -To build a Python distribution for Linux x64 using musl libc:: +Linux +===== - $ ./build-linux.py --target x86_64-unknown-linux-musl +The host system must be x86-64 or aarch64. +The execution environment must have access to a Docker +daemon (all build operations are performed in Docker containers for +isolation from the host system). -Building a 32-bit x86 Python distribution is also possible:: +``build.py`` accepts a ``--target-triple`` argument to support building +for non-native targets (i.e. cross-compiling). - $ ./build-linux.py --target i686-unknown-linux-gnu +This option can be used to build for musl libc:: -As are various other targets:: + $ ./build.py --target-triple x86_64-unknown-linux-musl + +Or on a x86-64 host for different architectures:: + + $ ./build.py --target-triple i686-unknown-linux-gnu + $ ./build.py --target-triple armv7-unknown-linux-gnueabi + $ ./build.py --target-triple armv7-unknown-linux-gnueabihf + $ ./build.py --target-triple loongarch64-unknown-linux-gnu + $ ./build.py --target-triple mips-unknown-linux-gnu + $ ./build.py --target-triple mipsel-unknown-linux-gnu + $ ./build.py --target-triple ppc64le-unknown-linux-gnu + $ ./build.py --target-triple riscv64-unknown-linux-gnu + $ ./build.py --target-triple s390x-unknown-linux-gnu - $ ./build-linux.py --target aarch64-unknown-linux-gnu - $ ./build-linux.py --target armv7-unknown-linux-gnueabi - $ ./build-linux.py --target armv7-unknown-linux-gnueabihf - $ ./build-linux.py --target mips-unknown-linux-gnu - $ ./build-linux.py --target mipsel-unknown-linux-gnu - $ ./build-linux.py --target s390x-unknown-linux-gnu macOS ===== -The XCode command line tools must be installed. A Python 3 interpreter -is required to execute the build. ``/usr/bin/clang`` must exist. +The XCode command line tools must be installed. +``/usr/bin/clang`` must exist. -macOS SDK headers must be installed. Try running ``xcode-select --install`` -to install them if you see errors about e.g. ``stdio.h`` not being found. -Verify they are installed by running ``xcrun --show-sdk-path``. It -should print something like +macOS SDK headers must be installed. +If you see errors such as ``stdio.h`` not being found, try running ``xcode-select --install`` to install them. +Verify they are installed by running ``xcrun --show-sdk-path``. +It should print something like ``/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk`` on modern versions of macOS. -To build a Python distribution for macOS:: +The ``--target-triple`` argument can be used to build for an Intel Mac on an arm64 (Apple Silicon) host:: - $ ./build-macos.py + $ ./build.py --target-triple x86_64-apple-darwin -macOS uses the same build code as Linux, just without Docker. -So similar build configuration options are available. +Additionally, an arm64 macOS host can be used to build Linux aarch64 targets using Docker:: -``build-macos.py`` accepts a ``--target-triple`` argument to support building -for non-native targets (i.e. cross-compiling). By default, macOS builds target -the currently running architecture. e.g. an Intel Mac will target -``x86_64-apple-darwin`` and an M1 (ARM) Mac will target ``aarch64-apple-darwin``. -It should be possible to build an ARM distribution on an Intel Mac and an Intel -distribution on an ARM Mac. + $ ./build.py --target-triple aarch64-unknown-linux-gnu The ``APPLE_SDK_PATH`` environment variable is recognized as the path to the Apple SDK to use. If not defined, the build will attempt to find an SDK by running ``xcrun --show-sdk-path``. -``aarch64-apple-darwin`` builds require a macOS 11.0+ SDK and building -Python 3.9+. It should be possible to build for ``aarch64-apple-darwin`` from +``aarch64-apple-darwin`` builds require a macOS 11.0+ SDK. +It should be possible to build for ``aarch64-apple-darwin`` from an Intel 10.15 machine (as long as the 11.0+ SDK is used). -Python 3.8 may not build properly with a macOS 11.0+ SDK: try using the -macOS 10.15 SDK instead. - Windows ======= -Visual Studio 2017 (or later) is required. A compatible Windows SDK is required -(10.0.17763.0 as per CPython 3.7.2). - -If building CPython 3.8+, there are the following additional requirements: - -* A ``git.exe`` on ``PATH`` (to clone ``libffi`` from source). -* An installation of Cywgin with the ``autoconf``, ``automake``, ``libtool``, - and ``make`` packages installed. (``libffi`` build dependency.) - -To build a Python distribution for Windows x64:: - - # From a Visual Studio 2017/2019 x64 native tools command prompt: - $ py.exe build-windows.py --profile static-noopt - -It is also possible to build a more traditional dynamically linked -distribution, optionally with PGO optimizations:: - - $ py.exe build-windows.py --profile shared-noopt - $ py.exe build-windows.py --profile shared-pgo - -If building CPython 3.8+, you will need to specify the path to a -``sh.exe`` installed from cygwin. e.g. - - $ py.exe build-windows.py --python cpython-3.8 --sh c:\cygwin\bin\sh.exe --profile shared - -To build a 32-bit x86 binary, simply use an ``x86 Native Tools -Command Prompt`` instead of ``x64``. - -Using sccache to Speed up Builds -================================ - -Builds can take a long time. The bulk of the CPU time is bootstrapping a modern -Clang toolchain, which requires building a modern GCC then potentially doing -a 3 stage bootstrap of Clang. - -python-build-standalone can automatically detect and use the -`sccache `_ compiler cache to speed -up subsequent builds on UNIX-like platforms. ``sccache`` can shave dozens -of minutes from fresh builds, even on a 16 core CPU! - -If there is an executable ``sccache`` in the source directory, it will -automatically be copied into the build environment and used. For non-container -builds, an ``sccache`` executable is also searched for on ``PATH``. - -The ``~/.python-build-standalone-env`` file is read if it exists (the format is -``key=value`` pairs) and variables are added to the build environment. +Visual Studio 2022 (or later) is required. +A compatible Windows SDK is required (10.0.26100.0 as of CPython 3.10). +A ``git.exe`` must be on ``PATH`` (to clone ``libffi`` from source). +Cygwin must be installed with the ``autoconf``, ``automake``, ``libtool``, +and ``make`` packages. (``libffi`` build dependency.) -In addition, environment variables ``AWS_ACCESS_KEY_ID``, -``AWS_SECRET_ACCESS_KEY``, and any variable beginning with ``SCCACHE_`` are -automatically added to the build environment. +Building can be done from the ``x64 Native Tools Command Prompt``, by calling +the vcvars batch file, or by adjusting the ``PATH`` and environment variables. -The environment variable support enables you to define remote build caches -(such as S3 buckets) to provide a persistent, shared cache across builds and -machines. +You will need to specify the path to ``sh.exe`` from cygwin:: -Keep in mind that when performing builds in containers in Linux (the default -behavior), the local filesystem is local to the container and does not survive -the build of a single package. So sccache is practically meaningless unless -configured to use an external store (such as S3). + $ uv run --no-dev build.py --sh c:\cygwin\bin\sh.exe -When using remote stores (such as S3), ``sccache`` can be constrained on -network I/O. We recommend having at least a 100mbps network connection to -a remote store and employing a network store with as little latency as possible -for best results. +To build a 32-bit x86 binary, simply use an ``x86 Native Tools Command Prompt`` instead of ``x64``. \ No newline at end of file diff --git a/docs/distributions.rst b/docs/distributions.rst index 15f74e107..16fb23809 100644 --- a/docs/distributions.rst +++ b/docs/distributions.rst @@ -4,11 +4,17 @@ Distribution Archives ===================== -The output of a build is referred to as a Python *distribution*. +This project produces tarball archives containing Python distributions. -A distribution is a zstandard-compressed tar file. All paths inside the -tar archive are prefixed with ``python/``. Within the ``python/`` directory -are the following well-known paths: +Full Archive +============ + +The canonical output of this project's build system are ``.tar.zst`` +(zstandard compressed tarballs) files. + +All files within the tar are prefixed with ``python/``. + +Within the ``python/`` directory are the following well-known paths: PYTHON.json Machine readable file describing this Python distribution. @@ -22,7 +28,7 @@ The ``PYTHON.json`` file should be read to determine where specific entities are located within the archive. PYTHON.json File -================ +---------------- The ``PYTHON.json`` file describes the Python distribution in a machine readable manner. This file is meant to be opened by downstream consumers @@ -32,7 +38,7 @@ without having to resort to heuristics. The file contains a JSON map. This map has the following keys: version - Version number of the file format. Currently ``7``. + Version number of the file format. Currently ``8``. target_triple A target triple defining the platform and architecture of the machine @@ -52,7 +58,16 @@ optimizations Known values include ``debug``, ``noopt``, ``pgo``, ``lto``, and ``pgo+lto``. - (Version 5 or above only.) + (Deprecated in version 8 in favor of ``build_options``.) + +build_options + String indicating what build options were used. Options are separated + by a ``+``. + + Known values include ``debug``, ``noopt``, ``pgo``, ``lto``, and + ``freethreading``. + + (Version 8 or above only.) os Target operating system for the distribution. e.g. ``linux``, ``macos``, @@ -67,12 +82,12 @@ arch (Deprecated in version 5 in favor of ``target_triple``.) python_tag - The PEP 425 *Python Tag* value. e.g. ``cp38``. + The PEP 425 *Python Tag* value. e.g. ``cp313``. (Version 5 or above only.) python_abi_tag - The PEP 425 *ABI Tag* value. e.g. ``cp37m``. + The PEP 425 *ABI Tag* value. e.g. ``cp313m``. This may be null if the distribution's platform doesn't expose the concept of an ABI tag. @@ -97,7 +112,7 @@ python_implementation_cache_tag (Version 5 or above only.) python_implementation_hex_version - Hexidecimal expression of implementation version. + Hexadecimal expression of implementation version. This is the value exposed by ``sys.implementation.hexversion``. @@ -121,7 +136,7 @@ python_implementation_version (Version 5 or above only.) python_version - Version of Python distribution. e.g. ``3.8.2``. + Version of Python distribution. e.g. ``3.13.0``. python_major_minor_version ``X.Y`` version string consisting of Python major and minor version. @@ -208,7 +223,7 @@ python_suffixes ``extension`` Suffixes for extension modules. Corresponds to ``importlib.machinery.EXTENSION_SUFFIXES``. e.g. - ``[".cpython-38-x86_64-linux-gnu.so", ".abi3.so", ".so"]``. + ``[".cpython-313-x86_64-linux-gnu.so", ".abi3.so", ".so"]``. ``optimized_bytecode`` Suffixes for optimized bytecode modules. Corresponds to @@ -222,7 +237,7 @@ python_suffixes (Version 5 or above only.) python_bytecode_magic_number - Magic number to use for bytecode files, expressed as a hexidecimal + Magic number to use for bytecode files, expressed as a hexadecimal string. (Version 5 or above only.) @@ -373,8 +388,8 @@ license_path tcl_library_path Relative path to location of tcl library files. The path should be a directory tree containing tcl files to support the tkinter extension. - This will include a subset of the library files provided by the tcl, tk, - and tix packages. + This will include a subset of the library files provided by the tcl + and tk packages. This points to the root directory containing tcl resources. Actual tcl resources are in sub-directories underneath, as identified by @@ -572,3 +587,23 @@ system System libraries are typically passed into the linker by name only and found using default library search paths. + +Install Only Archive +==================== + +At release time, this project produces tar files containing just the +Python installation, without the ``PYTHON.json`` or build files from +the full ``.tar.zst`` archives. These are referred to as *install only* +archives. + +An *install only* archive is created by taking a ``.tar.zst`` and +rewriting ``python/install/*`` to ``python/*``. All files not under +``python/install/*`` are not carried forward to the *install only* +archive. + +The fastest available build for a given target is used for the *install +only* archive. Builds are generally preferred in the following order: +``pgo+lto``, ``pgo``, ``lto``, ``noopt``. + +For maximum compatibility, gzipped compressed versions of the +*install only* archives are made available. diff --git a/docs/index.rst b/docs/index.rst index 923383fef..d0d450e4a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,17 +3,10 @@ Python Standalone Builds This project produces self-contained, highly-portable Python distributions. These Python distributions contain a fully-usable, -full-featured Python installation as well as their build artifacts -(object files, libraries, etc). - -The included build artifacts can be recombined by downstream -repackagers to derive a custom Python distribution, possibly without -certain features like SQLite and OpenSSL. This is useful for -embedding Python in a larger binary, where a full Python is -often not needed and where interfacing with the Python C API -is desirable. (See the -`PyOxidizer `_ sister project -for such a downstream repackager.) +full-featured Python installation: most extension modules from +the Python standard library are present and their library +dependencies are either distributed with the distribution or +are statically linked. The Python distributions are built in a manner to minimize run-time dependencies. This includes limiting the CPU instructions @@ -21,16 +14,32 @@ that can be used and limiting the set of shared libraries required at run-time. The goal is for the produced distribution to work on any system for the targeted architecture. +Some distributions ship with their build artifacts (object files, +libraries, etc) along with rich metadata describing the distribution +and how it was assembled. The build artifacts can be recombined by +downstream repackagers to derive a custom Python distribution, possibly +without certain features like SQLite and OpenSSL. This is useful for +embedding Python in a larger binary. See the +`PyOxidizer `_ sister project +for such a downstream repackager. + +Many users of these distributions might be better served by the +`PyOxy `_ +sister project. PyOxy takes these Python distributions and adds some +Rust code for enhancing the functionality of the Python interpreter. +The official PyOxy release binaries are single file executables providing +a full-featured Python interpreter. + .. toctree:: :maxdepth: 2 :caption: Contents: - building.rst - running.rst - quirks.rst - technotes.rst - distributions.rst - status.rst + running + building + quirks + technotes + distributions + status Indices and tables ================== diff --git a/docs/quirks.rst b/docs/quirks.rst index 249c35126..1259f23ef 100644 --- a/docs/quirks.rst +++ b/docs/quirks.rst @@ -4,15 +4,25 @@ Behavior Quirks =============== +While these Python distributions are intended to be broadly compatible +with the Python ecosystem, there are a few known behavior quirks that +affect specific environments, packages, or use cases. + .. _quirk_backspace_key: -Backscape Key Doesn't work in Python REPL -========================================= +If special keys do not work in the Python REPL +============================================== If you attempt to run ``python`` and the backspace key doesn't erase characters or the arrow keys don't work as expected, this is because the executable can't find the *terminfo database*. +If this happens, the Python REPL will print the following warning +message on startup:: + + Cannot read termcap database; + using dumb terminal settings. + When you type a special key like the backspace key, this is registered as a key press. There is special software (typically ``readline`` or ``libedit``) that most interactive programs use @@ -29,126 +39,74 @@ you build a program (like Python) locally, you link against ``readline`` or ``libedit`` and get these default locations *for free*. -Because python-build-standalone Python distributions compile -and use their own version of ``readline``/``libedit`` and -because the build environment is different from your -machine, the default search locations for the *terminfo -database* built into binaries distributed with this project -may point to a path that doesn't exist. The *terminfo database* -cannot be located and ``readline``/``libedit`` do not know -how to convert special key presses to special behavior. - -The solution to this is to set an environment variable -with the location of the *terminfo database*. - -If running a Debian based Linux distribution (including Ubuntu):: - - $ TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo - -If running a RedHat based Linux distribution:: - - $ TERMINFO_DIRS=/etc/terminfo:/usr/share/terminfo - -If running macOS:: - - $ TERMINFO_DIRS=/usr/share/terminfo - -e.g.:: +These Python distributions compile and use their own version of +``libedit`` to avoid a dependency on what is (or isn't) installed on +your system. This means that they do not use your system-provided +libraries for reading the *terminfo database*. This version of +``libedit`` is configured to look for in locations that should work for +most OSes (specifically, ``/usr/share/terminfo`` on macOS, and +``/etc/terminfo``, ``/lib/terminfo``, and ``/usr/share/terminfo`` on +Linux, which should cover all major Linux distributions), but it is +possible that your environment has it somewhere else. If your OS stores +the *terminfo database* in an uncommon location, you can set the +``TERMINFO_DIRS`` environment variable so that ``libedit`` can find it. - $ TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo install/bin/python3.9 +For instance, you may need to do something like: -The macOS distributions built with this project should automatically -use the terminfo database in ``/usr/share/terminfo``. Please file -a bug report if the macOS distributions do not behave as expected. + $ TERMINFO_DIRS=/uncommon/place/terminfo install/bin/python3.10 -.. _quirk_tcl: +If you are running on a relatively standard OS and this does not work +out of the box, please file a bug report so we can add the location of +the *terminfo database* to the build. -Tcl/tk Support Files -==================== +For convenience, a relatively recent copy of the terminfo database +is distributed in the ``share/terminfo`` directory (``../../share/terminfo`` +relative to the ``bin/python3`` executable) in Linux distributions. Note +that ncurses and derived libraries don't know how to find this directory +since they are configured to use absolute paths to the terminfo database +and the absolute path of the Python distribution is obviously not known +at build time! So actually using this bundled terminfo database will +require custom code setting ``TERMINFO_DIRS`` before +ncurses/libedit/readline are loaded. -Python functionality using tcl/tk (such as the ``tkinter`` or ``turtle`` -modules) requires loading ``.tcl`` support files from the filesystem. -If these support files cannot be found, you'll get an error like -``_tkinter.TclError: Can't find a usable init.tcl in the following -directories:``. +.. _quirk_macos_no_tix: -Distributions produced from this project contain tcl/tk support files. -The paths to these files in the extracted distribution are advertised -in the ``PYTHON.json`` file. +No tix on UNIX +============== -When tcl is initialized by Python, Python and tcl attempt to locate the -``.tcl`` support files. If the ``tcl/init.tcl`` file cannot be found, -an error occurs. +Tix is an old widget library for Tcl/Tk. Python previously had a wrapper +for it in ``tkinter.tix``, but it was deprecated in Python 3.6 (the +recommendation is to use ``tkinter.ttk``) and removed in Python 3.13. -But the mechanism for finding the ``.tcl`` files varies by platform. +The macOS and Linux distributions from this project do not build and +ship Tix, even for Python versions 3.12 and below. -On all platforms, if the ``TCL_LIBRARY`` environment variable is set, -it will be used to locate the ``.tcl`` support files. This environment -variable is processed by tcl itself and is documented at -https://wiki.tcl-lang.org/page/TCL_LIBRARY. +We had previously attempted to ship Tix support on Linux, but it was +broken and nobody reported an issue about it. The macOS distributions +from this project never shipped support for Tix. The official Python.org +macOS installers and Apple's build of Python do not ship support for +Tix, either, so this project behaves similarly to those distributions. -On Windows, CPython will attempt to locate the ``.tcl`` support files in -well-defined directories. The C code performs the equivalent of the -following: +.. _quirk_windows_no_pip: -.. code-block:: python +No ``pip.exe`` on Windows +========================= - import os - import sys +The Windows distributions have ``pip`` installed however no ``Scripts/pip.exe``, +``Scripts/pip3.exe``, and ``Scripts/pipX.Y.exe`` files are provided because +the way these executables are built isn't portable. (It might be possible to +change how these are built to make them portable.) - def get_tcl_path(): - # e.g. sys.prefix/tcl/tcl8.6 - p = os.path.join(sys.prefix, "tcl", "tcl") - if os.path.exists(p): - return p - - return None - -If Python's code can find the support files in the well-defined location, -it calls into the tcl C API and defines the ``tcl_library`` variable to the -found path. - -The most robust way to ensure Python/tcl can find the ``.tcl`` support files -is to define ``TCL_LIBRARY`` to the path to the ``.tcl`` files present in -the extracted Python distribution. It is possible to define this environment -variable from within Python. But it must be done before running any Python -code in the ``tkinter`` module. The following example should work on Linux -and macOS distributions: - -.. code-block:: python - - import os - import sys - - os.environ["TCL_LIBRARY"] = os.path.join(os.path.dirname(sys.executable), "..", "lib", "tcl8.6") - - import turtle - -If you don't set ``TCL_LIBRARY`` on Linux and macOS, the default search -mechanics implemented by Tcl are used. These may pick up ``.tcl`` files from -a location outside the Python distribution. This may *just work*. This may -fail fast. Or it could result in undefined behavior. For best results, -forcefully point Tcl at the ``.tcl`` files from the Python distribution -produced by this project. - -On Windows, explicitly setting ``TCL_LIBRARY`` is not required as the -default install layout of this project's Python distributions allows CPython's -filesystem probing code to find the ``.tcl`` files. As long as the -files from ``python/install/tcl`` are present (in a ``tcl`` directory -under the directory where the ``python.exe`` is), things should *just work*. - -For reference, PyOxidizer's approach to this problem is to copy all the -``.tcl`` files from the Python distribution into an install location. At -run time, the ``TCL_LIBRARY`` environment variable is set from within -the process before the Python interpreter is initialized. This ensures the -``.tcl`` files from the Python distribution are used. +To use pip, run ``python.exe -m pip``. (It is generally a best practice to +invoke pip via ``python -m pip`` on all platforms so you can be explicit +about the ``python`` executable that pip uses.) .. _quirk_macos_linking: Linking Static Library on macOS =============================== -Python 3.9+ makes use of the ``__builtin_available()`` compiler feature. +Python 3.10+ makes use of the ``__builtin_available()`` compiler feature. This functionality requires a symbol from ``libclang_rt``, which may not be linked by default. Failure to link against ``libclang_rt`` could result in a linker error due to an undefined symbol ``___isOSVersionAtLeast``. @@ -167,3 +125,114 @@ build/version of Clang is supported. Use at your own risk. See https://jonnyzzz.com/blog/2018/06/05/link-error-2/ and https://jonnyzzz.com/blog/2018/06/13/link-error-3/ for more on this topic. + +.. _quirk_linux_libedit: + +Use of ``libedit`` on Linux +=========================== + +Python 3.10+ Linux distributions link against ``libedit`` (as opposed to +``readline``) by default, as ``libedit`` is supported on 3.10+ outside of +macOS. + +Most Python builds on Linux will link against ``readline`` because ``readline`` +is the dominant library on Linux. + +Some functionality may behave subtly differently as a result of our choice +to link ``libedit`` by default. (We choose ``libedit`` by default to +avoid GPL licensing requirements of ``readline``.) + + +.. _quirk_references_to_build_paths: + +References to Build-Time Paths +============================== + +The built Python distribution captures some absolute paths and other +build-time configuration in a handful of files: + +* In a ``_sysconfigdata_*.py`` file in the standard library. e.g. + ``lib/python3.10/_sysconfigdata__linux_x86_64-linux-gnu.py``. +* In a ``Makefile`` under a ``config-*`` directory in the standard library. + e.g. ``lib/python3.10/config-3.10-x86_64-linux-gnu/Makefile``. +* In python-build-standalone's metadata file ``PYTHON.json`` (mostly + reflected values from ``_sysconfigdata_*.py``). + +Each of these serves a different use case. But the general theme is various +aspects of the Python distribution attempt to capture how Python was built. +The most common use of these values is to facilitate compiling or linking +other software against this Python build. For example, the ``_sysconfigdata*`` +module is loaded by the `sysconfig `_ +module. ``sysconfig`` in turn is used by packaging tools like ``setuptools`` +and ``pip`` to figure out how to invoke a compiler for e.g. compiling C +extensions from source. + +When installed by `uv `_, these absolute +paths are fixed up to point to the actual location on your system where +the distribution was installed, so **this quirk generally does not +affect uv users**. The third-party tool `sysconfigpatcher +`_ also does this and might +be helpful to use or reference if you are installing these distributions +on your own. + +In particular, you may see references to our install-time paths on the +build infrastructure, e.g., ``/build`` and ``/install`` on Linux, a +particular SDK in ``/Applications/Xcode.app`` on macOS, and temporary +directories on Windows. + +Also, Python reports the compiler and flags in use, just in case it is +needed to make binary-compatible extensions. On Linux, for instance, we +use our own builds of Clang and potentially some flags (warnings, +optimizations, locations of the build environment) that do not work or +apply in other environments. We try to configure Python to remove +unneeded flags and absolute paths to files in the build environment. +references to build-time paths. Python's ``sysconfig`` system requires +listing a compiler, so we leave it set to ``clang`` without the absolute +path, but you should be able to use another compiler like ``gcc`` to +compile extensions, too. + +If there is a build time normalization that you think should be performed to +make distributions more portable, please file a GitHub issue. + +.. _quirk_former: +.. _quirk_missing_libcrypt: +.. _quirk_linux_libx11: + +Former quirks +============= + +The following quirks were previously listed on this page but have since +been resolved. + +* "Static Linking of musl libc Prevents Extension Module Library + Loading": Starting with the 20250311 release, the default musl + distributions are dynamically linked by default, so extension modules + should work properly. Note that these now require a system-wide + installation of the musl C library. (This is present by default on + musl-based OSes like Alpine, and many glibc-based distros have a + ``musl`` package you can safely co-install with glibc, too.) If you + specifically need a statically-linked binary, variants with the + ``+static`` build option are available, but these retain the quirk + that compiled extension modules (e.g., ``musllinux`` wheels) cannot be + loaded. + +* "Missing ``libcrypt.so.1``": The 20230507 release and earlier required + the system library ``libcrypt.so.1``, which stopped being shipped by + default in several Linux distributions around 2022. Starting with the + 20230726 release, this dependency is now only needed by the deprecated + ``crypt`` module, which only exists on Python 3.12 and lower. If you + still need this module, your OS may offer a ``libxcrypt`` package to + provide this library. Alternatively, there are suggestions in `What's + New in Python 3.13`_ about third-party replacements for the ``crypt`` + module. + +* "Static Linking of ``libX11`` / Incompatibility with PyQt on Linux": + The 20220318 release and earlier exported dynamic symbols for the + internal, statically-linked build of ``libX11`` and other libraries. + These would cause conflicts and potential crashes when using + third-party extension modules such as PyQt that load an actual shared + ``libX11`` library (usually provided by your OS). Starting with the + 20220502 release, symbols from internal dependencies are no longer + exported. + +.. _What's New in Python 3.13: https://docs.python.org/3/whatsnew/3.13.html#whatsnew313-pep594 diff --git a/docs/running.rst b/docs/running.rst index 5d2bc973c..d2d2dc9a8 100644 --- a/docs/running.rst +++ b/docs/running.rst @@ -4,10 +4,173 @@ Running Distributions ===================== +Obtaining Distributions +======================= + +Pre-built distributions are published as releases on GitHub at +https://github.com/astral-sh/python-build-standalone/releases. +Simply go to that page and find the latest release along with +its release notes. + +Machines can find the latest release by querying the GitHub releases +API. Alternatively, a JSON file publishing metadata about the latest +release can be fetched from +https://raw.githubusercontent.com/astral-sh/python-build-standalone/latest-release/latest-release.json. +The JSON format is simple and hopefully self-descriptive. + +Published distributions vary by their: + +* Python version +* Target machine architecture +* Build configuration +* Archive flavor + +The Python version is hopefully pretty obvious. + +The target machine architecture defines the CPU type and operating +system the distribution runs on. We use LLVM target triples. If you aren't +familiar with LLVM target triples, here is an overview: + +``aarch64-apple-darwin`` + macOS ARM CPUs. i.e. M1 native binaries. + +``x86_64-apple-darwin`` + macOS Intel CPUs. + +``i686-pc-windows-msvc`` + Windows 32-bit Intel/AMD CPUs. + +``x86_64-pc-windows-msvc`` + Windows 64-bit Intel/AMD CPUs. + +``*-windows-msvc-shared`` + This is a standard build of Python for Windows. There are DLLs for + Python and extensions. These builds behave like the official Python + for Windows distributions. + + These builds are now published without the `-shared` suffix. + +``*-windows-msvc-static`` + These builds of Python are statically linked. + + These builds are extremely brittle and have several known incompatibilities. + We recommend not using them unless you have comprehensive test coverage and + have confidence they work for your use case. + + See :ref:`quirk_windows_static_distributions` for more. + + These builds are no longer published. + +``x86_64-unknown-linux-gnu`` + Linux 64-bit Intel/AMD CPUs linking against GNU libc. + +``x86_64-unknown-linux-musl`` + Linux 64-bit Intel/AMD CPUs linking against musl libc. + + These binaries are static and have no shared library dependencies. + A side-effect of static binaries is they cannot load Python ``.so`` + extensions, as static binaries cannot load shared libraries. + +``aarch64-unknown-linux-*`` + Similar to above except targeting Linux on ARM64 CPUs. + + This is what you want for e.g. AWS Graviton EC2 instances. Many Linux + ARM devices are also ``aarch64``. + +``i686-unknown-linux-*`` + Linux 32-bit Intel/AMD CPUs. + +``x86_64_v2-*`` + Targets 64-bit Intel/AMD CPUs approximately newer than + `Nehalem `_ + (released in 2008). + + Binaries will have SSE3, SSE4, and other CPU instructions added after the + ~initial x86-64 CPUs were launched in 2003. + + Binaries will crash if you attempt to run them on an older CPU not + supporting the newer instructions. + +``x86_64_v3-*`` + Targets 64-bit Intel/AMD CPUs approximately newer than + `Haswell `_ + (released in 2013) and + `Excavator `_ + (released in 2015). + + Binaries will have AVX, AVX2, MOVBE and other newer CPU instructions. + + Binaries will crash if you attempt to run them on an older CPU not + supporting the newer instructions. + + Most x86-64 CPUs manufactured after 2013 (Intel) or 2015 (AMD) support + this microarchitecture level. An exception is Intel Atom P processors, + which Intel released in 2020 but did not include AVX. + +``x86_64_v4-*`` + Targets 64-bit Intel/AMD CPUs with some AVX-512 instructions. + + Requires Intel CPUs manufactured after ~2017. But many Intel CPUs don't + have AVX-512. + +The ``x86_64_v2``, ``x86_64_v3``, and ``x86_64_v4`` binaries usually crash +on startup when run on an incompatible CPU. We don't recommend running the +``x86_64_v4`` builds in production because they likely don't yield a reliable +performance benefit. Unless you are executing these binaries on a CPU older +than ~2008 or ~2013, we recommend running the ``x86_64_v2`` or ``x86_64_v3`` +binaries, as these should be slightly faster since they take advantage +of more modern CPU instructions which are more efficient. But if you want +maximum portability, stick with the baseline ``x86_64`` builds. + +We recommend using the ``*-windows-msvc-shared`` builds on Windows, as these +are highly compatible with the official Python distributions. + +We recommend using the ``*-unknown-linux-gnu`` builds on Linux, since they +are able to load compiled Python extensions. If you don't need to load +compiled extensions not provided by the standard library or you are willing +to compile and link 3rd party extensions into a custom binary, the +``*-unknown-linux-musl`` builds should work just fine. + +The build configuration denotes how Python and its dependencies were built. +Common configurations include: + +``pgo+lto`` + Profile guided optimization and link-time optimization. **These should be + the fastest distributions since they have the most build-time + optimizations.** + +``pgo`` + Profile guided optimization. + + Starting with CPython 3.12, BOLT is also applied alongside traditional + PGO on platforms supporting BOLT. (Currently just Linux x86-64.) + +``lto`` + Link-time optimization. + +``noopt`` + A regular optimized build without PGO or LTO. + +``debug`` + A debug build. No optimizations. + +The archive flavor denotes the content in the archive. See +:ref:`distributions` for more. + +Casual users will likely want to use the ``install_only`` archive, as most +users do not need the build artifacts present in the ``full`` archive. +The ``install_only`` archive doesn't include the build configuration in its +file name. It's based on the fastest available build configuration for a given +target. + +An ``install_only_stripped`` archive is also available. This archive is +equivalent to ``install_only``, but without debug symbols, which results in a +smaller download and on-disk footprint. + Extracting Distributions ======================== -Distributions are defined as zstandard-compressed tarballs. +Distributions are defined as zstandard or gzip compressed tarballs. Modern versions of ``tar`` support zstandard and you can extract like any normal archive:: @@ -17,6 +180,11 @@ like any normal archive:: (The ``-a`` argument tells tar to guess the compression format by the file extension.) +If your ``tar`` doesn't support ``-a`` (e.g. the default macOS ``tar``), +try:: + + $ tar xvf path/to/distribution.tar.zstd + If you do not have ``tar``, you can install and use the ``zstd`` tool (typically available via a ``zstd`` or ``zstandard`` system package):: @@ -59,8 +227,8 @@ The following shared libraries are referenced: * libc.so.6 * ld-linux-x86-64.so.2 -The minimum glibc version required is 2.17. This should make binaries -compatible with the following Linux distributions: +The minimum glibc version required for most targets is 2.17. This should make +binaries compatible with the following Linux distributions: * Fedora 21+ * RHEL/CentOS 7+ @@ -68,6 +236,9 @@ compatible with the following Linux distributions: * Debian 8+ (Jessie) * Ubuntu 14.04+ +For the ``mips-unknown-linux-gnu`` and ``mipsel-unknown-linux-gnu`` targets, +the minimum glibc version is 2.19. + If built with MUSL, no shared library dependencies nor glibc version requirements exist and the binaries should *just work* on practically any Linux system. @@ -75,16 +246,29 @@ Linux system. Windows ------- -Windows distributions require Windows 8 or Windows Server 2012 or newer. (The -official Python distributions support Windows 7 up to Python 3.8 and dropped -support for Windows 7 with Python 3.9. However, we've decided to drop -support for Windows 7 from our Windows distributions.) +Windows distributions model the requirements of the official Python +distributions: + +* Windows 8 or Windows Server 2012 or newer Windows binaries have a dependency on the Microsoft Visual C++ Redistributable, likely from MSVC 2015 (``vcruntime140.dll``). This dependency is not provided in the distribution and will need to be provided by downstream distributors. +Extra Python Software +===================== + +Python installations have some additional software pre-installed: + +* `pip `_ +* `setuptools `_ (for Python 3.11 and older) + +The intent of the pre-installed software is to facilitate end-user +package installation without having to first bootstrap a packaging +tool via an insecure installation technique (such as `curl | sh` +patterns). + Licensing ========= @@ -94,6 +278,14 @@ licenses. This impacts the rights and requirements of downstream consumers. Most licenses are fairly permissive. Notable exceptions to this are GDBM and readline, which are both licensed under GPL Version 3. +We build CPython against libedit - as opposed to readline - to avoid this +GPL dependency. This requires patches on CPython < 3.10. Distribution releases +before 2023 may link against readline and are therefore subject to the GPL. + +We globally disable the ``_gdbm`` extension module to avoid linking against +GDBM and introducing a GPL dependency. Distribution releases before 2023 may +link against GDBM and be subject to the GPL. + **It is important to understand the licensing requirements when integrating the output of this project into derived works.** To help with this, the JSON document describing the Python distribution contains licensing metadata diff --git a/docs/status.rst b/docs/status.rst index c466094f9..9a8f067cb 100644 --- a/docs/status.rst +++ b/docs/status.rst @@ -5,7 +5,7 @@ Project Status ============== There is support for producing CPython distributions for Windows, -macOS, Linux, and iOS. All distributions are highly self-contained and have +macOS, and Linux. All distributions are highly self-contained and have limited shared library dependencies. Planned and features include: @@ -19,15 +19,6 @@ Planned and features include: Target Notes ============ -Non-Darwin Apple Targets ------------------------- - -Apple targets that aren't Darwin/macOS (iOS, tvOS, watchOS, and corresponding -simulators) are considered alpha quality. The builds may or may not work. The -builds haven't been widely tested. - -Only Python 3.9 is currently supported. - Non-x86 Linux Targets --------------------- @@ -50,7 +41,7 @@ This repository contains ``test-distribution.py`` script that can be used to run the Python test harness from a distribution archive. Here, we track the various known failures when running -``test-distribution.py /path/to/distribution.tar.zst -u all``. +``test-distribution.py --stdlib -- /path/to/distribution.tar.zst -u all``. ``test__locale`` ---------------- @@ -96,27 +87,6 @@ This test fails on Windows:: ---------------------------------------------------------------------- -``test_ssl`` ------------- - -Known failing on: Linux - -When building against LibreSSL instead of OpenSSL, ``test_ssl`` fails -in the following manner:: - - ====================================================================== - FAIL: test_openssl_version (test.test_ssl.BasicSocketTests) - ---------------------------------------------------------------------- - Traceback (most recent call last): - File "/tmp/tmpinhxfy8t/python/install/lib/python3.7/test/test_ssl.py", line 573, in test_openssl_version - (s, t, hex(n))) - AssertionError: False is not true : ('LibreSSL 3.0.2', (2, 0, 0, 0, 0), '0x20000000') - - ---------------------------------------------------------------------- - -This seems to be the test assuming the existence of OpenSSL and not -coping with LibreSSl. This failure seems pretty benign. - ``test_subprocess`` ------------------- @@ -306,7 +276,7 @@ test_spwd test_startfile object has no attribute 'startfile' test_tix - cannot run without OS X gui process + tix is not built by this project test_tk cannot run without OS X gui process test_ttk_guionly diff --git a/docs/technotes.rst b/docs/technotes.rst index c31272bfb..613f10c6e 100644 --- a/docs/technotes.rst +++ b/docs/technotes.rst @@ -12,7 +12,7 @@ for building Python. On Linux, a base Docker image based on a deterministic snapshot of Debian Wheezy is created. A modern binutils and GCC are built in this environment. That modern GCC is then used to build a modern Clang. Clang is then used to build all of Python's dependencies (openssl, ncurses, -readline, sqlite, etc). Finally, Python itself is built. +libedit, sqlite, etc). Finally, Python itself is built. Python is built in such a way that extensions are statically linked against their dependencies. e.g. instead of the ``sqlite3`` Python @@ -57,6 +57,9 @@ older are licensed under the Sleepycat License. The Sleepycat License is more permissive. So we build the ``_dbm`` extension against BDB 6.0.19. +We explicitly disable the ``_gdbm`` extension on all targets to avoid +the GPL dependency. + readline / libedit / ncurses ---------------------------- @@ -71,29 +74,12 @@ distribute because end-users often want software to respect their terminal databases. But for that to work, ``ncurses`` needs to be compiled in a way that respects the user's environment. -On macOS, we statically link a ``libedit`` we compile ourselves. We -dynamically link against ``libncurses``, which is provided by the -system, typically in ``/usr/lib``. - -On Linux, we produce ``readline`` extension module variants compiled -against both ``libreadline`` and ``libedit``, which are statically linked -against libraries built ourselves. These libraries each statically link -against a ``libncurses`` built ourselves. The ``readline`` extension -module variant is the default, as Python compiles against ``readline`` -by default. - -OpenSSL / LibreSSL ------------------- - -By default we compile with OpenSSL. We have some support for compiling -against LibreSSL. +On macOS, we use the system ``libedit`` and ``libncurses``, which is +typically provided in ``/usr/lib``. -LibreSSL is currently required for musl libc builds because -https://github.com/openssl/openssl/commit/38023b87f037f4b832c236dfce2a76272be08763 -broke OpenSSL in our build environment. Projects like Alpine Linux appear -to still be able to build OpenSSL 1.1.1c. It requires certain headers -to be in place though. When we tried to work around this, it turned out to -be easier to compile with LibreSSL than with OpenSSL. +On Linux, we build ``libedit`` and ``ncurses`` from source and statically +link against their respective libraries. Project releases before 2023 linked +against ``readline`` on Linux. gettext / locale Module ----------------------- @@ -134,7 +120,7 @@ versions of Fedora and RHEL. Because the ``nis`` extension is perceived to be likely unused functionality, we've decided to not build it instead of adding complexity to deal with the ``libnsl`` dependency. See further discussion in -https://github.com/indygreg/python-build-standalone/issues/51. +https://github.com/astral-sh/python-build-standalone/issues/51. If ``nis`` functionality is important to you, please file a GitHub issue to request it. @@ -149,7 +135,7 @@ Review Release Notes -------------------- CPython's release notes often have a section on build system changes. -e.g. https://docs.python.org/3/whatsnew/3.8.html#build-and-c-api-changes. +e.g. https://docs.python.org/3/whatsnew/3.13.html#build-changes. These are a must review. ``Modules/Setup`` diff --git a/generate-version-metadata.py b/generate-version-metadata.py new file mode 100755 index 000000000..64466e0c4 --- /dev/null +++ b/generate-version-metadata.py @@ -0,0 +1,100 @@ +# /// script +# requires-python = ">=3.11" +# /// +"""Generate versions payload for python-build-standalone releases.""" + +from __future__ import annotations + +import json +import os +import re +from collections import defaultdict +from datetime import datetime, timezone +from pathlib import Path +from urllib.parse import quote + +FILENAME_RE = re.compile( + r"""(?x) + ^ + cpython- + (?P\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?)(?:\+\d+)?\+ + (?P\d+)- + (?P[a-z\d_]+-[a-z\d]+-[a-z\d]+(?:-(?:gnu(?:eabi(?:hf)?)?|musl|msvc))?)- + (?:(?P.+)-)? + (?P[a-z_]+)? + \.tar\.(?:gz|zst) + $ + """ +) + + +def main() -> None: + tag = os.environ["GITHUB_EVENT_INPUTS_TAG"] + repo = os.environ["GITHUB_REPOSITORY"] + dist = Path("dist") + checksums = dist / "SHA256SUMS" + + if not checksums.exists(): + raise SystemExit("SHA256SUMS not found in dist/") + + # Parse filenames and checksums directly from SHA256SUMS to avoid downloading + # all release artifacts (tens of GB). + entries: list[tuple[str, str]] = [] + for line in checksums.read_text().splitlines(): + line = line.strip() + if not line: + continue + checksum, filename = line.split(maxsplit=1) + filename = filename.lstrip("*") + entries.append((filename, checksum)) + + versions: dict[str, list[dict[str, str]]] = defaultdict(list) + for filename, checksum in sorted(entries): + match = FILENAME_RE.match(filename) + if match is None: + continue + python_version = match.group("py") + build_version = match.group("tag") + version = f"{python_version}+{build_version}" + build = match.group("build") + flavor = match.group("flavor") + variant_parts: list[str] = [] + if build: + variant_parts.extend(build.split("+")) + if flavor: + variant_parts.append(flavor) + variant = "+".join(variant_parts) if variant_parts else "" + + url_prefix = f"https://github.com/{repo}/releases/download/{tag}/" + url = url_prefix + quote(filename, safe="") + archive_format = "tar.zst" if filename.endswith(".tar.zst") else "tar.gz" + + artifact = { + "platform": match.group("triple"), + "variant": variant, + "url": url, + "archive_format": archive_format, + "sha256": checksum, + } + versions[version].append(artifact) + + payload_versions: list[dict[str, object]] = [] + now = datetime.now(timezone.utc).isoformat() + for version, artifacts in sorted(versions.items(), reverse=True): + artifacts.sort( + key=lambda artifact: (artifact["platform"], artifact.get("variant", "")) + ) + payload_versions.append( + { + "version": version, + "date": now, + "artifacts": artifacts, + } + ) + + for version in payload_versions: + print(json.dumps(version, separators=(",", ":"))) + + +if __name__ == "__main__": + main() diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 000000000..579b81675 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,9 @@ +[mypy] +warn_no_return = True +warn_redundant_casts = True +warn_return_any = True +warn_unused_configs = True +warn_unused_ignores = True +warn_unreachable = True + +files = pythonbuild,check.py,build-linux.py,build-macos.py,build-windows.py diff --git a/prune-distribution.py b/prune-distribution.py deleted file mode 100755 index 1556b95ea..000000000 --- a/prune-distribution.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -import os -import pathlib -import subprocess -import sys -import venv - - -ROOT = pathlib.Path(os.path.abspath(__file__)).parent -BUILD = ROOT / "build" -VENV = BUILD / "venv" -PIP = VENV / "bin" / "pip" -PYTHON = VENV / "bin" / "python" -REQUIREMENTS = ROOT / "requirements.txt" - - -def bootstrap(): - BUILD.mkdir(exist_ok=True) - - venv.create(VENV, with_pip=True) - - subprocess.run([str(PIP), "install", "-r", str(REQUIREMENTS)], check=True) - - os.environ["PYBUILD_BOOTSTRAPPED"] = "1" - os.environ["PATH"] = "%s:%s" % (str(VENV / "bin"), os.environ["PATH"]) - os.environ["PYTHONPATH"] = str(ROOT) - - args = [str(PYTHON), __file__, *sys.argv[1:]] - - os.execv(str(PYTHON), args) - - -def run(): - from pythonbuild.utils import prune_distribution_archive - - for p in sys.argv[1:]: - prune_distribution_archive(pathlib.Path(p)) - - -if __name__ == "__main__": - try: - if "PYBUILD_BOOTSTRAPPED" not in os.environ: - bootstrap() - else: - run() - except subprocess.CalledProcessError as e: - sys.exit(e.returncode) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..43e5f4eda --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,38 @@ +[project] +name = "python-build-standalone" +version = "0.1.0" +description = "Produces standalone, highly-redistributable builds of Python." +readme = "README.rst" +requires-python = ">=3.10" +dependencies = [ + "boto3>=1.37", + "boto3-stubs[s3]>=1.42", + "docker>=7.1.0", + "jinja2>=3.1.5", + "jsonschema>=4.23.0", + "pyyaml>=6.0.2", + "six>=1.17.0", + "tomli>=2.2.1", + "typing-extensions>=4.14.1", + "zstandard>=0.23.0", +] + +[build-system] +requires = ["uv_build>=0.9.12,<0.12"] +build-backend = "uv_build" + +[tool.uv.build-backend] +module-name = "pythonbuild" +module-root = "" + +[dependency-groups] +check = [ + "mypy>=1.19.1", + "ruff>=0.15.7", + "types-jinja2>=2.11.9", + "types-jsonschema>=4.26.0.20260324", + "types-pyyaml>=6.0.12.20250915", +] +dev = [ + "moto[server]>=5.1.22", +] diff --git a/pythonbuild/buildenv.py b/pythonbuild/buildenv.py index 75110be83..a946813ed 100644 --- a/pythonbuild/buildenv.py +++ b/pythonbuild/buildenv.py @@ -15,6 +15,7 @@ from .downloads import DOWNLOADS from .logging import log from .utils import ( + clang_toolchain, create_tar_from_directory, exec_and_log, extract_tar_to_directory, @@ -22,7 +23,7 @@ ) -class ContainerContext(object): +class ContainerContext: def __init__(self, container): self.container = container @@ -37,50 +38,58 @@ def copy_file(self, source: pathlib.Path, dest_path=None, dest_name=None): dest_path = dest_path or "/build" copy_file_to_container(source, self.container, dest_path, dest_name) - def install_toolchain_archive(self, build_dir, package_name, host_platform): + def install_toolchain_archive( + self, build_dir, package_name, host_platform, version=None + ): entry = DOWNLOADS[package_name] - basename = "%s-%s-%s.tar" % (package_name, entry["version"], host_platform) + basename = "%s-%s-%s.tar" % ( + package_name, + version or entry["version"], + host_platform, + ) p = build_dir / basename self.copy_file(p) - self.run(["/bin/tar", "-C", "/tools", "-xf", "/build/%s" % p.name], user="root") + self.run(["/bin/tar", "-C", "/tools", "-xf", "/build/%s" % p.name]) def install_artifact_archive( - self, build_dir, package_name, target_triple, optimizations + self, build_dir, package_name, target_triple, build_options ): entry = DOWNLOADS[package_name] basename = "%s-%s-%s-%s.tar" % ( package_name, entry["version"], target_triple, - optimizations, + build_options, ) p = build_dir / basename self.copy_file(p) - self.run(["/bin/tar", "-C", "/tools", "-xf", "/build/%s" % p.name], user="root") + self.run(["/bin/tar", "-C", "/tools", "-xf", "/build/%s" % p.name]) def install_toolchain( self, build_dir, host_platform, + target_triple: str, binutils=False, - gcc=False, musl=False, clang=False, + static=False, ): if binutils: self.install_toolchain_archive(build_dir, "binutils", host_platform) - if gcc: - self.install_toolchain_archive(build_dir, "gcc", host_platform) - if clang: - self.install_toolchain_archive(build_dir, "clang", host_platform) + self.install_toolchain_archive( + build_dir, clang_toolchain(host_platform, target_triple), host_platform + ) if musl: - self.install_toolchain_archive(build_dir, "musl", host_platform) + self.install_toolchain_archive( + build_dir, "musl-static" if static else "musl", host_platform + ) def run(self, program, user="build", environment=None): if isinstance(program, str) and not program.startswith("/"): @@ -130,7 +139,7 @@ def find_output_files(self, base_path, pattern): yield line[len("/build/out/%s/" % base_path) :].decode("ascii") -class TempdirContext(object): +class TempdirContext: def __init__(self, td): self.td = pathlib.Path(td) @@ -152,9 +161,14 @@ def copy_file(self, source: pathlib.Path, dest_path=None, dest_name=None): log("copying %s to %s/%s" % (source, dest_dir, dest_name)) shutil.copy(source, dest_dir / dest_name) - def install_toolchain_archive(self, build_dir, package_name, host_platform): - entry = DOWNLOADS[package_name] - basename = "%s-%s-%s.tar" % (package_name, entry["version"], host_platform) + def install_toolchain_archive( + self, build_dir, package_name, host_platform, version=None + ): + basename = "%s-%s-%s.tar" % ( + package_name, + version or DOWNLOADS[package_name]["version"], + host_platform, + ) p = build_dir / basename dest_path = self.td / "tools" @@ -162,14 +176,14 @@ def install_toolchain_archive(self, build_dir, package_name, host_platform): extract_tar_to_directory(p, dest_path) def install_artifact_archive( - self, build_dir, package_name, target_triple, optimizations + self, build_dir, package_name, target_triple, build_options ): entry = DOWNLOADS[package_name] basename = "%s-%s-%s-%s.tar" % ( package_name, entry["version"], target_triple, - optimizations, + build_options, ) p = build_dir / basename @@ -178,19 +192,27 @@ def install_artifact_archive( extract_tar_to_directory(p, dest_path) def install_toolchain( - self, build_dir, platform, binutils=False, gcc=False, musl=False, clang=False + self, + build_dir, + platform, + target_triple, + binutils=False, + musl=False, + clang=False, + static=False, ): if binutils: self.install_toolchain_archive(build_dir, "binutils", platform) - if gcc: - self.install_toolchain_archive(build_dir, "gcc", platform) - if clang: - self.install_toolchain_archive(build_dir, "clang", platform) + self.install_toolchain_archive( + build_dir, clang_toolchain(platform, target_triple), platform + ) if musl: - self.install_toolchain_archive(build_dir, "musl", platform) + self.install_toolchain_archive( + build_dir, "musl-static" if static else "musl", platform + ) def run(self, program, user="build", environment=None): if user != "build": diff --git a/pythonbuild/cpython.py b/pythonbuild/cpython.py index d3437c96f..3221760ab 100644 --- a/pythonbuild/cpython.py +++ b/pythonbuild/cpython.py @@ -2,77 +2,142 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -import os import pathlib import re import tarfile -from .downloads import DOWNLOADS - -# Module entries from Setup.dist that we can copy verbatim without -# issue. -STATIC_MODULES = { - b"_asyncio", - b"_bisect", - b"_blake2", - b"_codecs_cn", - b"_codecs_hk", - b"_codecs_iso2022", - b"_codecs_jp", - b"_codecs_kr", - b"_codecs_tw", - b"_contextvars", - b"_csv", - b"_datetime", - b"_heapq", - b"_md5", - b"_multibytecodec", - b"_pickle", - b"_posixsubprocess", - b"_random", - b"_sha1", - b"_sha256", - b"_sha512", - b"_sha3", - b"_socket", - b"_statistics", - b"_struct", - # Despite being a test module, this needs to be built as a - # built-in in order to facilitate testing. - b"_testinternalcapi", - b"_weakref", - b"_zoneinfo", - b"array", - b"audioop", - b"binascii", - b"cmath", - b"fcntl", - b"grp", - b"math", - b"mmap", - b"nis", - b"parser", - b"resource", - b"select", - b"spwd", - b"syslog", - b"termios", - b"unicodedata", - b"zlib", +import jsonschema +import yaml + +from pythonbuild.logging import log + +EXTENSION_MODULE_SCHEMA = { + "type": "object", + "properties": { + "build-mode": {"type": "string"}, + "config-c-only": {"type": "boolean"}, + "config-c-only-conditional": { + "type": "array", + "items": { + "type": "object", + "properties": { + "config-c-only": {"type": "boolean"}, + "minimum-python-version": {"type": "string"}, + "maximum-python-version": {"type": "string"}, + }, + "additionalProperties": False, + "required": ["config-c-only"], + }, + }, + "defines": {"type": "array", "items": {"type": "string"}}, + "defines-conditional": { + "type": "array", + "items": { + "type": "object", + "properties": { + "define": {"type": "string"}, + "targets": {"type": "array", "items": {"type": "string"}}, + "minimum-python-version": {"type": "string"}, + "maximum-python-version": {"type": "string"}, + }, + "additionalProperties": False, + "required": ["define"], + }, + }, + "disabled-targets": {"type": "array", "items": {"type": "string"}}, + "frameworks": {"type": "array", "items": {"type": "string"}}, + "includes": {"type": "array", "items": {"type": "string"}}, + "includes-conditional": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": {"type": "string"}, + "includes": {"type": "array", "items": {"type": "string"}}, + "targets": {"type": "array", "items": {"type": "string"}}, + "minimum-python-version": {"type": "string"}, + "maximum-python-version": {"type": "string"}, + }, + "additionalProperties": False, + }, + }, + "includes-deps": {"type": "array", "items": {"type": "string"}}, + "links": {"type": "array", "items": {"type": "string"}}, + "links-conditional": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "targets": {"type": "array", "items": {"type": "string"}}, + "build-mode": {"type": "string"}, + }, + "additionalProperties": False, + }, + }, + "linker-args": { + "type": "array", + "items": { + "type": "object", + "properties": { + "args": {"type": "array", "items": {"type": "string"}}, + "targets": {"type": "array", "items": {"type": "string"}}, + }, + "additionalProperties": False, + }, + }, + "minimum-python-version": {"type": "string"}, + "maximum-python-version": {"type": "string"}, + "required-targets": {"type": "array", "items": {"type": "string"}}, + "setup-enabled": {"type": "boolean"}, + "setup-enabled-conditional": { + "type": "array", + "items": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "minimum-python-version": {"type": "string"}, + "maximum-python-version": {"type": "string"}, + }, + "additionalProperties": False, + "required": ["enabled"], + }, + }, + "sources": {"type": "array", "items": {"type": "string"}}, + "sources-conditional": { + "type": "array", + "items": { + "type": "object", + "properties": { + "source": {"type": "string"}, + "sources": {"type": "array", "items": {"type": "string"}}, + "targets": {"type": "array", "items": {"type": "string"}}, + "minimum-python-version": {"type": "string"}, + "maximum-python-version": {"type": "string"}, + }, + "additionalProperties": False, + "oneOf": [ + { + "required": ["source"], + }, + { + "required": ["sources"], + }, + ], + }, + }, + }, + "additionalProperties": False, } -# Modules we don't (yet) support building. -UNSUPPORTED_MODULES = { - # nis (only installable on UNIX platforms) is globally disabled because - # it has a dependency on libnsl, which isn't part of the Linux Standard - # Base specification. This library has a wonky history where it was once - # part of glibc and core system installs but is slowly being phased away - # from base installations. There are potential workarounds to adding nis - # support. See discussion in - # https://github.com/indygreg/python-build-standalone/issues/51. - b"nis", +EXTENSION_MODULES_SCHEMA = { + "type": "object", + "patternProperties": { + "^[a-z0-9_]+$": EXTENSION_MODULE_SCHEMA, + }, } + # Packages that define tests. STDLIB_TEST_PACKAGES = { "bsddb.test", @@ -90,7 +155,7 @@ } -def parse_setup_line(line: bytes, variant: str): +def parse_setup_line(line: bytes, python_version: str): """Parse a line in a ``Setup.*`` file.""" if b"#" in line: line = line[: line.index(b"#")].rstrip() @@ -113,15 +178,15 @@ def parse_setup_line(line: bytes, variant: str): # directories they may happen to reside in are stripped out. source_path = pathlib.Path(word.decode("ascii")) - if variant: - obj_path = pathlib.Path( - "Modules/VARIANT-%s-%s-%s" - % (extension, variant, source_path.with_suffix(".o").name) + # Python 3.11 changed the path of the object file. + if meets_python_minimum_version(python_version, "3.11") and b"/" in word: + obj_path = ( + pathlib.Path("Modules") + / source_path.parent + / source_path.with_suffix(".o").name ) else: - obj_path = pathlib.Path( - "Modules/%s" % source_path.with_suffix(".o").name - ) + obj_path = pathlib.Path("Modules") / source_path.with_suffix(".o").name objs.add(obj_path) @@ -130,201 +195,481 @@ def parse_setup_line(line: bytes, variant: str): elif word.startswith(b"-l"): links.add(word[2:].decode("ascii")) + elif word.startswith(b"-hidden-l"): + links.add(word[len("-hidden-l") :].decode("ascii")) + elif word == b"-framework": frameworks.add(words[i + 1].decode("ascii")) return { "extension": extension, + "line": line, "posix_obj_paths": objs, "links": links, "frameworks": frameworks, - "variant": variant or "default", + "variant": "default", } +def link_for_target(lib: str, target_triple: str) -> str: + # TODO use -Wl,-hidden-lbz2? + # TODO use -Wl,--exclude-libs,libfoo.a? + + if "-apple-" in target_triple: + # The -l:filename syntax doesn't appear to work on Apple. + # just give the library name and hope it turns out OK. + if lib.startswith(":lib") and lib.endswith(".a"): + return f"-Xlinker -hidden-l{lib[4:-2]}" + else: + return f"-Xlinker -hidden-l{lib}" + else: + return f"-l{lib}" + + +def meets_python_minimum_version(got: str, wanted: str) -> bool: + parts = got.split(".") + got_major, got_minor = int(parts[0]), int(parts[1]) + + parts = wanted.split(".") + wanted_major, wanted_minor = int(parts[0]), int(parts[1]) + + return (got_major, got_minor) >= (wanted_major, wanted_minor) + + +def meets_python_maximum_version(got: str, wanted: str) -> bool: + parts = got.split(".") + got_major, got_minor = int(parts[0]), int(parts[1]) + + parts = wanted.split(".") + wanted_major, wanted_minor = int(parts[0]), int(parts[1]) + + return (got_major, got_minor) <= (wanted_major, wanted_minor) + + def derive_setup_local( - static_modules_lines, cpython_source_archive, python_version, - disabled=None, - musl=False, - debug=False, + target_triple, + build_options, + extension_modules, ): """Derive the content of the Modules/Setup.local file.""" - # makesetup parses lines with = as extra config options. There appears - # to be no easy way to define e.g. -Dfoo=bar in Setup.local. We hack - # around this by producing a Makefile supplement that overrides the build - # rules for certain targets to include these missing values. - extra_cflags = {} - disabled = disabled or set() - disabled |= UNSUPPORTED_MODULES + # The first part of this function validates that our extension modules YAML + # based metadata is in sync with the various files declaring extension + # modules in the Python distribution. + + disabled = set() + ignored = set() + setup_enabled_wanted = set() + config_c_only_wanted = set() - if musl: - # Missing header dependencies. - disabled.add(b"nis") - disabled.add(b"ossaudiodev") + # Collect metadata about our extension modules as they relate to this + # Python target. + for name, info in sorted(extension_modules.items()): + python_min_match = meets_python_minimum_version( + python_version, info.get("minimum-python-version", "1.0") + ) + python_max_match = meets_python_maximum_version( + python_version, info.get("maximum-python-version", "100.0") + ) + + if info.get("build-mode") not in ( + None, + "shared", + "static", + "shared-or-disabled", + ): + raise Exception("unsupported build-mode for extension module %s" % name) + + if not (python_min_match and python_max_match): + log(f"ignoring extension module {name} because Python version incompatible") + ignored.add(name) + continue + + if targets := info.get("disabled-targets"): + if any(re.match(p, target_triple) for p in targets): + log( + "disabling extension module %s because disabled for this target triple" + % name + ) + disabled.add(name) - if debug: - # Doesn't work in debug builds. - disabled.add(b"xxlimited") + # Extension is demanding it be built as shared. If this isn't possible due to + # a static build, disable the extension. + if info.get("build-mode") == "shared-or-disabled" and "static" in build_options: + disabled.add(name) + + if info.get("setup-enabled", False): + setup_enabled_wanted.add(name) + + for entry in info.get("setup-enabled-conditional", []): + python_min_match_setup = meets_python_minimum_version( + python_version, entry.get("minimum-python-version", "1.0") + ) + python_max_match_setup = meets_python_maximum_version( + python_version, entry.get("maximum-python-version", "100.0") + ) + + if entry.get("enabled", False) and ( + python_min_match_setup and python_max_match_setup + ): + setup_enabled_wanted.add(name) + + if info.get("config-c-only"): + config_c_only_wanted.add(name) + + for entry in info.get("config-c-only-conditional", []): + python_min_match_setup = meets_python_minimum_version( + python_version, entry.get("minimum-python-version", "1.0") + ) + python_max_match_setup = meets_python_maximum_version( + python_version, entry.get("maximum-python-version", "100.0") + ) + if entry.get("config-c-only", False) and ( + python_min_match_setup and python_max_match_setup + ): + config_c_only_wanted.add(name) + + # Parse more files in the distribution for their metadata. with tarfile.open(str(cpython_source_archive)) as tf: - # Setup.dist removed in Python 3.8. + ifh = tf.extractfile("Python-%s/Modules/Setup" % python_version) + setup_lines = ifh.readlines() + try: - ifh = tf.extractfile("Python-%s/Modules/Setup.dist" % python_version) + ifh = tf.extractfile(f"Python-{python_version}/Modules/Setup.bootstrap.in") + setup_bootstrap_in = ifh.readlines() except KeyError: - ifh = tf.extractfile("Python-%s/Modules/Setup" % python_version) - - source_lines = ifh.readlines() + setup_bootstrap_in = [] ifh = tf.extractfile("Python-%s/Modules/config.c.in" % python_version) config_c_in = ifh.read() - found_shared = False + dist_modules = set() + setup_enabled_actual = set() + setup_enabled_lines = {} + section = "static" - dest_lines = [] - make_lines = [] + RE_VARIABLE = re.compile(rb"^[a-zA-Z_]+\s*=") + RE_EXTENSION_MODULE = re.compile(rb"^([a-z_]+)\s.*[a-zA-Z/_-]+\.c\b") + + # Setup.bootstrap.in has a simple format. + for line in setup_bootstrap_in: + if b"#" in line: + line = line[: line.index(b"#")] + + # There is special `@MODULE__TRUE@` syntax that gets elided or turned + # into a comment during Makefile expansion. For now, just pretend it always + # goes away. This assumption may not be valid in future Python versions. But + # as of 3.11 only pwd is defined this way. + if line.startswith(b"@") and line.count(b"@") == 2: + line = line.split(b"@")[-1] + + line = line.strip() - for line in source_lines: + if not line: + continue + + if m := RE_EXTENSION_MODULE.match(line): + name = m.group(1).decode("ascii") + + dist_modules.add(name) + setup_enabled_actual.add(name) + setup_enabled_lines[name] = line + + for line in setup_lines: line = line.rstrip() - if line == b"#*shared*": - found_shared = True - dest_lines.append(b"*static*") + if not line: + continue - if not found_shared: + # Looks like a variable assignment. + if RE_VARIABLE.match(line): continue - # Stop processing at the #*disabled* line. - if line == b"#*disabled*": - break + # Look for extension syntax before and after comment. + for i, part in enumerate(line.split(b"#")): + if m := RE_EXTENSION_MODULE.match(part): + dist_modules.add(m.group(1).decode("ascii")) - if line.startswith(tuple(b"#%s" % k for k in STATIC_MODULES)): - line = line[1:] + if i == 0: + setup_enabled_actual.add(m.group(1).decode("ascii")) - if b"#" in line: - line = line[: line.index(b"#")] + break - module = line.split()[0] - if module in disabled: - continue + # Now look for enabled extensions and stash away the line. - dest_lines.append(line) + if line == b"*static*": + section = "static" + continue + elif line == b"*shared*": + section = "shared" + continue + elif line == b"*disabled*": + section = "disabled" + continue + + if b"#" in line: + line = line[: line.index(b"#")].strip() + + if line and section != "disabled": + setup_enabled_lines[line.split()[0].decode("ascii")] = line + + config_c_extensions = parse_config_c(config_c_in.decode("utf-8")) + + for extension in sorted(config_c_extensions): + dist_modules.add(extension) + + # With ours and theirs extension module metadata collections, compare and + # make sure our metadata is comprehensive. This isn't strictly necessary. + # But it makes it drastically easier to catch bugs due to our metadata being + # out of sync with the distribution. This has historically caused several + # subtle and hard-to-diagnose bugs, which is why we do it. + + missing = dist_modules - set(extension_modules.keys()) - RE_DEFINE = re.compile(b"-D[^=]+=[^\s]+") - RE_VARIANT = re.compile(b"VARIANT=([^\s]+)\s") + if missing: + raise Exception( + "missing extension modules from YAML: %s" % ", ".join(sorted(missing)) + ) + + missing = setup_enabled_actual - setup_enabled_wanted + if missing: + raise Exception( + "Setup enabled extensions missing YAML setup-enabled annotation: %s" + % ", ".join(sorted(missing)) + ) + + extra = setup_enabled_wanted - setup_enabled_actual + if extra: + raise Exception( + "YAML setup-enabled extensions not present in Setup: %s" + % ", ".join(sorted(extra)) + ) + + if missing := set(config_c_extensions) - config_c_only_wanted: + raise Exception( + "config.c.in extensions missing YAML config-c-only annotation: %s" + % ", ".join(sorted(missing)) + ) - seen_variants = set() + if extra := config_c_only_wanted - set(config_c_extensions): + raise Exception( + "YAML config-c-only extensions not present in config.c.in: %s" + % ", ".join(sorted(extra)) + ) - for line in static_modules_lines: - if not line.strip(): + # And with verification out of way, now we generate a Setup.local file + # from our metadata. The verification above ensured that our metadata + # agrees fully with the distribution's knowledge of extensions. So we can + # treat our metadata as canonical. + + RE_DEFINE = re.compile(rb"-D[^=]+=[^\s]+") + + # Translate our YAML metadata into Setup lines. + + section_lines = { + "disabled": [], + "shared": [], + "static": [], + } + + # makesetup parses lines with = as extra config options. There appears + # to be no easy way to define e.g. -Dfoo=bar in Setup.local. We hack + # around this by producing a Makefile supplement that overrides the build + # rules for certain targets to include these missing values. + extra_cflags = {} + + enabled_extensions = {} + + for name, info in sorted(extension_modules.items()): + if name in ignored: continue - # This was added to support musl, since not all extensions build in the - # musl environment. - if line.split()[0] in disabled: + if name in disabled: + section_lines["disabled"].append(name.encode("ascii")) continue - # We supplement the format to support declaring extension variants. - # A variant results in multiple compiles of a given extension. - # However, the CPython build system doesn't take kindly to this because - # a) we can only have a single extension with a given name - # b) it assumes the init function matches the extension name - # c) attempting to link multiple variants into the same binary can often - # result in duplicate symbols when variants use different libraries - # implementing the same API. - # - # When we encounter a variant, we only pass the 1st variant through to - # Setup.local. We then supplement the Makefile with rules to build - # remaining variants. - m = RE_VARIANT.search(line) - if m: - line = RE_VARIANT.sub(b"", line) - variant = m.group(1) - extension = line.split()[0] - - cflags = [] - ldflags = [] - - for w in line.split()[1:]: - if w.startswith((b"-I", b"-D")): - cflags.append(w) - elif w.startswith((b"-L", b"-l")): - ldflags.append(w) - elif w.endswith(b".c"): - pass - else: - raise ValueError("unexpected content in Setup variant line: %s" % w) - - if extension in seen_variants: - sources = [w for w in line.split() if w.endswith(b".c")] - object_files = [] - for source in sources: - basename = os.path.basename(source)[:-2] - - # Use a unique name to ensure there aren't collisions - # across extension variants. - object_file = b"Modules/VARIANT-%s-%s-%s.o" % ( - extension, - variant, - basename, - ) - object_files.append(object_file) - make_lines.append( - b"%s: $(srcdir)/Modules/%s; " - b"$(CC) $(PY_BUILTIN_MODULE_CFLAGS) " - b"%s -c $(srcdir)/Modules/%s -o %s" - % (object_file, source, b" ".join(cflags), source, object_file) - ) - - # This is kind of a lie in the case of musl. That's fine. - extension_target = b"Modules/%s-VARIANT-%s$(EXT_SUFFIX)" % ( - extension, - variant, - ) + enabled_extensions[name] = dict(info) + + # The initialization function is usually PyInit_{extension}. But some + # config.c.in extensions don't follow this convention! + if name in config_c_extensions: + init_fn = config_c_extensions[name] + in_core = True + else: + init_fn = f"PyInit_{name}" + in_core = False + + enabled_extensions[name]["init_fn"] = init_fn + enabled_extensions[name]["in_core"] = in_core + + # config.c.in only extensions are part of core object files. There is + # nothing else to process. + if name in config_c_only_wanted: + log(f"extension {name} enabled through config.c") + enabled_extensions[name]["setup_line"] = name.encode("ascii") + continue - make_lines.append( - b"%s: %s" % (extension_target, b" ".join(object_files)) - ) + # Force static linking if we're doing a fully static build, otherwise, + # respect the `build-mode` falling back to `static` if not defined. + section = ( + "static" if "static" in build_options else info.get("build-mode", "static") + ) - # We can't link a shared library in MUSL since everything is static. - if not musl: - make_lines.append( - b"\t$(BLDSHARED) %s %s -o Modules/%s-VARIANT-%s$(EXT_SUFFIX)" - % ( - b" ".join(object_files), - b" ".join(ldflags), - extension, - variant, - ) - ) - - make_lines.append( - b'\techo "%s" > Modules/VARIANT-%s-%s.data' - % (line, extension, variant) + # shared-or-disabled maps to shared or disabled. + if section == "shared-or-disabled": + section = "shared" + + enabled_extensions[name]["build-mode"] = section + + # Presumably this means the extension comes from the distribution's + # Setup. Lack of sources means we don't need to derive a Setup.local + # line. + if "sources" not in info and "sources-conditional" not in info: + if name not in setup_enabled_lines: + raise Exception( + f"found a sourceless extension ({name}) with no Setup entry" ) - # Add dependencies to $(LIBRARY) target to force compilation of - # extension variant. - make_lines.append(b"$(LIBRARY): %s" % extension_target) + log(f"extension {name} enabled through distribution's Modules/Setup file") + + # But we do record a placeholder Setup line in the returned metadata + # as this is what's used to derive PYTHON.json metadata. + enabled_extensions[name]["setup_line"] = setup_enabled_lines[name] + continue + + log(f"extension {name} being configured via YAML metadata") + + line = name + + for source in info.get("sources", []): + line += " %s" % source + + for entry in info.get("sources-conditional", []): + if targets := entry.get("targets", []): + target_match = any(re.match(p, target_triple) for p in targets) + else: + target_match = True + + python_min_match = meets_python_minimum_version( + python_version, entry.get("minimum-python-version", "1.0") + ) + python_max_match = meets_python_maximum_version( + python_version, entry.get("maximum-python-version", "100.0") + ) + + if build_mode := entry.get("build-mode"): + build_mode_match = section == build_mode + else: + build_mode_match = True + + if ( + target_match + and python_min_match + and python_max_match + and build_mode_match + ): + if source := entry.get("source"): + line += f" {source}" + for source in entry.get("sources", []): + line += f" {source}" + + for define in info.get("defines", []): + line += f" -D{define}" + + for entry in info.get("defines-conditional", []): + if targets := entry.get("targets", []): + target_match = any(re.match(p, target_triple) for p in targets) + else: + target_match = True + + python_min_match = meets_python_minimum_version( + python_version, entry.get("minimum-python-version", "1.0") + ) + python_max_match = meets_python_maximum_version( + python_version, entry.get("maximum-python-version", "100.0") + ) + + if target_match and (python_min_match and python_max_match): + line += f" -D{entry['define']}" + + for path in info.get("includes", []): + line += f" -I{path}" + + for entry in info.get("includes-conditional", []): + if targets := entry.get("targets", []): + target_match = any(re.match(p, target_triple) for p in targets) + else: + target_match = True + + python_min_match = meets_python_minimum_version( + python_version, entry.get("minimum-python-version", "1.0") + ) + python_max_match = meets_python_maximum_version( + python_version, entry.get("maximum-python-version", "100.0") + ) + if target_match and (python_min_match and python_max_match): + # TODO: Change to `include` and drop support for `path` + if include := entry.get("path"): + line += f" -I{include}" + for include in entry.get("includes", []): + line += f" -I{include}" + + for path in info.get("includes-deps", []): + # Includes are added to global search path. + if "-apple-" in target_triple: continue + line += f" -I/tools/deps/{path}" + + for lib in info.get("links", []): + line += " %s" % link_for_target(lib, target_triple) + + for entry in info.get("links-conditional", []): + if targets := entry.get("targets", []): + target_match = any(re.match(p, target_triple) for p in targets) else: - seen_variants.add(extension) + target_match = True + + python_min_match = meets_python_minimum_version( + python_version, entry.get("minimum-python-version", "1.0") + ) + python_max_match = meets_python_maximum_version( + python_version, entry.get("maximum-python-version", "100.0") + ) + + if target_match and (python_min_match and python_max_match): + line += " %s" % link_for_target(entry["name"], target_triple) + + if "-apple-" in target_triple: + for framework in info.get("frameworks", []): + line += f" -framework {framework}" + + for entry in info.get("linker-args", []): + if any(re.match(p, target_triple) for p in entry["targets"]): + for arg in entry["args"]: + line += f" -Xlinker {arg}" + + line = line.encode("ascii") + + # This extra parse is a holder from older code and could likely be + # factored away. + parsed = parse_setup_line(line, python_version=python_version) + + if not parsed: + raise Exception("we should always parse a setup line we generated") # makesetup parses lines with = as extra config options. There appears # to be no easy way to define e.g. -Dfoo=bar in Setup.local. We hack # around this by detecting the syntax we'd like to support and move the # variable defines to a Makefile supplement that overrides variables for # specific targets. - for m in RE_DEFINE.finditer(line): - sources = [w for w in line.split() if w.endswith(b".c")] - for source in sources: - obj = b"Modules/%s.o" % os.path.basename(source)[:-2] - - extra_cflags.setdefault(obj, []).append(m.group(0)) + for m in RE_DEFINE.finditer(parsed["line"]): + for obj_path in sorted(parsed["posix_obj_paths"]): + extra_cflags.setdefault(bytes(obj_path), []).append(m.group(0)) line = RE_DEFINE.sub(b"", line) @@ -333,27 +678,36 @@ def derive_setup_local( "= appears in EXTRA_MODULES line; will confuse " "makesetup: %s" % line.decode("utf-8") ) - dest_lines.append(line) - dest_lines.append(b"\n*disabled*\n") - dest_lines.extend(sorted(disabled)) + section_lines[section].append(line) + enabled_extensions[name]["setup_line"] = line + + dest_lines = [] + + for section, lines in sorted(section_lines.items()): + if not lines: + continue + + dest_lines.append(b"\n*%s*\n" % section.encode("ascii")) + dest_lines.extend(lines) dest_lines.append(b"") + make_lines = [] + for target in sorted(extra_cflags): make_lines.append( b"%s: PY_STDMODULE_CFLAGS += %s" % (target, b" ".join(extra_cflags[target])) ) return { - "config_c_in": config_c_in, - "setup_dist": b"\n".join(source_lines), + "extensions": enabled_extensions, "setup_local": b"\n".join(dest_lines), "make_data": b"\n".join(make_lines), } -RE_INITTAB_ENTRY = re.compile('\{"([^"]+)", ([^\}]+)\},') +RE_INITTAB_ENTRY = re.compile(r'\{"([^"]+)", ([^\}]+)\},') def parse_config_c(s: str): @@ -386,3 +740,13 @@ def parse_config_c(s: str): extensions[m.group(1)] = m.group(2) return extensions + + +def extension_modules_config(yaml_path: pathlib.Path): + """Loads the extension-modules.yml file.""" + with yaml_path.open("r", encoding="utf-8") as fh: + data = yaml.load(fh, Loader=yaml.SafeLoader) + + jsonschema.validate(data, EXTENSION_MODULES_SCHEMA) + + return data diff --git a/pythonbuild/disttests/__init__.py b/pythonbuild/disttests/__init__.py new file mode 100644 index 000000000..f7186e13d --- /dev/null +++ b/pythonbuild/disttests/__init__.py @@ -0,0 +1,361 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import importlib.machinery +import os +import struct +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from typing import Optional + +TERMINFO_DIRS = [ + "/etc/terminfo", + "/lib/terminfo", + "/usr/share/terminfo", +] + +TCL_PATHS = [ + # POSIX + ("lib", "tcl", "tcl"), + # Windows. + ("tcl",), +] + +HERE = os.path.dirname(sys.executable) +INSTALL_ROOT = os.path.dirname(HERE) + +# Need to set TCL_LIBRARY so local tcl/tk files get picked up. +for parts in TCL_PATHS: + candidate = os.path.join(INSTALL_ROOT, *parts) + + if os.path.exists(candidate): + os.environ["TCL_LIBRARY"] = candidate + break + +# Need to set TERMINFO_DIRS so terminfo database can be located. +if "TERMINFO_DIRS" not in os.environ: + terminfo_dirs = [p for p in TERMINFO_DIRS if os.path.exists(p)] + if terminfo_dirs: + os.environ["TERMINFO_DIRS"] = ":".join(terminfo_dirs) + + +class TestPythonInterpreter(unittest.TestCase): + def test_compression(self): + import bz2 + import lzma + import zlib + + self.assertTrue(lzma.is_check_supported(lzma.CHECK_CRC64)) + self.assertTrue(lzma.is_check_supported(lzma.CHECK_SHA256)) + + bz2.compress(b"test") + zlib.compress(b"test") + + def test_ctypes(self): + import ctypes + + # pythonapi will be None on statically linked binaries. + is_static = "static" in os.environ["BUILD_OPTIONS"] + if is_static: + self.assertIsNone(ctypes.pythonapi) + else: + self.assertIsNotNone(ctypes.pythonapi) + + # https://bugs.python.org/issue42688 + @ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) + def error_handler(fif, message): + pass + + @unittest.skipIf(os.name == "nt", "curses not available on Windows") + def test_curses_import(self): + import curses + + assert curses is not None + + @unittest.skipIf(os.name == "nt", "curses not available on Windows") + @unittest.skipIf("TERM" not in os.environ, "TERM not set") + def test_curses_interactive(self): + import curses + + curses.initscr() + curses.endwin() + + def test_hashlib(self): + import hashlib + + wanted_hashes = { + "blake2b", + "blake2s", + "md5", + "md5-sha1", + "ripemd160", + "sha1", + "sha224", + "sha256", + "sha384", + "sha3_224", + "sha3_256", + "sha3_384", + "sha3_512", + "sha512", + "sha512_224", + "sha512_256", + "shake_128", + "shake_256", + "sm3", + } + + # Legacy algorithms only present on OpenSSL 1.1. + if os.name == "nt" and sys.version_info[0:2] < (3, 11): + wanted_hashes.add("md4") + wanted_hashes.add("whirlpool") + + for hash in wanted_hashes: + self.assertIn(hash, hashlib.algorithms_available) + + @unittest.skipIf(os.name == "nt", "_testcapi not built on Windows") + @unittest.skipIf( + os.environ["TARGET_TRIPLE"].endswith("-musl") + and "static" in os.environ["BUILD_OPTIONS"], + "_testcapi not available on statically-linked distributions", + ) + def test_testcapi(self): + import _testcapi # type: ignore + + self.assertIsNotNone(_testcapi) + + if sys.version_info[0:2] >= (3, 13): + import _testlimitedcapi # type: ignore + + self.assertIsNotNone(_testlimitedcapi) + + def test_sqlite(self): + import sqlite3 + + self.assertEqual(sqlite3.sqlite_version_info, (3, 50, 4)) + + # Optional SQLite3 features are enabled. + conn = sqlite3.connect(":memory:") + # Extension loading enabled. + self.assertTrue(hasattr(conn, "enable_load_extension")) + # Backup feature requires modern SQLite, which we always have. + self.assertTrue(hasattr(conn, "backup")) + # Ensure that various extensions are present. These will raise if they are not. + extensions = ["fts3", "fts4", "fts5", "geopoly", "rtree"] + cursor = conn.cursor() + for extension in extensions: + with self.subTest(extension=extension): + cursor.execute( + f"CREATE VIRTUAL TABLE test{extension} USING {extension}(a, b, c);" + ) + + # Test various SQLite flags and features requested / expected by users. + # The DBSTAT virtual table shows some metadata about disk usage. + # https://www.sqlite.org/dbstat.html + self.assertNotEqual( + cursor.execute("SELECT COUNT(*) FROM dbstat;").fetchone()[0], + 0, + ) + + # The serialize/deserialize API is configurable at compile time. + if sys.version_info[0:2] >= (3, 11): + self.assertEqual(conn.serialize()[:15], b"SQLite format 3") + + # The "enhanced query syntax" (-DSQLITE_ENABLE_FTS3_PARENTHESIS) allows parenthesizable + # AND, OR, and NOT operations. The "standard query syntax" only has OR as a keyword, so we + # can test for the difference with a query using AND. + # https://www.sqlite.org/fts3.html#_set_operations_using_the_enhanced_query_syntax + cursor.execute("INSERT INTO testfts3 VALUES('hello world', '', '');") + self.assertEqual( + cursor.execute( + "SELECT COUNT(*) FROM testfts3 WHERE a MATCH 'hello AND world';" + ).fetchone()[0], + 1, + ) + + # fts3_tokenizer() takes/returns native pointers. Newer SQLite versions require the use of + # bound parameters with this function to avoid the risk of a SQL injection esclating into a + # full RCE. This requirement can be disabled at either compile time or runtime for + # backwards compatibility. Ensure that the check is enabled (more secure) by default but + # applications can still use fts3_tokenize with a bound parameter. See discussion at + # https://github.com/astral-sh/python-build-standalone/pull/562#issuecomment-3254522958 + wild_pointer = struct.pack("P", 0xDEADBEEF) + with self.assertRaises(sqlite3.OperationalError) as caught: + cursor.execute( + f"SELECT fts3_tokenizer('mytokenizer', x'{wild_pointer.hex()}')" + ) + self.assertEqual(str(caught.exception), "fts3tokenize disabled") + cursor.execute("SELECT fts3_tokenizer('mytokenizer', ?)", (wild_pointer,)) + + conn.close() + + def test_ssl(self): + import ssl + + self.assertTrue(ssl.HAS_TLSv1) + self.assertTrue(ssl.HAS_TLSv1_1) + self.assertTrue(ssl.HAS_TLSv1_2) + self.assertTrue(ssl.HAS_TLSv1_3) + + # OpenSSL 1.1 on older CPython versions on Windows. 3.5 everywhere + # else. The format is documented a bit here: + # https://docs.openssl.org/1.1.1/man3/OPENSSL_VERSION_NUMBER/ + # https://docs.openssl.org/3.5/man3/OpenSSL_version/ + # For 1.x it is the three numerical version components, the + # suffix letter as a 1-based integer, and 0xF for "release". For + # 3.x it is the major, minor, 0, patch, and 0. + if os.name == "nt" and sys.version_info[0:2] < (3, 11): + wanted_version = (1, 1, 1, 23, 15) + else: + wanted_version = (3, 5, 0, 6, 0) + + self.assertEqual(ssl.OPENSSL_VERSION_INFO, wanted_version) + + ssl.create_default_context() + + @unittest.skipIf( + sys.version_info[:2] < (3, 13), + "Free-threaded builds are only available in 3.13+", + ) + def test_gil_disabled(self): + import sysconfig + + if "freethreaded" in os.environ.get("BUILD_OPTIONS", "").split("+"): + wanted = 1 + else: + wanted = 0 + + self.assertEqual(sysconfig.get_config_var("Py_GIL_DISABLED"), wanted) + + @unittest.skipIf( + sys.version_info[:2] < (3, 14), + "zstd is only available in 3.14+", + ) + def test_zstd_multithreaded(self): + from compression import zstd # type: ignore + + max_threads = zstd.CompressionParameter.nb_workers.bounds()[1] + assert max_threads > 0, ( + "Expected multithreading to be enabled but max threads is zero" + ) + + @unittest.skipIf("TCL_LIBRARY" not in os.environ, "TCL_LIBRARY not set") + @unittest.skipIf("DISPLAY" not in os.environ, "DISPLAY not set") + def test_tkinter(self): + import tkinter as tk + + class Application(tk.Frame): + def __init__(self, master=None): + super().__init__(master) + self.master = master + self.pack() + + self.hi_there = tk.Button(self) + self.hi_there["text"] = "Hello World\n(click me)" + self.hi_there["command"] = self.say_hi + self.hi_there.pack(side="top") + + self.quit = tk.Button( + self, text="QUIT", fg="red", command=self.master.destroy + ) + self.quit.pack(side="bottom") + + def say_hi(self): + print("hi there, everyone!") + + root = tk.Tk() + Application(master=root) + + def test_hash_algorithm(self): + self.assertTrue( + sys.hash_info.algorithm.startswith("siphash"), + msg=f"{sys.hash_info.algorithm=!r} is not siphash", + ) + + def test_libc_identity(self): + def assertLibc(value): + for libc in ("-gnu", "-musl"): + if os.environ["TARGET_TRIPLE"].endswith(libc): + self.assertIn(libc, value) + else: + self.assertNotIn(libc, value) + + if hasattr(sys.implementation, "_multiarch"): + assertLibc(sys.implementation._multiarch) + + assertLibc(importlib.machinery.EXTENSION_SUFFIXES[0]) + + @unittest.skipIf( + sys.version_info[:2] < (3, 11), + "not yet implemented", + ) + @unittest.skipIf(os.name == "nt", "no symlinks or argv[0] on Windows") + def test_getpath(self): + def assertPythonWorks(path: Path, argv0: Optional[str] = None): + output = subprocess.check_output( + [argv0 or path, "-c", "print(42)"], executable=path, text=True + ) + self.assertEqual(output.strip(), "42") + + with tempfile.TemporaryDirectory(prefix="disttests-") as t: + tmpdir = Path(t) + symlink = tmpdir / "python" + symlink.symlink_to(sys.executable) + with self.subTest(msg="symlink without venv"): + assertPythonWorks(symlink) + + # TODO: --copies does not work right + for flag in ("--symlinks",): + with self.subTest(flag=flag): + venv = tmpdir / f"venv_{flag}" + subprocess.check_call( + [symlink, "-m", "venv", flag, "--without-pip", venv] + ) + assertPythonWorks(venv / "bin" / "python") + + with self.subTest(msg="weird argv[0]"): + assertPythonWorks(sys.executable, argv0="/dev/null") + + @unittest.skipUnless(sys.platform == "linux", "Linux-specific prctl") + @unittest.skipIf( + "static" in os.environ["BUILD_OPTIONS"], + "cannot import libc on static builds", + ) + def test_nx_thread_creation(self): + "Test that thread creation works under e.g. systemd's MemoryDenyWriteExecute." + # Note that NX cannot be unset so this pollutes the current process, + # but if something else breaks under NX we probably want to know! + import ctypes + import threading + + libc = ctypes.CDLL(None, use_errno=True) + # + PR_SET_MDWE = 65 + PR_GET_MDWE = 66 + PR_MDWE_REFUSE_EXEC_GAIN = 1 << 0 + PR_MDWE_NO_INHERIT = 1 << 1 + mdwe = libc.prctl(PR_GET_MDWE, 0, 0, 0, 0) + if mdwe < 0: + self.skipTest("prctl(PR_SET_MDWE) unsupported") + elif not (mdwe & PR_MDWE_REFUSE_EXEC_GAIN): + if ( + libc.prctl( + PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT, 0, 0, 0 + ) + != 0 + ): + self.fail("prctl(PR_SET_MDWE): " + os.strerror(ctypes.get_errno())) + + a = [] + t = threading.Thread(target=a.append, args=("Thread was here",)) + t.start() + t.join() + self.assertEqual(a, ["Thread was here"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/pythonbuild/docker.py b/pythonbuild/docker.py index 905340c80..0be78e4f8 100644 --- a/pythonbuild/docker.py +++ b/pythonbuild/docker.py @@ -9,7 +9,7 @@ import pathlib import tarfile -import docker +import docker # type: ignore import jinja2 from .logging import log, log_raw @@ -29,8 +29,10 @@ def write_dockerfiles(source_dir: pathlib.Path, dest_dir: pathlib.Path): write_if_different(dest_dir / f, data.encode("utf-8")) -def build_docker_image(client, image_data: bytes, image_dir: pathlib.Path, name): - image_path = image_dir / ("image-%s" % name) +def build_docker_image( + client, image_data: bytes, image_dir: pathlib.Path, name, host_platform +): + image_path = image_dir / f"image-{name}.{host_platform}" return ensure_docker_image(client, io.BytesIO(image_data), image_path=image_path) @@ -48,6 +50,9 @@ def ensure_docker_image(client, fh, image_path=None): if "aux" in s and "ID" in s["aux"]: image = s["aux"]["ID"] + if "error" in s: + log(s["error"]) + if not image: raise Exception("unable to determine built Docker image") @@ -63,11 +68,14 @@ def ensure_docker_image(client, fh, image_path=None): return image -def get_image(client, source_dir: pathlib.Path, image_dir: pathlib.Path, name): +def get_image( + client, source_dir: pathlib.Path, image_dir: pathlib.Path, name, host_platform +): if client is None: return None - image_path = image_dir / ("image-%s" % name) + image_name = f"image-{name}.{host_platform}" + image_path = image_dir / image_name tar_path = image_path.with_suffix(".tar") with image_path.open("r") as fh: @@ -85,7 +93,9 @@ def get_image(client, source_dir: pathlib.Path, image_dir: pathlib.Path, name): return image_id else: - return build_docker_image(client, source_dir, image_dir, name) + return build_docker_image( + client, str(source_dir).encode(), image_dir, name, host_platform + ) def copy_file_to_container(path, container, container_path, archive_path=None): @@ -155,9 +165,10 @@ def container_get_archive(container, path): new_data = io.BytesIO() - with tarfile.open(fileobj=old_data) as itf, tarfile.open( - fileobj=new_data, mode="w" - ) as otf: + with ( + tarfile.open(fileobj=old_data) as itf, + tarfile.open(fileobj=new_data, mode="w") as otf, + ): for member in sorted(itf.getmembers(), key=operator.attrgetter("name")): file_data = itf.extractfile(member) if not member.linkname else None member.mtime = DEFAULT_MTIME diff --git a/pythonbuild/downloads.py b/pythonbuild/downloads.py index 6ee82f575..8a28180e8 100644 --- a/pythonbuild/downloads.py +++ b/pythonbuild/downloads.py @@ -2,7 +2,24 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. +# Several files here are mirrored from their upstream sources due to flaky +# downloads from upstream hosts (either intentional rate limiting or general +# low-availability / non-CDN infrastructure) and to reduce load on them. To +# update a file, push the new artifact to github.com/astral-sh/mirror (without +# removing the old artifact) and then update here once GitHub Pages has +# deployed. Feel free to point directly to the upstream source while working on +# a PR, especially if you don't have push access to astral-sh/mirror or are +# unsure if the PR will land, but we should make sure to switch back to the +# mirror shortly after landing the dependency. + DOWNLOADS = { + "autoconf": { + # Mirrored from https://ftp.gnu.org/gnu/autoconf/autoconf-2.72.tar.gz + "url": "https://astral-sh.github.io/mirror/files/autoconf-2.72.tar.gz", + "size": 2143794, + "sha256": "afb181a76e1ee72832f6581c0eddf8df032b83e2e0239ef79ebedc4467d92d6e", + "version": "2.72", + }, # 6.0.19 is the last version licensed under the Sleepycat license. "bdb": { "url": "https://ftp.osuosl.org/pub/blfs/conglomeration/db/db-6.0.19.tar.gz", @@ -14,13 +31,15 @@ "license_file": "LICENSE.bdb.txt", }, "binutils": { - "url": "https://ftp.gnu.org/gnu/binutils/binutils-2.36.1.tar.xz", - "size": 22772248, - "sha256": "e81d9edf373f193af428a0f256674aea62a9d74dfe93f65192d4eae030b0f3b0", - "version": "2.36.1", + # Mirrored from https://ftp.gnu.org/gnu/binutils/binutils-2.43.tar.xz + "url": "https://astral-sh.github.io/mirror/files/binutils-2.43.tar.xz", + "size": 28175768, + "sha256": "b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365", + "version": "2.43", }, "bzip2": { - "url": "https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz", + # Mirrored from https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz + "url": "https://astral-sh.github.io/mirror/files/bzip2-1.0.8.tar.gz", "size": 810029, "sha256": "ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269", "version": "1.0.8", @@ -28,134 +47,85 @@ "licenses": ["bzip2-1.0.6"], "license_file": "LICENSE.bzip2.txt", }, - "clang": { - "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/clang-12.0.1.src.tar.xz", - "size": 15323860, - "sha256": "6e912133bcf56e9cfe6a346fa7e5c52c2cde3e4e48b7a6cc6fcc7c75047da45f", - "version": "12.0.1", - }, - "clang-compiler-rt": { - "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/compiler-rt-12.0.1.src.tar.xz", - "size": 2201284, - "sha256": "b4c8d5f2a802332987c1c0a95b5afb35b1a66a96fe44add4e4ed4792c4cba0a4", - "version": "12.0.1", - }, - "cmake-linux-bin": { - "url": "https://github.com/Kitware/CMake/releases/download/v3.19.2/cmake-3.19.2-Linux-x86_64.tar.gz", - "size": 42931014, - "sha256": "4d8a6d852c530f263b22479aad196416bb4406447e918bd9759c6593b7f5f3f9", - "version": "3.19.2", - }, - "cmake-macos-bin": { - "url": "https://github.com/Kitware/CMake/releases/download/v3.19.2/cmake-3.19.2-macos-universal.tar.gz", - "size": 63739197, - "sha256": "50afa2cb66bea6a0314ef28034f3ff1647325e30cf5940f97906a56fd9640bd8", - "version": "3.19.2", - }, - "cpython-3.8": { - "url": "https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tar.xz", - "size": 18443568, - "sha256": "b1d3a76420375343b5e8a22fceb1ac65b77193e9ed27146524f0a9db058728ea", - "version": "3.8.12", + "cpython-3.10": { + "url": "https://www.python.org/ftp/python/3.10.20/Python-3.10.20.tar.xz", + "size": 19868028, + "sha256": "de6517421601e39a9a3bc3e1bc4c7b2f239297423ee05e282598c83ec0647505", + "version": "3.10.20", "licenses": ["Python-2.0", "CNRI-Python"], "license_file": "LICENSE.cpython.txt", - "python_tag": "cp38", + "python_tag": "cp310", }, - "cpython-3.9": { - "url": "https://www.python.org/ftp/python/3.9.6/Python-3.9.6.tar.xz", - "size": 19051972, - "sha256": "397920af33efc5b97f2e0b57e91923512ef89fc5b3c1d21dbfc8c4828ce0108a", - "version": "3.9.6", + "cpython-3.11": { + "url": "https://www.python.org/ftp/python/3.11.15/Python-3.11.15.tar.xz", + "size": 20332596, + "sha256": "272179ddd9a2e41a0fc8e42e33dfbdca0b3711aa5abf372d3f2d51543d09b625", + "version": "3.11.15", "licenses": ["Python-2.0", "CNRI-Python"], "license_file": "LICENSE.cpython.txt", - "python_tag": "cp39", + "python_tag": "cp311", }, - "cpython-3.10": { - "url": "https://www.python.org/ftp/python/3.10.0/Python-3.10.0rc1.tar.xz", - "size": 18680452, - "sha256": "797061692282d0e573a0093476e57e39cf52cad56782ab2dfb79afadb3c1e638", - "version": "3.10.0rc1", + "cpython-3.12": { + "url": "https://www.python.org/ftp/python/3.12.13/Python-3.12.13.tar.xz", + "size": 20801708, + "sha256": "c08bc65a81971c1dd5783182826503369466c7e67374d1646519adf05207b684", + "version": "3.12.13", "licenses": ["Python-2.0", "CNRI-Python"], "license_file": "LICENSE.cpython.txt", - "python_tag": "cp310", + "python_tag": "cp312", + }, + "cpython-3.13": { + "url": "https://www.python.org/ftp/python/3.13.13/Python-3.13.13.tar.xz", + "size": 22957612, + "sha256": "2ab91ff401783ccca64f75d10c882e957bdfd60e2bf5a72f8421793729b78a71", + "version": "3.13.13", + "licenses": ["Python-2.0", "CNRI-Python"], + "license_file": "LICENSE.cpython.txt", + "python_tag": "cp313", + }, + "cpython-3.14": { + "url": "https://www.python.org/ftp/python/3.14.4/Python-3.14.4.tar.xz", + "size": 23855332, + "sha256": "d923c51303e38e249136fc1bdf3568d56ecb03214efdef48516176d3d7faaef8", + "version": "3.14.4", + "licenses": ["Python-2.0", "CNRI-Python"], + "license_file": "LICENSE.cpython.txt", + "python_tag": "cp314", }, - "gcc": { - "url": "https://ftp.gnu.org/gnu/gcc/gcc-10.3.0/gcc-10.3.0.tar.xz", - "size": 76692288, - "sha256": "64f404c1a650f27fc33da242e1f2df54952e3963a49e06e73f6940f3223ac344", - "version": "10.3.0", - }, - "gdbm": { - "url": "https://ftp.gnu.org/gnu/gdbm/gdbm-1.18.1.tar.gz", - "size": 941863, - "sha256": "86e613527e5dba544e73208f42b78b7c022d4fa5a6d5498bf18c8d6f745b91dc", - "version": "1.18.1", - "library_names": ["gdbm"], - "licenses": ["GPL-3.0-or-later"], - "license_file": "LICENSE.gdbm.txt", - }, - "gettext": { - "url": "https://ftp.gnu.org/pub/gnu/gettext/gettext-0.21.tar.gz", - "size": 24181849, - "sha256": "c77d0da3102aec9c07f43671e60611ebff89a996ef159497ce8e59d075786b12", - "version": "0.21", - "library_names": ["intl", "textstyle"], - "licenses": ["GPL-3.0-or-later"], - "license_file": "LICENSE.gettext.txt", - }, - # gmp 6.2 does not build on wheezy. - "gmp": { - "url": "https://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz", - "size": 1946336, - "sha256": "87b565e89a9a684fe4ebeeddb8399dce2599f9c9049854ca8c0dfbdea0e21912", - "version": "6.1.2", - }, - "inputproto": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/proto/inputproto-2.3.2.tar.gz", - "size": 244334, - "sha256": "10eaadd531f38f7c92ab59ef0708ca195caf3164a75c4ed99f0c04f2913f6ef3", - "version": "2.3.2", - }, - "isl": { - "url": "ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2", - "size": 1658291, - "sha256": "6b8b0fd7f81d0a957beb3679c81bbb34ccc7568d5682844d8924424a0dadcb1b", - "version": "0.18", + "cpython-3.15": { + "url": "https://www.python.org/ftp/python/3.15.0/Python-3.15.0a8.tar.xz", + "size": 35130268, + "sha256": "28f1b6358609042ebcc81488ec24569519f50804bb07dc23cc707b281b031c69", + "version": "3.15.0a8", + "licenses": ["Python-2.0", "CNRI-Python"], + "license_file": "LICENSE.cpython.txt", + "python_tag": "cp315", + }, + "expat": { + "url": "https://github.com/libexpat/libexpat/releases/download/R_2_6_3/expat-2.6.3.tar.xz", + "size": 485600, + "sha256": "274db254a6979bde5aad404763a704956940e465843f2a9bd9ed7af22e2c0efc", + "version": "2.6.3", + "library_names": ["expat"], + "licenses": ["MIT"], + "license_file": "LICENSE.expat.txt", }, "jom-windows-bin": { - "url": "http://download.qt.io/official_releases/jom/jom_1_1_3.zip", - "size": 1213852, - "sha256": "128fdd846fe24f8594eed37d1d8929a0ea78df563537c0c1b1861a635013fff8", - "version": "1.1.3", - }, - "kbproto": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/proto/kbproto-1.0.7.tar.gz", - "size": 325858, - "sha256": "828cb275b91268b1a3ea950d5c0c5eb076c678fdf005d517411f89cc8c3bb416", - "version": "1.0.7", - }, - "libc++": { - "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/libcxx-12.0.1.src.tar.xz", - "size": 1882840, - "sha256": "a42089cd358f661823c490741f8be98701d983a7ee5152c8649075da681a9d15", - "version": "12.0.1", - }, - "libc++abi": { - "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/libcxxabi-12.0.1.src.tar.xz", - "size": 552980, - "sha256": "88efe8e391767a1e8f42b509879abe766c9f44b1781ad1900975ae6b516b91d0", - "version": "12.0.1", + "url": "http://download.qt.io/official_releases/jom/jom_1_1_4.zip", + "size": 1696930, + "sha256": "d533c1ef49214229681e90196ed2094691e8c4a0a0bef0b2c901debcb562682b", + "version": "1.1.4", }, "libedit": { - "url": "https://thrysoee.dk/editline/libedit-20210714-3.1.tar.gz", - "size": 522662, - "sha256": "3023b498ad593fd7745ae3b20abad546de506b67b8fbb5579637ca69ab82dbc9", - "version": "20210714-3.1", + "url": "https://thrysoee.dk/editline/libedit-20240808-3.1.tar.gz", + "size": 538611, + "sha256": "5f0573349d77c4a48967191cdd6634dd7aa5f6398c6a57fe037cc02696d6099f", + "version": "20240808-3.1", "library_names": ["edit"], "licenses": ["BSD-3-Clause"], "license_file": "LICENSE.libedit.txt", }, - "libffi": { + "libffi-3.3": { "url": "https://github.com/libffi/libffi/releases/download/v3.3/libffi-3.3.tar.gz", "size": 1305466, "sha256": "72fba7922703ddfa7a028d513ac15a85c8d54c8d67f55fa5a4802885dc652056", @@ -164,171 +134,196 @@ "licenses": ["MIT"], "license_file": "LICENSE.libffi.txt", }, - "libpthread-stubs": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/lib/libpthread-stubs-0.1.tar.gz", - "size": 301448, - "sha256": "f8f7ca635fa54bcaef372fd5fd9028f394992a743d73453088fcadc1dbf3a704", - "version": "0.1", - }, - "libressl": { - "url": "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.3.3.tar.gz", - "size": 3848064, - "sha256": "a471565b36ccd1a70d0bd7d37c6e95c43a26a62829b487d9d2cdebfe58be3066", - "version": "3.3.3", - "library_names": ["crypto", "ssl"], - "licenses": ["OpenSSL"], - "license_file": "LICENSE.libressl.txt", + "libffi": { + "url": "https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz", + "size": 1391684, + "sha256": "b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e", + "version": "3.4.6", + "library_names": ["ffi"], + "licenses": ["MIT"], + "license_file": "LICENSE.libffi.txt", }, - "libunwind": { - "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/libunwind-12.0.1.src.tar.xz", - "size": 98348, - "sha256": "0bea6089518395ca65cf58b0a450716c5c99ce1f041079d3aa42d280ace15ca4", - "version": "12.0.1", + "libpthread-stubs": { + "url": "https://www.x.org/archive/individual/lib/libpthread-stubs-0.5.tar.gz", + "size": 74938, + "sha256": "593196cc746173d1e25cb54a93a87fd749952df68699aab7e02c085530e87747", + "version": "0.5", }, "libX11": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/lib/libX11-1.6.8.tar.gz", - "size": 3144482, - "sha256": "69d1a27cba722dca897198a23fa8d3cad3ec0c715e00205ea4398ec68a4258a5", - "version": "1.6.8", + "url": "https://www.x.org/archive/individual/lib/libX11-1.6.12.tar.gz", + "size": 3168158, + "sha256": "0fce5fc0a24a3dc728174eccd0cb8d6a1b37a2ec1654bd5628c84e5bc200d594", + "version": "1.6.12", "library_names": ["X11", "X11-xcb"], "licenses": ["MIT", "X11"], "license_file": "LICENSE.libX11.txt", }, "libXau": { - "url": "https://www.x.org/releases/individual/lib/libXau-1.0.7.tar.gz", - "size": 349278, - "sha256": "cbb81b4ba0f585faac8b9914b0bdbef6e0ef886a30c70d6584e2b30efeda9ac4", - "version": "1.0.7", + "url": "https://www.x.org/releases/individual/lib/libXau-1.0.11.tar.gz", + "size": 404973, + "sha256": "3a321aaceb803577a4776a5efe78836eb095a9e44bbc7a465d29463e1a14f189", + "version": "1.0.11", "library_names": ["Xau"], "licenses": ["MIT"], "license_file": "LICENSE.libXau.txt", }, "libxcb": { - "url": "https://xcb.freedesktop.org/dist/libxcb-1.13.1.tar.gz", - "size": 636748, - "sha256": "f09a76971437780a602303170fd51b5f7474051722bc39d566a272d2c4bde1b5", - "version": "1.13.1", + "url": "https://xcb.freedesktop.org/dist/libxcb-1.17.0.tar.gz", + "size": 661593, + "sha256": "2c69287424c9e2128cb47ffe92171e10417041ec2963bceafb65cb3fcf8f0b85", + "version": "1.17.0", "library_names": ["xcb"], "licenses": ["MIT"], "license_file": "LICENSE.libxcb.txt", }, - "lld": { - "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/lld-12.0.1.src.tar.xz", - "size": 1351580, - "sha256": "690b3f6a76310e13a783a142f87500ade9cafe003e088b678364487ed873e361", - "version": "12.0.1", - }, - "llvm": { - "url": "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/llvm-12.0.1.src.tar.xz", - "size": 42898504, - "sha256": "7d9a8405f557cefc5a21bf5672af73903b64749d9bc3a50322239f56f34ffddf", - "version": "12.0.1", - }, - "mpc": { - "url": "http://www.multiprecision.org/downloads/mpc-1.0.3.tar.gz", - "size": 669925, - "sha256": "617decc6ea09889fb08ede330917a00b16809b8db88c29c31bfbb49cbf88ecc3", - "version": "1.0.3", - }, - # We seem to have problems building newer MPFR in Debian Jessie. - "mpfr": { - "url": "https://ftp.gnu.org/gnu/mpfr/mpfr-3.1.6.tar.xz", - "size": 1133672, - "sha256": "7a62ac1a04408614fccdc506e4844b10cf0ad2c2b1677097f8f35d3a1344a950", - "version": "3.1.6", - }, + # Remember to update LLVM_URL in src/release.rs whenever upgrading. + "llvm-aarch64-linux": { + "url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20260410/llvm-22.1.3+20260410-gnu_only-aarch64-unknown-linux-gnu.tar.zst", + "size": 237655768, + "sha256": "9cb4b562323a3d899fffe6393148e92447e17b69a2501d7c6e7f2a86c32cddc1", + "version": "22.1.3+20260410", + }, + # Remember to update LLVM_URL in src/release.rs whenever upgrading. + "llvm-x86_64-linux": { + "url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20260410/llvm-22.1.3+20260410-gnu_only-x86_64-unknown-linux-gnu.tar.zst", + "size": 281109065, + "sha256": "0ee8e4f89c20983d62547b1e665147ee08a7413f478e9e443fa991548da1030d", + "version": "22.1.3+20260410", + }, + # Remember to update LLVM_URL in src/release.rs whenever upgrading. + "llvm-aarch64-macos": { + "url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20260410/llvm-22.1.3+20260410-aarch64-apple-darwin.tar.zst", + "size": 159775425, + "sha256": "98171836c31c04edec074e5f3fee67fcace4bf3859b68a770dd9ff2039ea127d", + "version": "22.1.3+20260410", + }, + # Remember to update LLVM_URL in src/release.rs whenever upgrading. + "llvm-x86_64-macos": { + "url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20260410/llvm-22.1.3+20260410-x86_64-apple-darwin.tar.zst", + "size": 167376011, + "sha256": "15bfda1bc8bc920658c09ef1ebb4593f0705b3314beab23989f14775f2e3f3f0", + "version": "22.1.3+20260410", + }, + "m4": { + # Mirrored from https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.xz + "url": "https://astral-sh.github.io/mirror/files/m4-1.4.19.tar.xz", + "size": 1654908, + "sha256": "63aede5c6d33b6d9b13511cd0be2cac046f2e70fd0a07aa9573a04a82783af96", + "version": "1.4.19", + }, + "mpdecimal": { + # Mirrored from https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-4.0.0.tar.gz + "url": "https://astral-sh.github.io/mirror/files/mpdecimal-4.0.0.tar.gz", + "size": 315325, + "sha256": "942445c3245b22730fd41a67a7c5c231d11cb1b9936b9c0f76334fb7d0b4468c", + "version": "4.0.0", + "library_names": ["mpdec"], + "licenses": ["BSD-2-Clause"], + "license_file": "LICENSE.mpdecimal.txt", + }, + # The musl toolchain for shared / dynamically linked builds "musl": { - "url": "https://www.musl-libc.org/releases/musl-1.2.2.tar.gz", + "url": "https://musl.libc.org/releases/musl-1.2.2.tar.gz", "size": 1055220, "sha256": "9b969322012d796dc23dda27a35866034fa67d8fb67e0e2c45c913c3d43219dd", "version": "1.2.2", }, + # The musl toolchain for static builds + "musl-static": { + "url": "https://musl.libc.org/releases/musl-1.2.5.tar.gz", + "size": 1080786, + "sha256": "a9a118bbe84d8764da0ea0d28b3ab3fae8477fc7e4085d90102b8596fc7c75e4", + "version": "1.2.5", + }, "ncurses": { - "url": "https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.2.tar.gz", - "size": 3425862, - "sha256": "30306e0c76e0f9f1f0de987cf1c82a5c21e1ce6568b9227f7da5b71cbea86c9d", - "version": "6.2", + # Mirrored from https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.5.tar.gz + "url": "https://astral-sh.github.io/mirror/files/ncurses-6.5.tar.gz", + "size": 3688489, + "sha256": "136d91bc269a9a5785e5f9e980bc76ab57428f604ce3e5a5a90cebc767971cc6", + "version": "6.5", "library_names": ["ncurses", "ncursesw", "panel", "panelw"], "licenses": ["X11"], "license_file": "LICENSE.ncurses.txt", }, - "ninja-linux-bin": { - "url": "https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-linux.zip", - "size": 103219, - "sha256": "763464859c7ef2ea3a0a10f4df40d2025d3bb9438fcb1228404640410c0ec22d", - "version": "1.10.2", - }, - "ninja-macos-bin": { - "url": "https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip", - "size": 234089, - "sha256": "6fa359f491fac7e5185273c6421a000eea6a2f0febf0ac03ac900bd4d80ed2a5", - "version": "1.10.2", - }, - "openssl": { - "url": "https://www.openssl.org/source/openssl-1.1.1l.tar.gz", - "size": 9834044, - "sha256": "0b7a3e5e59c34827fe0c3a74b7ec8baef302b98fa80088d7f9153aa16fa76bd1", - "version": "1.1.1l", + # Remember to update OPENSSL_VERSION_INFO in pythonbuild/disttests/ whenever upgrading. + "openssl-1.1": { + "url": "https://www.openssl.org/source/openssl-1.1.1w.tar.gz", + "size": 9893384, + "sha256": "cf3098950cb4d853ad95c0841f1f9c6d3dc102dccfcacd521d93925208b76ac8", + "version": "1.1.1w", "library_names": ["crypto", "ssl"], "licenses": ["OpenSSL"], - "license_file": "LICENSE.openssl.txt", + "license_file": "LICENSE.openssl-1.1.txt", + }, + # Remember to update OPENSSL_VERSION_INFO in pythonbuild/disttests/ whenever upgrading. + "openssl-3.5": { + "url": "https://github.com/openssl/openssl/releases/download/openssl-3.5.6/openssl-3.5.6.tar.gz", + "size": 53121812, + "sha256": "deae7c80cba99c4b4f940ecadb3c3338b13cb77418409238e57d7f31f2a3b736", + "version": "3.5.6", + "library_names": ["crypto", "ssl"], + "licenses": ["Apache-2.0"], + "license_file": "LICENSE.openssl-3.txt", }, "nasm-windows-bin": { - "url": "https://github.com/python/cpython-bin-deps/archive/nasm-2.11.06.tar.gz", - "size": 384826, - "sha256": "8af0ae5ceed63fa8a2ded611d44cc341027a91df22aaaa071efedc81437412a5", - "version": "2.11.06", + # Mirrored from https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/win64/nasm-2.16.03-win64.zip + "url": "https://astral-sh.github.io/mirror/files/nasm-2.16.03-win64.zip", + "size": 513543, + "sha256": "3ee4782247bcb874378d02f7eab4e294a84d3d15f3f6ee2de2f47a46aa7226e6", + "version": "2.16.03", }, "patchelf": { - "url": "https://github.com/NixOS/patchelf/releases/download/0.12/patchelf-0.12.tar.bz2", - "size": 165069, - "sha256": "699a31cf52211cf5ad6e35a8801eb637bc7f3c43117140426400d67b7babd792", - "version": "0.12", + "url": "https://github.com/NixOS/patchelf/releases/download/0.13.1/patchelf-0.13.1.tar.bz2", + "size": 173598, + "sha256": "39e8aeccd7495d54df094d2b4a7c08010ff7777036faaf24f28e07777d1598e2", + "version": "0.13.1", }, "pip": { - "url": "https://files.pythonhosted.org/packages/52/e1/06c018197d8151383f66ebf6979d951995cf495629fc54149491f5d157d0/pip-21.2.4.tar.gz", - "size": 1564487, - "sha256": "0eb8a1516c3d138ae8689c0c1a60fde7143310832f9dc77e11d8a4bc62de193b", - "version": "21.2.4", + "url": "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", + "size": 1787723, + "sha256": "bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", + "version": "26.0.1", }, "readline": { - "url": "https://ftp.gnu.org/gnu/readline/readline-8.1.tar.gz", - "size": 2993288, - "sha256": "f8ceb4ee131e3232226a17f51b164afc46cd0b9e6cef344be87c65962cb82b02", - "version": "8.1", + # Mirrored from https://ftp.gnu.org/gnu/readline/readline-8.2.tar.gz + "url": "https://astral-sh.github.io/mirror/files/readline-8.2.tar.gz", + "size": 3043952, + "sha256": "3feb7171f16a84ee82ca18a36d7b9be109a52c04f492a053331d7d1095007c35", + "version": "8.2", "library_names": ["readline"], - "licenses": ["GPL-3.0"], + "licenses": ["GPL-3.0-only"], "license_file": "LICENSE.readline.txt", }, "setuptools": { - "url": "https://files.pythonhosted.org/packages/db/e2/c0ced9ccffb61432305665c22842ea120c0f649eec47ecf2a45c596707c4/setuptools-57.4.0.tar.gz", - "size": 2141309, - "sha256": "6bac238ffdf24e8806c61440e755192470352850f3419a52f26ffe0a1a64f465", - "version": "57.4.0", + "url": "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", + "size": 1006223, + "sha256": "a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", + "version": "82.0.1", }, + # Remember to update pythonbuild/disttests/ when version changed. "sqlite": { - "url": "https://www.sqlite.org/2021/sqlite-autoconf-3360000.tar.gz", - "size": 2977080, - "sha256": "bd90c3eb96bee996206b83be7065c9ce19aef38c3f4fb53073ada0d0b69bbce3", - "version": "3360000", - "actual_version": "3.36.0.0", + "url": "https://www.sqlite.org/2025/sqlite-autoconf-3500400.tar.gz", + "size": 3173050, + "sha256": "a3db587a1b92ee5ddac2f66b3edb41b26f9c867275782d46c3a088977d6a5b18", + "version": "3500400", + "actual_version": "3.50.4.0", "library_names": ["sqlite3"], "licenses": [], "license_file": "LICENSE.sqlite.txt", "license_public_domain": True, }, "strawberryperl": { - "url": "http://strawberryperl.com/download/5.28.1.1/strawberry-perl-5.28.1.1-32bit-portable.zip", - "size": 143242779, - "sha256": "8b15c7c9574989568254a7859e473b7d5f68a1145d2e4418036600a81b13805c", - "version": "5.28.1.1", + "url": "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_53822_64bit/strawberry-perl-5.38.2.2-64bit-portable.zip", + "size": 264199638, + "sha256": "ea451686065d6338d7e4d4a04c9af49f17951d15aa4c2e19ab8cb56fa2373440", + "version": "5.38.2.2", }, "tcl": { - "url": "https://prdownloads.sourceforge.net/tcl/tcl8.6.10-src.tar.gz", - "size": 10144235, - "sha256": "5196dbf6638e3df8d5c87b5815c8c2b758496eb6f0e41446596c9a4e638d87ed", - "version": "8.6.10", - "library_names": ["tcl8.6"], + "url": "https://prdownloads.sourceforge.net/tcl/tcl9.0.3-src.tar.gz", + "size": 11922915, + "sha256": "2537ba0c86112c8c953f7c09d33f134dd45c0fb3a71f2d7f7691fd301d2c33a6", + "version": "9.0.3", + "library_names": ["tcl9.0"], "licenses": ["TCL"], "license_file": "LICENSE.tcl.txt", }, @@ -341,20 +336,45 @@ "license_file": "LICENSE.tix.txt", }, "tk": { - "url": "https://prdownloads.sourceforge.net/tcl/tk8.6.10-src.tar.gz", - "size": 4444764, - "sha256": "63df418a859d0a463347f95ded5cd88a3dd3aaa1ceecaeee362194bc30f3e386", - "version": "8.6.10", - "library_names": ["tk8.6"], + "url": "https://prdownloads.sourceforge.net/tcl/tk9.0.3-src.tar.gz", + "size": 4644835, + "sha256": "bf344efadb618babb7933f69275620f72454d1c8220130da93e3f7feb0efbf9b", + "version": "9.0.3", + "library_names": ["tk9.0"], "licenses": ["TCL"], "license_file": "LICENSE.tcl.txt", }, "tk-windows-bin": { - "url": "https://github.com/python/cpython-bin-deps/archive/86027ce3edda1284ae4bf6c2c580288366af4052.tar.gz", - "size": 7162470, - "sha256": "34400f7b76a13389a475fc1a4d6e33d5ca21dda6f6ff11b04759865814bdf3d2", - "version": "6.6.9", - "git_commit": "86027ce3edda1284ae4bf6c2c580288366af4052", + "url": "https://github.com/python/cpython-bin-deps/archive/c624cc881bd0e5071dec9de4b120cbe9985d8c14.tar.gz", + "size": 9497943, + "sha256": "9b8e77d55f40ceaedd140ccca0daa804f0d43346d5abfcead9b547b5590f82f8", + "version": "8.6.14", + "git_commit": "c624cc881bd0e5071dec9de4b120cbe9985d8c14", + }, + "tcl-8612": { + "url": "https://prdownloads.sourceforge.net/tcl/tcl8.6.12-src.tar.gz", + "size": 10353486, + "sha256": "26c995dd0f167e48b11961d891ee555f680c175f7173ff8cb829f4ebcde4c1a6", + "version": "8.6.12", + "library_names": ["tcl8.6"], + "licenses": ["TCL"], + "license_file": "LICENSE.tcl.txt", + }, + "tk-8612": { + "url": "https://prdownloads.sourceforge.net/tcl/tk8.6.12-src.tar.gz", + "size": 4515393, + "sha256": "12395c1f3fcb6bed2938689f797ea3cdf41ed5cb6c4766eec8ac949560310630", + "version": "8.6.12", + "library_names": ["tk8.6"], + "licenses": ["TCL"], + "license_file": "LICENSE.tcl.txt", + }, + "tk-windows-bin-8612": { + "url": "https://github.com/python/cpython-bin-deps/archive/e3c3e9a2856124aa32b608632a52742d479eb7a9.tar.gz", + "size": 6787654, + "sha256": "01ad9c663659224e075d487cbc33ea2fed7a225593965b79bed92ca7f79b676f", + "version": "8.6.12", + "git_commit": "e3c3e9a2856124aa32b608632a52742d479eb7a9", }, "uuid": { "url": "https://sourceforge.net/projects/libuuid/files/libuuid-1.0.3.tar.gz", @@ -366,60 +386,67 @@ "license_file": "LICENSE.libuuid.txt", }, "x11-util-macros": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/util/util-macros-1.19.2.tar.gz", - "size": 103001, - "sha256": "9225c45c3de60faf971979a55a5536f3562baa4b6f02246c23e98ac0c09a75b7", - "version": "1.19.2", + "url": "https://www.x.org/archive/individual/util/util-macros-1.20.2.tar.gz", + "size": 105410, + "sha256": "f642f8964d81acdf06653fdf9dbc210c43ce4bd308bd644a8d573148d0ced76b", + "version": "1.20.2", }, "xcb-proto": { - "url": "https://xcb.freedesktop.org/dist/xcb-proto-1.13.tar.gz", - "size": 191771, - "sha256": "0698e8f596e4c0dbad71d3dc754d95eb0edbb42df5464e0f782621216fa33ba7", - "version": "1.13", - }, - "xextproto": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/proto/xextproto-7.3.0.tar.gz", - "size": 290814, - "sha256": "1b1bcdf91221e78c6c33738667a57bd9aaa63d5953174ad8ed9929296741c9f5", - "version": "7.3.0", + "url": "https://xcb.freedesktop.org/dist/xcb-proto-1.17.0.tar.xz", + "size": 151748, + "sha256": "2c1bacd2110f4799f74de6ebb714b94cf6f80fb112316b1219480fd22562148c", + "version": "1.17.0", }, "xorgproto": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/proto/xorgproto-2019.1.tar.gz", - "size": 1119813, - "sha256": "38ad1d8316515785d53c5162b4b7022918e03c11d72a5bd9df0a176607f42bca", - "version": "2019.1", - }, - "xproto": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/proto/xproto-7.0.31.tar.gz", - "size": 367979, - "sha256": "6d755eaae27b45c5cc75529a12855fed5de5969b367ed05003944cf901ed43c7", - "version": "7.0.31", + "url": "https://www.x.org/archive/individual/proto/xorgproto-2024.1.tar.gz", + "size": 1115486, + "sha256": "4f6b9b4faf91e5df8265b71843a91fc73dc895be6210c84117a996545df296ce", + "version": "2024.1", }, "xtrans": { - "url": "ftp://mirror.csclub.uwaterloo.ca/x.org/pub/current/src/lib/xtrans-1.4.0.tar.gz", - "size": 225941, - "sha256": "48ed850ce772fef1b44ca23639b0a57e38884045ed2cbb18ab137ef33ec713f9", - "version": "1.4.0", + "url": "https://www.x.org/archive/individual/lib/xtrans-1.6.0.tar.gz", + "size": 239113, + "sha256": "936b74c60b19c317c3f3cb1b114575032528dbdaf428740483200ea874c2ca0a", + "version": "1.6.0", }, + # IMPORTANT: xz 5.6.0 was released with a backdoor (CVE-2024-3094). This has been resolved. + # Be cautious before taking any xz upgrades given this past behavior. "xz": { - "url": "https://tukaani.org/xz/xz-5.2.5.tar.gz", - "size": 1791345, - "sha256": "f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10", - "version": "5.2.5", + "url": "https://github.com/tukaani-project/xz/releases/download/v5.8.1/xz-5.8.1.tar.gz", + "size": 2587189, + "sha256": "507825b599356c10dca1cd720c9d0d0c9d5400b9de300af00e4d1ea150795543", + "version": "5.8.1", "library_names": ["lzma"], - # liblzma is in the public domain. Other parts of code have licenses. But - # we only use liblzma. - "licenses": [], + # liblzma is licensed as 0BSD. Other parts of code have different licenses. + # But we only use liblzma. + "licenses": ["0BSD"], "license_file": "LICENSE.liblzma.txt", - "license_public_domain": True, }, "zlib": { - "url": "https://zlib.net/zlib-1.2.11.tar.gz", - "size": 607698, - "sha256": "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1", - "version": "1.2.11", + "url": "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz", + "size": 1512791, + "sha256": "9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23", + "version": "1.3.1", "library_names": ["z"], "licenses": ["Zlib"], "license_file": "LICENSE.zlib.txt", }, + "zlib-ng": { + "url": "https://github.com/python/cpython-source-deps/archive/refs/tags/zlib-ng-2.2.4.tar.gz", + "size": 2415819, + "sha256": "00bbd88709bc416cb96160ab61d3e1c8f76e106799af7328d0fe434dc7dd5004", + "version": "2.2.4", + "library_names": ["z"], + "licenses": ["Zlib"], + "license_file": "LICENSE.zlib-ng.txt", + }, + "zstd": { + "url": "https://github.com/python/cpython-source-deps/archive/refs/tags/zstd-1.5.7.tar.gz", + "size": 2440298, + "sha256": "f24b52470d12f466e9fa4fcc94e6c530625ada51d7b36de7fdc6ed7e6f499c8e", + "version": "1.5.7", + "library_names": ["zstd"], + "licenses": ["BSD-3-Clause"], + "license_file": "LICENSE.zstd.txt", + }, } diff --git a/pythonbuild/mirror.py b/pythonbuild/mirror.py new file mode 100644 index 000000000..84d48f6fb --- /dev/null +++ b/pythonbuild/mirror.py @@ -0,0 +1,265 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +"""Upload release artifacts to an S3-compatible mirror bucket. + +This mirrors the exact filenames referenced by ``dist/SHA256SUMS``. That file is +written by the GitHub release upload step, so using it here keeps the mirror in +lock-step with the GitHub Release contents without duplicating the Rust release +matrix logic in Python. +""" + +from __future__ import annotations + +import argparse +import concurrent.futures +import os +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Sequence + +import boto3 +from boto3.s3.transfer import TransferConfig +from botocore.config import Config +from mypy_boto3_s3.client import S3Client as Boto3S3Client + +CACHE_CONTROL = "public, max-age=31536000, immutable" +UPLOAD_CONCURRENCY = 4 +MAX_ATTEMPTS = 5 +BUILD_DATETIME_RE = re.compile(r"-(\d{8}T\d{4})\.tar\.(?:gz|zst)$") +DESTINATION_TAG_RE_TEMPLATE = r"^(cpython-[^+-]+)\+{tag}-(.+)$" + + +class MirrorError(RuntimeError): + """Base exception for mirror upload failures.""" + + +@dataclass(frozen=True) +class UploadEntry: + source_name: str + dest_name: str + + +def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Upload release distributions to an S3-compatible mirror bucket" + ) + parser.add_argument( + "--dist", type=Path, required=True, help="Directory with release artifacts" + ) + parser.add_argument("--tag", required=True, help="Release tag") + parser.add_argument("--bucket", required=True, help="S3 bucket name") + parser.add_argument( + "--prefix", + default="", + help="Key prefix within the bucket (e.g. 'github/python-build-standalone/releases/download/20250317/')", + ) + parser.add_argument( + "-n", + "--dry-run", + action="store_true", + help="Dry run mode; do not actually upload", + ) + parser.add_argument( + "--ignore-missing", + action="store_true", + help="Continue even if there are missing artifacts", + ) + return parser.parse_args(argv) + + +def infer_build_datetime(dist_dir: Path) -> str: + datetimes = { + match.group(1) + for path in dist_dir.iterdir() + if path.is_file() + and path.name.startswith("cpython-") + and (match := BUILD_DATETIME_RE.search(path.name)) is not None + } + + if not datetimes: + raise SystemExit(f"could not infer build datetime from {dist_dir}") + if len(datetimes) != 1: + values = ", ".join(sorted(datetimes)) + raise SystemExit(f"expected one build datetime in {dist_dir}; found: {values}") + + return datetimes.pop() + + +def parse_shasums(shasums_path: Path) -> list[str]: + if not shasums_path.exists(): + raise SystemExit(f"{shasums_path} not found") + + filenames: list[str] = [] + for line in shasums_path.read_text().splitlines(): + line = line.strip() + if not line: + continue + + try: + _digest, filename = line.split(maxsplit=1) + except ValueError as e: + raise SystemExit(f"malformed line in {shasums_path}: {line!r}") from e + + filenames.append(filename.lstrip("*")) + + return filenames + + +def destination_to_source_name(dest_name: str, tag: str, build_datetime: str) -> str: + pattern = DESTINATION_TAG_RE_TEMPLATE.format(tag=re.escape(tag)) + if (match := re.match(pattern, dest_name)) is None: + raise MirrorError( + f"release filename does not contain expected tag {tag}: {dest_name}" + ) + + source_name = f"{match.group(1)}-{match.group(2)}" + + if source_name.endswith("-full.tar.zst"): + prefix = source_name.removesuffix("-full.tar.zst") + return f"{prefix}-{build_datetime}.tar.zst" + + if source_name.endswith(".tar.gz"): + prefix = source_name.removesuffix(".tar.gz") + return f"{prefix}-{build_datetime}.tar.gz" + + raise MirrorError(f"unsupported release filename: {dest_name}") + + +def build_upload_entries( + dist_dir: Path, tag: str +) -> tuple[list[UploadEntry], list[str]]: + build_datetime = infer_build_datetime(dist_dir) + dest_names = parse_shasums(dist_dir / "SHA256SUMS") + + uploads: list[UploadEntry] = [] + missing: list[str] = [] + + for dest_name in dest_names: + source_name = destination_to_source_name(dest_name, tag, build_datetime) + if not (dist_dir / source_name).exists(): + missing.append(source_name) + continue + uploads.append(UploadEntry(source_name=source_name, dest_name=dest_name)) + + return uploads, missing + + +def make_s3_client() -> tuple[Boto3S3Client, TransferConfig]: + endpoint_url = os.environ.get("AWS_ENDPOINT_URL") + region_name = os.environ.get("AWS_DEFAULT_REGION") or os.environ.get("AWS_REGION") + if endpoint_url and region_name is None: + region_name = "auto" + + client_kwargs: dict[str, Any] = { + "config": Config( + signature_version="s3v4", + retries={"max_attempts": MAX_ATTEMPTS, "mode": "standard"}, + s3={"addressing_style": "path"}, + ) + } + if endpoint_url: + client_kwargs["endpoint_url"] = endpoint_url + if region_name: + client_kwargs["region_name"] = region_name + + session = boto3.session.Session() + client = session.client("s3", **client_kwargs) + transfer_config = TransferConfig() + + return client, transfer_config + + +@dataclass(kw_only=True) +class S3MirrorClient: + client: Boto3S3Client | None + transfer_config: TransferConfig | None + dry_run: bool + + def upload_file(self, bucket: str, key: str, path: Path) -> None: + print(f"uploading {path.name} -> s3://{bucket}/{key}") + if self.dry_run: + return + + if self.client is None or self.transfer_config is None: + raise MirrorError("S3 client not initialised") + + try: + self.client.upload_file( + str(path), + bucket, + key, + ExtraArgs={"CacheControl": CACHE_CONTROL}, + Config=self.transfer_config, + ) + except Exception as e: + raise MirrorError(f"failed to upload {path.name}: {e}") from e + + +def main(argv: Sequence[str] | None = None) -> int: + args = parse_args(argv) + + try: + uploads, missing = build_upload_entries(args.dist, args.tag) + + for filename in missing: + print(f"missing release artifact: {filename}") + + if missing and not args.ignore_missing: + raise SystemExit(f"missing {len(missing)} release artifacts") + + if not missing: + print(f"found all {len(uploads)} release artifacts") + + client = None + transfer_config = None + if not args.dry_run: + client, transfer_config = make_s3_client() + + mirror = S3MirrorClient( + client=client, + transfer_config=transfer_config, + dry_run=args.dry_run, + ) + + errors: list[str] = [] + with concurrent.futures.ThreadPoolExecutor( + max_workers=UPLOAD_CONCURRENCY + ) as executor: + futures = [ + executor.submit( + mirror.upload_file, + args.bucket, + f"{args.prefix}{entry.dest_name}", + args.dist / entry.source_name, + ) + for entry in uploads + ] + + for future in concurrent.futures.as_completed(futures): + try: + future.result() + except MirrorError as e: + errors.append(str(e)) + + if errors: + error_lines = "\n".join(f"- {error}" for error in sorted(errors)) + raise MirrorError( + f"encountered {len(errors)} upload errors:\n{error_lines}" + ) + + mirror.upload_file( + args.bucket, + f"{args.prefix}SHA256SUMS", + args.dist / "SHA256SUMS", + ) + except MirrorError as e: + raise SystemExit(str(e)) from e + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/pythonbuild/testdist.py b/pythonbuild/testdist.py new file mode 100644 index 000000000..96f65790e --- /dev/null +++ b/pythonbuild/testdist.py @@ -0,0 +1,137 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import argparse +import json +import os +import subprocess +import tempfile +from pathlib import Path +from typing import Optional + +from .utils import extract_python_archive + + +def run_dist_python( + dist_root: Path, + python_info, + args: list[str], + extra_env: Optional[dict[str, str]] = None, + **runargs, +) -> subprocess.CompletedProcess[str]: + """Runs a `python` process from an extracted PBS distribution. + + This function attempts to isolate the spawned interpreter from any + external interference (PYTHON* environment variables), etc. + """ + env = dict(os.environ) + + # Wipe PYTHON environment variables. + for k in env: + if k.startswith("PYTHON"): + del env[k] + + if extra_env: + env.update(extra_env) + + return subprocess.run( + [str(dist_root / python_info["python_exe"])] + args, + cwd=dist_root, + env=env, + **runargs, + ) + + +def run_custom_unittests(pbs_source_dir: Path, dist_root: Path, python_info) -> int: + """Runs custom PBS unittests against a distribution.""" + + args = [ + "-m", + "unittest", + "pythonbuild.disttests", + ] + + env = { + "PYTHONPATH": str(pbs_source_dir), + "TARGET_TRIPLE": python_info["target_triple"], + "BUILD_OPTIONS": python_info["build_options"], + } + + res = run_dist_python(dist_root, python_info, args, env, stderr=subprocess.STDOUT) + + return res.returncode + + +def run_stdlib_tests(dist_root: Path, python_info, harness_args: list[str]) -> int: + """Run Python stdlib tests for a PBS distribution. + + The passed path is the `python` directory from the extracted distribution + archive. + """ + args = [ + str(dist_root / python_info["run_tests"]), + ] + + args.extend(harness_args) + + return run_dist_python(dist_root, python_info, args).returncode + + +def main(pbs_source_dir: Path, raw_args: list[str]) -> int: + """test-distribution.py functionality.""" + + parser = argparse.ArgumentParser() + + parser.add_argument( + "--stdlib", + action="store_true", + help="Run the stdlib test harness", + ) + parser.add_argument( + "dist", + nargs=1, + help="Path to distribution to test", + ) + parser.add_argument( + "harness_args", + nargs=argparse.REMAINDER, + help="Raw arguments to pass to Python's test harness", + ) + + args = parser.parse_args(raw_args) + + dist_path_raw = Path(args.dist[0]) + + td = None + try: + if dist_path_raw.is_file(): + td = tempfile.TemporaryDirectory() + dist_path = extract_python_archive(dist_path_raw, Path(td.name)) + else: + dist_path = dist_path_raw + + python_json = dist_path / "PYTHON.json" + + with python_json.open("r", encoding="utf-8") as fh: + python_info = json.load(fh) + + codes = [] + + codes.append(run_custom_unittests(pbs_source_dir, dist_path, python_info)) + + if args.stdlib: + codes.append(run_stdlib_tests(dist_path, python_info, args.harness_args)) + + if len(codes) == 0: + print("no tests run") + return 1 + + if any(code != 0 for code in codes): + return 1 + + return 0 + + finally: + if td: + td.cleanup() diff --git a/pythonbuild/utils.py b/pythonbuild/utils.py index a2e79ca5c..f17ab12f2 100644 --- a/pythonbuild/utils.py +++ b/pythonbuild/utils.py @@ -4,16 +4,23 @@ import gzip import hashlib +import http.client import io +import json import multiprocessing import os import pathlib +import platform +import random import stat +import string import subprocess import sys import tarfile -import zipfile +import time +import urllib.error import urllib.request +import zipfile import yaml import zstandard @@ -22,6 +29,45 @@ from .logging import log +def current_host_platform() -> str: + """Resolve the name of the current machine's host platform. + + This is conceptually a simplified machine triple. + """ + machine = platform.machine() + if sys.platform == "linux": + if machine == "x86_64": + return "linux_x86_64" + elif machine == "aarch64": + return "linux_aarch64" + else: + raise Exception(f"unsupported Linux host platform: {machine}") + elif sys.platform == "darwin": + if machine == "arm64": + return "macos_arm64" + elif machine == "x86_64": + return "macos_x86_64" + else: + raise Exception(f"unhanded macOS machine type: {machine}") + else: + raise Exception(f"unsupported host platform: {sys.platform}") + + +def default_target_triple() -> str: + """Resolve the default target triple to build for.""" + host = current_host_platform() + if host == "linux_x86_64": + return "x86_64-unknown-linux-gnu" + elif host == "linux_aarch64": + return "aarch64-unknown-linux-gnu" + elif host == "macos_arm64": + return "aarch64-apple-darwin" + elif host == "macos_x86_64": + return "x86_64-apple-darwin" + else: + raise Exception(f"unrecognized host platform: {host}") + + def get_targets(yaml_path: pathlib.Path): """Obtain the parsed targets YAML file.""" with yaml_path.open("rb") as fh: @@ -38,10 +84,12 @@ def supported_targets(yaml_path: pathlib.Path): targets = set() for target, settings in get_targets(yaml_path).items(): - for platform in settings["host_platforms"]: - if sys.platform == "linux" and platform == "linux64": + for host_platform in settings["host_platforms"]: + if sys.platform == "linux" and host_platform == "linux_x86_64": targets.add(target) - elif sys.platform == "darwin" and platform == "macos": + elif sys.platform == "linux" and host_platform == "linux_aarch64": + targets.add(target) + elif sys.platform == "darwin" and host_platform.startswith("macos_"): targets.add(target) return targets @@ -53,10 +101,8 @@ def target_needs(yaml_path: pathlib.Path, target: str): needs = set(settings["needs"]) - if "PYBUILD_LIBRESSL" in os.environ: - needs.add("libressl") - else: - needs.add("openssl") + # Ship libedit linked readline extension to avoid a GPL dependency. + needs.discard("readline") return needs @@ -135,38 +181,42 @@ def write_triples_makefiles( for triple, settings in targets.items(): for host_platform in settings["host_platforms"]: - for python in settings["pythons_supported"]: - makefile_path = dest_dir / ( - "Makefile.%s.%s.%s" % (host_platform, triple, python) + # IMPORTANT: if we ever vary the content of these Makefiles by + # Python versions, the variable names will need add the Python + # version and the Makefile references updated to point to specific + # versions. If we don't do that, multi-version builds will fail + # to work correctly. + + makefile_path = dest_dir / ("Makefile.%s.%s" % (host_platform, triple)) + + lines = [] + for need in settings.get("needs", []): + lines.append( + "NEED_%s := 1\n" % need.upper().replace("-", "_").replace(".", "_") ) - lines = [] - for need in settings.get("needs", []): - lines.append("NEED_%s := 1\n" % need.upper()) + image_suffix = settings.get("docker_image_suffix", "") - if "PYBUILD_LIBRESSL" in os.environ: - lines.append("NEED_LIBRESSL := 1\n") - else: - lines.append("NEED_OPENSSL := 1\n") + # On cross builds, we can just use the bare `gcc` image + gcc_image_suffix = ( + image_suffix if not image_suffix.startswith(".cross") else "" + ) - image_suffix = settings.get("docker_image_suffix", "") + lines.append("DOCKER_IMAGE_BUILD := build%s\n" % image_suffix) + lines.append("DOCKER_IMAGE_GCC := gcc%s\n" % gcc_image_suffix) - lines.append("DOCKER_IMAGE_BUILD := build%s\n" % image_suffix) - lines.append("DOCKER_IMAGE_XCB := xcb%s\n" % image_suffix) + entry = clang_toolchain(host_platform, triple) + lines.append( + "CLANG_FILENAME := %s-%s-%s.tar\n" + % (entry, DOWNLOADS[entry]["version"], host_platform) + ) - for support_file in ( - "disabled-static-modules", - "required-extensions", - "static-modules", - ): - path = get_target_support_file( - support_search_dir, support_file, python, host_platform, triple - ) - lines.append( - "PYTHON_SUPPORT_FILES := $(PYTHON_SUPPORT_FILES) %s\n" % path - ) + lines.append( + "PYTHON_SUPPORT_FILES := $(PYTHON_SUPPORT_FILES) %s\n" + % (support_search_dir / "extension-modules.yml") + ) - write_if_different(makefile_path, "".join(lines).encode("ascii")) + write_if_different(makefile_path, "".join(lines).encode("ascii")) def write_package_versions(dest_path: pathlib.Path): @@ -179,9 +229,38 @@ def write_package_versions(dest_path: pathlib.Path): write_if_different(p, content.encode("ascii")) +def write_cpython_version(dest_path: pathlib.Path, version: str): + """Write a CPython version in a directory.""" + dest_path.mkdir(parents=True, exist_ok=True) + + major_minor = ".".join(version.split(".")[:2]) + k = "cpython-%s" % major_minor + p = dest_path / ("VERSION.%s" % k) + content = "%s_VERSION := %s\n" % (k.upper().replace("-", "_"), version) + write_if_different(p, content.encode("ascii")) + + +def write_target_settings(targets, dest_path: pathlib.Path): + dest_path.mkdir(parents=True, exist_ok=True) + + for triple, settings in targets.items(): + payload = {} + + for key in ("host_cc", "host_cxx", "target_cc", "target_cflags"): + payload[key] = settings.get(key) + + serialized_payload = json.dumps(payload, indent=4).encode("utf-8") + + write_if_different(dest_path / triple, serialized_payload) + + class IntegrityError(Exception): """Represents an integrity error when downloading a URL.""" + def __init__(self, *args, length: int): + self.length = length + super().__init__(*args) + def secure_download_stream(url, size, sha256): """Securely download a URL to a stream of chunks. @@ -211,7 +290,8 @@ def secure_download_stream(url, size, sha256): if length != size or digest != sha256: raise IntegrityError( "integrity mismatch on %s: wanted size=%d, sha256=%s; got size=%d, sha256=%s" - % (url, size, sha256, length, digest) + % (url, size, sha256, length, digest), + length=length, ) @@ -241,15 +321,39 @@ def download_to_path(url: str, path: pathlib.Path, size: int, sha256: str): path.unlink() - tmp = path.with_name("%s.tmp" % path.name) + # Need to write to random path to avoid race conditions. If there is a + # race, worst case we'll download the same file N>1 times. Meh. + tmp = path.with_name( + "%s.tmp%s" + % ( + path.name, + "".join(random.choices(string.ascii_uppercase + string.digits, k=8)), + ) + ) - try: - with tmp.open("wb") as fh: - for chunk in secure_download_stream(url, size, sha256): - fh.write(chunk) - except IntegrityError: - tmp.unlink() - raise + for attempt in range(8): + try: + try: + with tmp.open("wb") as fh: + for chunk in secure_download_stream(url, size, sha256): + fh.write(chunk) + + break + except IntegrityError as e: + tmp.unlink() + # If we didn't get most of the expected file, retry + if e.length > size * 0.75: + raise + print(f"Integrity error on {url}; retrying: {e}") + time.sleep(2**attempt) + except http.client.HTTPException as e: + print(f"HTTP exception on {url}; retrying: {e}") + time.sleep(2**attempt) + except urllib.error.URLError as e: + print(f"urllib error on {url}; retrying: {e}") + time.sleep(2**attempt) + else: + raise Exception("download failed after multiple retries: %s" % url) tmp.rename(path) print("successfully downloaded %s" % url) @@ -258,11 +362,15 @@ def download_to_path(url: str, path: pathlib.Path, size: int, sha256: str): def download_entry(key: str, dest_path: pathlib.Path, local_name=None) -> pathlib.Path: entry = DOWNLOADS[key] url = entry["url"] + size = entry["size"] + sha256 = entry["sha256"] - local_name = local_name or url[url.rindex("/") + 1 :] + assert isinstance(url, str) + assert isinstance(size, int) + assert isinstance(sha256, str) - local_path = dest_path / local_name - download_to_path(url, local_path, entry["size"], entry["sha256"]) + local_path = dest_path / (local_name or url[url.rindex("/") + 1 :]) + download_to_path(url, local_path, size, sha256) return local_path @@ -290,8 +398,8 @@ def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path): zf.extractall(dest) -# 2021-01-01T00:00:00 -DEFAULT_MTIME = 1609488000 +# 2024-01-01T00:00:00Z +DEFAULT_MTIME = 1704067200 def normalize_tar_archive(data: io.BytesIO) -> io.BytesIO: @@ -356,7 +464,7 @@ def sort_key(v): dest = io.BytesIO() with tarfile.open(fileobj=dest, mode="w") as tf: - for (ti, filedata) in members: + for ti, filedata in members: tf.addfile(ti, filedata) dest.seek(0) @@ -364,6 +472,19 @@ def sort_key(v): return dest +def clang_toolchain(host_platform: str, target_triple: str) -> str: + if host_platform == "linux_x86_64": + return "llvm-x86_64-linux" + elif host_platform == "linux_aarch64": + return "llvm-aarch64-linux" + elif host_platform == "macos_arm64": + return "llvm-aarch64-macos" + elif host_platform == "macos_x86_64": + return "llvm-x86_64-macos" + else: + raise Exception("unhandled host platform") + + def compress_python_archive( source_path: pathlib.Path, dist_path: pathlib.Path, basename: str ): @@ -381,50 +502,33 @@ def compress_python_archive( cctx.copy_stream(ifh, ofh, source_path.stat().st_size) temp_path.rename(dest_path) - except Exception: - temp_path.unlink() - raise + finally: + temp_path.unlink(missing_ok=True) print("%s has SHA256 %s" % (dest_path, hash_path(dest_path))) return dest_path -def prune_distribution_archive(source_path: pathlib.Path): - """Prunes a full Python distribution archive into one with just a Python install.""" - name_parts = source_path.name.split("-") - name_parts[-2] = "install_only" - - dest_path = source_path.with_name("-".join(name_parts)).with_suffix(".gz") +def extract_python_archive( + archive: pathlib.Path, dest_dir: pathlib.Path +) -> pathlib.Path: + """Extract a PBS .tar.zst distribution archive to the given directory. - with source_path.open("rb") as ifh, tarfile.open(dest_path, "w:gz") as otf: + Returns the path to the `python` directory, which is equivalent to + `dest_dir / "python"`. + """ + with archive.open("rb") as fh: dctx = zstandard.ZstdDecompressor() - with dctx.stream_reader(ifh) as tar_reader: - with tarfile.open(fileobj=tar_reader, mode="r|") as itf: - for member in itf: - if not member.name.startswith("python/install/"): - continue - - member.name = "python/%s" % member.name[len("python/install/") :] - - if member.pax_headers.get("path"): - member.pax_headers["path"] = ( - "python/%s" - % member.pax_headers["path"][len("python/install/") :] - ) - - if member.issym(): - data = None - else: - data = itf.extractfile(member) + with dctx.stream_reader(fh) as reader: + with tarfile.open(mode="r|", fileobj=reader) as tf: + dest_dir.mkdir(exist_ok=True, parents=True) + tf.extractall(dest_dir) - print("adding %s" % member.name) - otf.addfile(member, data) + return dest_dir / "python" - print("wrote %s" % dest_path) - -def add_licenses_to_extension_entry(entry, ignore_keys=None): +def add_licenses_to_extension_entry(entry): """Add licenses keys to a ``extensions`` entry for JSON distribution info.""" have_licenses = False @@ -439,10 +543,7 @@ def add_licenses_to_extension_entry(entry, ignore_keys=None): if "path_static" in link or "path_dynamic" in link: have_local_link = True - for key, value in DOWNLOADS.items(): - if ignore_keys and key in ignore_keys: - continue - + for value in DOWNLOADS.values(): if name not in value.get("library_names", []): continue @@ -481,7 +582,7 @@ def add_env_common(env): env_path = os.path.expanduser("~/.python-build-standalone-env") try: - with open(env_path, "r") as fh: + with open(env_path) as fh: for line in fh: line = line.strip() if line.startswith("#"): @@ -494,17 +595,6 @@ def add_env_common(env): except FileNotFoundError: pass - # Proxy sccache settings. - for k, v in os.environ.items(): - if k.startswith("SCCACHE_"): - env[k] = v - - # Proxy cloud provider credentials variables to enable sccache to - # use stores in those providers. - for k in ("AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"): - if k in os.environ: - env[k] = os.environ[k] - def exec_and_log(args, cwd, env): p = subprocess.Popen( @@ -531,16 +621,24 @@ def exec_and_log(args, cwd, env): sys.exit(p.returncode) -def validate_python_json(info): +def validate_python_json(info, extension_modules): """Validate a PYTHON.json file for problems. Raises an exception if an issue is detected. """ - for name, variants in info["build_info"]["extensions"].items(): - for ext in variants: - variant = ext["variant"] + if extension_modules: + missing = set(info["build_info"]["extensions"].keys()) - set( + extension_modules.keys() + ) + if missing: + raise Exception( + "extension modules in PYTHON.json lack metadata: %s" + % ", ".join(sorted(missing)) + ) + for name, variants in sorted(info["build_info"]["extensions"].items()): + for ext in variants: local_links = set() for link in ext["links"]: @@ -551,8 +649,7 @@ def validate_python_json(info): if not local_links and "framework" not in link and "system" not in link: raise Exception( - "Invalid link entry for extension %s[%s]: link type not defined" - % (name, variant) + f"Invalid link entry for extension {name}: link type not defined" ) if ( @@ -561,6 +658,6 @@ def validate_python_json(info): and not ext.get("license_public_domain") ): raise Exception( - "Missing license annotations for extension %s[%s] for library files %s" - % (name, variant, ", ".join(sorted(local_links))) + "Missing license annotations for extension %s for library files %s" + % (name, ", ".join(sorted(local_links))) ) diff --git a/requirements.dev.in b/requirements.dev.in new file mode 100644 index 000000000..2f64d57f3 --- /dev/null +++ b/requirements.dev.in @@ -0,0 +1,7 @@ +-r requirements.txt + +ruff +mypy +types-jsonschema +types-PyYAML +types-jinja2 diff --git a/requirements.dev.txt b/requirements.dev.txt new file mode 100644 index 000000000..f09be0336 --- /dev/null +++ b/requirements.dev.txt @@ -0,0 +1,612 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --generate-hashes requirements.dev.in -o requirements.dev.txt +attrs==24.3.0 \ + --hash=sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff \ + --hash=sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308 + # via + # -r requirements.txt + # jsonschema + # referencing +certifi==2024.12.14 \ + --hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \ + --hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db + # via + # -r requirements.txt + # requests +charset-normalizer==3.4.1 \ + --hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \ + --hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \ + --hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \ + --hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \ + --hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \ + --hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \ + --hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \ + --hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \ + --hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \ + --hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \ + --hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \ + --hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \ + --hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \ + --hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \ + --hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \ + --hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \ + --hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \ + --hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \ + --hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \ + --hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \ + --hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \ + --hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \ + --hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \ + --hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \ + --hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \ + --hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \ + --hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \ + --hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \ + --hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \ + --hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \ + --hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \ + --hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \ + --hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \ + --hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \ + --hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \ + --hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \ + --hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \ + --hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \ + --hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \ + --hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \ + --hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \ + --hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \ + --hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \ + --hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \ + --hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \ + --hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \ + --hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \ + --hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \ + --hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \ + --hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \ + --hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \ + --hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \ + --hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \ + --hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \ + --hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \ + --hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \ + --hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \ + --hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \ + --hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \ + --hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \ + --hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \ + --hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \ + --hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \ + --hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \ + --hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \ + --hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \ + --hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \ + --hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \ + --hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \ + --hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \ + --hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \ + --hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \ + --hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \ + --hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \ + --hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \ + --hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \ + --hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \ + --hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \ + --hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \ + --hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \ + --hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \ + --hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \ + --hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \ + --hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \ + --hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \ + --hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \ + --hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \ + --hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \ + --hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \ + --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \ + --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \ + --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616 + # via + # -r requirements.txt + # requests +docker==7.1.0 \ + --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ + --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 + # via -r requirements.txt +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 + # via + # -r requirements.txt + # requests +jinja2==3.1.5 \ + --hash=sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb \ + --hash=sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb + # via -r requirements.txt +jsonschema==4.23.0 \ + --hash=sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4 \ + --hash=sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566 + # via -r requirements.txt +jsonschema-specifications==2024.10.1 \ + --hash=sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272 \ + --hash=sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf + # via + # -r requirements.txt + # jsonschema +markupsafe==3.0.2 \ + --hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \ + --hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \ + --hash=sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0 \ + --hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \ + --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ + --hash=sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13 \ + --hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \ + --hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \ + --hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \ + --hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \ + --hash=sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0 \ + --hash=sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b \ + --hash=sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579 \ + --hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \ + --hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \ + --hash=sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff \ + --hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \ + --hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \ + --hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \ + --hash=sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb \ + --hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \ + --hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \ + --hash=sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a \ + --hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \ + --hash=sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a \ + --hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \ + --hash=sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8 \ + --hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \ + --hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \ + --hash=sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144 \ + --hash=sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f \ + --hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \ + --hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \ + --hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \ + --hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \ + --hash=sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158 \ + --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ + --hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \ + --hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \ + --hash=sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171 \ + --hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \ + --hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \ + --hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \ + --hash=sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d \ + --hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \ + --hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \ + --hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \ + --hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \ + --hash=sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29 \ + --hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \ + --hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \ + --hash=sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c \ + --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \ + --hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \ + --hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \ + --hash=sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a \ + --hash=sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178 \ + --hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \ + --hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \ + --hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \ + --hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50 + # via + # -r requirements.txt + # jinja2 +mypy==1.18.1 \ + --hash=sha256:040ecc95e026f71a9ad7956fea2724466602b561e6a25c2e5584160d3833aaa8 \ + --hash=sha256:1a0e70b87eb27b33209fa4792b051c6947976f6ab829daa83819df5f58330c71 \ + --hash=sha256:1cabb353194d2942522546501c0ff75c4043bf3b63069cb43274491b44b773c9 \ + --hash=sha256:1f95cc4f01c0f1701ca3b0355792bccec13ecb2ec1c469e5b85a6ef398398b1d \ + --hash=sha256:261fbfced030228bc0f724d5d92f9ae69f46373bdfd0e04a533852677a11dbea \ + --hash=sha256:2657654d82fcd2a87e02a33e0d23001789a554059bbf34702d623dafe353eabf \ + --hash=sha256:2761b6ae22a2b7d8e8607fb9b81ae90bc2e95ec033fd18fa35e807af6c657763 \ + --hash=sha256:2a0c8392c19934c2b6c65566d3a6abdc6b51d5da7f5d04e43f0eb627d6eeee65 \ + --hash=sha256:2cd2c1e0f3a7465f22731987fff6fc427e3dcbb4ca5f7db5bbeaff2ff9a31f6d \ + --hash=sha256:2fbcecbe5cf213ba294aa8c0b8c104400bf7bb64db82fb34fe32a205da4b3531 \ + --hash=sha256:320f0ad4205eefcb0e1a72428dde0ad10be73da9f92e793c36228e8ebf7298c0 \ + --hash=sha256:4dc6b34a1c6875e6286e27d836a35c0d04e8316beac4482d42cfea7ed2527df8 \ + --hash=sha256:502cde8896be8e638588b90fdcb4c5d5b8c1b004dfc63fd5604a973547367bb9 \ + --hash=sha256:51531b6e94f34b8bd8b01dee52bbcee80daeac45e69ec5c36e25bce51cbc46e6 \ + --hash=sha256:5956ecaabb3a245e3f34100172abca1507be687377fe20e24d6a7557e07080e2 \ + --hash=sha256:5b10e3ea7f2eec23b4929a3fabf84505da21034a4f4b9613cda81217e92b74f3 \ + --hash=sha256:6c903857b3e28fc5489e54042684a9509039ea0aedb2a619469438b544ae1961 \ + --hash=sha256:738b171690c8e47c93569635ee8ec633d2cdb06062f510b853b5f233020569a9 \ + --hash=sha256:7509549b5e41be279afc1228242d0e397f1af2919a8f2877ad542b199dc4083e \ + --hash=sha256:82ace21edf7ba8af31c3308a61dc72df30500f4dbb26f99ac36b4b80809d7e94 \ + --hash=sha256:8750ceb014a96c9890421c83f0db53b0f3b8633e2864c6f9bc0a8e93951ed18d \ + --hash=sha256:8c05a7f8c00300a52f3a4fcc95a185e99bf944d7e851ff141bae8dcf6dcfeac4 \ + --hash=sha256:913f668ec50c3337b89df22f973c1c8f0b29ee9e290a8b7fe01cc1ef7446d42e \ + --hash=sha256:937e3ed86cb731276706e46e03512547e43c391a13f363e08d0fee49a7c38a0d \ + --hash=sha256:99f272c9b59f5826fffa439575716276d19cbf9654abc84a2ba2d77090a0ba14 \ + --hash=sha256:9e988c64ad3ac5987f43f5154f884747faf62141b7f842e87465b45299eea5a9 \ + --hash=sha256:a2dfd53dfe632f1ef5d161150a4b1f2d0786746ae02950eb3ac108964ee2975a \ + --hash=sha256:b76a4de66a0ac01da1be14ecc8ae88ddea33b8380284a9e3eae39d57ebcbe26e \ + --hash=sha256:b8367e33506300f07a43012fc546402f283c3f8bcff1dc338636affb710154ce \ + --hash=sha256:ba24603c58e34dd5b096dfad792d87b304fc6470cbb1c22fd64e7ebd17edcc61 \ + --hash=sha256:c378d946e8a60be6b6ede48c878d145546fb42aad61df998c056ec151bf6c746 \ + --hash=sha256:d70d2b5baf9b9a20bc9c730015615ae3243ef47fb4a58ad7b31c3e0a59b5ef1f \ + --hash=sha256:dbfdea20e90e9c5476cea80cfd264d8e197c6ef2c58483931db2eefb2f7adc14 \ + --hash=sha256:e37763af63a8018308859bc83d9063c501a5820ec5bd4a19f0a2ac0d1c25c061 \ + --hash=sha256:e4f16c0019d48941220ac60b893615be2f63afedaba6a0801bdcd041b96991ce \ + --hash=sha256:ed36662fb92ae4cb3cacc682ec6656208f323bbc23d4b08d091eecfc0863d4b5 \ + --hash=sha256:f85eb7efa2ec73ef63fc23b8af89c2fe5bf2a4ad985ed2d3ff28c1bb3c317c92 \ + --hash=sha256:fb89ea08ff41adf59476b235293679a6eb53a7b9400f6256272fb6029bec3ce5 + # via -r requirements.dev.in +mypy-extensions==1.0.0 \ + --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ + --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 + # via mypy +pathspec==0.12.1 \ + --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ + --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 + # via mypy +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via -r requirements.txt +referencing==0.35.1 \ + --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ + --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de + # via + # -r requirements.txt + # jsonschema + # jsonschema-specifications + # types-jsonschema +requests==2.32.3 \ + --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ + --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 + # via + # -r requirements.txt + # docker +rpds-py==0.22.3 \ + --hash=sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518 \ + --hash=sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059 \ + --hash=sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61 \ + --hash=sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5 \ + --hash=sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9 \ + --hash=sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543 \ + --hash=sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2 \ + --hash=sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a \ + --hash=sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d \ + --hash=sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56 \ + --hash=sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d \ + --hash=sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd \ + --hash=sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b \ + --hash=sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4 \ + --hash=sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99 \ + --hash=sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d \ + --hash=sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd \ + --hash=sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe \ + --hash=sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1 \ + --hash=sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e \ + --hash=sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f \ + --hash=sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3 \ + --hash=sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca \ + --hash=sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d \ + --hash=sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e \ + --hash=sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc \ + --hash=sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea \ + --hash=sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38 \ + --hash=sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b \ + --hash=sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c \ + --hash=sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff \ + --hash=sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723 \ + --hash=sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e \ + --hash=sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493 \ + --hash=sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6 \ + --hash=sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83 \ + --hash=sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091 \ + --hash=sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1 \ + --hash=sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627 \ + --hash=sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1 \ + --hash=sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728 \ + --hash=sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16 \ + --hash=sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c \ + --hash=sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45 \ + --hash=sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7 \ + --hash=sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a \ + --hash=sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730 \ + --hash=sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967 \ + --hash=sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25 \ + --hash=sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24 \ + --hash=sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055 \ + --hash=sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d \ + --hash=sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0 \ + --hash=sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e \ + --hash=sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7 \ + --hash=sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c \ + --hash=sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f \ + --hash=sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd \ + --hash=sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652 \ + --hash=sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8 \ + --hash=sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11 \ + --hash=sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333 \ + --hash=sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96 \ + --hash=sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64 \ + --hash=sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b \ + --hash=sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e \ + --hash=sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c \ + --hash=sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9 \ + --hash=sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec \ + --hash=sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb \ + --hash=sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37 \ + --hash=sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad \ + --hash=sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9 \ + --hash=sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c \ + --hash=sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf \ + --hash=sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4 \ + --hash=sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f \ + --hash=sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d \ + --hash=sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09 \ + --hash=sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d \ + --hash=sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566 \ + --hash=sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74 \ + --hash=sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338 \ + --hash=sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15 \ + --hash=sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c \ + --hash=sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648 \ + --hash=sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84 \ + --hash=sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3 \ + --hash=sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123 \ + --hash=sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520 \ + --hash=sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831 \ + --hash=sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e \ + --hash=sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf \ + --hash=sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b \ + --hash=sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2 \ + --hash=sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3 \ + --hash=sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130 \ + --hash=sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b \ + --hash=sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de \ + --hash=sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5 \ + --hash=sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d \ + --hash=sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00 \ + --hash=sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e + # via + # -r requirements.txt + # jsonschema + # referencing +ruff==0.13.0 \ + --hash=sha256:03447f3d18479df3d24917a92d768a89f873a7181a064858ea90a804a7538991 \ + --hash=sha256:0f96a8d90bb258d7d3358b372905fe7333aaacf6c39e2408b9f8ba181f4b6ef2 \ + --hash=sha256:137f3d65d58ee828ae136a12d1dc33d992773d8f7644bc6b82714570f31b2004 \ + --hash=sha256:21ae48151b66e71fd111b7d79f9ad358814ed58c339631450c66a4be33cc28b9 \ + --hash=sha256:2b2c653ae9b9d46e0ef62fc6fbf5b979bda20a0b1d2b22f8f7eb0cde9f4963b8 \ + --hash=sha256:48e5c25c7a3713eea9ce755995767f4dcd1b0b9599b638b12946e892123d1efb \ + --hash=sha256:4cec632534332062bc9eb5884a267b689085a1afea9801bf94e3ba7498a2d207 \ + --hash=sha256:4e473e8f0e6a04e4113f2e1de12a5039579892329ecc49958424e5568ef4f768 \ + --hash=sha256:5b4b1ee7eb35afae128ab94459b13b2baaed282b1fb0f472a73c82c996c8ae60 \ + --hash=sha256:64de45f4ca5441209e41742d527944635a05a6e7c05798904f39c85bafa819e3 \ + --hash=sha256:79ea0c44a3032af768cabfd9616e44c24303af49d633b43e3a5096e009ebe823 \ + --hash=sha256:94b5e3d883e4f924c5298e3f2ee0f3085819c14f68d1e5b6715597681433f153 \ + --hash=sha256:a8ab6a3e03665d39d4a25ee199d207a488724f022db0e1fe4002968abdb8001b \ + --hash=sha256:ab80525317b1e1d38614addec8ac954f1b3e662de9d59114ecbf771d00cf613e \ + --hash=sha256:afe37db8e1466acb173bb2a39ca92df00570e0fd7c94c72d87b51b21bb63efea \ + --hash=sha256:b7b85ca27aeeb1ab421bc787009831cffe6048faae08ad80867edab9f2760945 \ + --hash=sha256:d2a5c62f8ccc6dd2fe259917482de7275cecc86141ee10432727c4816235bc41 \ + --hash=sha256:dcd628101d9f7d122e120ac7c17e0a0f468b19bc925501dbe03c1cb7f5415b24 \ + --hash=sha256:fbc6b1934eb1c0033da427c805e27d164bb713f8e273a024a7e86176d7f462cf + # via -r requirements.dev.in +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via -r requirements.txt +tomli==2.2.1 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 + # via -r requirements.txt +types-jinja2==2.11.9 \ + --hash=sha256:60a1e21e8296979db32f9374d8a239af4cb541ff66447bb915d8ad398f9c63b2 \ + --hash=sha256:dbdc74a40aba7aed520b7e4d89e8f0fe4286518494208b35123bcf084d4b8c81 + # via -r requirements.dev.in +types-jsonschema==4.25.1.20250822 \ + --hash=sha256:aac69ed4b23f49aaceb7fcb834141d61b9e4e6a7f6008cb2f0d3b831dfa8464a \ + --hash=sha256:f82c2d7fa1ce1c0b84ba1de4ed6798469768188884db04e66421913a4e181294 + # via -r requirements.dev.in +types-markupsafe==1.1.10 \ + --hash=sha256:85b3a872683d02aea3a5ac2a8ef590193c344092032f58457287fbf8e06711b1 \ + --hash=sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5 + # via types-jinja2 +types-pyyaml==6.0.12.20250915 \ + --hash=sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3 \ + --hash=sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6 + # via -r requirements.dev.in +typing-extensions==4.14.1 \ + --hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \ + --hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76 + # via + # -r requirements.txt + # mypy +urllib3==2.3.0 \ + --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ + --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d + # via + # -r requirements.txt + # docker + # requests +zstandard==0.23.0 \ + --hash=sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473 \ + --hash=sha256:0a7f0804bb3799414af278e9ad51be25edf67f78f916e08afdb983e74161b916 \ + --hash=sha256:11e3bf3c924853a2d5835b24f03eeba7fc9b07d8ca499e247e06ff5676461a15 \ + --hash=sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072 \ + --hash=sha256:1516c8c37d3a053b01c1c15b182f3b5f5eef19ced9b930b684a73bad121addf4 \ + --hash=sha256:157e89ceb4054029a289fb504c98c6a9fe8010f1680de0201b3eb5dc20aa6d9e \ + --hash=sha256:1bfe8de1da6d104f15a60d4a8a768288f66aa953bbe00d027398b93fb9680b26 \ + --hash=sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8 \ + --hash=sha256:1fd7e0f1cfb70eb2f95a19b472ee7ad6d9a0a992ec0ae53286870c104ca939e5 \ + --hash=sha256:203d236f4c94cd8379d1ea61db2fce20730b4c38d7f1c34506a31b34edc87bdd \ + --hash=sha256:27d3ef2252d2e62476389ca8f9b0cf2bbafb082a3b6bfe9d90cbcbb5529ecf7c \ + --hash=sha256:29a2bc7c1b09b0af938b7a8343174b987ae021705acabcbae560166567f5a8db \ + --hash=sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5 \ + --hash=sha256:2ef3775758346d9ac6214123887d25c7061c92afe1f2b354f9388e9e4d48acfc \ + --hash=sha256:2f146f50723defec2975fb7e388ae3a024eb7151542d1599527ec2aa9cacb152 \ + --hash=sha256:2fb4535137de7e244c230e24f9d1ec194f61721c86ebea04e1581d9d06ea1269 \ + --hash=sha256:32ba3b5ccde2d581b1e6aa952c836a6291e8435d788f656fe5976445865ae045 \ + --hash=sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e \ + --hash=sha256:379b378ae694ba78cef921581ebd420c938936a153ded602c4fea612b7eaa90d \ + --hash=sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a \ + --hash=sha256:3aa014d55c3af933c1315eb4bb06dd0459661cc0b15cd61077afa6489bec63bb \ + --hash=sha256:4051e406288b8cdbb993798b9a45c59a4896b6ecee2f875424ec10276a895740 \ + --hash=sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105 \ + --hash=sha256:43da0f0092281bf501f9c5f6f3b4c975a8a0ea82de49ba3f7100e64d422a1274 \ + --hash=sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2 \ + --hash=sha256:48ef6a43b1846f6025dde6ed9fee0c24e1149c1c25f7fb0a0585572b2f3adc58 \ + --hash=sha256:50a80baba0285386f97ea36239855f6020ce452456605f262b2d33ac35c7770b \ + --hash=sha256:519fbf169dfac1222a76ba8861ef4ac7f0530c35dd79ba5727014613f91613d4 \ + --hash=sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db \ + --hash=sha256:53ea7cdc96c6eb56e76bb06894bcfb5dfa93b7adcf59d61c6b92674e24e2dd5e \ + --hash=sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9 \ + --hash=sha256:59556bf80a7094d0cfb9f5e50bb2db27fefb75d5138bb16fb052b61b0e0eeeb0 \ + --hash=sha256:5d41d5e025f1e0bccae4928981e71b2334c60f580bdc8345f824e7c0a4c2a813 \ + --hash=sha256:61062387ad820c654b6a6b5f0b94484fa19515e0c5116faf29f41a6bc91ded6e \ + --hash=sha256:61f89436cbfede4bc4e91b4397eaa3e2108ebe96d05e93d6ccc95ab5714be512 \ + --hash=sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0 \ + --hash=sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b \ + --hash=sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48 \ + --hash=sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a \ + --hash=sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772 \ + --hash=sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed \ + --hash=sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373 \ + --hash=sha256:752bf8a74412b9892f4e5b58f2f890a039f57037f52c89a740757ebd807f33ea \ + --hash=sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd \ + --hash=sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f \ + --hash=sha256:77da4c6bfa20dd5ea25cbf12c76f181a8e8cd7ea231c673828d0386b1740b8dc \ + --hash=sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23 \ + --hash=sha256:80080816b4f52a9d886e67f1f96912891074903238fe54f2de8b786f86baded2 \ + --hash=sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db \ + --hash=sha256:82d17e94d735c99621bf8ebf9995f870a6b3e6d14543b99e201ae046dfe7de70 \ + --hash=sha256:837bb6764be6919963ef41235fd56a6486b132ea64afe5fafb4cb279ac44f259 \ + --hash=sha256:84433dddea68571a6d6bd4fbf8ff398236031149116a7fff6f777ff95cad3df9 \ + --hash=sha256:8c24f21fa2af4bb9f2c492a86fe0c34e6d2c63812a839590edaf177b7398f700 \ + --hash=sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003 \ + --hash=sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba \ + --hash=sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a \ + --hash=sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c \ + --hash=sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90 \ + --hash=sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690 \ + --hash=sha256:a05e6d6218461eb1b4771d973728f0133b2a4613a6779995df557f70794fd60f \ + --hash=sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840 \ + --hash=sha256:a4ae99c57668ca1e78597d8b06d5af837f377f340f4cce993b551b2d7731778d \ + --hash=sha256:a8c86881813a78a6f4508ef9daf9d4995b8ac2d147dcb1a450448941398091c9 \ + --hash=sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35 \ + --hash=sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd \ + --hash=sha256:ab19a2d91963ed9e42b4e8d77cd847ae8381576585bad79dbd0a8837a9f6620a \ + --hash=sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea \ + --hash=sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1 \ + --hash=sha256:b2170c7e0367dde86a2647ed5b6f57394ea7f53545746104c6b09fc1f4223573 \ + --hash=sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09 \ + --hash=sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094 \ + --hash=sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78 \ + --hash=sha256:b8c0bd73aeac689beacd4e7667d48c299f61b959475cdbb91e7d3d88d27c56b9 \ + --hash=sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5 \ + --hash=sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9 \ + --hash=sha256:c16842b846a8d2a145223f520b7e18b57c8f476924bda92aeee3a88d11cfc391 \ + --hash=sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847 \ + --hash=sha256:c7c517d74bea1a6afd39aa612fa025e6b8011982a0897768a2f7c8ab4ebb78a2 \ + --hash=sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c \ + --hash=sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2 \ + --hash=sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057 \ + --hash=sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20 \ + --hash=sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d \ + --hash=sha256:dc5d1a49d3f8262be192589a4b72f0d03b72dcf46c51ad5852a4fdc67be7b9e4 \ + --hash=sha256:e2d1a054f8f0a191004675755448d12be47fa9bebbcffa3cdf01db19f2d30a54 \ + --hash=sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171 \ + --hash=sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e \ + --hash=sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160 \ + --hash=sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b \ + --hash=sha256:f8346bfa098532bc1fb6c7ef06783e969d87a99dd1d2a5a18a892c1d7a643c58 \ + --hash=sha256:f83fa6cae3fff8e98691248c9320356971b59678a17f20656a9e59cd32cee6d8 \ + --hash=sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33 \ + --hash=sha256:fb2b1ecfef1e67897d336de3a0e3f52478182d6a47eda86cbd42504c5cbd009a \ + --hash=sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880 \ + --hash=sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca \ + --hash=sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b \ + --hash=sha256:fe3b385d996ee0822fd46528d9f0443b880d4d05528fd26a9119a54ec3f91c69 + # via -r requirements.txt diff --git a/requirements.in b/requirements.in new file mode 100644 index 000000000..0e9ae39ab --- /dev/null +++ b/requirements.in @@ -0,0 +1,11 @@ +docker +jinja2 +jsonschema +PyYAML +# Undeclared dependency in docker 5.0 package. +six +# This is a transitive dependency that doesn't get picked up when running on +# modern Python. So include to force it in requirements.txt. +tomli +typing-extensions +zstandard diff --git a/requirements.txt b/requirements.txt index 34c48711b..6770e892f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,179 +1,508 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile --generate-hashes --output-file=requirements.txt requirements.txt.in -# -certifi==2021.5.30 \ - --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ - --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 +# This file was autogenerated by uv via the following command: +# uv pip compile --generate-hashes requirements.in -o requirements.txt +attrs==24.3.0 \ + --hash=sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff \ + --hash=sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308 + # via + # jsonschema + # referencing +certifi==2024.12.14 \ + --hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \ + --hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db # via requests -charset-normalizer==2.0.4 \ - --hash=sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b \ - --hash=sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3 +charset-normalizer==3.4.1 \ + --hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \ + --hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \ + --hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \ + --hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \ + --hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \ + --hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \ + --hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \ + --hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \ + --hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \ + --hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \ + --hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \ + --hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \ + --hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \ + --hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \ + --hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \ + --hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \ + --hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \ + --hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \ + --hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \ + --hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \ + --hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \ + --hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \ + --hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \ + --hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \ + --hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \ + --hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \ + --hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \ + --hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \ + --hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \ + --hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \ + --hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \ + --hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \ + --hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \ + --hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \ + --hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \ + --hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \ + --hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \ + --hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \ + --hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \ + --hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \ + --hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \ + --hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \ + --hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \ + --hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \ + --hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \ + --hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \ + --hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \ + --hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \ + --hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \ + --hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \ + --hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \ + --hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \ + --hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \ + --hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \ + --hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \ + --hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \ + --hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \ + --hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \ + --hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \ + --hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \ + --hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \ + --hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \ + --hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \ + --hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \ + --hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \ + --hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \ + --hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \ + --hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \ + --hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \ + --hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \ + --hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \ + --hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \ + --hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \ + --hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \ + --hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \ + --hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \ + --hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \ + --hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \ + --hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \ + --hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \ + --hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \ + --hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \ + --hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \ + --hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \ + --hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \ + --hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \ + --hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \ + --hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \ + --hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \ + --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \ + --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \ + --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616 # via requests -docker==5.0.0 \ - --hash=sha256:3e8bc47534e0ca9331d72c32f2881bb13b93ded0bcdeab3c833fb7cf61c0a9a5 \ - --hash=sha256:fc961d622160e8021c10d1bcabc388c57d55fb1f917175afbe24af442e6879bd - # via -r requirements.txt.in -idna==3.2 \ - --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ - --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 +docker==7.1.0 \ + --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ + --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 + # via -r requirements.in +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 # via requests -jinja2==3.0.1 \ - --hash=sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4 \ - --hash=sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4 - # via -r requirements.txt.in -markupsafe==2.0.1 \ - --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ - --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \ - --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ - --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ - --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ - --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \ - --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ - --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \ - --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ - --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \ - --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \ - --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \ - --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ - --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \ - --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \ - --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ - --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \ - --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ - --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ - --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ - --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ - --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \ - --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \ - --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \ - --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \ - --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ - --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ - --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ - --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ - --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ - --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \ - --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ - --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ - --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ - --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ - --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \ - --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ - --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ - --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \ - --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ - --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ - --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \ - --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ - --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \ - --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \ - --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ - --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \ - --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \ - --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \ - --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ - --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ - --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ - --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ - --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872 +jinja2==3.1.5 \ + --hash=sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb \ + --hash=sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb + # via -r requirements.in +jsonschema==4.23.0 \ + --hash=sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4 \ + --hash=sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566 + # via -r requirements.in +jsonschema-specifications==2024.10.1 \ + --hash=sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272 \ + --hash=sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf + # via jsonschema +markupsafe==3.0.2 \ + --hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \ + --hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \ + --hash=sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0 \ + --hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \ + --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ + --hash=sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13 \ + --hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \ + --hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \ + --hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \ + --hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \ + --hash=sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0 \ + --hash=sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b \ + --hash=sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579 \ + --hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \ + --hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \ + --hash=sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff \ + --hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \ + --hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \ + --hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \ + --hash=sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb \ + --hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \ + --hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \ + --hash=sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a \ + --hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \ + --hash=sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a \ + --hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \ + --hash=sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8 \ + --hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \ + --hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \ + --hash=sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144 \ + --hash=sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f \ + --hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \ + --hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \ + --hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \ + --hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \ + --hash=sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158 \ + --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ + --hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \ + --hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \ + --hash=sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171 \ + --hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \ + --hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \ + --hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \ + --hash=sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d \ + --hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \ + --hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \ + --hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \ + --hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \ + --hash=sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29 \ + --hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \ + --hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \ + --hash=sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c \ + --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \ + --hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \ + --hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \ + --hash=sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a \ + --hash=sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178 \ + --hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \ + --hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \ + --hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \ + --hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50 # via jinja2 -pyyaml==5.4.1 \ - --hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \ - --hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \ - --hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \ - --hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \ - --hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \ - --hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \ - --hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \ - --hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \ - --hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \ - --hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \ - --hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e \ - --hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \ - --hash=sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347 \ - --hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \ - --hash=sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541 \ - --hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \ - --hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \ - --hash=sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc \ - --hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \ - --hash=sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa \ - --hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \ - --hash=sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122 \ - --hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \ - --hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \ - --hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \ - --hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \ - --hash=sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247 \ - --hash=sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6 \ - --hash=sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0 - # via -r requirements.txt.in -requests==2.26.0 \ - --hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \ - --hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7 +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via -r requirements.in +referencing==0.35.1 \ + --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ + --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de + # via + # jsonschema + # jsonschema-specifications +requests==2.32.3 \ + --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ + --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 # via docker -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via -r requirements.txt.in -urllib3==1.26.6 \ - --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ - --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f - # via requests -websocket-client==1.2.1 \ - --hash=sha256:0133d2f784858e59959ce82ddac316634229da55b498aac311f1620567a710ec \ - --hash=sha256:8dfb715d8a992f5712fff8c843adae94e22b22a99b2c5e6b0ec4a1a981cc4e0d - # via docker -zstandard==0.15.2 \ - --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ - --hash=sha256:1faefe33e3d6870a4dce637bcb41f7abb46a1872a595ecc7b034016081c37543 \ - --hash=sha256:1fb23b1754ce834a3a1a1e148cc2faad76eeadf9d889efe5e8199d3fb839d3c6 \ - --hash=sha256:22f127ff5da052ffba73af146d7d61db874f5edb468b36c9cb0b857316a21b3d \ - --hash=sha256:2353b61f249a5fc243aae3caa1207c80c7e6919a58b1f9992758fa496f61f839 \ - --hash=sha256:24cdcc6f297f7c978a40fb7706877ad33d8e28acc1786992a52199502d6da2a4 \ - --hash=sha256:31e35790434da54c106f05fa93ab4d0fab2798a6350e8a73928ec602e8505836 \ - --hash=sha256:3547ff4eee7175d944a865bbdf5529b0969c253e8a148c287f0668fe4eb9c935 \ - --hash=sha256:378ac053c0cfc74d115cbb6ee181540f3e793c7cca8ed8cd3893e338af9e942c \ - --hash=sha256:3e1cd2db25117c5b7c7e86a17cde6104a93719a9df7cb099d7498e4c1d13ee5c \ - --hash=sha256:3fe469a887f6142cc108e44c7f42c036e43620ebaf500747be2317c9f4615d4f \ - --hash=sha256:4800ab8ec94cbf1ed09c2b4686288750cab0642cb4d6fba2a56db66b923aeb92 \ - --hash=sha256:52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f \ - --hash=sha256:5752f44795b943c99be367fee5edf3122a1690b0d1ecd1bd5ec94c7fd2c39c94 \ - --hash=sha256:5d53f02aeb8fdd48b88bc80bece82542d084fb1a7ba03bf241fd53b63aee4f22 \ - --hash=sha256:69b7a5720b8dfab9005a43c7ddb2e3ccacbb9a2442908ae4ed49dd51ab19698a \ - --hash=sha256:6cc162b5b6e3c40b223163a9ea86cd332bd352ddadb5fd142fc0706e5e4eaaff \ - --hash=sha256:6f5d0330bc992b1e267a1b69fbdbb5ebe8c3a6af107d67e14c7a5b1ede2c5945 \ - --hash=sha256:6ffadd48e6fe85f27ca3ca10cfd3ef3d0f933bef7316870285ffeb58d791ca9c \ - --hash=sha256:72a011678c654df8323aa7b687e3147749034fdbe994d346f139ab9702b59cea \ - --hash=sha256:77d26452676f471223571efd73131fd4a626622c7960458aab2763e025836fc5 \ - --hash=sha256:7a88cc773ffe55992ff7259a8df5fb3570168d7138c69aadba40142d0e5ce39a \ - --hash=sha256:7b16bd74ae7bfbaca407a127e11058b287a4267caad13bd41305a5e630472549 \ - --hash=sha256:855d95ec78b6f0ff66e076d5461bf12d09d8e8f7e2b3fc9de7236d1464fd730e \ - --hash=sha256:8baf7991547441458325ca8fafeae79ef1501cb4354022724f3edd62279c5b2b \ - --hash=sha256:8fb77dd152054c6685639d855693579a92f276b38b8003be5942de31d241ebfb \ - --hash=sha256:92d49cc3b49372cfea2d42f43a2c16a98a32a6bc2f42abcde121132dbfc2f023 \ - --hash=sha256:94d0de65e37f5677165725f1fc7fb1616b9542d42a9832a9a0bdcba0ed68b63b \ - --hash=sha256:9867206093d7283d7de01bd2bf60389eb4d19b67306a0a763d1a8a4dbe2fb7c3 \ - --hash=sha256:9ee3c992b93e26c2ae827404a626138588e30bdabaaf7aa3aa25082a4e718790 \ - --hash=sha256:a4f8af277bb527fa3d56b216bda4da931b36b2d3fe416b6fc1744072b2c1dbd9 \ - --hash=sha256:ab9f19460dfa4c5dd25431b75bee28b5f018bf43476858d64b1aa1046196a2a0 \ - --hash=sha256:ac43c1821ba81e9344d818c5feed574a17f51fca27976ff7d022645c378fbbf5 \ - --hash=sha256:af5a011609206e390b44847da32463437505bf55fd8985e7a91c52d9da338d4b \ - --hash=sha256:b0975748bb6ec55b6d0f6665313c2cf7af6f536221dccd5879b967d76f6e7899 \ - --hash=sha256:b4963dad6cf28bfe0b61c3265d1c74a26a7605df3445bfcd3ba25de012330b2d \ - --hash=sha256:b7d3a484ace91ed827aa2ef3b44895e2ec106031012f14d28bd11a55f24fa734 \ - --hash=sha256:bd3c478a4a574f412efc58ba7e09ab4cd83484c545746a01601636e87e3dbf23 \ - --hash=sha256:c9e2dcb7f851f020232b991c226c5678dc07090256e929e45a89538d82f71d2e \ - --hash=sha256:d25c8eeb4720da41e7afbc404891e3a945b8bb6d5230e4c53d23ac4f4f9fc52c \ - --hash=sha256:dc8c03d0c5c10c200441ffb4cce46d869d9e5c4ef007f55856751dc288a2dffd \ - --hash=sha256:ec58e84d625553d191a23d5988a19c3ebfed519fff2a8b844223e3f074152163 \ - --hash=sha256:eda0719b29792f0fea04a853377cfff934660cb6cd72a0a0eeba7a1f0df4a16e \ - --hash=sha256:edde82ce3007a64e8434ccaf1b53271da4f255224d77b880b59e7d6d73df90c8 \ - --hash=sha256:f36722144bc0a5068934e51dca5a38a5b4daac1be84f4423244277e4baf24e7a \ - --hash=sha256:f8bb00ced04a8feff05989996db47906673ed45b11d86ad5ce892b5741e5f9dd \ - --hash=sha256:f98fc5750aac2d63d482909184aac72a979bfd123b112ec53fd365104ea15b1c \ - --hash=sha256:ff5b75f94101beaa373f1511319580a010f6e03458ee51b1a386d7de5331440a - # via -r requirements.txt.in +rpds-py==0.22.3 \ + --hash=sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518 \ + --hash=sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059 \ + --hash=sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61 \ + --hash=sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5 \ + --hash=sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9 \ + --hash=sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543 \ + --hash=sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2 \ + --hash=sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a \ + --hash=sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d \ + --hash=sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56 \ + --hash=sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d \ + --hash=sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd \ + --hash=sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b \ + --hash=sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4 \ + --hash=sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99 \ + --hash=sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d \ + --hash=sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd \ + --hash=sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe \ + --hash=sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1 \ + --hash=sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e \ + --hash=sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f \ + --hash=sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3 \ + --hash=sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca \ + --hash=sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d \ + --hash=sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e \ + --hash=sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc \ + --hash=sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea \ + --hash=sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38 \ + --hash=sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b \ + --hash=sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c \ + --hash=sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff \ + --hash=sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723 \ + --hash=sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e \ + --hash=sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493 \ + --hash=sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6 \ + --hash=sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83 \ + --hash=sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091 \ + --hash=sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1 \ + --hash=sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627 \ + --hash=sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1 \ + --hash=sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728 \ + --hash=sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16 \ + --hash=sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c \ + --hash=sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45 \ + --hash=sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7 \ + --hash=sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a \ + --hash=sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730 \ + --hash=sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967 \ + --hash=sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25 \ + --hash=sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24 \ + --hash=sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055 \ + --hash=sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d \ + --hash=sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0 \ + --hash=sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e \ + --hash=sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7 \ + --hash=sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c \ + --hash=sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f \ + --hash=sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd \ + --hash=sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652 \ + --hash=sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8 \ + --hash=sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11 \ + --hash=sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333 \ + --hash=sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96 \ + --hash=sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64 \ + --hash=sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b \ + --hash=sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e \ + --hash=sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c \ + --hash=sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9 \ + --hash=sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec \ + --hash=sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb \ + --hash=sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37 \ + --hash=sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad \ + --hash=sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9 \ + --hash=sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c \ + --hash=sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf \ + --hash=sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4 \ + --hash=sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f \ + --hash=sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d \ + --hash=sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09 \ + --hash=sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d \ + --hash=sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566 \ + --hash=sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74 \ + --hash=sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338 \ + --hash=sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15 \ + --hash=sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c \ + --hash=sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648 \ + --hash=sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84 \ + --hash=sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3 \ + --hash=sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123 \ + --hash=sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520 \ + --hash=sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831 \ + --hash=sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e \ + --hash=sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf \ + --hash=sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b \ + --hash=sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2 \ + --hash=sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3 \ + --hash=sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130 \ + --hash=sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b \ + --hash=sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de \ + --hash=sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5 \ + --hash=sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d \ + --hash=sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00 \ + --hash=sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e + # via + # jsonschema + # referencing +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via -r requirements.in +tomli==2.2.1 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 + # via -r requirements.in +typing-extensions==4.14.1 \ + --hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \ + --hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76 + # via -r requirements.in +urllib3==2.3.0 \ + --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ + --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d + # via + # docker + # requests +zstandard==0.23.0 \ + --hash=sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473 \ + --hash=sha256:0a7f0804bb3799414af278e9ad51be25edf67f78f916e08afdb983e74161b916 \ + --hash=sha256:11e3bf3c924853a2d5835b24f03eeba7fc9b07d8ca499e247e06ff5676461a15 \ + --hash=sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072 \ + --hash=sha256:1516c8c37d3a053b01c1c15b182f3b5f5eef19ced9b930b684a73bad121addf4 \ + --hash=sha256:157e89ceb4054029a289fb504c98c6a9fe8010f1680de0201b3eb5dc20aa6d9e \ + --hash=sha256:1bfe8de1da6d104f15a60d4a8a768288f66aa953bbe00d027398b93fb9680b26 \ + --hash=sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8 \ + --hash=sha256:1fd7e0f1cfb70eb2f95a19b472ee7ad6d9a0a992ec0ae53286870c104ca939e5 \ + --hash=sha256:203d236f4c94cd8379d1ea61db2fce20730b4c38d7f1c34506a31b34edc87bdd \ + --hash=sha256:27d3ef2252d2e62476389ca8f9b0cf2bbafb082a3b6bfe9d90cbcbb5529ecf7c \ + --hash=sha256:29a2bc7c1b09b0af938b7a8343174b987ae021705acabcbae560166567f5a8db \ + --hash=sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5 \ + --hash=sha256:2ef3775758346d9ac6214123887d25c7061c92afe1f2b354f9388e9e4d48acfc \ + --hash=sha256:2f146f50723defec2975fb7e388ae3a024eb7151542d1599527ec2aa9cacb152 \ + --hash=sha256:2fb4535137de7e244c230e24f9d1ec194f61721c86ebea04e1581d9d06ea1269 \ + --hash=sha256:32ba3b5ccde2d581b1e6aa952c836a6291e8435d788f656fe5976445865ae045 \ + --hash=sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e \ + --hash=sha256:379b378ae694ba78cef921581ebd420c938936a153ded602c4fea612b7eaa90d \ + --hash=sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a \ + --hash=sha256:3aa014d55c3af933c1315eb4bb06dd0459661cc0b15cd61077afa6489bec63bb \ + --hash=sha256:4051e406288b8cdbb993798b9a45c59a4896b6ecee2f875424ec10276a895740 \ + --hash=sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105 \ + --hash=sha256:43da0f0092281bf501f9c5f6f3b4c975a8a0ea82de49ba3f7100e64d422a1274 \ + --hash=sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2 \ + --hash=sha256:48ef6a43b1846f6025dde6ed9fee0c24e1149c1c25f7fb0a0585572b2f3adc58 \ + --hash=sha256:50a80baba0285386f97ea36239855f6020ce452456605f262b2d33ac35c7770b \ + --hash=sha256:519fbf169dfac1222a76ba8861ef4ac7f0530c35dd79ba5727014613f91613d4 \ + --hash=sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db \ + --hash=sha256:53ea7cdc96c6eb56e76bb06894bcfb5dfa93b7adcf59d61c6b92674e24e2dd5e \ + --hash=sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9 \ + --hash=sha256:59556bf80a7094d0cfb9f5e50bb2db27fefb75d5138bb16fb052b61b0e0eeeb0 \ + --hash=sha256:5d41d5e025f1e0bccae4928981e71b2334c60f580bdc8345f824e7c0a4c2a813 \ + --hash=sha256:61062387ad820c654b6a6b5f0b94484fa19515e0c5116faf29f41a6bc91ded6e \ + --hash=sha256:61f89436cbfede4bc4e91b4397eaa3e2108ebe96d05e93d6ccc95ab5714be512 \ + --hash=sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0 \ + --hash=sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b \ + --hash=sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48 \ + --hash=sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a \ + --hash=sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772 \ + --hash=sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed \ + --hash=sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373 \ + --hash=sha256:752bf8a74412b9892f4e5b58f2f890a039f57037f52c89a740757ebd807f33ea \ + --hash=sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd \ + --hash=sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f \ + --hash=sha256:77da4c6bfa20dd5ea25cbf12c76f181a8e8cd7ea231c673828d0386b1740b8dc \ + --hash=sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23 \ + --hash=sha256:80080816b4f52a9d886e67f1f96912891074903238fe54f2de8b786f86baded2 \ + --hash=sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db \ + --hash=sha256:82d17e94d735c99621bf8ebf9995f870a6b3e6d14543b99e201ae046dfe7de70 \ + --hash=sha256:837bb6764be6919963ef41235fd56a6486b132ea64afe5fafb4cb279ac44f259 \ + --hash=sha256:84433dddea68571a6d6bd4fbf8ff398236031149116a7fff6f777ff95cad3df9 \ + --hash=sha256:8c24f21fa2af4bb9f2c492a86fe0c34e6d2c63812a839590edaf177b7398f700 \ + --hash=sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003 \ + --hash=sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba \ + --hash=sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a \ + --hash=sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c \ + --hash=sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90 \ + --hash=sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690 \ + --hash=sha256:a05e6d6218461eb1b4771d973728f0133b2a4613a6779995df557f70794fd60f \ + --hash=sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840 \ + --hash=sha256:a4ae99c57668ca1e78597d8b06d5af837f377f340f4cce993b551b2d7731778d \ + --hash=sha256:a8c86881813a78a6f4508ef9daf9d4995b8ac2d147dcb1a450448941398091c9 \ + --hash=sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35 \ + --hash=sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd \ + --hash=sha256:ab19a2d91963ed9e42b4e8d77cd847ae8381576585bad79dbd0a8837a9f6620a \ + --hash=sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea \ + --hash=sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1 \ + --hash=sha256:b2170c7e0367dde86a2647ed5b6f57394ea7f53545746104c6b09fc1f4223573 \ + --hash=sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09 \ + --hash=sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094 \ + --hash=sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78 \ + --hash=sha256:b8c0bd73aeac689beacd4e7667d48c299f61b959475cdbb91e7d3d88d27c56b9 \ + --hash=sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5 \ + --hash=sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9 \ + --hash=sha256:c16842b846a8d2a145223f520b7e18b57c8f476924bda92aeee3a88d11cfc391 \ + --hash=sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847 \ + --hash=sha256:c7c517d74bea1a6afd39aa612fa025e6b8011982a0897768a2f7c8ab4ebb78a2 \ + --hash=sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c \ + --hash=sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2 \ + --hash=sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057 \ + --hash=sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20 \ + --hash=sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d \ + --hash=sha256:dc5d1a49d3f8262be192589a4b72f0d03b72dcf46c51ad5852a4fdc67be7b9e4 \ + --hash=sha256:e2d1a054f8f0a191004675755448d12be47fa9bebbcffa3cdf01db19f2d30a54 \ + --hash=sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171 \ + --hash=sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e \ + --hash=sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160 \ + --hash=sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b \ + --hash=sha256:f8346bfa098532bc1fb6c7ef06783e969d87a99dd1d2a5a18a892c1d7a643c58 \ + --hash=sha256:f83fa6cae3fff8e98691248c9320356971b59678a17f20656a9e59cd32cee6d8 \ + --hash=sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33 \ + --hash=sha256:fb2b1ecfef1e67897d336de3a0e3f52478182d6a47eda86cbd42504c5cbd009a \ + --hash=sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880 \ + --hash=sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca \ + --hash=sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b \ + --hash=sha256:fe3b385d996ee0822fd46528d9f0443b880d4d05528fd26a9119a54ec3f91c69 + # via -r requirements.in diff --git a/requirements.txt.in b/requirements.txt.in deleted file mode 100644 index c3d2c153e..000000000 --- a/requirements.txt.in +++ /dev/null @@ -1,6 +0,0 @@ -docker -jinja2 -PyYAML -# Undeclared dependency in docker 5.0 package. -six -zstandard diff --git a/requirements.win-arm64.txt b/requirements.win-arm64.txt new file mode 100644 index 000000000..411d63cfc --- /dev/null +++ b/requirements.win-arm64.txt @@ -0,0 +1,593 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --python-platform aarch64-pc-windows-msvc --generate-hashes requirements.in -o requirements.win-arm64.txt +attrs==25.4.0 \ + --hash=sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11 \ + --hash=sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373 + # via + # jsonschema + # referencing +certifi==2025.11.12 \ + --hash=sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b \ + --hash=sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316 + # via requests +charset-normalizer==3.4.4 \ + --hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \ + --hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \ + --hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \ + --hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \ + --hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \ + --hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \ + --hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \ + --hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \ + --hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \ + --hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \ + --hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \ + --hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \ + --hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \ + --hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \ + --hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \ + --hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \ + --hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \ + --hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \ + --hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \ + --hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \ + --hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \ + --hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \ + --hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \ + --hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \ + --hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \ + --hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \ + --hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \ + --hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \ + --hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \ + --hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \ + --hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \ + --hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \ + --hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \ + --hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \ + --hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \ + --hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \ + --hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \ + --hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \ + --hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \ + --hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \ + --hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \ + --hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \ + --hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \ + --hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \ + --hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \ + --hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \ + --hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \ + --hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \ + --hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \ + --hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \ + --hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \ + --hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \ + --hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \ + --hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \ + --hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \ + --hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \ + --hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \ + --hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \ + --hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \ + --hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \ + --hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \ + --hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \ + --hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \ + --hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \ + --hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \ + --hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \ + --hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \ + --hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \ + --hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \ + --hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \ + --hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \ + --hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \ + --hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \ + --hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \ + --hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \ + --hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \ + --hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \ + --hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \ + --hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \ + --hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \ + --hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \ + --hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \ + --hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \ + --hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \ + --hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \ + --hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \ + --hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \ + --hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \ + --hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \ + --hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \ + --hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \ + --hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \ + --hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \ + --hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \ + --hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \ + --hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \ + --hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \ + --hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \ + --hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \ + --hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \ + --hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \ + --hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \ + --hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \ + --hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \ + --hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \ + --hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \ + --hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \ + --hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \ + --hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \ + --hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \ + --hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \ + --hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \ + --hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608 + # via requests +docker==7.1.0 \ + --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ + --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 + # via -r requirements.in +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via -r requirements.in +jsonschema==4.25.1 \ + --hash=sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63 \ + --hash=sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85 + # via -r requirements.in +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via jinja2 +pywin32==311 \ + --hash=sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b \ + --hash=sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151 \ + --hash=sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87 \ + --hash=sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503 \ + --hash=sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d \ + --hash=sha256:6c6f2969607b5023b0d9ce2541f8d2cbb01c4f46bc87456017cf63b73f1e2d8c \ + --hash=sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d \ + --hash=sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31 \ + --hash=sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b \ + --hash=sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a \ + --hash=sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42 \ + --hash=sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2 \ + --hash=sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b \ + --hash=sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee \ + --hash=sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067 \ + --hash=sha256:c8015b09fb9a5e188f83b7b04de91ddca4658cee2ae6f3bc483f0b21a77ef6cd \ + --hash=sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3 \ + --hash=sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91 \ + --hash=sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852 \ + --hash=sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d + # via docker +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via -r requirements.in +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # jsonschema + # jsonschema-specifications +requests==2.32.5 \ + --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ + --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf + # via docker +rpds-py==0.29.0 \ + --hash=sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9 \ + --hash=sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2 \ + --hash=sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea \ + --hash=sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7 \ + --hash=sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014 \ + --hash=sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed \ + --hash=sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d \ + --hash=sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310 \ + --hash=sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61 \ + --hash=sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb \ + --hash=sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef \ + --hash=sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf \ + --hash=sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6 \ + --hash=sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3 \ + --hash=sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55 \ + --hash=sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2 \ + --hash=sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7 \ + --hash=sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a \ + --hash=sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea \ + --hash=sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4 \ + --hash=sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b \ + --hash=sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967 \ + --hash=sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b \ + --hash=sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d \ + --hash=sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e \ + --hash=sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe \ + --hash=sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63 \ + --hash=sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22 \ + --hash=sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c \ + --hash=sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c \ + --hash=sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8 \ + --hash=sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1 \ + --hash=sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760 \ + --hash=sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383 \ + --hash=sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e \ + --hash=sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688 \ + --hash=sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32 \ + --hash=sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756 \ + --hash=sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c \ + --hash=sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c \ + --hash=sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113 \ + --hash=sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154 \ + --hash=sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2 \ + --hash=sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60 \ + --hash=sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8 \ + --hash=sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2 \ + --hash=sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181 \ + --hash=sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a \ + --hash=sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866 \ + --hash=sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e \ + --hash=sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545 \ + --hash=sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb \ + --hash=sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2 \ + --hash=sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3 \ + --hash=sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0 \ + --hash=sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce \ + --hash=sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a \ + --hash=sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318 \ + --hash=sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae \ + --hash=sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b \ + --hash=sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9 \ + --hash=sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a \ + --hash=sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a \ + --hash=sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed \ + --hash=sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311 \ + --hash=sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212 \ + --hash=sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0 \ + --hash=sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee \ + --hash=sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17 \ + --hash=sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4 \ + --hash=sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79 \ + --hash=sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5 \ + --hash=sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437 \ + --hash=sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9 \ + --hash=sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5 \ + --hash=sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec \ + --hash=sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954 \ + --hash=sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95 \ + --hash=sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588 \ + --hash=sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca \ + --hash=sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d \ + --hash=sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949 \ + --hash=sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7 \ + --hash=sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e \ + --hash=sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d \ + --hash=sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977 \ + --hash=sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b \ + --hash=sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7 \ + --hash=sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d \ + --hash=sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c \ + --hash=sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761 \ + --hash=sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7 \ + --hash=sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950 \ + --hash=sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94 \ + --hash=sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f \ + --hash=sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f \ + --hash=sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b \ + --hash=sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c \ + --hash=sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295 \ + --hash=sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83 \ + --hash=sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c \ + --hash=sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10 \ + --hash=sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a \ + --hash=sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0 \ + --hash=sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43 \ + --hash=sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1 \ + --hash=sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352 \ + --hash=sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd \ + --hash=sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f \ + --hash=sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b \ + --hash=sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626 \ + --hash=sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19 \ + --hash=sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244 \ + --hash=sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808 \ + --hash=sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359 + # via + # jsonschema + # referencing +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via -r requirements.in +tomli==2.2.1 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 + # via -r requirements.in +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via -r requirements.in +urllib3==2.5.0 \ + --hash=sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760 \ + --hash=sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc + # via + # docker + # requests +zstandard==0.25.0 \ + --hash=sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64 \ + --hash=sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a \ + --hash=sha256:05353cef599a7b0b98baca9b068dd36810c3ef0f42bf282583f438caf6ddcee3 \ + --hash=sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f \ + --hash=sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6 \ + --hash=sha256:07b527a69c1e1c8b5ab1ab14e2afe0675614a09182213f21a0717b62027b5936 \ + --hash=sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431 \ + --hash=sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250 \ + --hash=sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa \ + --hash=sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f \ + --hash=sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851 \ + --hash=sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3 \ + --hash=sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9 \ + --hash=sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6 \ + --hash=sha256:19796b39075201d51d5f5f790bf849221e58b48a39a5fc74837675d8bafc7362 \ + --hash=sha256:1cd5da4d8e8ee0e88be976c294db744773459d51bb32f707a0f166e5ad5c8649 \ + --hash=sha256:1f3689581a72eaba9131b1d9bdbfe520ccd169999219b41000ede2fca5c1bfdb \ + --hash=sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5 \ + --hash=sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439 \ + --hash=sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137 \ + --hash=sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa \ + --hash=sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd \ + --hash=sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701 \ + --hash=sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0 \ + --hash=sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043 \ + --hash=sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1 \ + --hash=sha256:37daddd452c0ffb65da00620afb8e17abd4adaae6ce6310702841760c2c26860 \ + --hash=sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611 \ + --hash=sha256:3b870ce5a02d4b22286cf4944c628e0f0881b11b3f14667c1d62185a99e04f53 \ + --hash=sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b \ + --hash=sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088 \ + --hash=sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e \ + --hash=sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa \ + --hash=sha256:4b14abacf83dfb5c25eb4e4a79520de9e7e205f72c9ee7702f91233ae57d33a2 \ + --hash=sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0 \ + --hash=sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7 \ + --hash=sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf \ + --hash=sha256:51526324f1b23229001eb3735bc8c94f9c578b1bd9e867a0a646a3b17109f388 \ + --hash=sha256:53e08b2445a6bc241261fea89d065536f00a581f02535f8122eba42db9375530 \ + --hash=sha256:53f94448fe5b10ee75d246497168e5825135d54325458c4bfffbaafabcc0a577 \ + --hash=sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902 \ + --hash=sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc \ + --hash=sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98 \ + --hash=sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a \ + --hash=sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097 \ + --hash=sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea \ + --hash=sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09 \ + --hash=sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb \ + --hash=sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7 \ + --hash=sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74 \ + --hash=sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b \ + --hash=sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b \ + --hash=sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b \ + --hash=sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91 \ + --hash=sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150 \ + --hash=sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049 \ + --hash=sha256:89c4b48479a43f820b749df49cd7ba2dbc2b1b78560ecb5ab52985574fd40b27 \ + --hash=sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a \ + --hash=sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00 \ + --hash=sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd \ + --hash=sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072 \ + --hash=sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c \ + --hash=sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c \ + --hash=sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065 \ + --hash=sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512 \ + --hash=sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1 \ + --hash=sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f \ + --hash=sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2 \ + --hash=sha256:a51ff14f8017338e2f2e5dab738ce1ec3b5a851f23b18c1ae1359b1eecbee6df \ + --hash=sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab \ + --hash=sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7 \ + --hash=sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b \ + --hash=sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550 \ + --hash=sha256:b9af1fe743828123e12b41dd8091eca1074d0c1569cc42e6e1eee98027f2bbd0 \ + --hash=sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea \ + --hash=sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277 \ + --hash=sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2 \ + --hash=sha256:c2ba942c94e0691467ab901fc51b6f2085ff48f2eea77b1a48240f011e8247c7 \ + --hash=sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778 \ + --hash=sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859 \ + --hash=sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d \ + --hash=sha256:d8c56bb4e6c795fc77d74d8e8b80846e1fb8292fc0b5060cd8131d522974b751 \ + --hash=sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12 \ + --hash=sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2 \ + --hash=sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d \ + --hash=sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0 \ + --hash=sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3 \ + --hash=sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd \ + --hash=sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e \ + --hash=sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f \ + --hash=sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e \ + --hash=sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94 \ + --hash=sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708 \ + --hash=sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313 \ + --hash=sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4 \ + --hash=sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c \ + --hash=sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344 \ + --hash=sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551 \ + --hash=sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01 + # via -r requirements.in diff --git a/requirements.win.txt b/requirements.win.txt index 8c07b9cbd..c2cbcae4b 100644 --- a/requirements.win.txt +++ b/requirements.win.txt @@ -1,193 +1,593 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile --generate-hashes --output-file=requirements.win.txt requirements.txt.in -# -certifi==2021.5.30 \ - --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ - --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 +# This file was autogenerated by uv via the following command: +# uv pip compile --python-platform windows --generate-hashes requirements.in -o requirements.win.txt +attrs==25.4.0 \ + --hash=sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11 \ + --hash=sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373 + # via + # jsonschema + # referencing +certifi==2025.11.12 \ + --hash=sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b \ + --hash=sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316 # via requests -charset-normalizer==2.0.4 \ - --hash=sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b \ - --hash=sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3 +charset-normalizer==3.4.4 \ + --hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \ + --hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \ + --hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \ + --hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \ + --hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \ + --hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \ + --hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \ + --hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \ + --hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \ + --hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \ + --hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \ + --hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \ + --hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \ + --hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \ + --hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \ + --hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \ + --hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \ + --hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \ + --hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \ + --hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \ + --hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \ + --hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \ + --hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \ + --hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \ + --hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \ + --hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \ + --hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \ + --hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \ + --hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \ + --hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \ + --hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \ + --hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \ + --hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \ + --hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \ + --hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \ + --hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \ + --hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \ + --hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \ + --hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \ + --hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \ + --hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \ + --hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \ + --hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \ + --hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \ + --hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \ + --hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \ + --hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \ + --hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \ + --hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \ + --hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \ + --hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \ + --hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \ + --hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \ + --hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \ + --hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \ + --hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \ + --hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \ + --hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \ + --hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \ + --hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \ + --hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \ + --hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \ + --hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \ + --hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \ + --hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \ + --hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \ + --hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \ + --hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \ + --hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \ + --hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \ + --hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \ + --hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \ + --hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \ + --hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \ + --hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \ + --hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \ + --hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \ + --hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \ + --hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \ + --hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \ + --hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \ + --hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \ + --hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \ + --hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \ + --hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \ + --hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \ + --hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \ + --hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \ + --hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \ + --hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \ + --hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \ + --hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \ + --hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \ + --hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \ + --hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \ + --hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \ + --hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \ + --hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \ + --hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \ + --hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \ + --hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \ + --hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \ + --hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \ + --hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \ + --hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \ + --hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \ + --hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \ + --hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \ + --hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \ + --hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \ + --hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \ + --hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \ + --hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608 # via requests -docker==5.0.0 \ - --hash=sha256:3e8bc47534e0ca9331d72c32f2881bb13b93ded0bcdeab3c833fb7cf61c0a9a5 \ - --hash=sha256:fc961d622160e8021c10d1bcabc388c57d55fb1f917175afbe24af442e6879bd - # via -r requirements.txt.in -idna==3.2 \ - --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ - --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 +docker==7.1.0 \ + --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ + --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 + # via -r requirements.in +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 # via requests -jinja2==3.0.1 \ - --hash=sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4 \ - --hash=sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4 - # via -r requirements.txt.in -markupsafe==2.0.1 \ - --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ - --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \ - --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ - --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ - --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ - --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \ - --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ - --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \ - --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ - --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \ - --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \ - --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \ - --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ - --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \ - --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \ - --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ - --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \ - --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ - --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ - --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ - --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ - --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \ - --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \ - --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \ - --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \ - --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ - --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ - --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ - --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ - --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ - --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \ - --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ - --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ - --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ - --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ - --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \ - --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ - --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ - --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \ - --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ - --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ - --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \ - --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ - --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \ - --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \ - --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ - --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \ - --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \ - --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \ - --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ - --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ - --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ - --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ - --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872 +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via -r requirements.in +jsonschema==4.25.1 \ + --hash=sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63 \ + --hash=sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85 + # via -r requirements.in +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 # via jinja2 -pywin32==227 \ - --hash=sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be \ - --hash=sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511 \ - --hash=sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0 \ - --hash=sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507 \ - --hash=sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116 \ - --hash=sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e \ - --hash=sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc \ - --hash=sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2 \ - --hash=sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4 \ - --hash=sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295 \ - --hash=sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c \ - --hash=sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa +pywin32==311 \ + --hash=sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b \ + --hash=sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151 \ + --hash=sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87 \ + --hash=sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503 \ + --hash=sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d \ + --hash=sha256:6c6f2969607b5023b0d9ce2541f8d2cbb01c4f46bc87456017cf63b73f1e2d8c \ + --hash=sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d \ + --hash=sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31 \ + --hash=sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b \ + --hash=sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a \ + --hash=sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42 \ + --hash=sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2 \ + --hash=sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b \ + --hash=sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee \ + --hash=sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067 \ + --hash=sha256:c8015b09fb9a5e188f83b7b04de91ddca4658cee2ae6f3bc483f0b21a77ef6cd \ + --hash=sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3 \ + --hash=sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91 \ + --hash=sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852 \ + --hash=sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d # via docker -pyyaml==5.4.1 \ - --hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \ - --hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \ - --hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \ - --hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \ - --hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \ - --hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \ - --hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \ - --hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \ - --hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \ - --hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \ - --hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e \ - --hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \ - --hash=sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347 \ - --hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \ - --hash=sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541 \ - --hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \ - --hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \ - --hash=sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc \ - --hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \ - --hash=sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa \ - --hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \ - --hash=sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122 \ - --hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \ - --hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \ - --hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \ - --hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \ - --hash=sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247 \ - --hash=sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6 \ - --hash=sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0 - # via -r requirements.txt.in -requests==2.26.0 \ - --hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \ - --hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7 +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via -r requirements.in +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # jsonschema + # jsonschema-specifications +requests==2.32.5 \ + --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ + --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf # via docker -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via -r requirements.txt.in -urllib3==1.26.6 \ - --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ - --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f - # via requests -websocket-client==1.2.1 \ - --hash=sha256:0133d2f784858e59959ce82ddac316634229da55b498aac311f1620567a710ec \ - --hash=sha256:8dfb715d8a992f5712fff8c843adae94e22b22a99b2c5e6b0ec4a1a981cc4e0d - # via docker -zstandard==0.15.2 \ - --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ - --hash=sha256:1faefe33e3d6870a4dce637bcb41f7abb46a1872a595ecc7b034016081c37543 \ - --hash=sha256:1fb23b1754ce834a3a1a1e148cc2faad76eeadf9d889efe5e8199d3fb839d3c6 \ - --hash=sha256:22f127ff5da052ffba73af146d7d61db874f5edb468b36c9cb0b857316a21b3d \ - --hash=sha256:2353b61f249a5fc243aae3caa1207c80c7e6919a58b1f9992758fa496f61f839 \ - --hash=sha256:24cdcc6f297f7c978a40fb7706877ad33d8e28acc1786992a52199502d6da2a4 \ - --hash=sha256:31e35790434da54c106f05fa93ab4d0fab2798a6350e8a73928ec602e8505836 \ - --hash=sha256:3547ff4eee7175d944a865bbdf5529b0969c253e8a148c287f0668fe4eb9c935 \ - --hash=sha256:378ac053c0cfc74d115cbb6ee181540f3e793c7cca8ed8cd3893e338af9e942c \ - --hash=sha256:3e1cd2db25117c5b7c7e86a17cde6104a93719a9df7cb099d7498e4c1d13ee5c \ - --hash=sha256:3fe469a887f6142cc108e44c7f42c036e43620ebaf500747be2317c9f4615d4f \ - --hash=sha256:4800ab8ec94cbf1ed09c2b4686288750cab0642cb4d6fba2a56db66b923aeb92 \ - --hash=sha256:52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f \ - --hash=sha256:5752f44795b943c99be367fee5edf3122a1690b0d1ecd1bd5ec94c7fd2c39c94 \ - --hash=sha256:5d53f02aeb8fdd48b88bc80bece82542d084fb1a7ba03bf241fd53b63aee4f22 \ - --hash=sha256:69b7a5720b8dfab9005a43c7ddb2e3ccacbb9a2442908ae4ed49dd51ab19698a \ - --hash=sha256:6cc162b5b6e3c40b223163a9ea86cd332bd352ddadb5fd142fc0706e5e4eaaff \ - --hash=sha256:6f5d0330bc992b1e267a1b69fbdbb5ebe8c3a6af107d67e14c7a5b1ede2c5945 \ - --hash=sha256:6ffadd48e6fe85f27ca3ca10cfd3ef3d0f933bef7316870285ffeb58d791ca9c \ - --hash=sha256:72a011678c654df8323aa7b687e3147749034fdbe994d346f139ab9702b59cea \ - --hash=sha256:77d26452676f471223571efd73131fd4a626622c7960458aab2763e025836fc5 \ - --hash=sha256:7a88cc773ffe55992ff7259a8df5fb3570168d7138c69aadba40142d0e5ce39a \ - --hash=sha256:7b16bd74ae7bfbaca407a127e11058b287a4267caad13bd41305a5e630472549 \ - --hash=sha256:855d95ec78b6f0ff66e076d5461bf12d09d8e8f7e2b3fc9de7236d1464fd730e \ - --hash=sha256:8baf7991547441458325ca8fafeae79ef1501cb4354022724f3edd62279c5b2b \ - --hash=sha256:8fb77dd152054c6685639d855693579a92f276b38b8003be5942de31d241ebfb \ - --hash=sha256:92d49cc3b49372cfea2d42f43a2c16a98a32a6bc2f42abcde121132dbfc2f023 \ - --hash=sha256:94d0de65e37f5677165725f1fc7fb1616b9542d42a9832a9a0bdcba0ed68b63b \ - --hash=sha256:9867206093d7283d7de01bd2bf60389eb4d19b67306a0a763d1a8a4dbe2fb7c3 \ - --hash=sha256:9ee3c992b93e26c2ae827404a626138588e30bdabaaf7aa3aa25082a4e718790 \ - --hash=sha256:a4f8af277bb527fa3d56b216bda4da931b36b2d3fe416b6fc1744072b2c1dbd9 \ - --hash=sha256:ab9f19460dfa4c5dd25431b75bee28b5f018bf43476858d64b1aa1046196a2a0 \ - --hash=sha256:ac43c1821ba81e9344d818c5feed574a17f51fca27976ff7d022645c378fbbf5 \ - --hash=sha256:af5a011609206e390b44847da32463437505bf55fd8985e7a91c52d9da338d4b \ - --hash=sha256:b0975748bb6ec55b6d0f6665313c2cf7af6f536221dccd5879b967d76f6e7899 \ - --hash=sha256:b4963dad6cf28bfe0b61c3265d1c74a26a7605df3445bfcd3ba25de012330b2d \ - --hash=sha256:b7d3a484ace91ed827aa2ef3b44895e2ec106031012f14d28bd11a55f24fa734 \ - --hash=sha256:bd3c478a4a574f412efc58ba7e09ab4cd83484c545746a01601636e87e3dbf23 \ - --hash=sha256:c9e2dcb7f851f020232b991c226c5678dc07090256e929e45a89538d82f71d2e \ - --hash=sha256:d25c8eeb4720da41e7afbc404891e3a945b8bb6d5230e4c53d23ac4f4f9fc52c \ - --hash=sha256:dc8c03d0c5c10c200441ffb4cce46d869d9e5c4ef007f55856751dc288a2dffd \ - --hash=sha256:ec58e84d625553d191a23d5988a19c3ebfed519fff2a8b844223e3f074152163 \ - --hash=sha256:eda0719b29792f0fea04a853377cfff934660cb6cd72a0a0eeba7a1f0df4a16e \ - --hash=sha256:edde82ce3007a64e8434ccaf1b53271da4f255224d77b880b59e7d6d73df90c8 \ - --hash=sha256:f36722144bc0a5068934e51dca5a38a5b4daac1be84f4423244277e4baf24e7a \ - --hash=sha256:f8bb00ced04a8feff05989996db47906673ed45b11d86ad5ce892b5741e5f9dd \ - --hash=sha256:f98fc5750aac2d63d482909184aac72a979bfd123b112ec53fd365104ea15b1c \ - --hash=sha256:ff5b75f94101beaa373f1511319580a010f6e03458ee51b1a386d7de5331440a - # via -r requirements.txt.in +rpds-py==0.29.0 \ + --hash=sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9 \ + --hash=sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2 \ + --hash=sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea \ + --hash=sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7 \ + --hash=sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014 \ + --hash=sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed \ + --hash=sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d \ + --hash=sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310 \ + --hash=sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61 \ + --hash=sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb \ + --hash=sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef \ + --hash=sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf \ + --hash=sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6 \ + --hash=sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3 \ + --hash=sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55 \ + --hash=sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2 \ + --hash=sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7 \ + --hash=sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a \ + --hash=sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea \ + --hash=sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4 \ + --hash=sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b \ + --hash=sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967 \ + --hash=sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b \ + --hash=sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d \ + --hash=sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e \ + --hash=sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe \ + --hash=sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63 \ + --hash=sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22 \ + --hash=sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c \ + --hash=sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c \ + --hash=sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8 \ + --hash=sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1 \ + --hash=sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760 \ + --hash=sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383 \ + --hash=sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e \ + --hash=sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688 \ + --hash=sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32 \ + --hash=sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756 \ + --hash=sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c \ + --hash=sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c \ + --hash=sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113 \ + --hash=sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154 \ + --hash=sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2 \ + --hash=sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60 \ + --hash=sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8 \ + --hash=sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2 \ + --hash=sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181 \ + --hash=sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a \ + --hash=sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866 \ + --hash=sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e \ + --hash=sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545 \ + --hash=sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb \ + --hash=sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2 \ + --hash=sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3 \ + --hash=sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0 \ + --hash=sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce \ + --hash=sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a \ + --hash=sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318 \ + --hash=sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae \ + --hash=sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b \ + --hash=sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9 \ + --hash=sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a \ + --hash=sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a \ + --hash=sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed \ + --hash=sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311 \ + --hash=sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212 \ + --hash=sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0 \ + --hash=sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee \ + --hash=sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17 \ + --hash=sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4 \ + --hash=sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79 \ + --hash=sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5 \ + --hash=sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437 \ + --hash=sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9 \ + --hash=sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5 \ + --hash=sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec \ + --hash=sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954 \ + --hash=sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95 \ + --hash=sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588 \ + --hash=sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca \ + --hash=sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d \ + --hash=sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949 \ + --hash=sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7 \ + --hash=sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e \ + --hash=sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d \ + --hash=sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977 \ + --hash=sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b \ + --hash=sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7 \ + --hash=sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d \ + --hash=sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c \ + --hash=sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761 \ + --hash=sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7 \ + --hash=sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950 \ + --hash=sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94 \ + --hash=sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f \ + --hash=sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f \ + --hash=sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b \ + --hash=sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c \ + --hash=sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295 \ + --hash=sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83 \ + --hash=sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c \ + --hash=sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10 \ + --hash=sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a \ + --hash=sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0 \ + --hash=sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43 \ + --hash=sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1 \ + --hash=sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352 \ + --hash=sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd \ + --hash=sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f \ + --hash=sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b \ + --hash=sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626 \ + --hash=sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19 \ + --hash=sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244 \ + --hash=sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808 \ + --hash=sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359 + # via + # jsonschema + # referencing +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via -r requirements.in +tomli==2.2.1 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 + # via -r requirements.in +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via -r requirements.in +urllib3==2.5.0 \ + --hash=sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760 \ + --hash=sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc + # via + # docker + # requests +zstandard==0.25.0 \ + --hash=sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64 \ + --hash=sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a \ + --hash=sha256:05353cef599a7b0b98baca9b068dd36810c3ef0f42bf282583f438caf6ddcee3 \ + --hash=sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f \ + --hash=sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6 \ + --hash=sha256:07b527a69c1e1c8b5ab1ab14e2afe0675614a09182213f21a0717b62027b5936 \ + --hash=sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431 \ + --hash=sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250 \ + --hash=sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa \ + --hash=sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f \ + --hash=sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851 \ + --hash=sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3 \ + --hash=sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9 \ + --hash=sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6 \ + --hash=sha256:19796b39075201d51d5f5f790bf849221e58b48a39a5fc74837675d8bafc7362 \ + --hash=sha256:1cd5da4d8e8ee0e88be976c294db744773459d51bb32f707a0f166e5ad5c8649 \ + --hash=sha256:1f3689581a72eaba9131b1d9bdbfe520ccd169999219b41000ede2fca5c1bfdb \ + --hash=sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5 \ + --hash=sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439 \ + --hash=sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137 \ + --hash=sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa \ + --hash=sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd \ + --hash=sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701 \ + --hash=sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0 \ + --hash=sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043 \ + --hash=sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1 \ + --hash=sha256:37daddd452c0ffb65da00620afb8e17abd4adaae6ce6310702841760c2c26860 \ + --hash=sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611 \ + --hash=sha256:3b870ce5a02d4b22286cf4944c628e0f0881b11b3f14667c1d62185a99e04f53 \ + --hash=sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b \ + --hash=sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088 \ + --hash=sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e \ + --hash=sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa \ + --hash=sha256:4b14abacf83dfb5c25eb4e4a79520de9e7e205f72c9ee7702f91233ae57d33a2 \ + --hash=sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0 \ + --hash=sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7 \ + --hash=sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf \ + --hash=sha256:51526324f1b23229001eb3735bc8c94f9c578b1bd9e867a0a646a3b17109f388 \ + --hash=sha256:53e08b2445a6bc241261fea89d065536f00a581f02535f8122eba42db9375530 \ + --hash=sha256:53f94448fe5b10ee75d246497168e5825135d54325458c4bfffbaafabcc0a577 \ + --hash=sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902 \ + --hash=sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc \ + --hash=sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98 \ + --hash=sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a \ + --hash=sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097 \ + --hash=sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea \ + --hash=sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09 \ + --hash=sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb \ + --hash=sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7 \ + --hash=sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74 \ + --hash=sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b \ + --hash=sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b \ + --hash=sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b \ + --hash=sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91 \ + --hash=sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150 \ + --hash=sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049 \ + --hash=sha256:89c4b48479a43f820b749df49cd7ba2dbc2b1b78560ecb5ab52985574fd40b27 \ + --hash=sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a \ + --hash=sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00 \ + --hash=sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd \ + --hash=sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072 \ + --hash=sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c \ + --hash=sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c \ + --hash=sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065 \ + --hash=sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512 \ + --hash=sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1 \ + --hash=sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f \ + --hash=sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2 \ + --hash=sha256:a51ff14f8017338e2f2e5dab738ce1ec3b5a851f23b18c1ae1359b1eecbee6df \ + --hash=sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab \ + --hash=sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7 \ + --hash=sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b \ + --hash=sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550 \ + --hash=sha256:b9af1fe743828123e12b41dd8091eca1074d0c1569cc42e6e1eee98027f2bbd0 \ + --hash=sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea \ + --hash=sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277 \ + --hash=sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2 \ + --hash=sha256:c2ba942c94e0691467ab901fc51b6f2085ff48f2eea77b1a48240f011e8247c7 \ + --hash=sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778 \ + --hash=sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859 \ + --hash=sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d \ + --hash=sha256:d8c56bb4e6c795fc77d74d8e8b80846e1fb8292fc0b5060cd8131d522974b751 \ + --hash=sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12 \ + --hash=sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2 \ + --hash=sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d \ + --hash=sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0 \ + --hash=sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3 \ + --hash=sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd \ + --hash=sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e \ + --hash=sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f \ + --hash=sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e \ + --hash=sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94 \ + --hash=sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708 \ + --hash=sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313 \ + --hash=sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4 \ + --hash=sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c \ + --hash=sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344 \ + --hash=sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551 \ + --hash=sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01 + # via -r requirements.in diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 000000000..c914f91a7 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,2 @@ +[lint] +select = ["F", "I", "B"] diff --git a/src/github.rs b/src/github.rs new file mode 100644 index 000000000..6f7dcfe21 --- /dev/null +++ b/src/github.rs @@ -0,0 +1,600 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use { + crate::release::{ + RELEASE_TRIPLES, bootstrap_llvm, build_wanted_filenames, produce_install_only, + produce_install_only_stripped, + }, + anyhow::{Context, Result, anyhow}, + bytes::Bytes, + clap::ArgMatches, + futures::StreamExt, + octocrab::{ + Octocrab, OctocrabBuilder, + models::{repos::Release, workflows::WorkflowListArtifact}, + params::actions::ArchiveFormat, + }, + rayon::prelude::*, + reqwest::{Client, StatusCode}, + reqwest_retry::{ + RetryPolicy, Retryable, RetryableStrategy, default_on_request_failure, + policies::ExponentialBackoff, + }, + sha2::{Digest, Sha256}, + std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + io::Read, + path::PathBuf, + str::FromStr, + time::{Duration, SystemTime}, + }, + url::Url, + zip::ZipArchive, +}; + +/// A retry strategy for GitHub uploads. +struct GitHubUploadRetryStrategy; +impl RetryableStrategy for GitHubUploadRetryStrategy { + fn handle( + &self, + res: &std::result::Result, + ) -> Option { + match res { + // Retry on 403s, these indicate a transient failure. + Ok(success) if success.status() == StatusCode::FORBIDDEN => Some(Retryable::Transient), + Ok(_) => None, + Err(error) => default_on_request_failure(error), + } + } +} + +async fn fetch_artifact( + client: &Octocrab, + org: &str, + repo: &str, + artifact: WorkflowListArtifact, +) -> Result { + println!("downloading artifact {}", artifact.name); + + let res = client + .actions() + .download_artifact(org, repo, artifact.id, ArchiveFormat::Zip) + .await?; + + Ok(res) +} + +enum UploadSource { + Filename(PathBuf), + Data(Bytes), +} + +#[allow(clippy::too_many_arguments)] +async fn upload_release_artifact( + client: &Client, + retry_policy: &impl RetryPolicy, + retryable_strategy: &impl RetryableStrategy, + auth_token: String, + release: &Release, + filename: String, + body: UploadSource, + dry_run: bool, +) -> Result<()> { + if release.assets.iter().any(|asset| asset.name == filename) { + println!("release asset {filename} already present; skipping"); + return Ok(()); + } + + let mut url = Url::parse(&release.upload_url)?; + let path = url.path().to_string(); + + if let Some(path) = path.strip_suffix("%7B") { + url.set_path(path); + } + + url.query_pairs_mut().clear().append_pair("name", &filename); + + println!("uploading to {url}"); + + if dry_run { + return Ok(()); + } + + // Octocrab's high-level API for uploading release artifacts doesn't yet support streaming + // bodies, and their low-level API isn't more helpful than using our own HTTP client. + // + // Because we are streaming the body, we can't use the standard retry middleware for reqwest + // (see e.g. https://github.com/seanmonstar/reqwest/issues/2416), so we have to recreate the + // request on each retry and handle the retry logic ourself. This logic is inspired by + // uv/crates/uv-publish/src/lib.rs (which has the same problem), which in turn is inspired by + // reqwest-middleware/reqwest-retry/src/middleware.rs. + // + // (While Octocrab's API would work fine for the non-streaming case, we just use this function + // for both cases so that we can make a homogeneous Vec later in the file.) + + let mut n_past_retries = 0; + let start_time = SystemTime::now(); + let response = loop { + let request = client + .put(url.clone()) + .timeout(Duration::from_secs(60)) + .header("Authorization", format!("Bearer {auth_token}")) + .header("Content-Type", "application/octet-stream"); + let request = match body { + UploadSource::Filename(ref path) => { + let file = tokio::fs::File::open(&path).await?; + let len = file.metadata().await?.len(); + request.header("Content-Length", len).body(file) + } + UploadSource::Data(ref bytes) => request + .header("Content-Length", bytes.len()) + .body(bytes.clone()), + }; + let result = request.send().await.map_err(|e| e.into()); + + if retryable_strategy.handle(&result) == Some(Retryable::Transient) { + let retry_decision = retry_policy.should_retry(start_time, n_past_retries); + if let reqwest_retry::RetryDecision::Retry { execute_after } = retry_decision { + println!("retrying upload to {url} after {result:?}"); + let duration = execute_after + .duration_since(SystemTime::now()) + .unwrap_or_else(|_| Duration::default()); + tokio::time::sleep(duration).await; + n_past_retries += 1; + continue; + } + } + break result?; + }; + + if !response.status().is_success() { + return Err(anyhow!("HTTP {}", response.status())); + } + + Ok(()) +} + +fn new_github_client(args: &ArgMatches) -> Result<(Octocrab, String)> { + let token = args + .get_one::("token") + .expect("token should be specified") + .to_string(); + let github_uri = args.get_one::("github-uri"); + + let mut builder = OctocrabBuilder::new().personal_token(token.clone()); + if let Some(github_uri) = github_uri { + builder = builder.base_uri(github_uri.clone())?; + } + Ok((builder.build()?, token)) +} + +async fn get_draft_release_by_tag( + client: &Octocrab, + organization: &str, + repo: &str, + tag: &str, +) -> Result { + let mut page = client + .repos(organization, repo) + .releases() + .list() + .per_page(1) + .send() + .await?; + + let release = loop { + if let Some(release) = page + .take_items() + .into_iter() + .find(|release| release.tag_name == tag) + { + break Some(release); + } + + page = match client.get_page::(&page.next).await? { + Some(page) => page, + None => break None, + }; + }; + + let release = release.ok_or_else(|| anyhow!("release {tag} does not exist"))?; + + if !release.draft { + return Err(anyhow!("release {tag} exists but is not a draft")); + } + + Ok(release) +} + +pub async fn command_fetch_release_distributions(args: &ArgMatches) -> Result<()> { + let dest_dir = args + .get_one::("dest") + .expect("dest directory should be set"); + let org = args + .get_one::("organization") + .expect("organization should be set"); + let repo = args.get_one::("repo").expect("repo should be set"); + + let (client, _) = new_github_client(args)?; + + let release_version_range = pep440_rs::VersionSpecifier::from_str(">=3.10")?; + + let workflows = client.workflows(org, repo); + + let mut workflow_names = HashMap::new(); + + let workflow_ids = workflows + .list() + .send() + .await? + .into_iter() + .filter_map(|wf| { + if matches!( + wf.path.as_str(), + ".github/workflows/macos.yml" + | ".github/workflows/linux.yml" + | ".github/workflows/windows.yml" + ) { + workflow_names.insert(wf.id, wf.name); + + Some(wf.id) + } else { + None + } + }) + .collect::>(); + + if workflow_ids.is_empty() { + return Err(anyhow!( + "failed to find any workflows; this should not happen" + )); + } + + let mut runs: Vec = vec![]; + + for workflow_id in workflow_ids { + let commit = args + .get_one::("commit") + .expect("commit should be defined"); + let workflow_name = workflow_names + .get(&workflow_id) + .expect("should have workflow name"); + + runs.push( + workflows + .list_runs(format!("{workflow_id}")) + .event("push") + .status("success") + .send() + .await? + .into_iter() + .find(|run| { + run.head_sha.as_str() == commit + }) + .ok_or_else(|| { + anyhow!( + "could not find workflow run for commit {commit} for workflow {workflow_name}", + ) + })?, + ); + } + + let mut fs = vec![]; + + for run in runs { + let page = client + .actions() + .list_workflow_run_artifacts(org, repo, run.id) + .send() + .await?; + + let artifacts = client + .all_pages::( + page.value.expect("untagged request should have page"), + ) + .await?; + + for artifact in artifacts { + if matches!(artifact.name.as_str(), "pythonbuild" | "toolchain") + || artifact.name.contains("install-only") + || artifact.name.contains("dockerbuild") + || artifact.name.contains("crate-") + || artifact.name.contains("image-") + { + continue; + } + + fs.push(fetch_artifact(&client, org, repo, artifact)); + } + } + + let mut buffered = futures::stream::iter(fs).buffer_unordered(24); + + let mut install_paths = vec![]; + + while let Some(res) = buffered.next().await { + let data = res?; + + let mut za = ZipArchive::new(std::io::Cursor::new(data))?; + for i in 0..za.len() { + let mut zf = za.by_index(i)?; + + let name = zf.name().to_string(); + + let parts = name.split('-').collect::>(); + + if parts[0] != "cpython" { + println!("ignoring {name} not a cpython artifact"); + continue; + }; + + let python_version = pep440_rs::Version::from_str(parts[1])?; + if !release_version_range.contains(&python_version) { + println!("{name} not in release version range {release_version_range}"); + continue; + } + + // Iterate over `RELEASE_TRIPLES` in reverse-order to ensure that if any triple is a + // substring of another, the longest match is used. + let Some((triple, release)) = + RELEASE_TRIPLES.iter().rev().find_map(|(triple, release)| { + if name.contains(triple) { + Some((triple, release)) + } else { + None + } + }) + else { + println!("ignoring {name} does not match any registered release triples"); + continue; + }; + + let stripped_name = if let Some(s) = name.strip_suffix(".tar.zst") { + s + } else { + println!("ignoring {name} not a .tar.zst artifact"); + continue; + }; + + let stripped_name = &stripped_name[0..stripped_name.len() - "-YYYYMMDDTHHMM".len()]; + + let triple_start = stripped_name + .find(triple) + .expect("validated triple presence above"); + + let build_suffix = &stripped_name[triple_start + triple.len() + 1..]; + + if !release.suffixes(None).any(|suffix| build_suffix == suffix) { + println!("ignoring {name} not a release artifact for triple"); + continue; + } + + let dest_path = dest_dir.join(&name); + let mut buf = vec![]; + zf.read_to_end(&mut buf)?; + std::fs::write(&dest_path, &buf)?; + + println!("prepared {name} for release"); + + if build_suffix == release.install_only_suffix { + install_paths.push(dest_path); + } else if build_suffix == release.freethreaded_install_only_suffix { + install_paths.push(dest_path); + } + } + } + + let llvm_dir = bootstrap_llvm().await?; + + install_paths + .par_iter() + .try_for_each(|path| -> Result<()> { + // Create the `install_only` archive. + println!( + "producing install_only archive from {}", + path.file_name() + .expect("should have file name") + .to_string_lossy() + ); + + let dest_path = produce_install_only(path)?; + + println!( + "prepared {} for release", + dest_path + .file_name() + .expect("should have file name") + .to_string_lossy() + ); + + // Create the `install_only_stripped` archive. + println!( + "producing install_only_stripped archive from {}", + dest_path + .file_name() + .expect("should have file name") + .to_string_lossy() + ); + + let dest_path = produce_install_only_stripped(&dest_path, &llvm_dir)?; + + println!( + "prepared {} for release", + dest_path + .file_name() + .expect("should have file name") + .to_string_lossy() + ); + + Ok(()) + })?; + + Ok(()) +} + +pub async fn command_upload_release_distributions(args: &ArgMatches) -> Result<()> { + let dist_dir = args + .get_one::("dist") + .expect("dist should be specified"); + let datetime = args + .get_one::("datetime") + .expect("datetime should be specified"); + let tag = args + .get_one::("tag") + .expect("tag should be specified"); + let ignore_missing = args.get_flag("ignore_missing"); + let organization = args + .get_one::("organization") + .expect("organization should be specified"); + let repo = args + .get_one::("repo") + .expect("repo should be specified"); + let dry_run = args.get_flag("dry_run"); + + let mut filenames = std::fs::read_dir(dist_dir)? + .map(|x| { + let path = x?.path(); + let filename = path + .file_name() + .ok_or_else(|| anyhow!("unable to resolve file name"))?; + + Ok(filename.to_string_lossy().to_string()) + }) + .collect::>>()?; + filenames.sort(); + + let filenames = filenames + .into_iter() + .filter(|x| x.contains(datetime) && x.starts_with("cpython-")) + .collect::>(); + + let wanted_filenames = build_wanted_filenames(&filenames, datetime, tag)?; + + let missing = wanted_filenames + .keys() + .filter(|x| !filenames.contains(*x)) + .collect::>(); + + for f in &missing { + println!("missing release artifact: {f}"); + } + if missing.is_empty() { + println!("found all {} release artifacts", wanted_filenames.len()); + } else if !ignore_missing { + return Err(anyhow!("missing {} release artifacts", missing.len())); + } + + let mut digests = BTreeMap::new(); + + for (source, dest) in &wanted_filenames { + if !filenames.contains(source) { + continue; + } + + let local_filename = dist_dir.join(source); + + // Compute digests in a separate pass so we can always materialize + // SHA256SUMS locally before any GitHub interaction, including in dry-run + // mode. This also avoids trying to reuse the streamed upload body for hashing. + let digest = { + let file = tokio::fs::File::open(local_filename).await?; + let mut stream = tokio_util::io::ReaderStream::with_capacity(file, 1048576); + let mut hasher = Sha256::new(); + while let Some(chunk) = stream.next().await { + hasher.update(&chunk?); + } + hex::encode(hasher.finalize()) + }; + digests.insert(dest.clone(), digest); + } + + let shasums = digests + .iter() + .map(|(filename, digest)| format!("{digest} {filename}\n")) + .collect::>() + .join(""); + + std::fs::write(dist_dir.join("SHA256SUMS"), shasums.as_bytes())?; + + if dry_run { + println!("wrote local SHA256SUMS; skipping GitHub upload and verification"); + return Ok(()); + } + + let (client, token) = new_github_client(args)?; + let release = get_draft_release_by_tag(&client, organization, repo, tag).await?; + + let retry_policy = ExponentialBackoff::builder().build_with_max_retries(5); + let raw_client = Client::new(); + + { + let mut fs = vec![]; + + for (source, dest) in &wanted_filenames { + if !filenames.contains(source) { + continue; + } + + let local_filename = dist_dir.join(source); + fs.push(upload_release_artifact( + &raw_client, + &retry_policy, + &GitHubUploadRetryStrategy, + token.clone(), + &release, + dest.clone(), + UploadSource::Filename(local_filename), + dry_run, + )); + } + + let mut buffered = futures::stream::iter(fs).buffer_unordered(16); + + while let Some(res) = buffered.next().await { + res?; + } + } + + upload_release_artifact( + &raw_client, + &retry_policy, + &GitHubUploadRetryStrategy, + token.clone(), + &release, + "SHA256SUMS".to_string(), + UploadSource::Data(Bytes::copy_from_slice(shasums.as_bytes())), + dry_run, + ) + .await?; + + // Check that content wasn't munged as part of uploading. This once happened + // and created a busted release. Never again. + let release = get_draft_release_by_tag(&client, organization, repo, tag) + .await + .with_context(|| format!("could not find draft release {tag}; this should not happen"))?; + let shasums_asset = release + .assets + .into_iter() + .find(|x| x.name == "SHA256SUMS") + .ok_or_else(|| anyhow!("unable to find SHA256SUMs release asset"))?; + + let mut stream = client + .repos(organization, repo) + .release_assets() + .stream(shasums_asset.id.into_inner()) + .await?; + + let mut asset_bytes = Vec::::new(); + + while let Some(chunk) = stream.next().await { + asset_bytes.extend(chunk?.as_ref()); + } + + if shasums.as_bytes() != asset_bytes { + return Err(anyhow!("SHA256SUM content mismatch; release might be bad!")); + } + + Ok(()) +} diff --git a/src/github_api_tester.py b/src/github_api_tester.py new file mode 100755 index 000000000..18e1b2719 --- /dev/null +++ b/src/github_api_tester.py @@ -0,0 +1,394 @@ +#!/usr/bin/env -S uv run +# +# A fake GitHub API server for testing upload-release-distributions's +# behavior in the presence of API failures. +# +# Call with no arguments or with pytest CLI arguments to run the tests +# at the bottom which invoke `cargo run`. +# +# Call with one argument "serve" to start an HTTP server on 0.0.0.0. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "quart>=0.20.0", +# "quart-trio>=0.12.0", +# # Pinned because we mess with hypercorn internals, see below. +# "hypercorn==0.17.3", +# "pytest", +# "pytest-trio", +# ] +# /// + +import dataclasses +import hashlib +import logging +import os +import sys +from collections.abc import Callable + +import hypercorn +import pytest +import quart +import trio +from quart import request +from quart_trio import QuartTrio + +app = QuartTrio(__name__) +app.config["MAX_CONTENT_LENGTH"] = None + + +async def drop_connection(): + """Drop the (HTTP/1.1) connection belonging to the current Quart request.""" + # We need to do two things: + # - Convince hypercorn (specifically, around HTTPStream.app_send()) + # that it doesn't need to send a 500 and can just close the socket. + # - Convince h11's state machine that it's okay to close the socket + # without sending a response. + # We can't do this at the ASGI layer: hypercorn will insert the 500 + # for protocol compliance if the ASGI app doesn't provide a + # response. We need to modify the actual HTTP server, either with a + # pull request or by digging into its internals as follows: + # - Grab the HTTPStream whose bound method app_send was passed into + # the Quart request + # - Grab the H11Protocol whose bound method stream_send was passed + # into the HTTPStream's constructor + # - Tell the H11Protocol's underlying h11 state machine to act as if + # the remote side errored, so it thinks dropping the connection is + # the appropriate next step and not misbehavior on our end + # - Tell the HTTPStream to move the state machine forward with no + # further send on our side, which will drop the connection (and + # not consider it for keepalive) + import hypercorn.protocol as hp + + http_stream: hp.http_stream.HTTPStream = request._send_push_promise.args[0].__self__ + protocol: hp.h11.H11Protocol = http_stream.send.__self__ + protocol.connection._process_error(protocol.connection.their_role) + await http_stream.send(hp.events.EndBody(stream_id=http_stream.stream_id)) + await http_stream.app_send(None) + + # Some other things I tried, kept for reference: + # http_stream.state = hypercorn.protocol.http_stream.ASGIHTTPState.RESPONSE + # await http_stream._send_closed() + # http_stream.state = hypercorn.protocol.http_stream.ASGIHTTPState.CLOSED + + +# The following GitHub API datatypes are complete enough to satisfy +# octocrab's deserialization. + + +@dataclasses.dataclass +class Asset: + name: str + label: str | None + sha256: str + contents: bytes | None + + _ASSETS = [] + + def __post_init__(self): + self.id = len(self._ASSETS) + self._ASSETS.append(self) + + def render(self) -> dict: + return { + "url": quart.url_for("get_asset", id=self.id, _external=True), + "browser_download_url": "https://github.invalid/unneeded", + "id": self.id, + "node_id": "fakenode", + "name": self.name, + "label": self.label, + "state": "uploaded", + "content_type": "application/octet-stream", + "size": 1000, + "download_count": 1000, + "created_at": "2020-01-01T00:00:00Z", + "updated_at": "2020-01-01T00:00:00Z", + "uploader": None, + } + + +@dataclasses.dataclass +class Upload: + name: str + label: str | None + + def __post_init__(self): + self.hasher = hashlib.sha256() + if self.name == "SHA256SUMS": + self.contents = b"" + else: + self.contents = None + + def update(self, chunk: bytes) -> None: + self.hasher.update(chunk) + if self.contents is not None: + self.contents += chunk + + def to_asset(self) -> Asset: + return Asset(self.name, self.label, self.hasher.hexdigest(), self.contents) + + +@dataclasses.dataclass +class Release: + release_id: int + tag_name: str + assets: list = dataclasses.field(default_factory=list) + draft: bool = True + prerelease: bool = False + # fault0 and fault1 are called before and after receiving the first + # chunk of a PUT request, respectively. Each is called once per + # release - the first upload that hits it will disarm it. + fault0: Callable[[], None] | None = None + fault1: Callable[[], None] | None = None + + def render(self) -> dict: + upload_asset = quart.url_for( + "upload_asset", release=self.release_id, _external=True + ) + return { + "url": request.url, + "html_url": "https://github.invalid/unneeded", + "assets_url": "https://github.invalid/unneeded", + "upload_url": upload_asset + "{?name,label}", + "id": self.release_id, + "node_id": "fakenode", + "tag_name": self.tag_name, + "target_commitish": "main", + "draft": self.draft, + "prerelease": self.prerelease, + "assets": [i.render() for i in self.assets], + } + + +releases = [ + Release(1, "basic"), + Release(2, "draft"), + Release(11, "early-drop", fault0=drop_connection), + Release(12, "late-drop", fault1=drop_connection), + Release(4011, "early-401", fault0=lambda: quart.abort(401)), + Release(4012, "late-401", fault1=lambda: quart.abort(401)), + Release(4031, "early-403", fault0=lambda: quart.abort(403)), + Release(4032, "late-403", fault1=lambda: quart.abort(403)), + Release(5001, "early-500", fault0=lambda: quart.abort(500)), + Release(5002, "late-500", fault1=lambda: quart.abort(500)), +] + + +def get_release(*, tag=None, release=None) -> Release: + if tag is not None: + condition = lambda r: r.tag_name == tag + elif release is not None: + condition = lambda r: r.release_id == release + else: + raise TypeError("tag or release must be set") + + for r in releases: + if condition(r): + return r + quart.abort(404, response=quart.jsonify({"message": "Not Found", "status": "404"})) + + +# GitHub API functions + + +@app.route("/repos///releases/tags/") +async def get_release_by_tag(org, repo, tag): + release = get_release(tag=tag) + if release.draft: + quart.abort( + 404, response=quart.jsonify({"message": "Not Found", "status": "404"}) + ) + return release.render() + + +@app.route("/repos///releases") +async def list_releases(org, repo): + return quart.jsonify([release.render() for release in releases]) + + +@app.route("/repos///releases/") +async def get_release_by_id(org, repo, release): + return get_release(release=release).render() + + +@app.put("/upload//assets") +async def upload_asset(release): + filename = request.args["name"] + release = get_release(release=release) + + if (fault := release.fault0) is not None: + logging.info(f"{filename}: injecting fault0") + release.fault0 = None + return await fault() + + logging.info(f"{filename}: upload begin") + upload = Upload(filename, request.args.get("label")) + async for chunk in request.body: + logging.debug(f"{filename}: {len(chunk)=}") + upload.update(chunk) + if (fault := release.fault1) is not None: + if "SHA256" not in filename: + logging.info(f"{filename}: injecting fault1") + release.fault1 = None + return await fault() + + asset = upload.to_asset() + logging.info(f"{filename}: upload complete, {asset.sha256=}") + release.assets.append(asset) + return asset.render() + + +@app.route("/get_asset/") +@app.route("/repos///releases/assets/") +async def get_asset(id, org=None, repo=None): + try: + asset = Asset._ASSETS[id] + except IndexError: + quart.abort( + 404, response=quart.jsonify({"message": "Not Found", "status": "404"}) + ) + + if "application/octet-stream" in request.accept_mimetypes: + if asset.contents is None: + print( + f"USAGE ERROR: Received request for contents of {asset.filename=} which was not stored" + ) + return "Did not store contents", 410 + return asset.contents + else: + return asset.render() + + +# Generic upload function, useful for testing clients in isolation + + +@app.put("/file/") +async def upload_file(path): + logging.info(f"{path}: upload begin") + s = hashlib.sha256() + async for chunk in request.body: + logging.debug(f"{path}: {len(chunk)=}") + if "drop" in request.args: + await drop_connection() + s.update(chunk) + digest = s.hexdigest() + logging.info(f"{path}: {digest=}") + return f"{digest} {path}\n", 500 + + +# Test cases + + +@pytest.fixture +async def server(nursery): + await nursery.start(app.run_task) + + +FILENAME = "cpython-3.0.0-x86_64-unknown-linux-gnu-install_only-19700101T1234.tar.gz" +SHA256_20MEG = "9e21c61969cd3e077a1b2b58ddb583b175e13c6479d2d83912eaddc23c0cdd52" + + +@pytest.fixture(scope="session") +def upload_release_distributions(tmp_path_factory): + dist = tmp_path_factory.mktemp("dist") + filename = dist / FILENAME + filename.touch() + os.truncate(filename, 20_000_000) + + async def upload_release_distributions(*args): + return await trio.run_process( + [ + "cargo", + "run", + "--", + "upload-release-distributions", + "--github-uri", + "http://localhost:5000", + "--token", + "no-token-needed", + "--dist", + dist, + "--datetime", + "19700101T1234", + "--ignore-missing", + ] + + list(args) + ) + + return upload_release_distributions + + +# TODO: test all of [r.tag_name for r in releases] +TAGS_TO_TEST = ["basic", "draft", "early-drop", "late-drop", "early-403", "late-403"] + + +@pytest.mark.parametrize("tag", TAGS_TO_TEST) +async def test_upload(server, upload_release_distributions, tag): + with trio.fail_after(300): + await upload_release_distributions("--tag", tag) + release = get_release(tag=tag) + assets = sorted(release.assets, key=lambda a: a.name) + assert len(assets) == 2 + assert assets[0].name == "SHA256SUMS" + filename = FILENAME.replace("3.0.0", f"3.0.0+{tag}").replace("-19700101T1234", "") + assert assets[1].name == filename + assert assets[1].sha256 == SHA256_20MEG + assert assets[0].contents == f"{SHA256_20MEG} {filename}\n".encode() + + +async def test_dry_run_writes_shasums_without_contacting_github(tmp_path): + dist = tmp_path / "dist" + dist.mkdir() + + filename = dist / FILENAME + filename.touch() + os.truncate(filename, 20_000_000) + + tag = "missing-release" + with trio.fail_after(300): + await trio.run_process( + [ + "cargo", + "run", + "--", + "upload-release-distributions", + "--github-uri", + # Use a guaranteed-bad loopback port so this fails fast if the + # command unexpectedly tries to contact GitHub in dry-run mode. + "http://127.0.0.1:1", + "--token", + "no-token-needed", + "--dist", + dist, + "--datetime", + "19700101T1234", + "--ignore-missing", + "--tag", + tag, + "-n", + ] + ) + + release_filename = FILENAME.replace("3.0.0", f"3.0.0+{tag}").replace( + "-19700101T1234", "" + ) + assert (dist / "SHA256SUMS").read_bytes() == ( + f"{SHA256_20MEG} {release_filename}\n".encode() + ) + + +# Work around https://github.com/pgjones/hypercorn/issues/238 not being in a release +# Without it, test failures are unnecessarily noisy +hypercorn.trio.lifespan.LifespanFailureError = trio.Cancelled + +if __name__ == "__main__": + if len(sys.argv) > 1 and sys.argv[1] == "serve": + logging.basicConfig(level=logging.INFO) + app.run("0.0.0.0") + else: + pytest.main(["-o", "trio_mode=true", __file__] + sys.argv[1:]) diff --git a/src/json.rs b/src/json.rs index ae6b12e95..d31ef181c 100644 --- a/src/json.rs +++ b/src/json.rs @@ -5,11 +5,12 @@ use { anyhow::Result, serde::Deserialize, - std::collections::{BTreeMap, HashMap}, + std::collections::{BTreeMap, BTreeSet, HashMap}, }; #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] +#[allow(dead_code)] pub struct LinkEntry { pub name: String, pub path_static: Option, @@ -20,6 +21,7 @@ pub struct LinkEntry { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] +#[allow(dead_code)] pub struct PythonBuildExtensionInfo { pub in_core: bool, pub init_fn: String, @@ -36,6 +38,7 @@ pub struct PythonBuildExtensionInfo { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] +#[allow(dead_code)] pub struct PythonBuildCoreInfo { pub objs: Vec, pub links: Vec, @@ -45,6 +48,7 @@ pub struct PythonBuildCoreInfo { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] +#[allow(dead_code)] pub struct PythonBuildInfo { pub core: PythonBuildCoreInfo, pub extensions: BTreeMap>, @@ -56,12 +60,14 @@ pub struct PythonBuildInfo { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] +#[allow(dead_code)] pub struct PythonJsonMain { pub apple_sdk_canonical_name: Option, pub apple_sdk_deployment_target: Option, pub apple_sdk_platform: Option, pub apple_sdk_version: Option, pub build_info: PythonBuildInfo, + pub build_options: String, pub crt_features: Vec, pub libpython_link_mode: String, pub licenses: Option>, @@ -93,8 +99,22 @@ pub struct PythonJsonMain { pub version: String, } +impl PythonJsonMain { + pub fn all_object_paths(&self) -> BTreeSet<&str> { + let mut res = BTreeSet::from_iter(self.build_info.core.objs.iter().map(|x| x.as_str())); + + for entries in self.build_info.extensions.values() { + for ext in entries { + res.extend(ext.objs.iter().map(|x| x.as_str())); + } + } + + res + } +} + pub fn parse_python_json(json_data: &[u8]) -> Result { - let v: PythonJsonMain = serde_json::from_slice(&json_data)?; + let v: PythonJsonMain = serde_json::from_slice(json_data)?; Ok(v) } diff --git a/src/macho.rs b/src/macho.rs index e36f8bb0e..2173ee7b6 100644 --- a/src/macho.rs +++ b/src/macho.rs @@ -3,8 +3,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use { - anyhow::anyhow, - std::{convert::TryFrom, str::FromStr}, + crate::validation::ValidationContext, + anyhow::{Context, Result, anyhow}, + apple_sdk::{AppleSdk, SdkSearch, SdkSearchLocation, SdkSorting, SdkVersion, SimpleSdk}, + semver::Version, + std::{ + collections::{BTreeMap, BTreeSet}, + convert::TryFrom, + path::{Path, PathBuf}, + str::FromStr, + }, + text_stub_library::TbdVersionedRecord, }; #[derive(Clone, Debug, PartialEq, PartialOrd)] @@ -44,7 +53,7 @@ impl std::fmt::Display for MachOPackedVersion { let minor = (self.value >> 8) & 0xff; let subminor = self.value & 0xff; - f.write_str(&format!("{}.{}.{}", major, minor, subminor)) + f.write_str(&format!("{major}.{minor}.{subminor}")) } } @@ -62,3 +71,369 @@ pub struct MachOAllowedDylib { /// Whether the loading of this dylib must be present in the distribution. pub required: bool, } + +/// Holds required symbols defined in a library. +#[derive(Clone, Debug, Default)] +pub struct LibrarySymbols { + /// Symbol name -> source paths that require them. + pub symbols: BTreeMap>, +} + +impl LibrarySymbols { + /// Obtain all paths referenced in this collection. + pub fn all_paths(&self) -> BTreeSet { + let mut res = BTreeSet::new(); + + for paths in self.symbols.values() { + res.extend(paths.iter().cloned()); + } + + res + } +} + +/// Holds required symbols, indexed by library. +#[derive(Clone, Debug, Default)] +pub struct RequiredSymbols { + pub libraries: BTreeMap, +} + +impl RequiredSymbols { + /// Register a required symbol. + /// + /// `library` is the library that `symbol` is defined in. And `path` is the path needing + /// this symbol. + pub fn insert(&mut self, library: impl ToString, symbol: impl ToString, path: PathBuf) { + self.libraries + .entry(library.to_string()) + .or_default() + .symbols + .entry(symbol.to_string()) + .or_default() + .insert(path); + } + + /// Merge the contents of another instance into this one. + pub fn merge(&mut self, other: Self) { + for (library, symbols) in other.libraries { + let entry = self.libraries.entry(library).or_default(); + + for (name, paths) in symbols.symbols { + entry.symbols.entry(name).or_default().extend(paths); + } + } + } +} + +fn tbd_relative_path(path: &str) -> Result { + if let Some(stripped) = path.strip_prefix('/') { + if let Some(stem) = stripped.strip_suffix(".dylib") { + Ok(format!("{stem}.tbd")) + } else { + Ok(format!("{stripped}.tbd")) + } + } else { + Err(anyhow!("could not determine tbd path from {}", path)) + } +} + +#[derive(Default, Debug)] +struct TbdMetadata { + symbols: BTreeMap>, + weak_symbols: BTreeMap>, + re_export_paths: BTreeMap>, +} + +impl TbdMetadata { + fn from_path(path: &Path) -> Result { + let data = std::fs::read_to_string(path)?; + + let mut res = Self::default(); + + let process_export_v12 = + |res: &mut Self, export: text_stub_library::yaml::TbdVersion12ExportSection| { + for arch in export.archs { + res.symbols + .entry(format!("{}-macos", arch.clone())) + .or_default() + .extend( + export + .symbols + .iter() + .cloned() + .chain( + export + .objc_classes + .iter() + .map(|cls| format!("_OBJC_CLASS_${cls}")), + ) + .chain( + export + .objc_classes + .iter() + .map(|cls| format!("_OBJC_METACLASS_${cls}")), + ), + ); + + res.weak_symbols + .entry(format!("{}-macos", arch.clone())) + .or_default() + .extend(export.weak_def_symbols.iter().cloned()); + + res.re_export_paths + .entry(format!("{}-macos", arch.clone())) + .or_default() + .extend(export.re_exports.iter().cloned()); + } + }; + + for record in text_stub_library::parse_str(&data)? { + match record { + TbdVersionedRecord::V1(record) => { + for export in record.exports { + process_export_v12(&mut res, export); + } + } + TbdVersionedRecord::V2(record) => { + for export in record.exports { + process_export_v12(&mut res, export); + } + } + TbdVersionedRecord::V3(record) => { + for export in record.exports { + for arch in export.archs { + res.symbols + .entry(format!("{}-macos", arch.clone())) + .or_default() + .extend( + export + .symbols + .iter() + .cloned() + .chain( + export + .objc_classes + .iter() + .map(|cls| format!("_OBJC_CLASS_$_{cls}")), + ) + .chain( + export + .objc_classes + .iter() + .map(|cls| format!("_OBJC_METACLASS_$_{cls}")), + ), + ); + + res.weak_symbols + .entry(format!("{}-macos", arch.clone())) + .or_default() + .extend(export.weak_def_symbols.iter().cloned()); + + // In version 3 records, re-exports is a list of filenames. + res.re_export_paths + .entry(format!("{}-macos", arch.clone())) + .or_default() + .extend(export.re_exports.iter().cloned()); + } + } + } + TbdVersionedRecord::V4(record) => { + for export in record.exports { + for target in export.targets { + res.symbols.entry(target.clone()).or_default().extend( + export + .symbols + .iter() + .cloned() + .chain( + export + .objc_classes + .iter() + .map(|cls| format!("_OBJC_CLASS_$_{cls}")), + ) + .chain( + export + .objc_classes + .iter() + .map(|cls| format!("_OBJC_METACLASS_$_{cls}")), + ), + ); + res.weak_symbols + .entry(target) + .or_default() + .extend(export.weak_symbols.iter().cloned()); + } + } + for export in record.re_exports { + for target in export.targets { + res.symbols + .entry(target.clone()) + .or_default() + .extend(export.symbols.iter().cloned()); + res.weak_symbols + .entry(target.clone()) + .or_default() + .extend(export.weak_symbols.iter().cloned()); + } + } + } + } + } + + // Time for some hacks! + + // Some SDKs have a `R8289209$` prefix on symbol names. We have no clue what this + // is for. But we need to strip it for symbol references to resolve properly. + for (_, symbols) in res.symbols.iter_mut() { + let stripped = symbols + .iter() + .filter_map(|x| { + x.strip_prefix("R8289209$") + .map(|stripped| stripped.to_string()) + }) + .collect::>(); + + symbols.extend(stripped); + } + + Ok(res) + } + + fn expand_file_references(&mut self, root_path: &Path) -> Result<()> { + let mut extra_symbols: BTreeMap> = BTreeMap::new(); + + for (target, paths) in self.re_export_paths.iter_mut() { + for path in paths.iter() { + let tbd_path = root_path.join(tbd_relative_path(path)?); + let tbd_info = TbdMetadata::from_path(&tbd_path)?; + + if let Some(symbols) = tbd_info.symbols.get(target) { + extra_symbols + .entry(target.clone()) + .or_default() + .extend(symbols.iter().cloned()); + } + } + } + + for (target, symbols) in extra_symbols { + self.symbols.entry(target).or_default().extend(symbols); + } + + Ok(()) + } +} + +pub struct IndexedSdks { + sdks: Vec, +} + +impl IndexedSdks { + pub fn new(path: impl AsRef) -> Result { + let path = path.as_ref(); + + let sdks = SdkSearch::empty() + .location(SdkSearchLocation::Sdks(path.to_path_buf())) + .sorting(SdkSorting::VersionAscending) + .search::() + .context("searching for SDKs")?; + + Ok(Self { sdks }) + } + + fn required_sdks(&self, minimum_version: Version) -> Result> { + let mut res = vec![]; + + for sdk in &self.sdks { + if let Some(sdk_version) = sdk.version() { + if let Ok(sdk_version) = sdk_version.semantic_version() { + let sdk_version = Version::from_str(sdk_version.as_str())?; + + if sdk_version >= minimum_version { + res.push(sdk); + } + } + } + } + + Ok(res) + } + + pub fn validate_context( + &self, + context: &mut ValidationContext, + minimum_sdk: semver::Version, + triple: &str, + ) -> Result<()> { + let symbol_target = match triple { + "aarch64-apple-darwin" => "arm64e-macos", + "x86_64-apple-darwin" => "x86_64-macos", + _ => { + context.errors.push(format!( + "unknown target triple for Mach-O symbol analysis: {triple}" + )); + return Ok(()); + } + }; + + let sdks = self.required_sdks(minimum_sdk)?; + if sdks.is_empty() { + context + .errors + .push("failed to resolve Apple SDKs to test against (this is likely a bug)".into()); + return Ok(()); + } + + for (lib, symbols) in &context.macho_undefined_symbols_strong.libraries { + // Filter out `@executable_path`. + if lib.strip_prefix('/').is_some() { + let tbd_relative_path = tbd_relative_path(lib)?; + + for sdk in &sdks { + // The 10.9 SDK doesn't have TBDs. So skip it for now. + if let Some(version) = sdk.version() { + if version == &SdkVersion::from("10.9") { + continue; + } + } + + let tbd_path = sdk.path().join(&tbd_relative_path); + + if tbd_path.exists() { + let mut tbd_info = TbdMetadata::from_path(&tbd_path)?; + tbd_info.expand_file_references(sdk.path())?; + + let empty = BTreeSet::new(); + + let target_symbols = tbd_info.symbols.get(symbol_target).unwrap_or(&empty); + + for (symbol, paths) in &symbols.symbols { + if !target_symbols.contains(symbol) { + for path in paths { + context.errors.push(format!( + "{} references symbol {}:{} which doesn't exist in SDK {}", + path.display(), + lib, + symbol, + sdk.path().display() + )); + } + } + } + } else { + for path in symbols.all_paths() { + context.errors.push(format!( + "library {} does not exist in SDK {}; {} will likely fail to load", + lib, + sdk.version().unwrap_or(&SdkVersion::from("99.99")), + path.display() + )); + } + } + } + } + } + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 82305cbf6..f30b63b4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,682 +2,22 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +mod github; mod json; mod macho; +mod release; +mod validation; use { - crate::{json::*, macho::*}, - anyhow::{anyhow, Context, Result}, - clap::{App, AppSettings, Arg, ArgMatches, SubCommand}, - goblin::mach::load_command::CommandVariant, - once_cell::sync::Lazy, - scroll::Pread, + anyhow::{Context, Result, anyhow}, + clap::{Arg, ArgAction, Command, value_parser}, std::{ - collections::{BTreeSet, HashMap}, - convert::TryInto, io::Read, - iter::FromIterator, path::{Path, PathBuf}, }, }; -const RECOGNIZED_TRIPLES: &[&str] = &[ - "aarch64-apple-darwin", - "aarch64-apple-ios", - "aarch64-unknown-linux-gnu", - "armv7-unknown-linux-gnueabi", - "armv7-unknown-linux-gnueabihf", - "arm64-apple-tvos", - "i686-pc-windows-msvc", - "i686-unknown-linux-gnu", - "mips-unknown-linux-gnu", - "mipsel-unknown-linux-gnu", - "mips64el-unknown-linux-gnuabi64", - "s390x-unknown-linux-gnu", - "thumbv7k-apple-watchos", - "x86_64-apple-darwin", - "x86_64-apple-ios", - "x86_64-apple-tvos", - "x86_64-apple-watchos", - "x86_64-pc-windows-msvc", - "x86_64-unknown-linux-gnu", - "x86_64-unknown-linux-musl", -]; - -const ELF_ALLOWED_LIBRARIES: &[&str] = &[ - // LSB set. - "libc.so.6", - "libcrypt.so.1", - "libdl.so.2", - "libm.so.6", - "libpthread.so.0", - "librt.so.1", - "libutil.so.1", -]; - -const PE_ALLOWED_LIBRARIES: &[&str] = &[ - "ADVAPI32.dll", - "api-ms-win-core-path-l1-1-0.dll", - "api-ms-win-crt-conio-l1-1-0.dll", - "api-ms-win-crt-convert-l1-1-0.dll", - "api-ms-win-crt-heap-l1-1-0.dll", - "api-ms-win-crt-environment-l1-1-0.dll", - "api-ms-win-crt-filesystem-l1-1-0.dll", - "api-ms-win-crt-locale-l1-1-0.dll", - "api-ms-win-crt-math-l1-1-0.dll", - "api-ms-win-crt-process-l1-1-0.dll", - "api-ms-win-crt-runtime-l1-1-0.dll", - "api-ms-win-crt-stdio-l1-1-0.dll", - "api-ms-win-crt-string-l1-1-0.dll", - "api-ms-win-crt-time-l1-1-0.dll", - "api-ms-win-crt-utility-l1-1-0.dll", - "bcrypt.dll", - "Cabinet.dll", - "COMCTL32.dll", - "COMDLG32.dll", - "CRYPT32.dll", - "GDI32.dll", - "IMM32.dll", - "IPHLPAPI.DLL", - "KERNEL32.dll", - "msi.dll", - "NETAPI32.dll", - "ole32.dll", - "OLEAUT32.dll", - "RPCRT4.dll", - "SHELL32.dll", - "SHLWAPI.dll", - "USER32.dll", - "USERENV.dll", - "VERSION.dll", - "VCRUNTIME140.dll", - "WINMM.dll", - "WS2_32.dll", - // Our libraries. - "libcrypto-1_1.dll", - "libcrypto-1_1-x64.dll", - "libffi-7.dll", - "libssl-1_1.dll", - "libssl-1_1-x64.dll", - "python38.dll", - "python39.dll", - "sqlite3.dll", - "tcl86t.dll", - "tk86t.dll", -]; - -static GLIBC_MAX_VERSION_BY_TRIPLE: Lazy>> = - Lazy::new(|| { - let mut versions = HashMap::new(); - - versions.insert( - "aarch64-unknown-linux-gnu", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "armv7-unknown-linux-gnueabi", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "armv7-unknown-linux-gnueabihf", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "i686-unknown-linux-gnu", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "mips-unknown-linux-gnu", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "mipsel-unknown-linux-gnu", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "mips64el-unknown-linux-gnuabi64", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "s390x-unknown-linux-gnu", - version_compare::Version::from("2.17").unwrap(), - ); - versions.insert( - "x86_64-unknown-linux-gnu", - version_compare::Version::from("2.17").unwrap(), - ); - // musl shouldn't link against glibc. - versions.insert( - "x86_64-unknown-linux-musl", - version_compare::Version::from("1").unwrap(), - ); - - versions - }); - -static ELF_ALLOWED_LIBRARIES_BY_TRIPLE: Lazy>> = - Lazy::new(|| { - [ - ( - "armv7-unknown-linux-gnueabi", - vec!["ld-linux.so.3", "libgcc_s.so.1"], - ), - ( - "armv7-unknown-linux-gnueabihf", - vec!["ld-linux-armhf.so.3", "libgcc_s.so.1"], - ), - ("i686-unknown-linux-gnu", vec!["ld-linux-x86-64.so.2"]), - ("mips-unknown-linux-gnu", vec!["ld.so.1"]), - ("mipsel-unknown-linux-gnu", vec!["ld.so.1"]), - ("mips64el-unknown-linux-gnuabi64", vec![]), - ("s390x-unknown-linux-gnu", vec!["ld64.so.1"]), - ("x86_64-unknown-linux-gnu", vec!["ld-linux-x86-64.so.2"]), - ] - .iter() - .cloned() - .collect() - }); - -static DARWIN_ALLOWED_DYLIBS: Lazy> = Lazy::new(|| { - [ - MachOAllowedDylib { - name: "@executable_path/../lib/libpython3.8.dylib".to_string(), - max_compatibility_version: "3.8.0".try_into().unwrap(), - required: false, - }, - MachOAllowedDylib { - name: "@executable_path/../lib/libpython3.8d.dylib".to_string(), - max_compatibility_version: "3.8.0".try_into().unwrap(), - required: false, - }, - MachOAllowedDylib { - name: "@executable_path/../lib/libpython3.9.dylib".to_string(), - max_compatibility_version: "3.9.0".try_into().unwrap(), - required: false, - }, - MachOAllowedDylib { - name: "@executable_path/../lib/libpython3.9d.dylib".to_string(), - max_compatibility_version: "3.9.0".try_into().unwrap(), - required: false, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit".to_string(), - max_compatibility_version: "45.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon".to_string(), - max_compatibility_version: "2.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: - "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation" - .to_string(), - max_compatibility_version: "150.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics".to_string(), - max_compatibility_version: "64.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation".to_string(), - max_compatibility_version: "300.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/usr/lib/libedit.3.dylib".to_string(), - max_compatibility_version: "2.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/usr/lib/libncurses.5.4.dylib".to_string(), - max_compatibility_version: "5.4.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/usr/lib/libobjc.A.dylib".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/usr/lib/libpanel.5.4.dylib".to_string(), - max_compatibility_version: "5.4.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/usr/lib/libSystem.B.dylib".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/usr/lib/libz.1.dylib".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - ] - .to_vec() -}); - -static IOS_ALLOWED_DYLIBS: Lazy> = Lazy::new(|| { - [ - MachOAllowedDylib { - name: "@executable_path/../lib/libpython3.9.dylib".to_string(), - max_compatibility_version: "3.9.0".try_into().unwrap(), - required: false, - }, - MachOAllowedDylib { - name: "@executable_path/../lib/libpython3.9d.dylib".to_string(), - max_compatibility_version: "3.9.0".try_into().unwrap(), - required: false, - }, - // For some reason, CoreFoundation is present in debug/noopt builds but not - // LTO builds. - MachOAllowedDylib { - name: "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation".to_string(), - max_compatibility_version: "150.0.0".try_into().unwrap(), - required: false, - }, - MachOAllowedDylib { - name: "/usr/lib/libSystem.B.dylib".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - MachOAllowedDylib { - name: "/usr/lib/libz.1.dylib".to_string(), - max_compatibility_version: "1.0.0".try_into().unwrap(), - required: true, - }, - ] - .to_vec() -}); - -static PLATFORM_TAG_BY_TRIPLE: Lazy> = Lazy::new(|| { - [ - ("aarch64-apple-darwin", "macosx-11.0-arm64"), - ("aarch64-apple-ios", "iOS-aarch64"), - ("aarch64-unknown-linux-gnu", "linux-aarch64"), - ("armv7-unknown-linux-gnueabi", "linux-arm"), - ("armv7-unknown-linux-gnueabihf", "linux-arm"), - ("i686-pc-windows-msvc", "win32"), - ("i686-unknown-linux-gnu", "linux-i686"), - ("mips-unknown-linux-gnu", "linux-mips"), - ("mipsel-unknown-linux-gnu", "linux-mipsel"), - ("mips64el-unknown-linux-gnuabi64", "todo"), - ("s390x-unknown-linux-gnu", "linux-s390x"), - ("x86_64-apple-darwin", "macosx-10.9-x86_64"), - ("x86_64-apple-ios", "iOS-x86_64"), - ("x86_64-pc-windows-msvc", "win-amd64"), - ("x86_64-unknown-linux-gnu", "linux-x86_64"), - ("x86_64-unknown-linux-musl", "linux-x86_64"), - ] - .iter() - .cloned() - .collect() -}); - -/// Symbols that we don't want to appear in mach-o binaries. -const MACHO_BANNED_SYMBOLS_NON_AARCH64: &[&str] = &[ - // _readv and _pwritev are introduced when building with the macOS 11 SDK. - // If present, they can cause errors re-linking object files. So we ban their - // existence. - "_preadv", "_pwritev", -]; - -const MACHO_BANNED_SYMBOLS_NON_AARCH64_PYTHON_38: &[&str] = &[ - // This symbol was improperly introduced into the 10.15 SDK and wasn't - // guarded by availability checks. Users in the wild have problems linking - // against a modern macOS SDK. So to keep compatibility with the non-buggy - // 10.15 SDK, we prevent the presence of this symbol. - // See https://github.com/indygreg/PyOxidizer/issues/373 for more. - "___darwin_check_fd_set_overflow", -]; - -static WANTED_WINDOWS_STATIC_PATHS: Lazy> = Lazy::new(|| { - [ - PathBuf::from("python/build/lib/libffi.lib"), - PathBuf::from("python/build/lib/libcrypto_static.lib"), - PathBuf::from("python/build/lib/liblzma.lib"), - PathBuf::from("python/build/lib/libssl_static.lib"), - PathBuf::from("python/build/lib/sqlite3.lib"), - ] - .iter() - .cloned() - .collect() -}); - -const GLOBALLY_BANNED_EXTENSIONS: &[&str] = &[ - // Due to linking issues. See comment in cpython.py. - "nis", -]; - -const PYTHON_VERIFICATIONS: &str = include_str!("verify_distribution.py"); - -fn allowed_dylibs_for_triple(triple: &str) -> Vec { - match triple { - "aarch64-apple-darwin" => DARWIN_ALLOWED_DYLIBS.clone(), - "x86_64-apple-darwin" => DARWIN_ALLOWED_DYLIBS.clone(), - "aarch64-apple-ios" => IOS_ALLOWED_DYLIBS.clone(), - "x86_64-apple-ios" => IOS_ALLOWED_DYLIBS.clone(), - _ => vec![], - } -} - -fn validate_elf( - target_triple: &str, - python_major_minor: &str, - path: &Path, - elf: &goblin::elf::Elf, - bytes: &[u8], -) -> Result> { - let mut errors = vec![]; - - let wanted_cpu_type = match target_triple { - "aarch64-unknown-linux-gnu" => goblin::elf::header::EM_AARCH64, - "armv7-unknown-linux-gnueabi" => goblin::elf::header::EM_ARM, - "armv7-unknown-linux-gnueabihf" => goblin::elf::header::EM_ARM, - "i686-unknown-linux-gnu" => goblin::elf::header::EM_386, - "mips-unknown-linux-gnu" => goblin::elf::header::EM_MIPS, - "mipsel-unknown-linux-gnu" => goblin::elf::header::EM_MIPS, - "mips64el-unknown-linux-gnuabi64" => 0, - "s390x-unknown-linux-gnu" => goblin::elf::header::EM_S390, - "x86_64-unknown-linux-gnu" => goblin::elf::header::EM_X86_64, - "x86_64-unknown-linux-musl" => goblin::elf::header::EM_X86_64, - _ => panic!("unhandled target triple: {}", target_triple), - }; - - if elf.header.e_machine != wanted_cpu_type { - errors.push(format!( - "invalid ELF machine type in {}; wanted {}, got {}", - path.display(), - wanted_cpu_type, - elf.header.e_machine - )); - } - - let mut allowed_libraries = ELF_ALLOWED_LIBRARIES - .iter() - .map(|x| x.to_string()) - .collect::>(); - if let Some(extra) = ELF_ALLOWED_LIBRARIES_BY_TRIPLE.get(target_triple) { - allowed_libraries.extend(extra.iter().map(|x| x.to_string())); - } - - allowed_libraries.push(format!( - "$ORIGIN/../lib/libpython{}.so.1.0", - python_major_minor - )); - allowed_libraries.push(format!( - "$ORIGIN/../lib/libpython{}d.so.1.0", - python_major_minor - )); - - for lib in &elf.libraries { - if !allowed_libraries.contains(&lib.to_string()) { - errors.push(format!("{} loads illegal library {}", path.display(), lib)); - } - } - - let wanted_glibc_max_version = GLIBC_MAX_VERSION_BY_TRIPLE - .get(target_triple) - .expect("max glibc version not defined for target triple"); - - // functionality doesn't yet support mips. - if !target_triple.starts_with("mips") && !target_triple.starts_with("s390x-") { - let mut undefined_symbols = tugger_binary_analysis::find_undefined_elf_symbols(&bytes, elf); - undefined_symbols.sort(); - - for symbol in undefined_symbols { - if let Some(version) = &symbol.version { - let parts: Vec<&str> = version.splitn(2, '_').collect(); - - if parts.len() == 2 { - if parts[0] == "GLIBC" { - let v = version_compare::Version::from(parts[1]) - .expect("unable to parse version"); - - if &v > wanted_glibc_max_version { - errors.push(format!( - "{} references too new glibc symbol {:?}", - path.display(), - symbol - )) - } - } - } - } - } - } - - Ok(errors) -} - -fn validate_macho( - python_major_minor: &str, - target_triple: &str, - path: &Path, - macho: &goblin::mach::MachO, - bytes: &[u8], -) -> Result<(Vec, Vec)> { - let mut errors = vec![]; - let mut seen_dylibs = vec![]; - - let wanted_cpu_type = match target_triple { - "aarch64-apple-darwin" => goblin::mach::cputype::CPU_TYPE_ARM64, - "aarch64-apple-ios" => goblin::mach::cputype::CPU_TYPE_ARM64, - "x86_64-apple-darwin" => goblin::mach::cputype::CPU_TYPE_X86_64, - "x86_64-apple-ios" => goblin::mach::cputype::CPU_TYPE_X86_64, - _ => return Err(anyhow!("unhandled target triple: {}", target_triple)), - }; - - if macho.header.cputype() != wanted_cpu_type { - errors.push(format!( - "{} has incorrect CPU type; got {}, wanted {}", - path.display(), - macho.header.cputype(), - wanted_cpu_type - )); - } - - for load_command in &macho.load_commands { - match load_command.command { - CommandVariant::LoadDylib(command) - | CommandVariant::LoadUpwardDylib(command) - | CommandVariant::ReexportDylib(command) - | CommandVariant::LoadWeakDylib(command) - | CommandVariant::LazyLoadDylib(command) => { - let lib = bytes.pread::<&str>(load_command.offset + command.dylib.name as usize)?; - - let allowed = allowed_dylibs_for_triple(target_triple); - - if let Some(entry) = allowed.iter().find(|l| l.name == lib) { - let load_version = - MachOPackedVersion::from(command.dylib.compatibility_version); - if load_version > entry.max_compatibility_version { - errors.push(format!( - "{} loads too new version of {}; got {}, max allowed {}", - path.display(), - lib, - load_version, - entry.max_compatibility_version - )); - } - - seen_dylibs.push(lib.to_string()); - } else { - errors.push(format!("{} loads illegal library {}", path.display(), lib)); - } - } - _ => {} - } - } - - if let Some(symbols) = &macho.symbols { - for symbol in symbols { - let (name, _) = symbol?; - - if target_triple != "aarch64-apple-darwin" - && (MACHO_BANNED_SYMBOLS_NON_AARCH64.contains(&name) - || (python_major_minor == "3.8" - && MACHO_BANNED_SYMBOLS_NON_AARCH64_PYTHON_38.contains(&name))) - { - errors.push(format!( - "{} references unallowed symbol {}", - path.display(), - name - )); - } - } - } - - Ok((errors, seen_dylibs)) -} - -fn validate_pe(path: &Path, pe: &goblin::pe::PE) -> Result> { - let mut errors = vec![]; - - for lib in &pe.libraries { - if !PE_ALLOWED_LIBRARIES.contains(lib) { - errors.push(format!("{} loads illegal library {}", path.display(), lib)); - } - } - - Ok(errors) -} - -/// Attempt to parse data as an object file and validate it. -fn validate_possible_object_file( - python_major_minor: &str, - triple: &str, - path: &Path, - data: &[u8], -) -> Result<(Vec, BTreeSet)> { - let mut errors = vec![]; - let mut seen_dylibs = BTreeSet::new(); - - if let Ok(object) = goblin::Object::parse(&data) { - match object { - goblin::Object::Elf(elf) => { - errors.extend(validate_elf( - triple, - python_major_minor, - path.as_ref(), - &elf, - &data, - )?); - } - goblin::Object::Mach(mach) => match mach { - goblin::mach::Mach::Binary(macho) => { - let (local_errors, local_seen_dylibs) = - validate_macho(python_major_minor, triple, path.as_ref(), &macho, &data)?; - - errors.extend(local_errors); - seen_dylibs.extend(local_seen_dylibs); - } - goblin::mach::Mach::Fat(_) => { - if path.to_string_lossy() != "python/build/lib/libclang_rt.osx.a" { - errors.push(format!("unexpected fat mach-o binary: {}", path.display())); - } - } - }, - goblin::Object::PE(pe) => { - // We don't care about the wininst-*.exe distutils executables. - if !path.to_string_lossy().contains("wininst-") { - errors.extend(validate_pe(path.as_ref(), &pe)?); - } - } - _ => {} - } - } - - Ok((errors, seen_dylibs)) -} - -fn validate_json(json: &PythonJsonMain, triple: &str, is_debug: bool) -> Result> { - let mut errors = vec![]; - - if json.version != "7" { - errors.push(format!( - "expected version 7 in PYTHON.json; got {}", - json.version - )); - } - - // Distributions built with Apple SDKs should have SDK metadata. - if triple.contains("-apple-") { - if json.apple_sdk_canonical_name.is_none() { - errors.push("JSON missing apple_sdk_canonical_name on Apple triple".to_string()); - } - if json.apple_sdk_deployment_target.is_none() { - errors.push("JSON missing apple_sdk_deployment_target on Apple triple".to_string()); - } - if json.apple_sdk_platform.is_none() { - errors.push("JSON missing apple_sdk_platform on Apple triple".to_string()); - } - if json.apple_sdk_version.is_none() { - errors.push("JSON missing apple_sdk_version on Apple triple".to_string()); - } - } - - let wanted_platform_tag = PLATFORM_TAG_BY_TRIPLE - .get(triple) - .ok_or_else(|| anyhow!("platform tag not defined for triple {}", triple))? - .clone(); - - if json.python_platform_tag != wanted_platform_tag { - errors.push(format!( - "wanted platform tag {}; got {}", - wanted_platform_tag, json.python_platform_tag - )); - } - - if is_debug - && !json - .python_config_vars - .get("abiflags") - .unwrap() - .contains('d') - { - errors.push("abiflags does not contain 'd'".to_string()); - } - - for extension in json.build_info.extensions.keys() { - if GLOBALLY_BANNED_EXTENSIONS.contains(&extension.as_str()) { - errors.push(format!("banned extension detected: {}", extension)); - } - } - - Ok(errors) -} - -fn open_distribution_archive(path: &Path) -> Result> { +pub fn open_distribution_archive(path: &Path) -> Result> { let fh = std::fs::File::open(path).with_context(|| format!("unable to open {}", path.display()))?; @@ -687,261 +27,219 @@ fn open_distribution_archive(path: &Path) -> Result> { Ok(tar::Archive::new(dctx)) } -fn validate_distribution(dist_path: &Path) -> Result> { - let mut errors = vec![]; - let mut seen_dylibs = BTreeSet::new(); - let mut seen_paths = BTreeSet::new(); - let mut seen_symlink_targets = BTreeSet::new(); - - let dist_filename = dist_path - .file_name() - .expect("unable to obtain filename") - .to_string_lossy(); - - let triple = RECOGNIZED_TRIPLES - .iter() - .find(|triple| { - dist_path - .to_string_lossy() - .contains(&format!("-{}-", triple)) - }) - .ok_or_else(|| { - anyhow!( - "could not identify triple from distribution filename: {}", - dist_path.display() +fn main_impl() -> Result<()> { + let app = Command::new("Python Build") + .arg_required_else_help(true) + .version("0.1") + .author("Gregory Szorc ") + .about("Perform tasks related to building Python distributions"); + let app = app.subcommand( + Command::new("fetch-release-distributions") + .about("Fetch builds from GitHub Actions that are release artifacts") + .arg( + Arg::new("token") + .long("token") + .action(ArgAction::Set) + .required(true) + .help("GitHub API token"), ) - })?; - - let python_major_minor = if dist_filename.starts_with("cpython-3.8.") { - "3.8" - } else if dist_filename.starts_with("cpython-3.9.") { - "3.9" - } else if dist_filename.starts_with("cpython-3.10.") { - "3.10" - } else { - return Err(anyhow!("could not parse Python version from filename")); - }; - - let is_debug = dist_filename.contains("-debug-"); - - let mut tf = open_distribution_archive(&dist_path)?; - - // First entry in archive should be python/PYTHON.json. - let mut entries = tf.entries()?; - - let mut wanted_python_paths = BTreeSet::new(); - - let mut entry = entries.next().unwrap()?; - if entry.path()?.display().to_string() == "python/PYTHON.json" { - seen_paths.insert(entry.path()?.to_path_buf()); - - let mut data = Vec::new(); - entry.read_to_end(&mut data)?; - let json = parse_python_json(&data).context("parsing PYTHON.json")?; - errors.extend(validate_json(&json, triple, is_debug)?); - - wanted_python_paths.extend(json.python_paths.values().map(|x| format!("python/{}", x))); - } else { - errors.push(format!( - "1st archive entry should be for python/PYTHON.json; got {}", - entry.path()?.display() - )); - } - - for entry in entries { - let mut entry = entry.map_err(|e| anyhow!("failed to iterate over archive: {}", e))?; - let path = entry.path()?.to_path_buf(); - - seen_paths.insert(path.clone()); - - if let Some(link_name) = entry.link_name()? { - seen_symlink_targets.insert(path.parent().unwrap().join(link_name)); - } + .arg( + Arg::new("commit") + .long("commit") + .action(ArgAction::Set) + .help("Git commit whose artifacts to fetch"), + ) + .arg( + Arg::new("dest") + .long("dest") + .required(true) + .action(ArgAction::Set) + .value_parser(value_parser!(PathBuf)) + .help("Destination directory"), + ) + .arg( + Arg::new("organization") + .long("org") + .action(ArgAction::Set) + .default_value("astral-sh") + .help("GitHub organization"), + ) + .arg( + Arg::new("repo") + .long("repo") + .action(ArgAction::Set) + .default_value("python-build-standalone") + .help("GitHub repository name"), + ) + .arg( + Arg::new("github-uri") + .long("github-uri") + .action(ArgAction::Set) + .help("Alternative GitHub URI"), + ), + ); - // If this path starts with a path referenced in wanted_python_paths, - // remove the prefix from wanted_python_paths so we don't error on it - // later. - let removals = wanted_python_paths - .iter() - .filter(|prefix| path.starts_with(prefix)) - .map(|x| x.to_string()) - .collect::>(); - for p in removals { - wanted_python_paths.remove(&p); - } + let app = app.subcommand( + Command::new("convert-install-only") + .about("Convert a .tar.zst archive to an install_only tar.gz archive") + .arg( + Arg::new("path") + .required(true) + .action(ArgAction::Append) + .value_parser(value_parser!(PathBuf)) + .help("Path of archive to convert"), + ), + ); - let mut data = Vec::new(); - entry.read_to_end(&mut data)?; + let app = app.subcommand( + Command::new("convert-install-only-stripped") + .about("Convert an install_only .tar.gz archive to an install_only_stripped tar.gz archive") + .arg( + Arg::new("path") + .required(true) + .action(ArgAction::Append) + .value_parser(value_parser!(PathBuf)) + .help("Path of archive to convert"), + ), + ); - let (local_errors, local_seen_dylibs) = - validate_possible_object_file(python_major_minor, &triple, &path, &data)?; - errors.extend(local_errors); - seen_dylibs.extend(local_seen_dylibs); + let app = app.subcommand( + Command::new("upload-release-distributions") + .about("Upload release distributions to a GitHub release") + .arg( + Arg::new("token") + .long("token") + .action(ArgAction::Set) + .required(true) + .help("GitHub API token"), + ) + .arg( + Arg::new("dist") + .long("dist") + .action(ArgAction::Set) + .required(true) + .value_parser(value_parser!(PathBuf)) + .help("Directory with release artifacts"), + ) + .arg( + Arg::new("datetime") + .long("datetime") + .action(ArgAction::Set) + .required(true) + .help("Date/time tag associated with builds"), + ) + .arg( + Arg::new("dry_run") + .short('n') + .action(ArgAction::SetTrue) + .help("Dry run mode; do not actually upload"), + ) + .arg( + Arg::new("tag") + .long("tag") + .action(ArgAction::Set) + .required(true) + .help("Release tag"), + ) + .arg( + Arg::new("ignore_missing") + .long("ignore-missing") + .action(ArgAction::SetTrue) + .help("Continue even if there are missing artifacts"), + ) + .arg( + Arg::new("organization") + .long("org") + .action(ArgAction::Set) + .default_value("astral-sh") + .help("GitHub organization"), + ) + .arg( + Arg::new("repo") + .long("repo") + .action(ArgAction::Set) + .default_value("python-build-standalone") + .help("GitHub repository name"), + ) + .arg( + Arg::new("github-uri") + .long("github-uri") + .action(ArgAction::Set) + .help("Alternative GitHub URI"), + ), + ); - // Descend into archive files (static libraries are archive files and members - // are usually object files). - if let Ok(archive) = goblin::archive::Archive::parse(&data) { - for member in archive.members() { - let member_data = archive - .extract(member, &data) - .with_context(|| format!("extracting {} from {}", member, path.display()))?; + let app = app.subcommand( + Command::new("validate-distribution") + .about("Ensure a distribution archive conforms to standards") + .arg( + Arg::new("macos_sdks_path") + .long("macos-sdks-path") + .action(ArgAction::Set) + .help("Path to a directory containing MacOS SDKs (typically a checkout of https://github.com/phracker/MacOSX-SDKs)") + ) + .arg( + Arg::new("path") + .help("Path to tar.zst file to validate") + .action(ArgAction::Append) + .value_parser(value_parser!(PathBuf)) + .required(true), + ), + ); - let member_path = path.with_file_name(format!( - "{}:{}", - path.file_name().unwrap().to_string_lossy(), - member - )); + let matches = app.get_matches(); - let (local_errors, local_seen_dylibs) = validate_possible_object_file( - python_major_minor, - &triple, - &member_path, - &member_data, - )?; - errors.extend(local_errors); - seen_dylibs.extend(local_seen_dylibs); + match matches.subcommand() { + Some(("convert-install-only", args)) => { + for path in args.get_many::("path").unwrap() { + let dest_path = release::produce_install_only(path)?; + println!("wrote {}", dest_path.display()); } - } - - if path == PathBuf::from("python/PYTHON.json") { - let json = parse_python_json(&data).context("parsing PYTHON.json")?; - errors.extend(validate_json(&json, triple, is_debug)?); - } - } - - for path in seen_symlink_targets { - if !seen_paths.contains(&path) { - errors.push(format!( - "symlink target {} referenced in archive but not found", - path.display() - )); - } - } - - for path in wanted_python_paths { - errors.push(format!( - "path prefix {} seen in python_paths does not appear in archive", - path - )); - } - let wanted_dylibs = BTreeSet::from_iter( - allowed_dylibs_for_triple(triple) - .iter() - .filter(|d| d.required) - .map(|d| d.name.clone()), - ); - - for lib in wanted_dylibs.difference(&seen_dylibs) { - errors.push(format!("required library dependency {} not seen", lib)); - } + Ok(()) + } + Some(("convert-install-only-stripped", args)) => { + let llvm_dir = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(release::bootstrap_llvm())?; + for path in args.get_many::("path").unwrap() { + let dest_path = release::produce_install_only_stripped(path, &llvm_dir)?; + println!("wrote {}", dest_path.display()); + } - if triple.contains("-windows-") && dist_path.to_string_lossy().contains("-static-") { - for path in WANTED_WINDOWS_STATIC_PATHS.difference(&seen_paths) { - errors.push(format!("required path {} not seen", path.display())); + Ok(()) } - } - - Ok(errors) -} - -fn verify_distribution_behavior(dist_path: &Path) -> Result> { - let mut errors = vec![]; - - let temp_dir = tempfile::TempDir::new()?; - - let mut tf = open_distribution_archive(dist_path)?; - - tf.unpack(temp_dir.path())?; - - let python_json_path = temp_dir.path().join("python").join("PYTHON.json"); - let python_json_data = std::fs::read(&python_json_path)?; - let python_json = parse_python_json(&python_json_data)?; - - let python_exe = temp_dir.path().join("python").join(python_json.python_exe); - - let test_file = temp_dir.path().join("verify.py"); - std::fs::write(&test_file, PYTHON_VERIFICATIONS.as_bytes())?; - - eprintln!(" running interpreter tests (output should follow)"); - let output = duct::cmd(&python_exe, &[test_file.display().to_string()]) - .stdout_to_stderr() - .unchecked() - .run()?; - - if !output.status.success() { - errors.push("errors running interpreter tests".to_string()); - } - - Ok(errors) -} - -fn command_validate_distribution(args: &ArgMatches) -> Result<()> { - let run = args.is_present("run"); - - let mut success = true; - - for path in args.values_of("path").unwrap() { - let path = PathBuf::from(path); - println!("validating {}", path.display()); - let mut errors = validate_distribution(&path)?; - - if run { - errors.extend(verify_distribution_behavior(&path)?.into_iter()); + Some(("fetch-release-distributions", args)) => { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(github::command_fetch_release_distributions(args)) } - - if errors.is_empty() { - println!(" {} OK", path.display()); - } else { - for error in errors { - println!(" error: {}", error); - } - - success = false; + Some(("upload-release-distributions", args)) => { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(github::command_upload_release_distributions(args)) } - } - - if success { - Ok(()) - } else { - Err(anyhow!("errors found")) - } -} - -fn main_impl() -> Result<()> { - let matches = App::new("Python Build") - .setting(AppSettings::ArgRequiredElseHelp) - .version("0.1") - .author("Gregory Szorc ") - .about("Perform tasks related to building Python distributions") - .subcommand( - SubCommand::with_name("validate-distribution") - .about("Ensure a distribution archive conforms to standards") - .arg( - Arg::with_name("run") - .long("--run") - .help("Run the interpreter to verify behavior"), - ) - .arg( - Arg::with_name("path") - .help("Path to tar.zst file to validate") - .multiple(true) - .required(true), - ), - ) - .get_matches(); - - match matches.subcommand() { - ("validate-distribution", Some(args)) => command_validate_distribution(args), + Some(("validate-distribution", args)) => validation::command_validate_distribution(args), _ => Err(anyhow!("invalid sub-command")), } } fn main() { + // Install rustls' ring crypto provider before any TLS connection is attempted. + rustls::crypto::ring::default_provider() + .install_default() + .expect("failed to install rustls crypto provider"); + let exit_code = match main_impl() { Ok(()) => 0, Err(err) => { - eprintln!("Error: {:?}", err); + eprintln!("Error: {err:?}"); 1 } }; diff --git a/src/release.rs b/src/release.rs new file mode 100644 index 000000000..de074b32a --- /dev/null +++ b/src/release.rs @@ -0,0 +1,814 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use anyhow::Context; +use futures::StreamExt; + +use object::FileKind; +use std::{ + process::{Command, Stdio}, + str::FromStr, +}; +use url::Url; +use { + crate::json::parse_python_json, + anyhow::{Result, anyhow}, + once_cell::sync::Lazy, + pep440_rs::VersionSpecifier, + std::{ + collections::{BTreeMap, BTreeSet}, + io::{BufRead, Read, Write}, + path::{Path, PathBuf}, + }, +}; + +/// Describes a release for a given target triple. +pub struct TripleRelease { + /// Build suffixes to release. + pub suffixes: Vec<&'static str>, + /// Build suffix to use for the `install_only` artifact. + pub install_only_suffix: &'static str, + /// Build suffix to use for the freethreaded `install_only` artifact. + pub freethreaded_install_only_suffix: &'static str, + /// Minimum Python version this triple is released for. + pub python_version_requirement: Option, + /// Additional build suffixes to release conditional on the Python version. + pub conditional_suffixes: Vec, +} + +/// Describes additional build suffixes conditional on the Python version. +/// +/// e.g., free-threaded builds which are only available for Python 3.13+. +pub struct ConditionalSuffixes { + /// The minimum Python version to include these suffixes for. + pub python_version_requirement: VersionSpecifier, + /// Build suffixes to release. + pub suffixes: Vec<&'static str>, +} + +impl TripleRelease { + pub fn suffixes<'a>( + &'a self, + python_version: Option<&'a pep440_rs::Version>, + ) -> impl Iterator + 'a { + self.suffixes + .iter() + .copied() + .chain( + self.conditional_suffixes + .iter() + .flat_map(move |conditional| { + if python_version.is_none() + || python_version.is_some_and(|python_version| { + conditional + .python_version_requirement + .contains(python_version) + }) + { + conditional.suffixes.iter().copied() + } else { + [].iter().copied() + } + }), + ) + } +} + +pub static RELEASE_TRIPLES: Lazy> = Lazy::new(|| { + let mut h = BTreeMap::new(); + + // macOS. + let macos_suffixes = vec!["debug", "pgo+lto"]; + let macos_suffixes_313 = vec!["freethreaded+debug", "freethreaded+pgo+lto"]; + h.insert( + "aarch64-apple-darwin", + TripleRelease { + suffixes: macos_suffixes.clone(), + install_only_suffix: "pgo+lto", + freethreaded_install_only_suffix: "freethreaded+pgo+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13.0rc0").unwrap(), + suffixes: macos_suffixes_313.clone(), + }], + }, + ); + h.insert( + "x86_64-apple-darwin", + TripleRelease { + suffixes: macos_suffixes, + install_only_suffix: "pgo+lto", + freethreaded_install_only_suffix: "freethreaded+pgo+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13.0rc0").unwrap(), + suffixes: macos_suffixes_313.clone(), + }], + }, + ); + + // Windows. + h.insert( + "i686-pc-windows-msvc", + TripleRelease { + suffixes: vec!["pgo"], + install_only_suffix: "pgo", + freethreaded_install_only_suffix: "freethreaded+pgo", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: vec!["freethreaded+pgo"], + }], + }, + ); + h.insert( + "x86_64-pc-windows-msvc", + TripleRelease { + suffixes: vec!["pgo"], + install_only_suffix: "pgo", + freethreaded_install_only_suffix: "freethreaded+pgo", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: vec!["freethreaded+pgo"], + }], + }, + ); + h.insert( + "aarch64-pc-windows-msvc", + TripleRelease { + suffixes: vec!["pgo"], + install_only_suffix: "pgo", + freethreaded_install_only_suffix: "freethreaded+pgo", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.11").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: vec!["freethreaded+pgo"], + }], + }, + ); + + // Linux. + let linux_suffixes_pgo = vec!["debug", "pgo+lto"]; + let linux_suffixes_nopgo = vec!["debug", "lto", "noopt"]; + let linux_suffixes_musl = vec![ + "debug", + "lto", + "noopt", + "debug+static", + "lto+static", + "noopt+static", + ]; + let linux_suffixes_musl_freethreaded = vec![ + "freethreaded+debug", + "freethreaded+lto", + "freethreaded+noopt", + ]; + let linux_suffixes_pgo_freethreaded = vec!["freethreaded+debug", "freethreaded+pgo+lto"]; + let linux_suffixes_nopgo_freethreaded = vec![ + "freethreaded+debug", + "freethreaded+lto", + "freethreaded+noopt", + ]; + + h.insert( + "aarch64-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_pgo.clone(), + install_only_suffix: "pgo+lto", + freethreaded_install_only_suffix: "freethreaded+pgo+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_pgo_freethreaded.clone(), + }], + }, + ); + + h.insert( + "ppc64le-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_nopgo.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_nopgo_freethreaded.clone(), + }], + }, + ); + + h.insert( + "riscv64-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_nopgo.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_nopgo_freethreaded.clone(), + }], + }, + ); + + h.insert( + "s390x-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_nopgo.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_nopgo_freethreaded.clone(), + }], + }, + ); + + h.insert( + "armv7-unknown-linux-gnueabi", + TripleRelease { + suffixes: linux_suffixes_nopgo.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_nopgo_freethreaded.clone(), + }], + }, + ); + + h.insert( + "armv7-unknown-linux-gnueabihf", + TripleRelease { + suffixes: linux_suffixes_nopgo.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_nopgo_freethreaded.clone(), + }], + }, + ); + + h.insert( + "x86_64-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_pgo.clone(), + install_only_suffix: "pgo+lto", + freethreaded_install_only_suffix: "freethreaded+pgo+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_pgo_freethreaded.clone(), + }], + }, + ); + h.insert( + "x86_64_v2-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_pgo.clone(), + install_only_suffix: "pgo+lto", + freethreaded_install_only_suffix: "freethreaded+pgo+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_pgo_freethreaded.clone(), + }], + }, + ); + h.insert( + "x86_64_v3-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_pgo.clone(), + install_only_suffix: "pgo+lto", + freethreaded_install_only_suffix: "freethreaded+pgo+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_pgo_freethreaded.clone(), + }], + }, + ); + h.insert( + "x86_64_v4-unknown-linux-gnu", + TripleRelease { + suffixes: linux_suffixes_pgo.clone(), + install_only_suffix: "pgo+lto", + freethreaded_install_only_suffix: "freethreaded+pgo+lto", + python_version_requirement: Some(VersionSpecifier::from_str(">=3.10").unwrap()), + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_pgo_freethreaded.clone(), + }], + }, + ); + h.insert( + "x86_64-unknown-linux-musl", + TripleRelease { + suffixes: linux_suffixes_musl.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_musl_freethreaded.clone(), + }], + }, + ); + h.insert( + "x86_64_v2-unknown-linux-musl", + TripleRelease { + suffixes: linux_suffixes_musl.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_musl_freethreaded.clone(), + }], + }, + ); + h.insert( + "x86_64_v3-unknown-linux-musl", + TripleRelease { + suffixes: linux_suffixes_musl.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_musl_freethreaded.clone(), + }], + }, + ); + h.insert( + "x86_64_v4-unknown-linux-musl", + TripleRelease { + suffixes: linux_suffixes_musl.clone(), + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_musl_freethreaded.clone(), + }], + }, + ); + h.insert( + "aarch64-unknown-linux-musl", + TripleRelease { + suffixes: vec!["debug", "lto", "noopt"], + install_only_suffix: "lto", + freethreaded_install_only_suffix: "freethreaded+lto", + python_version_requirement: None, + conditional_suffixes: vec![ConditionalSuffixes { + python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + suffixes: linux_suffixes_musl_freethreaded.clone(), + }], + }, + ); + + h +}); + +/// Build a mapping from local artifact filenames (as found in the dist directory after +/// `fetch-release-distributions`) to their corresponding GitHub Release asset names. +/// +/// Both the source and destination names are derived from the same set of build artifacts; +/// the difference is that GitHub Release names embed the release tag and use a normalised +/// suffix (`-full`, no datetime component), while the local artifact names embed the build +/// datetime and no tag. +/// +/// Example: +/// * source: `cpython-3.12.4-x86_64-unknown-linux-gnu-pgo+lto-20240722T0909.tar.zst` +/// * dest: `cpython-3.12.4+20240722-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst` +pub fn build_wanted_filenames( + // `filenames` must already be filtered to entries that contain `datetime` and start with `cpython-`. + filenames: &BTreeSet, + datetime: &str, + tag: &str, +) -> Result> { + let mut python_versions = BTreeSet::new(); + for filename in filenames { + let parts = filename.split('-').collect::>(); + python_versions.insert(parts[1].to_string()); + } + + let mut wanted_filenames = BTreeMap::new(); + for version in &python_versions { + for (triple, release) in RELEASE_TRIPLES.iter() { + let python_version = pep440_rs::Version::from_str(version)?; + if let Some(req) = &release.python_version_requirement { + if !req.contains(&python_version) { + continue; + } + } + + for suffix in release.suffixes(Some(&python_version)) { + wanted_filenames.insert( + format!("cpython-{version}-{triple}-{suffix}-{datetime}.tar.zst"), + format!("cpython-{version}+{tag}-{triple}-{suffix}-full.tar.zst"), + ); + } + + wanted_filenames.insert( + format!("cpython-{version}-{triple}-install_only-{datetime}.tar.gz"), + format!("cpython-{version}+{tag}-{triple}-install_only.tar.gz"), + ); + + wanted_filenames.insert( + format!("cpython-{version}-{triple}-install_only_stripped-{datetime}.tar.gz"), + format!("cpython-{version}+{tag}-{triple}-install_only_stripped.tar.gz"), + ); + + // Free-threading only available for Python 3.13+ + let freethreaded_conditional = VersionSpecifier::from_str(">=3.13.0rc0").unwrap(); + if freethreaded_conditional.contains(&python_version) { + wanted_filenames.insert( + format!( + "cpython-{version}-{triple}-freethreaded-install_only-{datetime}.tar.gz" + ), + format!("cpython-{version}+{tag}-{triple}-freethreaded-install_only.tar.gz"), + ); + + wanted_filenames.insert( + format!("cpython-{version}-{triple}-freethreaded-install_only_stripped-{datetime}.tar.gz"), + format!("cpython-{version}+{tag}-{triple}-freethreaded-install_only_stripped.tar.gz"), + ); + } + } + } + + Ok(wanted_filenames) +} + +/// Extension modules that should not be included in "install only" archives. +const INSTALL_ONLY_DROP_EXTENSIONS: &[&str] = &[ + "_ctypes_test", + "_testbuffer", + "_testcapi", + "_testexternalinspection", + "_testimportmultiple", + "_testinternalcapi", + "_testlimitedcapi", + "_testmultiphase", + "_testsinglephase", +]; + +/// Convert a .tar.zst archive to an install-only .tar.gz archive. +pub fn convert_to_install_only(reader: impl BufRead, writer: W) -> Result { + let dctx = zstd::stream::Decoder::new(reader)?; + + let mut tar_in = tar::Archive::new(dctx); + + let writer = flate2::write::GzEncoder::new(writer, flate2::Compression::default()); + + let mut builder = tar::Builder::new(writer); + + let mut entries = tar_in.entries()?; + + // First entry in archive should be python/PYTHON.json. + let mut entry = entries.next().expect("tar must have content")?; + if entry.path_bytes().as_ref() != b"python/PYTHON.json" { + return Err(anyhow!("first archive entry not PYTHON.json")); + } + + let mut json_data = vec![]; + entry.read_to_end(&mut json_data)?; + + let json_main = parse_python_json(&json_data).context("failed to parse PYTHON.json")?; + + let stdlib_path = json_main + .python_paths + .get("stdlib") + .expect("stdlib entry expected"); + + let mut drop_paths = BTreeSet::new(); + + for (extension, info) in &json_main.build_info.extensions { + if !INSTALL_ONLY_DROP_EXTENSIONS.contains(&extension.as_str()) { + continue; + } + + for entry in info { + if let Some(rel_path) = entry.shared_lib.as_ref() { + let full_path = format!("python/{}", rel_path); + drop_paths.insert(full_path.into_bytes()); + } + } + } + + for entry in entries { + let mut entry = entry?; + + let path_bytes = entry.path_bytes(); + + if !path_bytes.starts_with(b"python/install/") { + continue; + } + + // Strip the libpython static library, as it significantly + // increases the size of the archive and isn't needed in most cases. + if path_bytes + .windows(b"/libpython".len()) + .any(|x| x == b"/libpython") + && path_bytes.ends_with(b".a") + { + continue; + } + + // Strip standard library test modules, as they aren't needed in regular + // installs. We do this based on the metadata in PYTHON.json for + // consistency. + if json_main + .python_stdlib_test_packages + .iter() + .any(|test_package| { + let package_path = + format!("python/{}/{}/", stdlib_path, test_package.replace('.', "/")); + + path_bytes.starts_with(package_path.as_bytes()) + }) + { + continue; + } + + if drop_paths.contains(&path_bytes.to_vec()) { + continue; + } + + let mut data = vec![]; + entry.read_to_end(&mut data)?; + + let path = entry.path()?; + let new_path = PathBuf::from("python").join(path.strip_prefix("python/install/")?); + + let mut header = entry.header().clone(); + header.set_path(&new_path)?; + header.set_cksum(); + + builder.append(&header, std::io::Cursor::new(data))?; + } + + Ok(builder.into_inner()?.finish()?) +} + +/// Run `llvm-strip` over the given data, returning the stripped data. +fn llvm_strip(data: &[u8], llvm_dir: &Path) -> Result> { + let mut command = Command::new(llvm_dir.join("bin/llvm-strip")) + .arg("--strip-debug") + .arg("-") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .with_context(|| "failed to spawn llvm-strip")?; + + command + .stdin + .as_mut() + .unwrap() + .write_all(data) + .with_context(|| "failed to write data to llvm-strip")?; + + let output = command + .wait_with_output() + .with_context(|| "failed to wait for llvm-strip")?; + if !output.status.success() { + return Err(anyhow!("llvm-strip failed: {}", output.status)); + } + + Ok(output.stdout) +} + +/// Given an install-only .tar.gz archive, strip the underlying build. +pub fn convert_to_stripped( + reader: impl BufRead, + writer: W, + llvm_dir: &Path, +) -> Result { + let dctx = flate2::read::GzDecoder::new(reader); + + let mut tar_in = tar::Archive::new(dctx); + + let writer = flate2::write::GzEncoder::new(writer, flate2::Compression::default()); + + let mut builder = tar::Builder::new(writer); + + for entry in tar_in.entries()? { + let mut entry = entry?; + + let mut data = vec![]; + entry.read_to_end(&mut data)?; + + let path = entry.path()?; + + // Drop PDB files. + match pdb::PDB::open(std::io::Cursor::new(&data)) { + Ok(_) => { + continue; + } + Err(err) => { + if path.extension().is_some_and(|ext| ext == "pdb") { + println!( + "file with `.pdb` extension ({}) failed to parse as PDB :{err}", + path.display() + ); + } + } + } + + // If we have an ELF, Mach-O, or PE file, strip it in-memory with `llvm-strip`, and + // return the stripped data. + if matches!( + FileKind::parse(data.as_slice()), + Ok(FileKind::Elf32 + | FileKind::Elf64 + | FileKind::MachO32 + | FileKind::MachO64 + | FileKind::MachOFat32 + | FileKind::MachOFat64 + | FileKind::Pe32 + | FileKind::Pe64) + ) { + // Skip stripping MSVC runtime DLLs + let filename = path.file_name().and_then(|n| n.to_str()); + if !matches!(filename, Some("vcruntime140.dll" | "vcruntime140_1.dll")) { + data = llvm_strip(&data, llvm_dir) + .with_context(|| format!("failed to strip {}", path.display()))?; + } + } + + let mut header = entry.header().clone(); + header.set_size(data.len() as u64); + header.set_cksum(); + + builder.append(&header, std::io::Cursor::new(data))?; + } + + Ok(builder.into_inner()?.finish()?) +} + +/// Create an install-only .tar.gz archive from a .tar.zst archive. +pub fn produce_install_only(tar_zst_path: &Path) -> Result { + let buf = std::fs::read(tar_zst_path)?; + + let gz_data = convert_to_install_only(std::io::Cursor::new(buf), std::io::Cursor::new(vec![])) + .context(format!( + "failed to convert `{}` to install_only", + tar_zst_path.display() + ))? + .into_inner(); + + let filename = tar_zst_path + .file_name() + .expect("should have filename") + .to_string_lossy(); + + let mut name_parts = filename + .split('-') + .map(|x| x.to_string()) + .collect::>(); + let parts_len = name_parts.len(); + let flavor_idx = parts_len - 2; + + if name_parts[flavor_idx].contains("freethreaded") { + name_parts + .splice( + flavor_idx..flavor_idx + 1, + ["freethreaded".to_string(), "install_only".to_string()], + ) + .for_each(drop); + } else { + name_parts[flavor_idx] = "install_only".to_string(); + } + + let install_only_name = name_parts.join("-"); + let install_only_name = install_only_name.replace(".tar.zst", ".tar.gz"); + + let dest_path = tar_zst_path.with_file_name(install_only_name); + std::fs::write(&dest_path, gz_data)?; + + Ok(dest_path) +} + +pub fn produce_install_only_stripped(tar_gz_path: &Path, llvm_dir: &Path) -> Result { + let buf = std::fs::read(tar_gz_path)?; + + let size_before = buf.len(); + + let gz_data = convert_to_stripped( + std::io::Cursor::new(buf), + std::io::Cursor::new(vec![]), + llvm_dir, + ) + .context(format!( + "failed to convert `{}` to install_only_stripped", + tar_gz_path.display() + ))? + .into_inner(); + + let size_after = gz_data.len(); + + println!( + "stripped {} from {size_before} to {size_after} bytes", + tar_gz_path.display() + ); + + // Given `cpython-3.12.4-x86_64_v3-unknown-linux-gnu-install_only-20240722T0909.tar.gz`, + // map to `cpython-3.12.4-x86_64_v3-unknown-linux-gnu-install_only_stripped-20240722T0909.tar.gz`. + let filename = tar_gz_path + .file_name() + .expect("should have filename") + .to_string_lossy(); + + let mut name_parts = filename + .split('-') + .map(|x| x.to_string()) + .collect::>(); + let parts_len = name_parts.len(); + + name_parts[parts_len - 2] = "install_only_stripped".to_string(); + + let install_only_name = name_parts.join("-"); + + let dest_path = tar_gz_path.with_file_name(install_only_name); + std::fs::write(&dest_path, gz_data)?; + + Ok(dest_path) +} + +/// URL from which to download LLVM. +/// +/// To be kept in sync with `pythonbuild/downloads.py`. +static LLVM_URL: Lazy = Lazy::new(|| { + if cfg!(target_os = "macos") { + if std::env::consts::ARCH == "aarch64" { + Url::parse("https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20260410/llvm-22.1.3+20260410-aarch64-apple-darwin.tar.zst").unwrap() + } else if std::env::consts::ARCH == "x86_64" { + Url::parse("https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20260410/llvm-22.1.3+20260410-x86_64-apple-darwin.tar.zst").unwrap() + } else { + panic!("unsupported macOS architecture"); + } + } else if cfg!(target_os = "linux") { + Url::parse("https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20260410/llvm-22.1.3+20260410-gnu_only-x86_64-unknown-linux-gnu.tar.zst").unwrap() + } else { + panic!("unsupported platform"); + } +}); + +/// Bootstrap `llvm` for the current platform. +/// +/// Returns the path to the top-level `llvm` directory. +pub async fn bootstrap_llvm() -> Result { + let url = &*LLVM_URL; + let filename = url.path_segments().unwrap().next_back().unwrap(); + + let llvm_dir = Path::new("build").join("llvm"); + std::fs::create_dir_all(&llvm_dir)?; + + // If `llvm` is already available with the target version, return it. + if llvm_dir.join(filename).exists() { + return Ok(llvm_dir.join("llvm")); + } + + println!("Downloading LLVM tarball from: {url}"); + + // Create a temporary directory to download and extract the LLVM tarball. + let temp_dir = tempfile::TempDir::new()?; + + // Download the tarball. + let tarball_path = temp_dir + .path() + .join(url.path_segments().unwrap().next_back().unwrap()); + let mut tarball_file = tokio::fs::File::create(&tarball_path).await?; + let mut bytes_stream = reqwest::Client::new() + .get(url.clone()) + .send() + .await? + .bytes_stream(); + while let Some(chunk) = bytes_stream.next().await { + tokio::io::copy(&mut chunk?.as_ref(), &mut tarball_file).await?; + } + + // Decompress the tarball. + let tarball = std::fs::File::open(&tarball_path)?; + let tar = zstd::stream::Decoder::new(std::io::BufReader::new(tarball))?; + let mut archive = tar::Archive::new(tar); + archive.unpack(temp_dir.path())?; + + // Persist the directory. + match tokio::fs::remove_dir_all(&llvm_dir).await { + Ok(_) => {} + Err(err) if err.kind() == std::io::ErrorKind::NotFound => {} + Err(err) => return Err(err).context("failed to remove existing llvm directory"), + } + tokio::fs::rename(temp_dir.keep(), &llvm_dir).await?; + + Ok(llvm_dir.join("llvm")) +} diff --git a/src/validation.rs b/src/validation.rs new file mode 100644 index 000000000..ec2ef2b43 --- /dev/null +++ b/src/validation.rs @@ -0,0 +1,2216 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use { + crate::{json::*, macho::*}, + anyhow::{Context, Result, anyhow}, + clap::ArgMatches, + normalize_path::NormalizePath, + object::{ + Architecture, Endianness, FileKind, Object, SectionIndex, SymbolScope, + elf::{ + ET_DYN, ET_EXEC, FileHeader32, FileHeader64, PF_X, PT_GNU_STACK, SHN_UNDEF, STB_GLOBAL, + STB_WEAK, STV_DEFAULT, STV_HIDDEN, + }, + macho::{LC_CODE_SIGNATURE, MH_OBJECT, MH_TWOLEVEL, MachHeader32, MachHeader64}, + read::{ + elf::{Dyn, FileHeader, ProgramHeader, SectionHeader, Sym}, + macho::{LoadCommandVariant, MachHeader, Nlist, Section, Segment}, + pe::{ImageNtHeaders, PeFile, PeFile32, PeFile64}, + }, + }, + once_cell::sync::Lazy, + std::{ + collections::{BTreeSet, HashMap}, + convert::TryInto, + io::Read, + iter::FromIterator, + path::{Path, PathBuf}, + }, +}; + +const RECOGNIZED_TRIPLES: &[&str] = &[ + "aarch64-apple-darwin", + "aarch64-pc-windows-msvc", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "armv7-unknown-linux-gnueabi", + "armv7-unknown-linux-gnueabihf", + "i686-pc-windows-msvc", + "i686-unknown-linux-gnu", + // Note there's build support for mips* targets but they are not tested + // See https://github.com/astral-sh/python-build-standalone/issues/412 + "mips-unknown-linux-gnu", + "mipsel-unknown-linux-gnu", + "mips64el-unknown-linux-gnuabi64", + "ppc64le-unknown-linux-gnu", + "riscv64-unknown-linux-gnu", + "s390x-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu", + "x86_64_v2-unknown-linux-gnu", + "x86_64_v3-unknown-linux-gnu", + "x86_64_v4-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "x86_64_v2-unknown-linux-musl", + "x86_64_v3-unknown-linux-musl", + "x86_64_v4-unknown-linux-musl", +]; + +const ELF_ALLOWED_LIBRARIES: &[&str] = &[ + // LSB set. + "libc.so.6", + "libdl.so.2", + "libm.so.6", + "libpthread.so.0", + "librt.so.1", + "libutil.so.1", +]; + +const PE_ALLOWED_LIBRARIES: &[&str] = &[ + "ADVAPI32.dll", + "api-ms-win-core-path-l1-1-0.dll", + "api-ms-win-crt-conio-l1-1-0.dll", + "api-ms-win-crt-convert-l1-1-0.dll", + "api-ms-win-crt-heap-l1-1-0.dll", + "api-ms-win-crt-environment-l1-1-0.dll", + "api-ms-win-crt-filesystem-l1-1-0.dll", + "api-ms-win-crt-locale-l1-1-0.dll", + "api-ms-win-crt-math-l1-1-0.dll", + "api-ms-win-crt-process-l1-1-0.dll", + "api-ms-win-crt-runtime-l1-1-0.dll", + "api-ms-win-crt-stdio-l1-1-0.dll", + "api-ms-win-crt-string-l1-1-0.dll", + "api-ms-win-crt-time-l1-1-0.dll", + "api-ms-win-crt-utility-l1-1-0.dll", + "bcrypt.dll", + "Cabinet.dll", + "COMCTL32.dll", + "COMDLG32.dll", + "CRYPT32.dll", + "GDI32.dll", + "IMM32.dll", + "IPHLPAPI.DLL", + "KERNEL32.dll", + "msi.dll", + "NETAPI32.dll", + "ole32.dll", + "OLEAUT32.dll", + "PROPSYS.dll", + "RPCRT4.dll", + "SHELL32.dll", + "SHLWAPI.dll", + "USER32.dll", + "USERENV.dll", + "VERSION.dll", + "VCRUNTIME140.dll", + "VCRUNTIME140_1.dll", + "WINMM.dll", + "WS2_32.dll", + // Our libraries. + "libcrypto-1_1.dll", + "libcrypto-1_1-x64.dll", + "libcrypto-3.dll", + "libcrypto-3-arm64.dll", + "libcrypto-3-x64.dll", + "libffi-8.dll", + "libssl-1_1.dll", + "libssl-1_1-x64.dll", + "libssl-3.dll", + "libssl-3-arm64.dll", + "libssl-3-x64.dll", + "python3.dll", + "python39.dll", + "python310.dll", + "python311.dll", + "python312.dll", + "python313.dll", + "python313t.dll", + "python314.dll", + "python314t.dll", + "python315.dll", + "python315t.dll", + "sqlite3.dll", + "tcl86t.dll", + "tk86t.dll", +]; + +// CPython 3.14 and ARM64 use a newer version of tcl/tk (8.6.14+) which includes a bundled zlib that +// dynamically links some system libraries +const PE_ALLOWED_LIBRARIES_314: &[&str] = &[ + "zlib1.dll", + "api-ms-win-crt-private-l1-1-0.dll", // zlib loads this library on arm64, 3.14+ + "msvcrt.dll", // zlib loads this library +]; +const PE_ALLOWED_LIBRARIES_ARM64: &[&str] = &["msvcrt.dll", "zlib1.dll"]; +const PE_ALLOWED_LIBRARIES_315: &[&str] = &[ + // See `PE_ALLOWED_LIBRARIES_314` for zlib-related libraries + "zlib1.dll", + "api-ms-win-crt-private-l1-1-0.dll", + "msvcrt.dll", + // `_remote_debugging` loads `ntdll` + // See https://github.com/python/cpython/pull/138710 + "ntdll.dll", +]; + +static GLIBC_MAX_VERSION_BY_TRIPLE: Lazy>> = + Lazy::new(|| { + let mut versions = HashMap::new(); + + versions.insert( + "aarch64-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "armv7-unknown-linux-gnueabi", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "armv7-unknown-linux-gnueabihf", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "i686-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "mips-unknown-linux-gnu", + version_compare::Version::from("2.19").unwrap(), + ); + versions.insert( + "mipsel-unknown-linux-gnu", + version_compare::Version::from("2.19").unwrap(), + ); + versions.insert( + "mips64el-unknown-linux-gnuabi64", + version_compare::Version::from("2.19").unwrap(), + ); + versions.insert( + "ppc64le-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "riscv64-unknown-linux-gnu", + version_compare::Version::from("2.28").unwrap(), + ); + versions.insert( + "s390x-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "x86_64-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "x86_64_v2-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "x86_64_v3-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + versions.insert( + "x86_64_v4-unknown-linux-gnu", + version_compare::Version::from("2.17").unwrap(), + ); + + // musl shouldn't link against glibc. + versions.insert( + "aarch64-unknown-linux-musl", + version_compare::Version::from("1").unwrap(), + ); + versions.insert( + "x86_64-unknown-linux-musl", + version_compare::Version::from("1").unwrap(), + ); + versions.insert( + "x86_64_v2-unknown-linux-musl", + version_compare::Version::from("1").unwrap(), + ); + versions.insert( + "x86_64_v3-unknown-linux-musl", + version_compare::Version::from("1").unwrap(), + ); + versions.insert( + "x86_64_v4-unknown-linux-musl", + version_compare::Version::from("1").unwrap(), + ); + + versions + }); + +static ELF_ALLOWED_LIBRARIES_BY_TRIPLE: Lazy>> = + Lazy::new(|| { + [ + ( + "armv7-unknown-linux-gnueabi", + vec!["ld-linux.so.3", "libgcc_s.so.1"], + ), + ( + "armv7-unknown-linux-gnueabihf", + vec!["ld-linux-armhf.so.3", "libgcc_s.so.1"], + ), + ("i686-unknown-linux-gnu", vec!["ld-linux-x86-64.so.2"]), + ("mips-unknown-linux-gnu", vec!["ld.so.1", "libatomic.so.1"]), + ( + "mipsel-unknown-linux-gnu", + vec!["ld.so.1", "libatomic.so.1"], + ), + ("mips64el-unknown-linux-gnuabi64", vec![]), + ("ppc64le-unknown-linux-gnu", vec!["ld64.so.1", "ld64.so.2"]), + ( + "riscv64-unknown-linux-gnu", + vec!["ld-linux-riscv64-lp64d.so.1", "libatomic.so.1"], + ), + ("s390x-unknown-linux-gnu", vec!["ld64.so.1"]), + ("x86_64-unknown-linux-gnu", vec!["ld-linux-x86-64.so.2"]), + ("x86_64_v2-unknown-linux-gnu", vec!["ld-linux-x86-64.so.2"]), + ("x86_64_v3-unknown-linux-gnu", vec!["ld-linux-x86-64.so.2"]), + ("x86_64_v4-unknown-linux-gnu", vec!["ld-linux-x86-64.so.2"]), + ] + .iter() + .cloned() + .collect() + }); + +static ELF_ALLOWED_LIBRARIES_BY_MODULE: Lazy>> = + Lazy::new(|| { + [ + ( + // libcrypt is provided by the system, but only on older distros. + "_crypt", + vec!["libcrypt.so.1"], + ), + ( + // libtcl and libtk are shipped in our distribution. + "_tkinter", + vec!["libtcl9.0.so", "libtcl9tk9.0.so"], + ), + ] + .iter() + .cloned() + .collect() + }); + +static DARWIN_ALLOWED_DYLIBS: Lazy> = Lazy::new(|| { + [ + MachOAllowedDylib { + name: "@rpath/libpython3.10.dylib".to_string(), + max_compatibility_version: "3.10.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.10d.dylib".to_string(), + max_compatibility_version: "3.10.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.11.dylib".to_string(), + max_compatibility_version: "3.11.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.11d.dylib".to_string(), + max_compatibility_version: "3.11.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.12.dylib".to_string(), + max_compatibility_version: "3.12.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.12d.dylib".to_string(), + max_compatibility_version: "3.12.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.13.dylib".to_string(), + max_compatibility_version: "3.13.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.13d.dylib".to_string(), + max_compatibility_version: "3.13.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.13t.dylib".to_string(), + max_compatibility_version: "3.13.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.13td.dylib".to_string(), + max_compatibility_version: "3.13.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.14.dylib".to_string(), + max_compatibility_version: "3.14.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.14d.dylib".to_string(), + max_compatibility_version: "3.14.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.14t.dylib".to_string(), + max_compatibility_version: "3.14.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.14td.dylib".to_string(), + max_compatibility_version: "3.14.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.15.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.15d.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.15t.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@rpath/libpython3.15td.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit".to_string(), + max_compatibility_version: "45.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon".to_string(), + max_compatibility_version: "2.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: + "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation" + .to_string(), + max_compatibility_version: "150.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics".to_string(), + max_compatibility_version: "64.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation".to_string(), + max_compatibility_version: "300.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore".to_string(), + max_compatibility_version: "1.2.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/System/Library/Frameworks/Security.framework/Versions/A/Security".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/usr/lib/libedit.3.dylib".to_string(), + max_compatibility_version: "2.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/usr/lib/libncurses.5.4.dylib".to_string(), + max_compatibility_version: "5.4.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/usr/lib/libobjc.A.dylib".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/usr/lib/libpanel.5.4.dylib".to_string(), + max_compatibility_version: "5.4.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/usr/lib/libSystem.B.dylib".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "/usr/lib/libz.1.dylib".to_string(), + max_compatibility_version: "1.0.0".try_into().unwrap(), + required: true, + }, + ] + .to_vec() +}); + +static ALLOWED_DYLIBS_BY_MODULE: Lazy>> = + Lazy::new(|| { + [( + // libtcl and libtk are shipped in our distribution. + "_tkinter", + vec![ + MachOAllowedDylib { + name: "@rpath/libtcl9.0.dylib".to_string(), + max_compatibility_version: "9.0.0".try_into().unwrap(), + required: true, + }, + MachOAllowedDylib { + name: "@rpath/libtcl9tk9.0.dylib".to_string(), + max_compatibility_version: "9.0.0".try_into().unwrap(), + required: true, + }, + ], + )] + .iter() + .cloned() + .collect() + }); + +static PLATFORM_TAG_BY_TRIPLE: Lazy> = Lazy::new(|| { + [ + ("aarch64-apple-darwin", "macosx-11.0-arm64"), + ("aarch64-pc-windows-msvc", "win-arm64"), + ("aarch64-unknown-linux-gnu", "linux-aarch64"), + ("aarch64-unknown-linux-musl", "linux-aarch64"), + ("armv7-unknown-linux-gnueabi", "linux-arm"), + ("armv7-unknown-linux-gnueabihf", "linux-arm"), + ("i686-pc-windows-msvc", "win32"), + ("i686-unknown-linux-gnu", "linux-i686"), + ("mips-unknown-linux-gnu", "linux-mips"), + ("mipsel-unknown-linux-gnu", "linux-mipsel"), + ("mips64el-unknown-linux-gnuabi64", "todo"), + ("ppc64le-unknown-linux-gnu", "linux-powerpc64le"), + ("riscv64-unknown-linux-gnu", "linux-riscv64"), + ("s390x-unknown-linux-gnu", "linux-s390x"), + ("x86_64-apple-darwin", "macosx-10.15-x86_64"), + ("x86_64-pc-windows-msvc", "win-amd64"), + ("x86_64-unknown-linux-gnu", "linux-x86_64"), + ("x86_64_v2-unknown-linux-gnu", "linux-x86_64"), + ("x86_64_v3-unknown-linux-gnu", "linux-x86_64"), + ("x86_64_v4-unknown-linux-gnu", "linux-x86_64"), + ("x86_64-unknown-linux-musl", "linux-x86_64"), + ("x86_64_v2-unknown-linux-musl", "linux-x86_64"), + ("x86_64_v3-unknown-linux-musl", "linux-x86_64"), + ("x86_64_v4-unknown-linux-musl", "linux-x86_64"), + ] + .iter() + .cloned() + .collect() +}); + +const ELF_BANNED_SYMBOLS: &[&str] = &[ + // Deprecated as of glibc 2.34 in favor of sched_yield. + "pthread_yield", +]; + +/// Symbols defined in dependency packages. +/// +/// We use this list to spot test behavior of symbols belonging to dependency packages. +/// The list is obviously not complete. +const DEPENDENCY_PACKAGE_SYMBOLS: &[&str] = &[ + /* TODO(geofft): Tk provides these as no-op stubs on macOS, make it + * stop doing that so we can re-enable the check + * // libX11 + * "XClearWindow", + * "XFlush", + */ + // OpenSSL + "BIO_ADDR_new", + "BN_new", + "DH_new", + "SSL_extension_supported", + "SSL_read", + "CRYPTO_memcmp", + "ecp_nistz256_neg", + "OPENSSL_instrument_bus", + "x25519_fe64_add", + // libdb + "__txn_begin", + // libedit / readline + "rl_prompt", + "readline", + "current_history", + "history_expand", + // libffi + "ffi_call", + "ffi_type_void", + // ncurses + "new_field", + "set_field_term", + "set_menu_init", + "winstr", + // gdbm + "gdbm_close", + "gdbm_import", + // sqlite3 + "sqlite3_initialize", + "sqlite3_close", + // libxcb + "xcb_create_window", + "xcb_get_property", + // libz + "deflateEnd", + "gzclose", + "inflate", + // tix + "Tix_DItemCreate", + "Tix_GrFormat", + // liblzma + "lzma_index_init", + "lzma_stream_encoder", +]; + +// TODO(geofft): Conditionally prohibit these exported symbols +// everywhere except libtcl and libtk. This should be a hashmap +const _DEPENDENCY_PACKAGE_SYMBOLS_BUNDLED: &[&str] = &[ + // tcl + "Tcl_Alloc", + "Tcl_ChannelName", + "Tcl_CreateInterp", + // tk + "TkBindInit", + "TkCreateFrame", + "Tk_FreeGC", + // _ctypes_test module + "my_free", + "mystrdup", + "top", +]; + +const PYTHON_EXPORTED_SYMBOLS: &[&str] = &[ + "Py_Initialize", + "PyList_New", + // From limited API. + "Py_CompileString", +]; + +static WANTED_WINDOWS_STATIC_PATHS: Lazy> = Lazy::new(|| { + [ + PathBuf::from("python/build/lib/libffi.lib"), + PathBuf::from("python/build/lib/libcrypto_static.lib"), + PathBuf::from("python/build/lib/liblzma.lib"), + PathBuf::from("python/build/lib/libssl_static.lib"), + PathBuf::from("python/build/lib/sqlite3.lib"), + ] + .iter() + .cloned() + .collect() +}); + +const GLOBALLY_BANNED_EXTENSIONS: &[&str] = &[ + // Due to linking issues. See comment in cpython.py. + "nis", +]; + +const GLOBAL_EXTENSIONS: &[&str] = &[ + "_abc", + "_ast", + "_asyncio", + "_bisect", + "_blake2", + "_bz2", + "_codecs", + "_codecs_cn", + "_codecs_hk", + "_codecs_iso2022", + "_codecs_jp", + "_codecs_kr", + "_codecs_tw", + "_collections", + "_contextvars", + "_csv", + "_ctypes", + "_datetime", + "_decimal", + "_elementtree", + "_functools", + "_hashlib", + "_heapq", + "_imp", + "_io", + "_json", + "_locale", + "_lsprof", + "_zoneinfo", + "_lzma", + "_md5", + "_multibytecodec", + "_multiprocessing", + "_opcode", + "_operator", + "_pickle", + "_queue", + "_random", + "_sha1", + "_sha3", + "_signal", + "_socket", + "_sqlite3", + "_sre", + "_ssl", + "_stat", + "_statistics", + "_string", + "_struct", + "_symtable", + "_thread", + "_tkinter", + "_tracemalloc", + "_warnings", + "_weakref", + "_uuid", + "array", + "atexit", + "binascii", + "builtins", + "cmath", + "errno", + "faulthandler", + "gc", + "itertools", + "marshal", + "math", + "mmap", + "pyexpat", + "select", + "sys", + "time", + "unicodedata", + "xxsubtype", + "zlib", +]; + +// _tokenize added in 3.11. +// _typing added in 3.11. +// _testsinglephase added in 3.12. +// _sha256 and _sha512 merged into _sha2 in 3.12. +// _xxinterpchannels added in 3.12. +// audioop removed in 3.13. + +const GLOBAL_EXTENSIONS_PYTHON_3_10: &[&str] = + &["audioop", "_sha256", "_sha512", "_xxsubinterpreters"]; + +const GLOBAL_EXTENSIONS_PYTHON_3_11: &[&str] = &[ + "audioop", + "_sha256", + "_sha512", + "_tokenize", + "_typing", + "_xxsubinterpreters", +]; + +const GLOBAL_EXTENSIONS_PYTHON_3_12: &[&str] = &[ + "audioop", + "_sha2", + "_tokenize", + "_typing", + "_xxinterpchannels", + "_xxsubinterpreters", +]; + +const GLOBAL_EXTENSIONS_PYTHON_3_13: &[&str] = &[ + "_interpchannels", + "_interpqueues", + "_interpreters", + "_sha2", + "_suggestions", + "_sysconfig", + "_tokenize", + "_typing", +]; + +const GLOBAL_EXTENSIONS_PYTHON_3_14: &[&str] = &[ + "_interpchannels", + "_interpqueues", + "_interpreters", + "_remote_debugging", + "_sha2", + "_suggestions", + "_sysconfig", + "_tokenize", + "_typing", + "_hmac", + "_types", + "_zstd", +]; + +const GLOBAL_EXTENSIONS_PYTHON_3_15: &[&str] = &[ + "_interpchannels", + "_interpqueues", + "_interpreters", + "_math_integer", + "_remote_debugging", + "_sha2", + "_suggestions", + "_sysconfig", + "_tokenize", + "_typing", + "_hmac", + "_types", + "_zstd", +]; + +const GLOBAL_EXTENSIONS_MACOS: &[&str] = &["_scproxy"]; + +const GLOBAL_EXTENSIONS_POSIX: &[&str] = &[ + "_ctypes_test", + "_curses", + "_curses_panel", + "_dbm", + "_posixshmem", + "_posixsubprocess", + "_testinternalcapi", + "fcntl", + "grp", + "posix", + "pwd", + "readline", + "resource", + "syslog", + "termios", +]; + +const GLOBAL_EXTENSIONS_POSIX_PRE_3_13: &[&str] = &["_crypt"]; + +const GLOBAL_EXTENSIONS_LINUX_PRE_3_13: &[&str] = &["spwd"]; + +const GLOBAL_EXTENSIONS_WINDOWS: &[&str] = &[ + "_overlapped", + "_winapi", + "msvcrt", + "nt", + "winreg", + "winsound", +]; + +const GLOBAL_EXTENSIONS_WINDOWS_3_14: &[&str] = &["_wmi"]; + +const GLOBAL_EXTENSIONS_WINDOWS_PRE_3_13: &[&str] = &["_msi"]; + +/// Extension modules not present in Windows static builds. +const GLOBAL_EXTENSIONS_WINDOWS_NO_STATIC: &[&str] = &["_testinternalcapi", "_tkinter"]; + +/// Extension modules that should be built as shared libraries. +const SHARED_LIBRARY_EXTENSIONS: &[&str] = &[ + "_crypt", + "_ctypes_test", + "_dbm", + "_testbuffer", + "_testcapi", + "_testexternalinspection", + "_testimportmultiple", + "_testlimitedcapi", + "_testmultiphase", + "_testsinglephase", + "_tkinter", +]; + +fn allowed_dylibs_for_triple(triple: &str) -> Vec { + match triple { + "aarch64-apple-darwin" => DARWIN_ALLOWED_DYLIBS.clone(), + "x86_64-apple-darwin" => DARWIN_ALLOWED_DYLIBS.clone(), + _ => vec![], + } +} + +#[derive(Clone, Default)] +pub struct ValidationContext { + /// Collected errors. + pub errors: Vec, + + /// Dynamic libraries required to be loaded. + pub seen_dylibs: BTreeSet, + + /// Symbols exported from dynamic libpython library. + pub libpython_exported_symbols: BTreeSet, + + /// Undefined Mach-O symbols that are required / non-weak. + pub macho_undefined_symbols_strong: RequiredSymbols, + + /// Undefined Mach-O symbols that are weakly referenced. + pub macho_undefined_symbols_weak: RequiredSymbols, +} + +impl ValidationContext { + /// Merge the contents of `other` into this instance. + pub fn merge(&mut self, other: Self) { + self.errors.extend(other.errors); + self.seen_dylibs.extend(other.seen_dylibs); + self.libpython_exported_symbols + .extend(other.libpython_exported_symbols); + self.macho_undefined_symbols_strong + .merge(other.macho_undefined_symbols_strong); + self.macho_undefined_symbols_weak + .merge(other.macho_undefined_symbols_weak); + } +} + +fn validate_elf>( + context: &mut ValidationContext, + json: &PythonJsonMain, + target_triple: &str, + python_major_minor: &str, + path: &Path, + elf: &Elf, + data: &[u8], +) -> Result<()> { + let mut system_links = BTreeSet::new(); + for link in &json.build_info.core.links { + if link.system.unwrap_or_default() { + system_links.insert(link.name.as_str()); + } + } + for extension in json.build_info.extensions.values() { + for variant in extension { + for link in &variant.links { + if link.system.unwrap_or_default() { + system_links.insert(link.name.as_str()); + } + } + } + } + + let wanted_cpu_type = match target_triple { + "aarch64-unknown-linux-gnu" => object::elf::EM_AARCH64, + "aarch64-unknown-linux-musl" => object::elf::EM_AARCH64, + "armv7-unknown-linux-gnueabi" => object::elf::EM_ARM, + "armv7-unknown-linux-gnueabihf" => object::elf::EM_ARM, + "i686-unknown-linux-gnu" => object::elf::EM_386, + "mips-unknown-linux-gnu" => object::elf::EM_MIPS, + "mipsel-unknown-linux-gnu" => object::elf::EM_MIPS, + "mips64el-unknown-linux-gnuabi64" => 0, + "ppc64le-unknown-linux-gnu" => object::elf::EM_PPC64, + "riscv64-unknown-linux-gnu" => object::elf::EM_RISCV, + "s390x-unknown-linux-gnu" => object::elf::EM_S390, + "x86_64-unknown-linux-gnu" => object::elf::EM_X86_64, + "x86_64_v2-unknown-linux-gnu" => object::elf::EM_X86_64, + "x86_64_v3-unknown-linux-gnu" => object::elf::EM_X86_64, + "x86_64_v4-unknown-linux-gnu" => object::elf::EM_X86_64, + "x86_64-unknown-linux-musl" => object::elf::EM_X86_64, + "x86_64_v2-unknown-linux-musl" => object::elf::EM_X86_64, + "x86_64_v3-unknown-linux-musl" => object::elf::EM_X86_64, + "x86_64_v4-unknown-linux-musl" => object::elf::EM_X86_64, + _ => panic!("unhandled target triple: {target_triple}"), + }; + + let endian = elf.endian()?; + + if elf.e_machine(endian) != wanted_cpu_type { + context.errors.push(format!( + "invalid ELF machine type in {}; wanted {}, got {}", + path.display(), + wanted_cpu_type, + elf.e_machine(endian), + )); + } + + let mut allowed_libraries = ELF_ALLOWED_LIBRARIES + .iter() + .map(|x| x.to_string()) + .collect::>(); + if let Some(extra) = ELF_ALLOWED_LIBRARIES_BY_TRIPLE.get(target_triple) { + allowed_libraries.extend(extra.iter().map(|x| x.to_string())); + } + + if json.libpython_link_mode == "shared" { + if target_triple.contains("-musl") { + // On musl, we link to `libpython` and rely on `RUN PATH` + allowed_libraries.push(format!("libpython{python_major_minor}.so.1.0")); + allowed_libraries.push(format!("libpython{python_major_minor}d.so.1.0")); + allowed_libraries.push(format!("libpython{python_major_minor}t.so.1.0")); + allowed_libraries.push(format!("libpython{python_major_minor}td.so.1.0")); + } else { + // On glibc, we can use `$ORIGIN` for relative, reloctable linking + allowed_libraries.push(format!( + "$ORIGIN/../lib/libpython{python_major_minor}.so.1.0" + )); + allowed_libraries.push(format!( + "$ORIGIN/../lib/libpython{python_major_minor}d.so.1.0" + )); + allowed_libraries.push(format!( + "$ORIGIN/../lib/libpython{python_major_minor}t.so.1.0" + )); + allowed_libraries.push(format!( + "$ORIGIN/../lib/libpython{python_major_minor}td.so.1.0" + )); + } + } + + if !json.build_options.contains("static") && target_triple.contains("-musl") { + // Allow linking musl `libc` + allowed_libraries.push("libc.so".to_string()); + } + + // Allow certain extension modules to link against shared libraries + // (either from the system or from our distribution). + if let Some(filename) = path.file_name() { + if let Some((module, _)) = filename.to_string_lossy().split_once(".cpython-") { + if let Some(extra) = ELF_ALLOWED_LIBRARIES_BY_MODULE.get(module) { + allowed_libraries.extend(extra.iter().map(|x| x.to_string())); + } + } + } + + let wanted_glibc_max_version = GLIBC_MAX_VERSION_BY_TRIPLE + .get(target_triple) + .expect("max glibc version not defined for target triple"); + + let sections = elf.sections(endian, data)?; + + let versions = sections.versions(endian, data)?; + + for (section_index, section) in sections.iter().enumerate() { + // Dynamic sections defined needed libraries, which we validate. + if let Some((entries, index)) = section.dynamic(endian, data)? { + let strings = sections.strings(endian, data, index).unwrap_or_default(); + + for entry in entries { + if entry.tag32(endian) == Some(object::elf::DT_NEEDED) { + let lib = entry.string(endian, strings)?; + let lib = String::from_utf8(lib.to_vec())?; + + if !allowed_libraries.contains(&lib.to_string()) { + context.errors.push(format!( + "{} loads illegal library {}", + path.display(), + lib + )); + } + + // Most linked libraries should have an annotation in the JSON metadata. + let requires_annotation = !lib.contains("libpython") + && !lib.starts_with("ld-linux") + && !lib.starts_with("ld64.so") + && !lib.starts_with("ld.so") + && !lib.starts_with("libc.so") + && !lib.starts_with("libgcc_s.so"); + + if requires_annotation { + if lib.starts_with("lib") { + if let Some(index) = lib.rfind(".so") { + let lib_name = &lib[3..index]; + + // There should be a system links entry for this library in the JSON + // metadata. + // + // Nominally we would look at where this ELF came from and make sure + // the annotation is present in its section (e.g. core or extension). + // But this is more work. + if !system_links.contains(lib_name) { + context.errors.push(format!( + "{} library load of {} does not have system link build annotation", + path.display(), + lib + )); + } + } else { + context.errors.push(format!( + "{} library load of {} does not have .so extension", + path.display(), + lib + )); + } + } else { + context.errors.push(format!( + "{} library load of {} does not begin with lib", + path.display(), + lib + )); + } + } + } + } + } + + if let Some(symbols) = + section.symbols(endian, data, §ions, SectionIndex(section_index))? + { + let strings = symbols.strings(); + + for (symbol_index, symbol) in symbols.enumerate() { + let name = String::from_utf8_lossy(symbol.name(endian, strings)?); + + // If symbol versions are defined and we're in the .dynsym section, there should + // be version info for every symbol. + let version_version = if section.sh_type(endian) == object::elf::SHT_DYNSYM { + if let Some(versions) = &versions { + let version_index = versions.version_index(endian, symbol_index); + + if let Some(version) = versions.version(version_index)? { + let version = String::from_utf8_lossy(version.name()).to_string(); + + Some(version) + } else { + None + } + } else { + None + } + } else { + None + }; + + if symbol.is_undefined(endian) { + if ELF_BANNED_SYMBOLS.contains(&name.as_ref()) { + context.errors.push(format!( + "{} defines banned ELF symbol {}", + path.display(), + name, + )); + } + + if let Some(version) = version_version { + let parts: Vec<&str> = version.splitn(2, '_').collect(); + + if parts.len() == 2 && parts[0] == "GLIBC" { + let v = version_compare::Version::from(parts[1]) + .expect("unable to parse version"); + + if &v > wanted_glibc_max_version { + context.errors.push(format!( + "{} references too new glibc symbol {:?} ({} > {})", + path.display(), + name, + v, + wanted_glibc_max_version, + )); + } + } + } + } + + // Ensure specific symbols in dynamic binaries have proper visibility. + if matches!(elf.e_type(endian), ET_EXEC | ET_DYN) { + // Non-local symbols belonging to dependencies should have hidden visibility + // to prevent them from being exported. + if DEPENDENCY_PACKAGE_SYMBOLS.contains(&name.as_ref()) + && matches!(symbol.st_bind(), STB_GLOBAL | STB_WEAK) + && symbol.st_shndx(endian) != SHN_UNDEF + && symbol.st_visibility() != STV_HIDDEN + { + context.errors.push(format!( + "{} contains non-hidden dependency symbol {}", + path.display(), + name + )); + } + + if let Some(filename) = path.file_name() { + let filename = filename.to_string_lossy(); + + if filename.starts_with("libpython") + && filename.ends_with(".so.1.0") + && matches!(symbol.st_bind(), STB_GLOBAL | STB_WEAK) + && symbol.st_shndx(endian) != SHN_UNDEF + && symbol.st_visibility() == STV_DEFAULT + { + context.libpython_exported_symbols.insert(name.to_string()); + } + } + } + } + } + } + + // Verify that objects are not requesting an executable stack. For backwards compatibility, + // Linux (the kernel when loading an executable, and glibc when loading a shared library) + // assumes you need an executable stack unless you request otherwise. In linked outputs + // (executables and shared libraries) this is in the program header: the flags of a + // PT_GNU_STACK entry specify stack permissions, and the default if unspecified is RWX. In + // intermediate objects (.o files) this is conveyed via the presence of an empty-length + // .note.GNU-stack, which is marked as an executable section (SHF_EXECINSTR) if the object + // needs an executable stack. + // + // For now we only check binaries because of an LLVM bug that causes .o files to be missing a + // .note.GNU-stack section, which we are overriding with -Wl,-z,noexecstack. + + if matches!(elf.e_type(endian), ET_EXEC | ET_DYN) { + let mut found_pt_gnu_stack = false; + for phdr in elf.program_headers(endian, data)? { + if phdr.p_type(endian) != PT_GNU_STACK { + continue; + } + found_pt_gnu_stack = true; + if (phdr.p_flags(endian) & PF_X) != 0 { + context + .errors + .push(format!("{} requests executable stack", path.display())); + } + } + if !found_pt_gnu_stack { + context.errors.push(format!( + "{} missing PT_GNU_STACK header (defaults to executable stack)", + path.display(), + )); + } + } + + Ok(()) +} + +#[derive(Debug)] +struct MachOSymbol { + name: String, + library_ordinal: u8, + weak: bool, +} + +/// Parses an integer with nibbles xxxx.yy.zz into a [semver::Version]. +fn parse_version_nibbles(v: u32) -> semver::Version { + let major = v >> 16; + let minor = v << 16 >> 24; + let patch = v & 0xff; + + semver::Version::new(major as _, minor as _, patch as _) +} + +#[allow(clippy::too_many_arguments)] +fn validate_macho>( + context: &mut ValidationContext, + target_triple: &str, + advertised_target_version: &str, + advertised_sdk_version: &str, + path: &Path, + header: &Mach, + bytes: &[u8], +) -> Result<()> { + let advertised_target_version = + semver::Version::parse(&format!("{advertised_target_version}.0"))?; + let advertised_sdk_version = semver::Version::parse(&format!("{advertised_sdk_version}.0"))?; + + let endian = header.endian()?; + + let wanted_cpu_type = match target_triple { + "aarch64-apple-darwin" => object::macho::CPU_TYPE_ARM64, + "x86_64-apple-darwin" => object::macho::CPU_TYPE_X86_64, + _ => return Err(anyhow!("unhandled target triple: {}", target_triple)), + }; + + if header.cputype(endian) != wanted_cpu_type { + context.errors.push(format!( + "{} has incorrect CPU type; got {}, wanted {}", + path.display(), + header.cputype(endian), + wanted_cpu_type + )); + } + + if header.filetype(endian) != MH_OBJECT && header.flags(endian) & MH_TWOLEVEL == 0 { + context.errors.push(format!( + "{} does not use two-level symbol lookup", + path.display() + )); + } + + let mut load_commands = header.load_commands(endian, bytes, 0)?; + + let mut dylib_names = vec![]; + let mut undefined_symbols = vec![]; + let mut target_version = None; + let mut sdk_version = None; + let mut has_code_signature = false; + let mut lowest_file_offset = u64::MAX; + + while let Some(load_command) = load_commands.next()? { + match load_command.variant()? { + LoadCommandVariant::BuildVersion(v) => { + // Sometimes the SDK version is advertised as 0.0.0. In that case just ignore it. + let version = parse_version_nibbles(v.sdk.get(endian)); + if version > semver::Version::new(0, 0, 0) { + sdk_version = Some(version); + } + + target_version = Some(parse_version_nibbles(v.minos.get(endian))); + } + LoadCommandVariant::VersionMin(v) => { + let version = parse_version_nibbles(v.sdk.get(endian)); + if version > semver::Version::new(0, 0, 0) { + sdk_version = Some(version); + } + + target_version = Some(parse_version_nibbles(v.version.get(endian))); + } + LoadCommandVariant::Dylib(command) => { + let raw_string = load_command.string(endian, command.dylib.name)?; + let lib = String::from_utf8(raw_string.to_vec())?; + + dylib_names.push(lib.clone()); + + let mut allowed = allowed_dylibs_for_triple(target_triple); + // Allow certain extension modules to link against shared libraries + // (either from the system or from our distribution). + if let Some(filename) = path.file_name() { + if let Some((module, _)) = filename.to_string_lossy().split_once(".cpython-") { + if let Some(extra) = ALLOWED_DYLIBS_BY_MODULE.get(module) { + allowed.extend(extra.clone()); + } + } + } + + if let Some(entry) = allowed.iter().find(|l| l.name == lib) { + let load_version = + MachOPackedVersion::from(command.dylib.compatibility_version.get(endian)); + + if load_version > entry.max_compatibility_version { + context.errors.push(format!( + "{} loads too new version of {}; got {}, max allowed {}", + path.display(), + lib, + load_version, + entry.max_compatibility_version + )); + } + + context.seen_dylibs.insert(lib.to_string()); + } else { + context.errors.push(format!( + "{} loads illegal library {}", + path.display(), + lib + )); + } + } + LoadCommandVariant::Symtab(symtab) => { + let table = symtab.symbols::(endian, bytes)?; + let strings = table.strings(); + + for symbol in table.iter() { + let name = symbol.name(endian, strings)?; + let name = String::from_utf8(name.to_vec())?; + + if symbol.is_undefined() { + undefined_symbols.push(MachOSymbol { + name: name.clone(), + library_ordinal: symbol.library_ordinal(endian), + weak: symbol.n_desc(endian) & (object::macho::N_WEAK_REF) != 0, + }); + } + + // Ensure specific symbols in dynamic binaries have proper visibility. + // Read: we don't want to export symbols from dependencies. + if header.filetype(endian) != MH_OBJECT { + let n_type = symbol.n_type(); + + let scope = if n_type & object::macho::N_TYPE == object::macho::N_UNDF { + SymbolScope::Unknown + } else if n_type & object::macho::N_EXT == 0 { + SymbolScope::Compilation + } else if n_type & object::macho::N_PEXT != 0 { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + }; + + let search_name = if let Some(v) = name.strip_prefix('_') { + v + } else { + name.as_str() + }; + + if DEPENDENCY_PACKAGE_SYMBOLS.contains(&search_name) + && scope == SymbolScope::Dynamic + { + context.errors.push(format!( + "{} contains dynamic symbol from dependency {}", + path.display(), + name + )); + } + + if let Some(filename) = path.file_name() { + let filename = filename.to_string_lossy(); + + if filename.starts_with("libpython") + && filename.ends_with(".dylib") + && scope == SymbolScope::Dynamic + { + context + .libpython_exported_symbols + .insert(search_name.to_string()); + } + } + } + } + } + LoadCommandVariant::Segment32(segment, segment_data) => { + for section in segment.sections(endian, segment_data)? { + if let Some((offset, _)) = section.file_range(endian) { + lowest_file_offset = lowest_file_offset.min(offset); + } + } + } + LoadCommandVariant::Segment64(segment, segment_data) => { + for section in segment.sections(endian, segment_data)? { + if let Some((offset, _)) = section.file_range(endian) { + lowest_file_offset = lowest_file_offset.min(offset); + } + } + } + LoadCommandVariant::LinkeditData(c) if c.cmd.get(endian) == LC_CODE_SIGNATURE => { + has_code_signature = true; + } + _ => {} + } + } + + let end_of_load_commands = + std::mem::size_of_val(header) as u64 + header.sizeofcmds(endian) as u64; + if header.filetype(endian) != MH_OBJECT + && end_of_load_commands + if has_code_signature { 0 } else { 16 } > lowest_file_offset + { + context.errors.push(format!( + "{}: Insufficient headerpad between end of load commands {end_of_load_commands:#x} and beginning of code {lowest_file_offset:#x}", + path.display(), + )); + } + + if let Some(actual_target_version) = target_version { + if actual_target_version != advertised_target_version { + context.errors.push(format!( + "{} targets SDK {} but JSON advertises SDK {}", + path.display(), + actual_target_version, + advertised_target_version + )); + } + } + + if let Some(actual_sdk_version) = sdk_version { + if actual_sdk_version != advertised_sdk_version { + context.errors.push(format!( + "{} was built with SDK {} but JSON advertises SDK {}", + path.display(), + actual_sdk_version, + advertised_sdk_version, + )) + } + } + + // Don't perform undefined symbol analysis for object files because the object file + // in isolation lacks context. + if header.filetype(endian) != MH_OBJECT { + for symbol in undefined_symbols { + // Assume undefined symbols provided by current library will resolve. + if symbol.library_ordinal == object::macho::SELF_LIBRARY_ORDINAL { + continue; + } + + if symbol.library_ordinal < object::macho::MAX_LIBRARY_ORDINAL { + let lib = dylib_names + .get(symbol.library_ordinal as usize - 1) + .ok_or_else(|| anyhow!("unable to resolve symbol's library name"))?; + + let symbols = if symbol.weak { + &mut context.macho_undefined_symbols_weak + } else { + &mut context.macho_undefined_symbols_strong + }; + + symbols.insert(lib, symbol.name, path.to_path_buf()); + } + } + } + + Ok(()) +} + +fn validate_pe<'data, Pe: ImageNtHeaders>( + context: &mut ValidationContext, + python_major_minor: &str, + path: &Path, + pe: &PeFile<'data, Pe, &'data [u8]>, +) -> Result<()> { + // We don't care about the wininst-*.exe distutils executables. + if path.to_string_lossy().contains("wininst-") { + return Ok(()); + } + + if let Some(import_table) = pe.import_table()? { + let mut descriptors = import_table.descriptors()?; + + while let Some(descriptor) = descriptors.next()? { + let lib = import_table.name(descriptor.name.get(object::LittleEndian))?; + let lib = String::from_utf8(lib.to_vec())?; + + match python_major_minor { + "3.11" | "3.12" | "3.13" if pe.architecture() == Architecture::Aarch64 => { + if PE_ALLOWED_LIBRARIES_ARM64.contains(&lib.as_str()) { + continue; + } + } + "3.14" => { + if PE_ALLOWED_LIBRARIES_314.contains(&lib.as_str()) { + continue; + } + } + "3.15" => { + if PE_ALLOWED_LIBRARIES_315.contains(&lib.as_str()) { + continue; + } + } + _ => {} + } + + if !PE_ALLOWED_LIBRARIES.contains(&lib.as_str()) { + context + .errors + .push(format!("{} loads illegal library {}", path.display(), lib)); + } + } + } + + let filename = path + .file_name() + .ok_or_else(|| anyhow!("should be able to resolve filename"))? + .to_string_lossy(); + + if filename.starts_with("python") && filename.ends_with(".dll") { + for symbol in pe.exports()? { + context + .libpython_exported_symbols + .insert(String::from_utf8(symbol.name().to_vec())?); + } + } + + Ok(()) +} + +/// Attempt to parse data as an object file and validate it. +fn validate_possible_object_file( + json: &PythonJsonMain, + python_major_minor: &str, + triple: &str, + path: &Path, + data: &[u8], +) -> Result { + let mut context = ValidationContext::default(); + + if let Ok(kind) = FileKind::parse(data) { + match kind { + FileKind::Elf32 => { + let header = FileHeader32::parse(data)?; + + validate_elf( + &mut context, + json, + triple, + python_major_minor, + path, + header, + data, + )?; + } + FileKind::Elf64 => { + let header = FileHeader64::parse(data)?; + + validate_elf( + &mut context, + json, + triple, + python_major_minor, + path, + header, + data, + )?; + } + FileKind::MachO32 => { + let header = MachHeader32::parse(data, 0)?; + + validate_macho( + &mut context, + triple, + json.apple_sdk_deployment_target + .as_ref() + .expect("apple_sdk_deployment_target should be set"), + json.apple_sdk_version + .as_ref() + .expect("apple_sdk_version should be set"), + path, + header, + data, + )?; + } + FileKind::MachO64 => { + let header = MachHeader64::parse(data, 0)?; + + validate_macho( + &mut context, + triple, + json.apple_sdk_deployment_target + .as_ref() + .expect("apple_sdk_deployment_target should be set"), + json.apple_sdk_version + .as_ref() + .expect("apple_sdk_version should be set"), + path, + header, + data, + )?; + } + FileKind::MachOFat32 | FileKind::MachOFat64 => { + if path.to_string_lossy() != "python/build/lib/libclang_rt.osx.a" { + context + .errors + .push(format!("unexpected fat mach-o binary: {}", path.display())); + } + } + FileKind::Pe32 => { + let file = PeFile32::parse(data)?; + validate_pe(&mut context, python_major_minor, path, &file)?; + } + FileKind::Pe64 => { + let file = PeFile64::parse(data)?; + validate_pe(&mut context, python_major_minor, path, &file)?; + } + _ => {} + } + } + + Ok(context) +} + +fn validate_extension_modules( + python_major_minor: &str, + target_triple: &str, + static_crt: bool, + have_extensions: &BTreeSet<&str>, +) -> Result> { + let mut errors = vec![]; + + let is_macos = target_triple.contains("-apple-darwin"); + let is_linux = target_triple.contains("-unknown-linux-"); + let is_windows = target_triple.contains("-pc-windows-"); + let is_linux_musl = target_triple.contains("-unknown-linux-musl"); + + let mut wanted = BTreeSet::from_iter(GLOBAL_EXTENSIONS.iter().copied()); + + match python_major_minor { + "3.10" => { + wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_10); + } + "3.11" => { + wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_11); + } + "3.12" => { + wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_12); + } + "3.13" => { + wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_13); + } + "3.14" => { + wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_14); + } + "3.15" => { + wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_15); + } + _ => { + panic!("unhandled Python version: {python_major_minor}"); + } + } + + if is_macos { + wanted.extend(GLOBAL_EXTENSIONS_POSIX); + + if matches!(python_major_minor, "3.10" | "3.11" | "3.12") { + wanted.extend(GLOBAL_EXTENSIONS_POSIX_PRE_3_13); + } + + wanted.extend(GLOBAL_EXTENSIONS_MACOS); + } + + if is_windows { + wanted.extend(GLOBAL_EXTENSIONS_WINDOWS); + + if matches!(python_major_minor, "3.10" | "3.11" | "3.12") { + wanted.extend(GLOBAL_EXTENSIONS_WINDOWS_PRE_3_13); + } + + if matches!(python_major_minor, "3.14" | "3.15") { + wanted.extend(GLOBAL_EXTENSIONS_WINDOWS_3_14); + } + + if static_crt { + for x in GLOBAL_EXTENSIONS_WINDOWS_NO_STATIC { + wanted.remove(*x); + } + } + } + + if is_linux { + wanted.extend(GLOBAL_EXTENSIONS_POSIX); + + if matches!(python_major_minor, "3.10" | "3.11" | "3.12") { + wanted.extend(GLOBAL_EXTENSIONS_POSIX_PRE_3_13); + } + + if matches!(python_major_minor, "3.10" | "3.11" | "3.12") { + wanted.extend(GLOBAL_EXTENSIONS_LINUX_PRE_3_13); + } + + if !is_linux_musl && matches!(python_major_minor, "3.10" | "3.11" | "3.12") { + wanted.insert("ossaudiodev"); + } + } + + if is_linux || is_macos { + wanted.extend([ + "_testbuffer", + "_testimportmultiple", + "_testmultiphase", + "_xxtestfuzz", + ]); + + if !static_crt { + wanted.insert("_testcapi"); + + if !matches!(python_major_minor, "3.10" | "3.11" | "3.12") { + wanted.insert("_testlimitedcapi"); + } + } + } + + if (is_linux || is_macos) && matches!(python_major_minor, "3.13") { + wanted.insert("_testexternalinspection"); + } + + if (is_linux || is_macos) && matches!(python_major_minor, "3.12" | "3.13" | "3.14" | "3.15") { + wanted.insert("_testsinglephase"); + } + + // _wmi is Windows only on 3.12+. + if matches!(python_major_minor, "3.12" | "3.13") && is_windows { + wanted.insert("_wmi"); + } + + for extra in have_extensions.difference(&wanted) { + errors.push(format!("extra/unknown extension module: {extra}")); + } + + for missing in wanted.difference(have_extensions) { + errors.push(format!("missing extension module: {missing}")); + } + + Ok(errors) +} + +fn validate_json(json: &PythonJsonMain, triple: &str, is_debug: bool) -> Result> { + let mut errors = vec![]; + + if json.version != "8" { + errors.push(format!( + "expected version 8 in PYTHON.json; got {}", + json.version + )); + } + + // Distributions built with Apple SDKs should have SDK metadata. + if triple.contains("-apple-") { + if json.apple_sdk_canonical_name.is_none() { + errors.push("JSON missing apple_sdk_canonical_name on Apple triple".to_string()); + } + if json.apple_sdk_deployment_target.is_none() { + errors.push("JSON missing apple_sdk_deployment_target on Apple triple".to_string()); + } + if json.apple_sdk_platform.is_none() { + errors.push("JSON missing apple_sdk_platform on Apple triple".to_string()); + } + if json.apple_sdk_version.is_none() { + errors.push("JSON missing apple_sdk_version on Apple triple".to_string()); + } + } + + let wanted_platform_tag = *PLATFORM_TAG_BY_TRIPLE + .get(triple) + .ok_or_else(|| anyhow!("platform tag not defined for triple {}", triple))?; + + if json.python_platform_tag != wanted_platform_tag { + errors.push(format!( + "wanted platform tag {}; got {}", + wanted_platform_tag, json.python_platform_tag + )); + } + + if is_debug + && !json + .python_config_vars + .get("abiflags") + .unwrap() + .contains('d') + { + errors.push("abiflags does not contain 'd'".to_string()); + } + + for extension in json.build_info.extensions.keys() { + if GLOBALLY_BANNED_EXTENSIONS.contains(&extension.as_str()) { + errors.push(format!("banned extension detected: {extension}")); + } + } + + let have_extensions = json + .build_info + .extensions + .keys() + .map(|x| x.as_str()) + .collect::>(); + + errors.extend(validate_extension_modules( + &json.python_major_minor_version, + triple, + json.crt_features.contains(&"static".to_string()), + &have_extensions, + )?); + + Ok(errors) +} + +fn validate_distribution( + dist_path: &Path, + macos_sdks: Option<&IndexedSdks>, +) -> Result> { + let mut context = ValidationContext::default(); + + let mut seen_paths = BTreeSet::new(); + let mut seen_symlink_targets = BTreeSet::new(); + + let dist_filename = dist_path + .file_name() + .expect("unable to obtain filename") + .to_string_lossy(); + + let triple = RECOGNIZED_TRIPLES + .iter() + .find(|triple| dist_path.to_string_lossy().contains(&format!("-{triple}-"))) + .ok_or_else(|| { + anyhow!( + "could not identify triple from distribution filename: {}", + dist_path.display() + ) + })?; + + let python_major_minor = if dist_filename.starts_with("cpython-3.10.") { + "3.10" + } else if dist_filename.starts_with("cpython-3.11.") { + "3.11" + } else if dist_filename.starts_with("cpython-3.12.") { + "3.12" + } else if dist_filename.starts_with("cpython-3.13.") { + "3.13" + } else if dist_filename.starts_with("cpython-3.14.") { + "3.14" + } else if dist_filename.starts_with("cpython-3.15.") { + "3.15" + } else { + return Err(anyhow!("could not parse Python version from filename")); + }; + + let is_debug = dist_filename.contains("-debug-"); + let is_static = dist_filename.contains("+static"); + + let mut tf = crate::open_distribution_archive(dist_path)?; + + // First entry in archive should be python/PYTHON.json. + let mut entries = tf.entries()?; + + let mut wanted_python_paths = BTreeSet::new(); + let mut json = None; + + let mut entry = entries.next().unwrap()?; + if entry.path()?.display().to_string() == "python/PYTHON.json" { + seen_paths.insert(entry.path()?.to_path_buf()); + + let mut data = Vec::new(); + entry.read_to_end(&mut data)?; + json = Some(parse_python_json(&data).context("parsing PYTHON.json")?); + context + .errors + .extend(validate_json(json.as_ref().unwrap(), triple, is_debug)?); + + wanted_python_paths.extend( + json.as_ref() + .unwrap() + .python_paths + .values() + .map(|x| format!("python/{x}")), + ); + } else { + context.errors.push(format!( + "1st archive entry should be for python/PYTHON.json; got {}", + entry.path()?.display() + )); + } + + let mut bin_python = None; + let mut bin_python3 = None; + + for entry in entries { + let mut entry = entry.map_err(|e| anyhow!("failed to iterate over archive: {}", e))?; + let path = entry.path()?.to_path_buf(); + + seen_paths.insert(path.clone()); + + if let Some(link_name) = entry.link_name()? { + let target = path.parent().unwrap().join(link_name).normalize(); + + seen_symlink_targets.insert(target); + } + + // If this path starts with a path referenced in wanted_python_paths, + // remove the prefix from wanted_python_paths so we don't error on it + // later. + let removals = wanted_python_paths + .iter() + .filter(|prefix| path.starts_with(prefix)) + .map(|x| x.to_string()) + .collect::>(); + for p in removals { + wanted_python_paths.remove(&p); + } + + let mut data = Vec::new(); + entry.read_to_end(&mut data)?; + + context.merge(validate_possible_object_file( + json.as_ref().unwrap(), + python_major_minor, + triple, + &path, + &data, + )?); + + // Descend into archive files (static libraries are archive files and members + // are usually object files). + if let Ok(archive) = goblin::archive::Archive::parse(&data) { + let mut members = archive.members(); + members.sort(); + + for member in members { + let member_data = archive + .extract(member, &data) + .with_context(|| format!("extracting {} from {}", member, path.display()))?; + + let member_path = path.with_file_name(format!( + "{}:{}", + path.file_name().unwrap().to_string_lossy(), + member + )); + + context.merge(validate_possible_object_file( + json.as_ref().unwrap(), + python_major_minor, + triple, + &member_path, + member_data, + )?); + } + } + + // Verify shebangs don't reference build environment. + if data.starts_with(b"#!/install") || data.starts_with(b"#!/build") { + context + .errors + .push(format!("{} has #!/install shebang", path.display())); + } + + if path == PathBuf::from("python/PYTHON.json") { + context + .errors + .push("python/PYTHON.json seen twice".to_string()); + } + + if path == PathBuf::from("python/install/bin/python") { + if let Some(link) = entry.link_name()? { + bin_python = Some(link.to_string_lossy().to_string()); + } else { + context + .errors + .push("python/install/bin/python is not a symlink".to_string()); + } + } + + if path == PathBuf::from("python/install/bin/python3") { + if let Some(link) = entry.link_name()? { + bin_python3 = Some(link.to_string_lossy().to_string()); + } else { + context + .errors + .push("python/install/bin/python3 is not a symlink".to_string()); + } + } + } + + match (bin_python, bin_python3) { + (None, None) => { + if !triple.contains("-windows-") { + context + .errors + .push("install/bin/python and python3 entries missing".to_string()); + } + } + (None, Some(_)) => { + context + .errors + .push("install/bin/python symlink missing".to_string()); + } + (Some(_), None) => { + context + .errors + .push("install/bin/python3 symlink missing".to_string()); + } + (Some(python), Some(python3)) => { + if python != python3 { + context.errors.push(format!( + "symlink targets of install/bin/python and python3 vary: {python} !+ {python3}" + )); + } + } + } + + // We've now read the contents of the archive. Move on to analyzing the results. + + for path in seen_symlink_targets { + if !seen_paths.contains(&path) { + context.errors.push(format!( + "symlink target {} referenced in archive but not found", + path.display() + )); + } + } + + for path in wanted_python_paths { + context.errors.push(format!( + "path prefix {path} seen in python_paths does not appear in archive" + )); + } + + let wanted_dylibs = BTreeSet::from_iter( + allowed_dylibs_for_triple(triple) + .iter() + .filter(|d| d.required) + .map(|d| d.name.clone()), + ); + + for lib in wanted_dylibs.difference(&context.seen_dylibs) { + context + .errors + .push(format!("required library dependency {lib} not seen")); + } + + if triple.contains("-windows-") && is_static { + for path in WANTED_WINDOWS_STATIC_PATHS.difference(&seen_paths) { + context + .errors + .push(format!("required path {} not seen", path.display())); + } + } + + if context.libpython_exported_symbols.is_empty() && !is_static { + context + .errors + .push("libpython does not export any symbols".to_string()); + } + + // Ensure that some well known Python symbols are being exported from libpython. + for symbol in PYTHON_EXPORTED_SYMBOLS { + let exported = context.libpython_exported_symbols.contains(*symbol); + let wanted = !is_static; + + if exported != wanted { + context.errors.push(format!( + "libpython {} {}", + if wanted { "doesn't export" } else { "exports" }, + symbol, + )); + } + } + + // Validate extension module metadata. + for (name, variants) in json.as_ref().unwrap().build_info.extensions.iter() { + for ext in variants { + if let Some(shared) = &ext.shared_lib { + if !seen_paths.contains(&PathBuf::from("python").join(shared)) { + context.errors.push(format!( + "extension module {name} references missing shared library path {shared}" + )); + } + } + + #[allow(clippy::if_same_then_else)] + // Static builds never have shared library extension modules. + let want_shared = if is_static { + false + // Extension modules in libpython core are never shared libraries. + } else if ext.in_core { + false + // All remaining extensions are shared on Windows. + } else if triple.contains("windows") { + true + // On POSIX platforms we maintain a list. + } else { + SHARED_LIBRARY_EXTENSIONS.contains(&name.as_str()) + }; + + if want_shared && ext.shared_lib.is_none() { + context.errors.push(format!( + "extension module {name} does not have a shared library" + )); + } else if !want_shared && ext.shared_lib.is_some() { + context.errors.push(format!( + "extension module {name} contains a shared library unexpectedly" + )); + } + + // Ensure initialization functions are exported. + + // Note that we export PyInit_* functions from libpython on POSIX whereas these + // aren't exported from official Python builds. We may want to consider changing + // this. + if ext.init_fn == "NULL" { + continue; + } + + let exported = context.libpython_exported_symbols.contains(&ext.init_fn); + + #[allow(clippy::needless_bool, clippy::if_same_then_else)] + // Static distributions never export symbols. + let wanted = if is_static { + false + // For some strange reason _PyWarnings_Init is exported as part of the ABI + } else if name == "_warnings" { + // But not on Python 3.13 on Windows + if triple.contains("-windows-") { + matches!(python_major_minor, "3.10" | "3.11" | "3.12") + } else { + true + } + // Windows dynamic doesn't export extension module init functions. + } else if triple.contains("-windows-") { + false + // Presence of a shared library extension implies no export. + } else if ext.shared_lib.is_some() { + false + } else { + true + }; + + if exported != wanted { + context.errors.push(format!( + "libpython {} {} for extension module {}", + if wanted { "doesn't export" } else { "exports" }, + ext.init_fn, + name + )); + } + } + } + + // Validate Mach-O symbols and libraries against what the SDKs say. This is only supported + // on macOS. + if triple.contains("-apple-darwin") { + if let Some(sdks) = macos_sdks { + if let Some(value) = json.as_ref().unwrap().apple_sdk_deployment_target.as_ref() { + let target_minimum_sdk = semver::Version::parse(&format!("{value}.0"))?; + + sdks.validate_context(&mut context, target_minimum_sdk, triple)?; + } else { + context.errors.push( + "cannot perform Apple targeting analysis due to missing SDK advertisement" + .into(), + ); + }; + } + } + + // Ensure all referenced object paths are in the archive. + for object_path in json.as_ref().unwrap().all_object_paths() { + let wanted_path = PathBuf::from("python").join(object_path); + + if !seen_paths.contains(&wanted_path) { + context.errors.push(format!( + "PYTHON.json referenced object file not in tar archive: {}", + wanted_path.display() + )); + } + } + + Ok(context.errors) +} + +pub fn command_validate_distribution(args: &ArgMatches) -> Result<()> { + let macos_sdks = if let Some(path) = args.get_one::("macos_sdks_path") { + Some(IndexedSdks::new(path)?) + } else { + None + }; + + let mut success = true; + + for path in args.get_many::("path").unwrap() { + println!("validating {}", path.display()); + let errors = validate_distribution(path, macos_sdks.as_ref())?; + + if errors.is_empty() { + println!(" {} OK", path.display()); + } else { + for error in errors { + println!(" error: {error}"); + } + + success = false; + } + } + + if success { + Ok(()) + } else { + Err(anyhow!("errors found")) + } +} diff --git a/src/verify_distribution.py b/src/verify_distribution.py deleted file mode 100644 index 67f455b65..000000000 --- a/src/verify_distribution.py +++ /dev/null @@ -1,156 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -import os -import sys -import unittest - - -TERMINFO_DIRS = [ - "/etc/terminfo", - "/lib/terminfo", - "/usr/share/terminfo", -] - -TCL_PATHS = [ - # POSIX - ("lib", "tcl", "tcl"), - # Windows. - ("tcl",), -] - -HERE = os.path.dirname(sys.executable) -INSTALL_ROOT = os.path.dirname(HERE) - -# Need to set TCL_LIBRARY so local tcl/tk files get picked up. -for parts in TCL_PATHS: - candidate = os.path.join(INSTALL_ROOT, *parts) - - if os.path.exists(candidate): - os.environ["TCL_LIBRARY"] = candidate - break - -# Need to set TERMINFO_DIRS so terminfo database can be located. -if "TERMINFO_DIRS" not in os.environ: - terminfo_dirs = [p for p in TERMINFO_DIRS if os.path.exists(p)] - if terminfo_dirs: - os.environ["TERMINFO_DIRS"] = ":".join(terminfo_dirs) - - -class TestPythonInterpreter(unittest.TestCase): - def test_compression(self): - import bz2, lzma, zlib - - self.assertTrue(lzma.is_check_supported(lzma.CHECK_CRC64)) - self.assertTrue(lzma.is_check_supported(lzma.CHECK_SHA256)) - - def test_ctypes(self): - import ctypes - - self.assertIsNotNone(ctypes.pythonapi) - - # https://bugs.python.org/issue42688 - @ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) - def error_handler(fif, message): - pass - - @unittest.skipIf(os.name == "nt", "curses not available on Windows") - def test_curses_import(self): - import curses - - @unittest.skipIf(os.name == "nt", "curses not available on Windows") - @unittest.skipIf("TERM" not in os.environ, "TERM not set") - def test_curses_interactive(self): - import curses - - curses.initscr() - curses.endwin() - - def test_hashlib(self): - import hashlib - - wanted_hashes = { - "blake2b", - "blake2s", - "md4", - "md5", - "md5-sha1", - "ripemd160", - "sha1", - "sha224", - "sha256", - "sha3_224", - "sha3_256", - "sha3_384", - "sha3_512", - "sha384", - "sha3_224", - "sha3_256", - "sha3_384", - "sha3_512", - "sha512", - "sha512_224", - "sha512_256", - "shake_128", - "shake_256", - "shake_128", - "shake_256", - "sm3", - "whirlpool", - } - - # Windows doesn't appear to support mdc2. - if os.name != "nt": - wanted_hashes.add("mdc2") - - for hash in wanted_hashes: - self.assertIn(hash, hashlib.algorithms_available) - - def test_sqlite(self): - import sqlite3 - - self.assertEqual(sqlite3.sqlite_version_info, (3, 36, 0)) - - def test_ssl(self): - import ssl - - self.assertTrue(ssl.HAS_TLSv1) - self.assertTrue(ssl.HAS_TLSv1_1) - self.assertTrue(ssl.HAS_TLSv1_2) - self.assertTrue(ssl.HAS_TLSv1_3) - - self.assertEqual(ssl.OPENSSL_VERSION_INFO, (1, 1, 1, 12, 15)) - - ssl.create_default_context() - - @unittest.skipIf("TCL_LIBRARY" not in os.environ, "TCL_LIBRARY not set") - @unittest.skipIf("DISPLAY" not in os.environ, "DISPLAY not set") - def test_tkinter(self): - import tkinter as tk - - class Application(tk.Frame): - def __init__(self, master=None): - super().__init__(master) - self.master = master - self.pack() - - self.hi_there = tk.Button(self) - self.hi_there["text"] = "Hello World\n(click me)" - self.hi_there["command"] = self.say_hi - self.hi_there.pack(side="top") - - self.quit = tk.Button( - self, text="QUIT", fg="red", command=self.master.destroy - ) - self.quit.pack(side="bottom") - - def say_hi(self): - print("hi there, everyone!") - - root = tk.Tk() - Application(master=root) - - -if __name__ == "__main__": - unittest.main() diff --git a/test-distribution.py b/test-distribution.py index b6114ea52..c4018f102 100755 --- a/test-distribution.py +++ b/test-distribution.py @@ -1,52 +1,23 @@ -#!/usr/bin/env python3 +#!/usr/bin/env -S uv run # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. """Script to run Python tests from a distribution archive.""" -import json +import os import pathlib -import subprocess import sys -import tarfile -import tempfile -import zstandard - - -def main(args): - if not args: - print("Usage: test-distribution.py path/to/distribution.tar.zst") - return 1 - - distribution_path = args[0] - - with tempfile.TemporaryDirectory() as td: - td = pathlib.Path(td) - - with open(distribution_path, "rb") as fh: - dctx = zstandard.ZstdDecompressor() - with dctx.stream_reader(fh) as reader: - with tarfile.open(mode="r|", fileobj=reader) as tf: - tf.extractall(td) - - root = td / "python" - - python_json = root / "PYTHON.json" - - with python_json.open("rb") as fh: - info = json.load(fh) - - test_args = [ - str(root / info["python_exe"]), - str(root / info["run_tests"]), - ] - - test_args.extend(args[1:]) - - return subprocess.run(test_args).returncode +from pythonbuild.testdist import main +ROOT = pathlib.Path(os.path.abspath(__file__)).parent if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) + # Unbuffer stdout. + sys.stdout.reconfigure(line_buffering=True) + + try: + sys.exit(main(ROOT, sys.argv[1:])) + except KeyboardInterrupt: + sys.exit(1) diff --git a/tests/test_mirror.py b/tests/test_mirror.py new file mode 100644 index 000000000..2d026867f --- /dev/null +++ b/tests/test_mirror.py @@ -0,0 +1,163 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +from __future__ import annotations + +import tempfile +import unittest +from pathlib import Path +from unittest import mock + +from boto3.s3.transfer import TransferConfig + +from pythonbuild.mirror import ( + MirrorError, + S3MirrorClient, + UploadEntry, + build_upload_entries, + destination_to_source_name, + main, +) + + +class MirrorTests(unittest.TestCase): + def test_destination_to_source_name_full_archive(self) -> None: + self.assertEqual( + destination_to_source_name( + "cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst", + "20260324", + "20260324T1200", + ), + "cpython-3.12.10-x86_64-unknown-linux-gnu-pgo+lto-20260324T1200.tar.zst", + ) + + def test_destination_to_source_name_install_only_archive(self) -> None: + self.assertEqual( + destination_to_source_name( + "cpython-3.13.2+20260324-x86_64-unknown-linux-gnu-freethreaded-install_only.tar.gz", + "20260324", + "20260324T1200", + ), + "cpython-3.13.2-x86_64-unknown-linux-gnu-freethreaded-install_only-20260324T1200.tar.gz", + ) + + def test_destination_to_source_name_rejects_wrong_tag(self) -> None: + with self.assertRaises(MirrorError): + destination_to_source_name( + "cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst", + "20260325", + "20260324T1200", + ) + + def test_build_upload_entries(self) -> None: + with tempfile.TemporaryDirectory() as td: + dist = Path(td) + ( + dist + / "cpython-3.12.10-x86_64-unknown-linux-gnu-pgo+lto-20260324T1200.tar.zst" + ).write_bytes(b"full") + ( + dist + / "cpython-3.12.10-x86_64-unknown-linux-gnu-install_only-20260324T1200.tar.gz" + ).write_bytes(b"install") + (dist / "SHA256SUMS").write_text( + "abc cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst\n" + "def cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-install_only.tar.gz\n" + ) + + uploads, missing = build_upload_entries(dist, "20260324") + + self.assertEqual(missing, []) + self.assertEqual( + uploads, + [ + UploadEntry( + source_name="cpython-3.12.10-x86_64-unknown-linux-gnu-pgo+lto-20260324T1200.tar.zst", + dest_name="cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst", + ), + UploadEntry( + source_name="cpython-3.12.10-x86_64-unknown-linux-gnu-install_only-20260324T1200.tar.gz", + dest_name="cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-install_only.tar.gz", + ), + ], + ) + + def test_build_upload_entries_reports_missing(self) -> None: + with tempfile.TemporaryDirectory() as td: + dist = Path(td) + ( + dist + / "cpython-3.12.10-x86_64-unknown-linux-gnu-pgo+lto-20260324T1200.tar.zst" + ).write_bytes(b"full") + (dist / "SHA256SUMS").write_text( + "abc cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst\n" + "def cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-install_only.tar.gz\n" + ) + + uploads, missing = build_upload_entries(dist, "20260324") + + self.assertEqual(len(uploads), 1) + self.assertEqual( + missing, + [ + "cpython-3.12.10-x86_64-unknown-linux-gnu-install_only-20260324T1200.tar.gz" + ], + ) + + def test_main_reports_all_upload_errors(self) -> None: + with tempfile.TemporaryDirectory() as td: + dist = Path(td) + full_path = ( + dist + / "cpython-3.12.10-x86_64-unknown-linux-gnu-pgo+lto-20260324T1200.tar.zst" + ) + install_only_path = ( + dist + / "cpython-3.12.10-x86_64-unknown-linux-gnu-install_only-20260324T1200.tar.gz" + ) + full_path.write_bytes(b"full") + install_only_path.write_bytes(b"install") + (dist / "SHA256SUMS").write_text( + "abc cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst\n" + "def cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-install_only.tar.gz\n" + ) + + def fail_upload(bucket: str, key: str, path: Path) -> None: + raise MirrorError(f"failed for {path.name}") + + with ( + mock.patch( + "pythonbuild.mirror.make_s3_client", + return_value=(object(), TransferConfig()), + ), + mock.patch.object( + S3MirrorClient, + "upload_file", + side_effect=fail_upload, + ), + ): + with self.assertRaises(SystemExit) as cm: + main( + [ + "--dist", + str(dist), + "--tag", + "20260324", + "--bucket", + "bucket", + "--prefix", + "prefix/", + ] + ) + + self.assertEqual( + str(cm.exception), + "encountered 2 upload errors:\n" + f"- failed for {install_only_path.name}\n" + f"- failed for {full_path.name}", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_mirror_integration.py b/tests/test_mirror_integration.py new file mode 100644 index 000000000..993d9e101 --- /dev/null +++ b/tests/test_mirror_integration.py @@ -0,0 +1,114 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +from __future__ import annotations + +import os +import tempfile +import unittest +from pathlib import Path +from unittest import mock + +import boto3 +from moto.server import ThreadedMotoServer + +from pythonbuild.mirror import CACHE_CONTROL, main + + +class MirrorIntegrationTests(unittest.TestCase): + def test_uploads_artifacts_to_mock_s3(self) -> None: + with tempfile.TemporaryDirectory() as td: + dist = Path(td) / "dist" + dist.mkdir() + + full_path = ( + dist + / "cpython-3.12.10-x86_64-unknown-linux-gnu-pgo+lto-20260324T1200.tar.zst" + ) + install_only_path = ( + dist + / "cpython-3.12.10-x86_64-unknown-linux-gnu-install_only-20260324T1200.tar.gz" + ) + shasums_path = dist / "SHA256SUMS" + + full_path.write_bytes(b"full") + install_only_path.write_bytes(b"install") + shasums_path.write_text( + "abc cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst\n" + "def cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-install_only.tar.gz\n" + ) + + server = ThreadedMotoServer(ip_address="127.0.0.1", port=0, verbose=False) + server.start() + try: + host, port = server.get_host_and_port() + endpoint_url = f"http://{host}:{port}" + bucket = "mirror-bucket" + prefix = "github/python-build-standalone/releases/download/20260324/" + + s3 = boto3.client( + "s3", + endpoint_url=endpoint_url, + region_name="us-east-1", + aws_access_key_id="testing", + aws_secret_access_key="testing", + ) + s3.create_bucket(Bucket=bucket) + + with mock.patch.dict( + os.environ, + { + "AWS_ACCESS_KEY_ID": "testing", + "AWS_SECRET_ACCESS_KEY": "testing", + "AWS_DEFAULT_REGION": "us-east-1", + "AWS_ENDPOINT_URL": endpoint_url, + }, + ): + self.assertEqual( + main( + [ + "--dist", + str(dist), + "--tag", + "20260324", + "--bucket", + bucket, + "--prefix", + prefix, + ] + ), + 0, + ) + + objects = s3.list_objects_v2(Bucket=bucket, Prefix=prefix) + self.assertEqual( + sorted(obj["Key"] for obj in objects["Contents"]), + [ + prefix + "SHA256SUMS", + prefix + + "cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-install_only.tar.gz", + prefix + + "cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-pgo+lto-full.tar.zst", + ], + ) + + install_only_object = s3.get_object( + Bucket=bucket, + Key=prefix + + "cpython-3.12.10+20260324-x86_64-unknown-linux-gnu-install_only.tar.gz", + ) + self.assertEqual(install_only_object["Body"].read(), b"install") + self.assertEqual(install_only_object["CacheControl"], CACHE_CONTROL) + + shasums_object = s3.get_object(Bucket=bucket, Key=prefix + "SHA256SUMS") + self.assertEqual( + shasums_object["Body"].read(), shasums_path.read_bytes() + ) + self.assertEqual(shasums_object["CacheControl"], CACHE_CONTROL) + finally: + server.stop() + + +if __name__ == "__main__": + unittest.main() diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..262e79edc --- /dev/null +++ b/uv.lock @@ -0,0 +1,2048 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version < '3.11'", +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "antlr4-python3-runtime" +version = "4.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/5f/2cdf6f7aca3b20d3f316e9f505292e1f256a32089bd702034c29ebde6242/antlr4_python3_runtime-4.13.2.tar.gz", hash = "sha256:909b647e1d2fc2b70180ac586df3933e38919c85f98ccc656a96cd3f25ef3916", size = 117467, upload-time = "2024-08-03T19:00:12.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/03/a851e84fcbb85214dc637b6378121ef9a0dd61b4c65264675d8a5c9b1ae7/antlr4_python3_runtime-4.13.2-py3-none-any.whl", hash = "sha256:fe3835eb8d33daece0e799090eda89719dbccee7aa39ef94eed3818cafa5a7e8", size = 144462, upload-time = "2024-08-03T19:00:11.134Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "aws-sam-translator" +version = "1.103.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "boto3" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/e3/82cc7240504b1c0d2d7ed7028b05ccceedb02932b8638c61a8372a5d875f/aws_sam_translator-1.103.0.tar.gz", hash = "sha256:8317b72ef412db581dc7846932a44dfc1729adea578d9307a3e6ece46a7882ca", size = 344881, upload-time = "2025-11-21T19:50:51.818Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/86/6414c215ff0a10b33bf89622951e7d4413106320657535d2ba0e4f634661/aws_sam_translator-1.103.0-py3-none-any.whl", hash = "sha256:d4eb4a1efa62f00b253ee5f8c0084bd4b7687186c6a12338f900ebe07ff74dad", size = 403100, upload-time = "2025-11-21T19:50:50.528Z" }, +] + +[[package]] +name = "aws-xray-sdk" +version = "2.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/14/25/0cbd7a440080def5e6f063720c3b190a25f8aa2938c1e34415dc18241596/aws_xray_sdk-2.15.0.tar.gz", hash = "sha256:794381b96e835314345068ae1dd3b9120bd8b4e21295066c37e8814dbb341365", size = 76315, upload-time = "2025-10-29T20:59:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c3/f30a7a63e664acc7c2545ca0491b6ce8264536e0e5cad3965f1d1b91e960/aws_xray_sdk-2.15.0-py2.py3-none-any.whl", hash = "sha256:422d62ad7d52e373eebb90b642eb1bb24657afe03b22a8df4a8b2e5108e278a3", size = 103228, upload-time = "2025-10-29T21:00:24.12Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "boto3" +version = "1.42.75" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/1c/f836f5e52095a3374eee9317f980a22d9139477fe6277498ebf4406e35b4/boto3-1.42.75.tar.gz", hash = "sha256:3c7fd95a50c69271bd7707b7eda07dcfddb30e961a392613010f7ee81d91acb3", size = 112812, upload-time = "2026-03-24T21:14:00.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/31/c04caef287a0ea507ba634f2280dbe8314d89c1d8da1aef648b661ad1201/boto3-1.42.75-py3-none-any.whl", hash = "sha256:16bc657d16403ee8e11c8b6920c245629e37a36ea60352b919da566f82b4cb4c", size = 140556, upload-time = "2026-03-24T21:13:58.004Z" }, +] + +[[package]] +name = "boto3-stubs" +version = "1.42.75" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore-stubs" }, + { name = "types-s3transfer" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/12/63/3b1e82011adafa79217e38c1675acfc2138d476246ce9987cc16d1970479/boto3_stubs-1.42.75.tar.gz", hash = "sha256:0c5341b62ea713328bbc66e455cd95490b4c4478e474a5e2022aa43e7e1f798e", size = 101354, upload-time = "2026-03-24T21:54:15.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/5d/1467972bdfaec7fb12493e767f4109ac327323b6ddf75530b557da94eaf4/boto3_stubs-1.42.75-py3-none-any.whl", hash = "sha256:1872b3c0a43051a58ff9e5a782070cde28db67e24f3f41c2d6124166a11e821f", size = 70012, upload-time = "2026-03-24T21:54:08.695Z" }, +] + +[package.optional-dependencies] +s3 = [ + { name = "mypy-boto3-s3" }, +] + +[[package]] +name = "botocore" +version = "1.42.75" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/05/b16d6ac5eea465d42e65941436eab7d2e6f6ebef01ba4d70b6f5d0b992ce/botocore-1.42.75.tar.gz", hash = "sha256:95c8e716b6be903ee1601531caa4f50217400aa877c18fe9a2c3047d2945d477", size = 15016308, upload-time = "2026-03-24T21:13:48.802Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/21/22148ff8d37d8706fc63cdc8ec292f4abbbd18b500d9970f6172f7f3bb30/botocore-1.42.75-py3-none-any.whl", hash = "sha256:915e43b7ac8f50cf3dbc937ba713de5acb999ea48ad8fecd1589d92ad415f787", size = 14689910, upload-time = "2026-03-24T21:13:43.939Z" }, +] + +[[package]] +name = "botocore-stubs" +version = "1.42.41" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-awscrt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/a8/a26608ff39e3a5866c6c79eda10133490205cbddd45074190becece3ff2a/botocore_stubs-1.42.41.tar.gz", hash = "sha256:dbeac2f744df6b814ce83ec3f3777b299a015cbea57a2efc41c33b8c38265825", size = 42411, upload-time = "2026-02-03T20:46:14.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/76/cab7af7f16c0b09347f2ebe7ffda7101132f786acb767666dce43055faab/botocore_stubs-1.42.41-py3-none-any.whl", hash = "sha256:9423110fb0e391834bd2ed44ae5f879d8cb370a444703d966d30842ce2bcb5f0", size = 66759, upload-time = "2026-02-03T20:46:13.02Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "cfn-lint" +version = "1.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aws-sam-translator" }, + { name = "jsonpatch" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "sympy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ee/b5/436c192cdf8dbddd8e09a591384f126c5a47937c14953d87b1dacacd0543/cfn_lint-1.41.0.tar.gz", hash = "sha256:6feca1cf57f9ed2833bab68d9b1d38c8033611e571fa792e45ab4a39e2b8ab57", size = 3408534, upload-time = "2025-11-18T20:03:33.431Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/5e/81ef8f87894543210d783a495c8880cfb0b5baa0ee3bcc6d852f1b343863/cfn_lint-1.41.0-py3-none-any.whl", hash = "sha256:cd43f76f59a664b2bad580840827849fac0d56a3b80e9a41315d8ab5ff6b563a", size = 5674429, upload-time = "2025-11-18T20:03:31.083Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/8c/2c56124c6dc53a774d435f985b5973bc592f42d437be58c0c92d65ae7296/charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95", size = 298751, upload-time = "2026-03-15T18:50:00.003Z" }, + { url = "https://files.pythonhosted.org/packages/86/2a/2a7db6b314b966a3bcad8c731c0719c60b931b931de7ae9f34b2839289ee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd", size = 200027, upload-time = "2026-03-15T18:50:01.702Z" }, + { url = "https://files.pythonhosted.org/packages/68/f2/0fe775c74ae25e2a3b07b01538fc162737b3e3f795bada3bc26f4d4d495c/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4", size = 220741, upload-time = "2026-03-15T18:50:03.194Z" }, + { url = "https://files.pythonhosted.org/packages/10/98/8085596e41f00b27dd6aa1e68413d1ddda7e605f34dd546833c61fddd709/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db", size = 215802, upload-time = "2026-03-15T18:50:05.859Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ce/865e4e09b041bad659d682bbd98b47fb490b8e124f9398c9448065f64fee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89", size = 207908, upload-time = "2026-03-15T18:50:07.676Z" }, + { url = "https://files.pythonhosted.org/packages/a8/54/8c757f1f7349262898c2f169e0d562b39dcb977503f18fdf0814e923db78/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565", size = 194357, upload-time = "2026-03-15T18:50:09.327Z" }, + { url = "https://files.pythonhosted.org/packages/6f/29/e88f2fac9218907fc7a70722b393d1bbe8334c61fe9c46640dba349b6e66/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9", size = 205610, upload-time = "2026-03-15T18:50:10.732Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c5/21d7bb0cb415287178450171d130bed9d664211fdd59731ed2c34267b07d/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7", size = 203512, upload-time = "2026-03-15T18:50:12.535Z" }, + { url = "https://files.pythonhosted.org/packages/a4/be/ce52f3c7fdb35cc987ad38a53ebcef52eec498f4fb6c66ecfe62cfe57ba2/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550", size = 195398, upload-time = "2026-03-15T18:50:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/81/a0/3ab5dd39d4859a3555e5dadfc8a9fa7f8352f8c183d1a65c90264517da0e/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0", size = 221772, upload-time = "2026-03-15T18:50:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/6a4e41a97ba6b2fa87f849c41e4d229449a586be85053c4d90135fe82d26/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8", size = 205759, upload-time = "2026-03-15T18:50:17.047Z" }, + { url = "https://files.pythonhosted.org/packages/db/3b/34a712a5ee64a6957bf355b01dc17b12de457638d436fdb05d01e463cd1c/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0", size = 216938, upload-time = "2026-03-15T18:50:18.44Z" }, + { url = "https://files.pythonhosted.org/packages/cb/05/5bd1e12da9ab18790af05c61aafd01a60f489778179b621ac2a305243c62/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b", size = 210138, upload-time = "2026-03-15T18:50:19.852Z" }, + { url = "https://files.pythonhosted.org/packages/bd/8e/3cb9e2d998ff6b21c0a1860343cb7b83eba9cdb66b91410e18fc4969d6ab/charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557", size = 144137, upload-time = "2026-03-15T18:50:21.505Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8f/78f5489ffadb0db3eb7aff53d31c24531d33eb545f0c6f6567c25f49a5ff/charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6", size = 154244, upload-time = "2026-03-15T18:50:22.81Z" }, + { url = "https://files.pythonhosted.org/packages/e4/74/e472659dffb0cadb2f411282d2d76c60da1fc94076d7fffed4ae8a93ec01/charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058", size = 143312, upload-time = "2026-03-15T18:50:24.074Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, + { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, + { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, + { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, + { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, + { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, + { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, + { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, + { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, + { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, + { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, + { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, + { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, + { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, + { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, + { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, + { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, + { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, + { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, + { url = "https://files.pythonhosted.org/packages/eb/dd/2d9fdb07cebdf3d51179730afb7d5e576153c6744c3ff8fded23030c204e/cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", size = 3476964, upload-time = "2026-02-10T19:18:20.687Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, + { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, + { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605, upload-time = "2026-02-10T19:18:29.233Z" }, +] + +[[package]] +name = "docker" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, +] + +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "flask-cors" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472, upload-time = "2025-12-12T20:31:42.861Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257, upload-time = "2025-12-12T20:31:41.3Z" }, +] + +[[package]] +name = "graphql-core" +version = "3.2.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/c5/36aa96205c3ecbb3d34c7c24189e4553c7ca2ebc7e1dd07432339b980272/graphql_core-3.2.8.tar.gz", hash = "sha256:015457da5d996c924ddf57a43f4e959b0b94fb695b85ed4c29446e508ed65cf3", size = 513181, upload-time = "2026-03-05T19:55:37.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/41/cb887d9afc5dabd78feefe6ccbaf83ff423c206a7a1b7aeeac05120b2125/graphql_core-3.2.8-py3-none-any.whl", hash = "sha256:cbee07bee1b3ed5e531723685369039f32ff815ef60166686e0162f540f1520c", size = 207349, upload-time = "2026-03-05T19:55:35.911Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "joserfc" +version = "1.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/90/b8cc8635c4ce2e5e8104bf26ef147f6e599478f6329107283cdc53aae97f/joserfc-1.6.3.tar.gz", hash = "sha256:c00c2830db969b836cba197e830e738dd9dda0955f1794e55d3c636f17f5c9a6", size = 229090, upload-time = "2026-02-25T15:33:38.167Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4f/124b3301067b752f44f292f0b9a74e837dd75ff863ee39500a082fc4c733/joserfc-1.6.3-py3-none-any.whl", hash = "sha256:6beab3635358cbc565cb94fb4c53d0557e6d10a15b933e2134939351590bda9a", size = 70465, upload-time = "2026-02-25T15:33:36.997Z" }, +] + +[[package]] +name = "jsonpatch" +version = "1.33" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonpointer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699, upload-time = "2023-06-26T12:07:29.144Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898, upload-time = "2023-06-16T21:01:28.466Z" }, +] + +[[package]] +name = "jsonpath-ng" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/58/250751940d75c8019659e15482d548a4aa3b6ce122c515102a4bfdac50e3/jsonpath_ng-1.8.0.tar.gz", hash = "sha256:54252968134b5e549ea5b872f1df1168bd7defe1a52fed5a358c194e1943ddc3", size = 74513, upload-time = "2026-02-24T14:42:06.182Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/99/33c7d78a3fb70d545fd5411ac67a651c81602cc09c9cf0df383733f068c5/jsonpath_ng-1.8.0-py3-none-any.whl", hash = "sha256:b8dde192f8af58d646fc031fac9c99fe4d00326afc4148f1f043c601a8cfe138", size = 67844, upload-time = "2026-02-28T00:53:19.637Z" }, +] + +[[package]] +name = "jsonpointer" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068, upload-time = "2026-03-23T22:32:32.458Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659, upload-time = "2026-03-23T22:32:31.568Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/6e/35174c1d3f30560848c82d3c233c01420e047d70925c897a4d6e932b4898/jsonschema-4.24.1.tar.gz", hash = "sha256:fe45a130cc7f67cd0d67640b4e7e3e2e666919462ae355eda238296eafeb4b5d", size = 356635, upload-time = "2025-07-17T14:40:01.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/7f/ea48ffb58f9791f9d97ccb35e42fea1ebc81c67ce36dc4b8b2eee60e8661/jsonschema-4.24.1-py3-none-any.whl", hash = "sha256:6b916866aa0b61437785f1277aa2cbd63512e8d4b47151072ef13292049b4627", size = 89060, upload-time = "2025-07-17T14:39:59.471Z" }, +] + +[[package]] +name = "jsonschema-path" +version = "0.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pathable" }, + { name = "pyyaml" }, + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/8a/7e6102f2b8bdc6705a9eb5294f8f6f9ccd3a8420e8e8e19671d1dd773251/jsonschema_path-0.4.5.tar.gz", hash = "sha256:c6cd7d577ae290c7defd4f4029e86fdb248ca1bd41a07557795b3c95e5144918", size = 15113, upload-time = "2026-03-03T09:56:46.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/d5/4e96c44f6c1ea3d812cf5391d81a4f5abaa540abf8d04ecd7f66e0ed11df/jsonschema_path-0.4.5-py3-none-any.whl", hash = "sha256:7d77a2c3f3ec569a40efe5c5f942c44c1af2a6f96fe0866794c9ef5b8f87fd65", size = 19368, upload-time = "2026-03-03T09:56:45.39Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "lazy-object-proxy" +version = "1.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/a2/69df9c6ba6d316cfd81fe2381e464db3e6de5db45f8c43c6a23504abf8cb/lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61", size = 43681, upload-time = "2025-08-22T13:50:06.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/2b/d5e8915038acbd6c6a9fcb8aaf923dc184222405d3710285a1fec6e262bc/lazy_object_proxy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61d5e3310a4aa5792c2b599a7a78ccf8687292c8eb09cf187cca8f09cf6a7519", size = 26658, upload-time = "2025-08-22T13:42:23.373Z" }, + { url = "https://files.pythonhosted.org/packages/da/8f/91fc00eeea46ee88b9df67f7c5388e60993341d2a406243d620b2fdfde57/lazy_object_proxy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ca33565f698ac1aece152a10f432415d1a2aa9a42dfe23e5ba2bc255ab91f6", size = 68412, upload-time = "2025-08-22T13:42:24.727Z" }, + { url = "https://files.pythonhosted.org/packages/07/d2/b7189a0e095caedfea4d42e6b6949d2685c354263bdf18e19b21ca9b3cd6/lazy_object_proxy-1.12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01c7819a410f7c255b20799b65d36b414379a30c6f1684c7bd7eb6777338c1b", size = 67559, upload-time = "2025-08-22T13:42:25.875Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ad/b013840cc43971582ff1ceaf784d35d3a579650eb6cc348e5e6ed7e34d28/lazy_object_proxy-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:029d2b355076710505c9545aef5ab3f750d89779310e26ddf2b7b23f6ea03cd8", size = 66651, upload-time = "2025-08-22T13:42:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/7e/6f/b7368d301c15612fcc4cd00412b5d6ba55548bde09bdae71930e1a81f2ab/lazy_object_proxy-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc6e3614eca88b1c8a625fc0a47d0d745e7c3255b21dac0e30b3037c5e3deeb8", size = 66901, upload-time = "2025-08-22T13:42:28.585Z" }, + { url = "https://files.pythonhosted.org/packages/61/1b/c6b1865445576b2fc5fa0fbcfce1c05fee77d8979fd1aa653dd0f179aefc/lazy_object_proxy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:be5fe974e39ceb0d6c9db0663c0464669cf866b2851c73971409b9566e880eab", size = 26536, upload-time = "2025-08-22T13:42:29.636Z" }, + { url = "https://files.pythonhosted.org/packages/01/b3/4684b1e128a87821e485f5a901b179790e6b5bc02f89b7ee19c23be36ef3/lazy_object_proxy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1cf69cd1a6c7fe2dbcc3edaa017cf010f4192e53796538cc7d5e1fedbfa4bcff", size = 26656, upload-time = "2025-08-22T13:42:30.605Z" }, + { url = "https://files.pythonhosted.org/packages/3a/03/1bdc21d9a6df9ff72d70b2ff17d8609321bea4b0d3cffd2cea92fb2ef738/lazy_object_proxy-1.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:efff4375a8c52f55a145dc8487a2108c2140f0bec4151ab4e1843e52eb9987ad", size = 68832, upload-time = "2025-08-22T13:42:31.675Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4b/5788e5e8bd01d19af71e50077ab020bc5cce67e935066cd65e1215a09ff9/lazy_object_proxy-1.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1192e8c2f1031a6ff453ee40213afa01ba765b3dc861302cd91dbdb2e2660b00", size = 69148, upload-time = "2025-08-22T13:42:32.876Z" }, + { url = "https://files.pythonhosted.org/packages/79/0e/090bf070f7a0de44c61659cb7f74c2fe02309a77ca8c4b43adfe0b695f66/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3605b632e82a1cbc32a1e5034278a64db555b3496e0795723ee697006b980508", size = 67800, upload-time = "2025-08-22T13:42:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d2/b320325adbb2d119156f7c506a5fbfa37fcab15c26d13cf789a90a6de04e/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a61095f5d9d1a743e1e20ec6d6db6c2ca511961777257ebd9b288951b23b44fa", size = 68085, upload-time = "2025-08-22T13:42:35.197Z" }, + { url = "https://files.pythonhosted.org/packages/6a/48/4b718c937004bf71cd82af3713874656bcb8d0cc78600bf33bb9619adc6c/lazy_object_proxy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:997b1d6e10ecc6fb6fe0f2c959791ae59599f41da61d652f6c903d1ee58b7370", size = 26535, upload-time = "2025-08-22T13:42:36.521Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1b/b5f5bd6bda26f1e15cd3232b223892e4498e34ec70a7f4f11c401ac969f1/lazy_object_proxy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ee0d6027b760a11cc18281e702c0309dd92da458a74b4c15025d7fc490deede", size = 26746, upload-time = "2025-08-22T13:42:37.572Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/314889b618075c2bfc19293ffa9153ce880ac6153aacfd0a52fcabf21a66/lazy_object_proxy-1.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ab2c584e3cc8be0dfca422e05ad30a9abe3555ce63e9ab7a559f62f8dbc6ff9", size = 71457, upload-time = "2025-08-22T13:42:38.743Z" }, + { url = "https://files.pythonhosted.org/packages/11/53/857fc2827fc1e13fbdfc0ba2629a7d2579645a06192d5461809540b78913/lazy_object_proxy-1.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14e348185adbd03ec17d051e169ec45686dcd840a3779c9d4c10aabe2ca6e1c0", size = 71036, upload-time = "2025-08-22T13:42:40.184Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/e581ffed864cd33c1b445b5763d617448ebb880f48675fc9de0471a95cbc/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4fcbe74fb85df8ba7825fa05eddca764138da752904b378f0ae5ab33a36c308", size = 69329, upload-time = "2025-08-22T13:42:41.311Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/15f8f5a0b0b2e668e756a152257d26370132c97f2f1943329b08f057eff0/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:563d2ec8e4d4b68ee7848c5ab4d6057a6d703cb7963b342968bb8758dda33a23", size = 70690, upload-time = "2025-08-22T13:42:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/5d/aa/f02be9bbfb270e13ee608c2b28b8771f20a5f64356c6d9317b20043c6129/lazy_object_proxy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:53c7fd99eb156bbb82cbc5d5188891d8fdd805ba6c1e3b92b90092da2a837073", size = 26563, upload-time = "2025-08-22T13:42:43.685Z" }, + { url = "https://files.pythonhosted.org/packages/f4/26/b74c791008841f8ad896c7f293415136c66cc27e7c7577de4ee68040c110/lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e", size = 26745, upload-time = "2025-08-22T13:42:44.982Z" }, + { url = "https://files.pythonhosted.org/packages/9b/52/641870d309e5d1fb1ea7d462a818ca727e43bfa431d8c34b173eb090348c/lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e", size = 71537, upload-time = "2025-08-22T13:42:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/47/b6/919118e99d51c5e76e8bf5a27df406884921c0acf2c7b8a3b38d847ab3e9/lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655", size = 71141, upload-time = "2025-08-22T13:42:47.375Z" }, + { url = "https://files.pythonhosted.org/packages/e5/47/1d20e626567b41de085cf4d4fb3661a56c159feaa73c825917b3b4d4f806/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff", size = 69449, upload-time = "2025-08-22T13:42:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/58/8d/25c20ff1a1a8426d9af2d0b6f29f6388005fc8cd10d6ee71f48bff86fdd0/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be", size = 70744, upload-time = "2025-08-22T13:42:49.608Z" }, + { url = "https://files.pythonhosted.org/packages/c0/67/8ec9abe15c4f8a4bcc6e65160a2c667240d025cbb6591b879bea55625263/lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1", size = 26568, upload-time = "2025-08-22T13:42:57.719Z" }, + { url = "https://files.pythonhosted.org/packages/23/12/cd2235463f3469fd6c62d41d92b7f120e8134f76e52421413a0ad16d493e/lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65", size = 27391, upload-time = "2025-08-22T13:42:50.62Z" }, + { url = "https://files.pythonhosted.org/packages/60/9e/f1c53e39bbebad2e8609c67d0830cc275f694d0ea23d78e8f6db526c12d3/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9", size = 80552, upload-time = "2025-08-22T13:42:51.731Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b6/6c513693448dcb317d9d8c91d91f47addc09553613379e504435b4cc8b3e/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66", size = 82857, upload-time = "2025-08-22T13:42:53.225Z" }, + { url = "https://files.pythonhosted.org/packages/12/1c/d9c4aaa4c75da11eb7c22c43d7c90a53b4fca0e27784a5ab207768debea7/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847", size = 80833, upload-time = "2025-08-22T13:42:54.391Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ae/29117275aac7d7d78ae4f5a4787f36ff33262499d486ac0bf3e0b97889f6/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac", size = 79516, upload-time = "2025-08-22T13:42:55.812Z" }, + { url = "https://files.pythonhosted.org/packages/19/40/b4e48b2c38c69392ae702ae7afa7b6551e0ca5d38263198b7c79de8b3bdf/lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f", size = 27656, upload-time = "2025-08-22T13:42:56.793Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3a/277857b51ae419a1574557c0b12e0d06bf327b758ba94cafc664cb1e2f66/lazy_object_proxy-1.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c9defba70ab943f1df98a656247966d7729da2fe9c2d5d85346464bf320820a3", size = 26582, upload-time = "2025-08-22T13:49:49.366Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/c5e0fa43535bb9c87880e0ba037cdb1c50e01850b0831e80eb4f4762f270/lazy_object_proxy-1.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6763941dbf97eea6b90f5b06eb4da9418cc088fce0e3883f5816090f9afcde4a", size = 71059, upload-time = "2025-08-22T13:49:50.488Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/7dcad19c685963c652624702f1a968ff10220b16bfcc442257038216bf55/lazy_object_proxy-1.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdc70d81235fc586b9e3d1aeef7d1553259b62ecaae9db2167a5d2550dcc391a", size = 71034, upload-time = "2025-08-22T13:49:54.224Z" }, + { url = "https://files.pythonhosted.org/packages/12/ac/34cbfb433a10e28c7fd830f91c5a348462ba748413cbb950c7f259e67aa7/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0a83c6f7a6b2bfc11ef3ed67f8cbe99f8ff500b05655d8e7df9aab993a6abc95", size = 69529, upload-time = "2025-08-22T13:49:55.29Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6a/11ad7e349307c3ca4c0175db7a77d60ce42a41c60bcb11800aabd6a8acb8/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:256262384ebd2a77b023ad02fbcc9326282bcfd16484d5531154b02bc304f4c5", size = 70391, upload-time = "2025-08-22T13:49:56.35Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/9b410ed8fbc6e79c1ee8b13f8777a80137d4bc189caf2c6202358e66192c/lazy_object_proxy-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7601ec171c7e8584f8ff3f4e440aa2eebf93e854f04639263875b8c2971f819f", size = 26988, upload-time = "2025-08-22T13:49:57.302Z" }, + { url = "https://files.pythonhosted.org/packages/41/a0/b91504515c1f9a299fc157967ffbd2f0321bce0516a3d5b89f6f4cad0355/lazy_object_proxy-1.12.0-pp39.pp310.pp311.graalpy311-none-any.whl", hash = "sha256:c3b2e0af1f7f77c4263759c4824316ce458fabe0fceadcd24ef8ca08b2d1e402", size = 15072, upload-time = "2025-08-22T13:50:05.498Z" }, +] + +[[package]] +name = "librt" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/5f/63f5fa395c7a8a93558c0904ba8f1c8d1b997ca6a3de61bc7659970d66bf/librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc", size = 65697, upload-time = "2026-02-17T16:11:06.903Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e0/0472cf37267b5920eff2f292ccfaede1886288ce35b7f3203d8de00abfe6/librt-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7", size = 68376, upload-time = "2026-02-17T16:11:08.395Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8bd1359fdcd27ab897cd5963294fa4a7c83b20a8564678e4fd12157e56a5/librt-0.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6", size = 197084, upload-time = "2026-02-17T16:11:09.774Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fe/163e33fdd091d0c2b102f8a60cc0a61fd730ad44e32617cd161e7cd67a01/librt-0.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0", size = 207337, upload-time = "2026-02-17T16:11:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/f85130582f05dcf0c8902f3d629270231d2f4afdfc567f8305a952ac7f14/librt-0.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b", size = 219980, upload-time = "2026-02-17T16:11:12.499Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/cb5e4d03659e043a26c74e08206412ac9a3742f0477d96f9761a55313b5f/librt-0.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6", size = 212921, upload-time = "2026-02-17T16:11:14.484Z" }, + { url = "https://files.pythonhosted.org/packages/b1/81/a3a01e4240579c30f3487f6fed01eb4bc8ef0616da5b4ebac27ca19775f3/librt-0.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71", size = 221381, upload-time = "2026-02-17T16:11:17.459Z" }, + { url = "https://files.pythonhosted.org/packages/08/b0/fc2d54b4b1c6fb81e77288ff31ff25a2c1e62eaef4424a984f228839717b/librt-0.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7", size = 216714, upload-time = "2026-02-17T16:11:19.197Z" }, + { url = "https://files.pythonhosted.org/packages/96/96/85daa73ffbd87e1fb287d7af6553ada66bf25a2a6b0de4764344a05469f6/librt-0.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05", size = 214777, upload-time = "2026-02-17T16:11:20.443Z" }, + { url = "https://files.pythonhosted.org/packages/12/9c/c3aa7a2360383f4bf4f04d98195f2739a579128720c603f4807f006a4225/librt-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891", size = 237398, upload-time = "2026-02-17T16:11:22.083Z" }, + { url = "https://files.pythonhosted.org/packages/61/19/d350ea89e5274665185dabc4bbb9c3536c3411f862881d316c8b8e00eb66/librt-0.8.1-cp310-cp310-win32.whl", hash = "sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7", size = 54285, upload-time = "2026-02-17T16:11:23.27Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d6/45d587d3d41c112e9543a0093d883eb57a24a03e41561c127818aa2a6bcc/librt-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2", size = 61352, upload-time = "2026-02-17T16:11:24.207Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/0e748af5e4fee180cf7cd12bd12b0513ad23b045dccb2a83191bde82d168/librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd", size = 65315, upload-time = "2026-02-17T16:11:25.152Z" }, + { url = "https://files.pythonhosted.org/packages/9d/4d/7184806efda571887c798d573ca4134c80ac8642dcdd32f12c31b939c595/librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965", size = 68021, upload-time = "2026-02-17T16:11:26.129Z" }, + { url = "https://files.pythonhosted.org/packages/ae/88/c3c52d2a5d5101f28d3dc89298444626e7874aa904eed498464c2af17627/librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da", size = 194500, upload-time = "2026-02-17T16:11:27.177Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5d/6fb0a25b6a8906e85b2c3b87bee1d6ed31510be7605b06772f9374ca5cb3/librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0", size = 205622, upload-time = "2026-02-17T16:11:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a6/8006ae81227105476a45691f5831499e4d936b1c049b0c1feb17c11b02d1/librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e", size = 218304, upload-time = "2026-02-17T16:11:29.344Z" }, + { url = "https://files.pythonhosted.org/packages/ee/19/60e07886ad16670aae57ef44dada41912c90906a6fe9f2b9abac21374748/librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3", size = 211493, upload-time = "2026-02-17T16:11:30.445Z" }, + { url = "https://files.pythonhosted.org/packages/9c/cf/f666c89d0e861d05600438213feeb818c7514d3315bae3648b1fc145d2b6/librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac", size = 219129, upload-time = "2026-02-17T16:11:32.021Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ef/f1bea01e40b4a879364c031476c82a0dc69ce068daad67ab96302fed2d45/librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596", size = 213113, upload-time = "2026-02-17T16:11:33.192Z" }, + { url = "https://files.pythonhosted.org/packages/9b/80/cdab544370cc6bc1b72ea369525f547a59e6938ef6863a11ab3cd24759af/librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99", size = 212269, upload-time = "2026-02-17T16:11:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9c/48d6ed8dac595654f15eceab2035131c136d1ae9a1e3548e777bb6dbb95d/librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe", size = 234673, upload-time = "2026-02-17T16:11:36.063Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/35b68b1db517f27a01be4467593292eb5315def8900afad29fabf56304ba/librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb", size = 54597, upload-time = "2026-02-17T16:11:37.544Z" }, + { url = "https://files.pythonhosted.org/packages/71/02/796fe8f02822235966693f257bf2c79f40e11337337a657a8cfebba5febc/librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b", size = 61733, upload-time = "2026-02-17T16:11:38.691Z" }, + { url = "https://files.pythonhosted.org/packages/28/ad/232e13d61f879a42a4e7117d65e4984bb28371a34bb6fb9ca54ec2c8f54e/librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9", size = 52273, upload-time = "2026-02-17T16:11:40.308Z" }, + { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" }, + { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, + { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, + { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, + { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, + { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, + { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, + { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" }, + { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" }, + { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, + { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" }, + { url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" }, + { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" }, + { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" }, + { url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" }, + { url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" }, + { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" }, + { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" }, + { url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" }, + { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" }, + { url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" }, + { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "moto" +version = "5.1.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "boto3" }, + { name = "botocore" }, + { name = "cryptography" }, + { name = "jinja2" }, + { name = "python-dateutil" }, + { name = "requests" }, + { name = "responses" }, + { name = "werkzeug" }, + { name = "xmltodict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/3d/1765accbf753dc1ae52f26a2e2ed2881d78c2eb9322c178e45312472e4a0/moto-5.1.22.tar.gz", hash = "sha256:e5b2c378296e4da50ce5a3c355a1743c8d6d396ea41122f5bb2a40f9b9a8cc0e", size = 8547792, upload-time = "2026-03-08T21:06:43.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/4f/8812a01e3e0bd6be3e13b90432fb5c696af9a720af3f00e6eba5ad748345/moto-5.1.22-py3-none-any.whl", hash = "sha256:d9f20ae3cf29c44f93c1f8f06c8f48d5560e5dc027816ef1d0d2059741ffcfbe", size = 6617400, upload-time = "2026-03-08T21:06:41.093Z" }, +] + +[package.optional-dependencies] +server = [ + { name = "antlr4-python3-runtime" }, + { name = "aws-sam-translator" }, + { name = "aws-xray-sdk" }, + { name = "cfn-lint" }, + { name = "docker" }, + { name = "flask" }, + { name = "flask-cors" }, + { name = "graphql-core" }, + { name = "joserfc" }, + { name = "jsonpath-ng" }, + { name = "openapi-spec-validator" }, + { name = "py-partiql-parser" }, + { name = "pydantic" }, + { name = "pyparsing" }, + { name = "pyyaml" }, + { name = "setuptools" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-boto3-s3" +version = "1.42.67" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/b3/d2cdd49f272add9a178a1a238f6bdd80f0e6b503506b002e727ba699f23a/mypy_boto3_s3-1.42.67.tar.gz", hash = "sha256:3a3a918a9949f2d6f8071d490b8968ddce634aa19590697537e5189cbdca403e", size = 76415, upload-time = "2026-03-12T20:02:08.476Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/a5/7d4a7bb51c7bb9b188f306555bfcf5537c7f0524964350ff9d04d86e0786/mypy_boto3_s3-1.42.67-py3-none-any.whl", hash = "sha256:93208799734611da4caa5fa8f5ce677b62758ddcd34b737b9f7ae471d179b95e", size = 83570, upload-time = "2026-03-12T20:02:04.391Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "openapi-schema-validator" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "jsonschema-specifications" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "referencing" }, + { name = "rfc3339-validator" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/4b/67b24b2b23d96ea862be2cca3632a546f67a22461200831213e80c3c6011/openapi_schema_validator-0.8.1.tar.gz", hash = "sha256:4c57266ce8cbfa37bb4eb4d62cdb7d19356c3a468e3535743c4562863e1790da", size = 23134, upload-time = "2026-03-02T08:46:29.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/87/e9f29f463b230d4b47d65e17858c595153a8ca8c1775f16e406aa82d455d/openapi_schema_validator-0.8.1-py3-none-any.whl", hash = "sha256:0f5859794c5bfa433d478dc5ac5e5768d50adc56b14380c8a6fd3a8113e89c9b", size = 19211, upload-time = "2026-03-02T08:46:28.154Z" }, +] + +[[package]] +name = "openapi-spec-validator" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "jsonschema-path" }, + { name = "lazy-object-proxy" }, + { name = "openapi-schema-validator" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/de/0199b15f5dde3ca61df6e6b3987420bfd424db077998f0162e8ffe12e4f5/openapi_spec_validator-0.8.4.tar.gz", hash = "sha256:8bb324b9b08b9b368b1359dec14610c60a8f3a3dd63237184eb04456d4546f49", size = 1756847, upload-time = "2026-03-01T15:48:19.499Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/70/52310f9ece5f4eb02e0b31d538b51f729169517767a8d0100a25db31d67f/openapi_spec_validator-0.8.4-py3-none-any.whl", hash = "sha256:cf905117063d7c4d495c8a5a167a1f2a8006da6ffa8ba234a7ed0d0f11454d51", size = 50330, upload-time = "2026-03-01T15:48:17.668Z" }, +] + +[[package]] +name = "pathable" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/55/b748445cb4ea6b125626f15379be7c96d1035d4fa3e8fee362fa92298abf/pathable-0.5.0.tar.gz", hash = "sha256:d81938348a1cacb525e7c75166270644782c0fb9c8cecc16be033e71427e0ef1", size = 16655, upload-time = "2026-02-20T08:47:00.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/96/5a770e5c461462575474468e5af931cff9de036e7c2b4fea23c1c58d2cbe/pathable-0.5.0-py3-none-any.whl", hash = "sha256:646e3d09491a6351a0c82632a09c02cdf70a252e73196b36d8a15ba0a114f0a6", size = 16867, upload-time = "2026-02-20T08:46:59.536Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "py-partiql-parser" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/7a/a0f6bda783eb4df8e3dfd55973a1ac6d368a89178c300e1b5b91cd181e5e/py_partiql_parser-0.6.3.tar.gz", hash = "sha256:09cecf916ce6e3da2c050f0cb6106166de42c33d34a078ec2eb19377ea70389a", size = 17456, upload-time = "2025-10-18T13:56:13.441Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/33/a7cbfccc39056a5cf8126b7aab4c8bafbedd4f0ca68ae40ecb627a2d2cd3/py_partiql_parser-0.6.3-py2.py3-none-any.whl", hash = "sha256:deb0769c3346179d2f590dcbde556f708cdb929059fb654bad75f4cf6e07f582", size = 23752, upload-time = "2025-10-18T13:56:12.256Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "python-build-standalone" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "boto3" }, + { name = "boto3-stubs", extra = ["s3"] }, + { name = "docker" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "pyyaml" }, + { name = "six" }, + { name = "tomli" }, + { name = "typing-extensions" }, + { name = "zstandard" }, +] + +[package.dev-dependencies] +check = [ + { name = "mypy" }, + { name = "ruff" }, + { name = "types-jinja2" }, + { name = "types-jsonschema" }, + { name = "types-pyyaml" }, +] +dev = [ + { name = "moto", extra = ["server"] }, +] + +[package.metadata] +requires-dist = [ + { name = "boto3", specifier = ">=1.37" }, + { name = "boto3-stubs", extras = ["s3"], specifier = ">=1.42" }, + { name = "docker", specifier = ">=7.1.0" }, + { name = "jinja2", specifier = ">=3.1.5" }, + { name = "jsonschema", specifier = ">=4.23.0" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "six", specifier = ">=1.17.0" }, + { name = "tomli", specifier = ">=2.2.1" }, + { name = "typing-extensions", specifier = ">=4.14.1" }, + { name = "zstandard", specifier = ">=0.23.0" }, +] + +[package.metadata.requires-dev] +check = [ + { name = "mypy", specifier = ">=1.19.1" }, + { name = "ruff", specifier = ">=0.15.7" }, + { name = "types-jinja2", specifier = ">=2.11.9" }, + { name = "types-jsonschema", specifier = ">=4.26.0.20260324" }, + { name = "types-pyyaml", specifier = ">=6.0.12.20250915" }, +] +dev = [{ name = "moto", extras = ["server"], specifier = ">=5.1.22" }] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "regex" +version = "2026.2.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/b8/845a927e078f5e5cc55d29f57becbfde0003d52806544531ab3f2da4503c/regex-2026.2.28-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fc48c500838be6882b32748f60a15229d2dea96e59ef341eaa96ec83538f498d", size = 488461, upload-time = "2026-02-28T02:15:48.405Z" }, + { url = "https://files.pythonhosted.org/packages/32/f9/8a0034716684e38a729210ded6222249f29978b24b684f448162ef21f204/regex-2026.2.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2afa673660928d0b63d84353c6c08a8a476ddfc4a47e11742949d182e6863ce8", size = 290774, upload-time = "2026-02-28T02:15:51.738Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ba/b27feefffbb199528dd32667cd172ed484d9c197618c575f01217fbe6103/regex-2026.2.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7ab218076eb0944549e7fe74cf0e2b83a82edb27e81cc87411f76240865e04d5", size = 288737, upload-time = "2026-02-28T02:15:53.534Z" }, + { url = "https://files.pythonhosted.org/packages/18/c5/65379448ca3cbfe774fcc33774dc8295b1ee97dc3237ae3d3c7b27423c9d/regex-2026.2.28-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d63db12e45a9b9f064bfe4800cefefc7e5f182052e4c1b774d46a40ab1d9bb", size = 782675, upload-time = "2026-02-28T02:15:55.488Z" }, + { url = "https://files.pythonhosted.org/packages/aa/30/6fa55bef48090f900fbd4649333791fc3e6467380b9e775e741beeb3231f/regex-2026.2.28-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:195237dc327858a7721bf8b0bbbef797554bc13563c3591e91cd0767bacbe359", size = 850514, upload-time = "2026-02-28T02:15:57.509Z" }, + { url = "https://files.pythonhosted.org/packages/a9/28/9ca180fb3787a54150209754ac06a42409913571fa94994f340b3bba4e1e/regex-2026.2.28-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b387a0d092dac157fb026d737dde35ff3e49ef27f285343e7c6401851239df27", size = 896612, upload-time = "2026-02-28T02:15:59.682Z" }, + { url = "https://files.pythonhosted.org/packages/46/b5/f30d7d3936d6deecc3ea7bea4f7d3c5ee5124e7c8de372226e436b330a55/regex-2026.2.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3935174fa4d9f70525a4367aaff3cb8bc0548129d114260c29d9dfa4a5b41692", size = 791691, upload-time = "2026-02-28T02:16:01.752Z" }, + { url = "https://files.pythonhosted.org/packages/f5/34/96631bcf446a56ba0b2a7f684358a76855dfe315b7c2f89b35388494ede0/regex-2026.2.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b2b23587b26496ff5fd40df4278becdf386813ec00dc3533fa43a4cf0e2ad3c", size = 783111, upload-time = "2026-02-28T02:16:03.651Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/f95cb7a85fe284d41cd2f3625e0f2ae30172b55dfd2af1d9b4eaef6259d7/regex-2026.2.28-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3b24bd7e9d85dc7c6a8bd2aa14ecd234274a0248335a02adeb25448aecdd420d", size = 767512, upload-time = "2026-02-28T02:16:05.616Z" }, + { url = "https://files.pythonhosted.org/packages/3d/af/a650f64a79c02a97f73f64d4e7fc4cc1984e64affab14075e7c1f9a2db34/regex-2026.2.28-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd477d5f79920338107f04aa645f094032d9e3030cc55be581df3d1ef61aa318", size = 773920, upload-time = "2026-02-28T02:16:08.325Z" }, + { url = "https://files.pythonhosted.org/packages/72/f8/3f9c2c2af37aedb3f5a1e7227f81bea065028785260d9cacc488e43e6997/regex-2026.2.28-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b49eb78048c6354f49e91e4b77da21257fecb92256b6d599ae44403cab30b05b", size = 846681, upload-time = "2026-02-28T02:16:10.381Z" }, + { url = "https://files.pythonhosted.org/packages/54/12/8db04a334571359f4d127d8f89550917ec6561a2fddfd69cd91402b47482/regex-2026.2.28-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a25c7701e4f7a70021db9aaf4a4a0a67033c6318752146e03d1b94d32006217e", size = 755565, upload-time = "2026-02-28T02:16:11.972Z" }, + { url = "https://files.pythonhosted.org/packages/da/bc/91c22f384d79324121b134c267a86ca90d11f8016aafb1dc5bee05890ee3/regex-2026.2.28-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9dd450db6458387167e033cfa80887a34c99c81d26da1bf8b0b41bf8c9cac88e", size = 835789, upload-time = "2026-02-28T02:16:14.036Z" }, + { url = "https://files.pythonhosted.org/packages/46/a7/4cc94fd3af01dcfdf5a9ed75c8e15fd80fcd62cc46da7592b1749e9c35db/regex-2026.2.28-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2954379dd20752e82d22accf3ff465311cbb2bac6c1f92c4afd400e1757f7451", size = 780094, upload-time = "2026-02-28T02:16:15.468Z" }, + { url = "https://files.pythonhosted.org/packages/3c/21/e5a38f420af3c77cab4a65f0c3a55ec02ac9babf04479cfd282d356988a6/regex-2026.2.28-cp310-cp310-win32.whl", hash = "sha256:1f8b17be5c27a684ea6759983c13506bd77bfc7c0347dff41b18ce5ddd2ee09a", size = 266025, upload-time = "2026-02-28T02:16:16.828Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0a/205c4c1466a36e04d90afcd01d8908bac327673050c7fe316b2416d99d3d/regex-2026.2.28-cp310-cp310-win_amd64.whl", hash = "sha256:dd8847c4978bc3c7e6c826fb745f5570e518b8459ac2892151ce6627c7bc00d5", size = 277965, upload-time = "2026-02-28T02:16:18.752Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4d/29b58172f954b6ec2c5ed28529a65e9026ab96b4b7016bcd3858f1c31d3c/regex-2026.2.28-cp310-cp310-win_arm64.whl", hash = "sha256:73cdcdbba8028167ea81490c7f45280113e41db2c7afb65a276f4711fa3bcbff", size = 270336, upload-time = "2026-02-28T02:16:20.735Z" }, + { url = "https://files.pythonhosted.org/packages/04/db/8cbfd0ba3f302f2d09dd0019a9fcab74b63fee77a76c937d0e33161fb8c1/regex-2026.2.28-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e621fb7c8dc147419b28e1702f58a0177ff8308a76fa295c71f3e7827849f5d9", size = 488462, upload-time = "2026-02-28T02:16:22.616Z" }, + { url = "https://files.pythonhosted.org/packages/5d/10/ccc22c52802223f2368731964ddd117799e1390ffc39dbb31634a83022ee/regex-2026.2.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d5bef2031cbf38757a0b0bc4298bb4824b6332d28edc16b39247228fbdbad97", size = 290774, upload-time = "2026-02-28T02:16:23.993Z" }, + { url = "https://files.pythonhosted.org/packages/62/b9/6796b3bf3101e64117201aaa3a5a030ec677ecf34b3cd6141b5d5c6c67d5/regex-2026.2.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bcb399ed84eabf4282587ba151f2732ad8168e66f1d3f85b1d038868fe547703", size = 288724, upload-time = "2026-02-28T02:16:25.403Z" }, + { url = "https://files.pythonhosted.org/packages/9c/02/291c0ae3f3a10cea941d0f5366da1843d8d1fa8a25b0671e20a0e454bb38/regex-2026.2.28-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c1b34dfa72f826f535b20712afa9bb3ba580020e834f3c69866c5bddbf10098", size = 791924, upload-time = "2026-02-28T02:16:26.863Z" }, + { url = "https://files.pythonhosted.org/packages/0f/57/f0235cc520d9672742196c5c15098f8f703f2758d48d5a7465a56333e496/regex-2026.2.28-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:851fa70df44325e1e4cdb79c5e676e91a78147b1b543db2aec8734d2add30ec2", size = 860095, upload-time = "2026-02-28T02:16:28.772Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7c/393c94cbedda79a0f5f2435ebd01644aba0b338d327eb24b4aa5b8d6c07f/regex-2026.2.28-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:516604edd17b1c2c3e579cf4e9b25a53bf8fa6e7cedddf1127804d3e0140ca64", size = 906583, upload-time = "2026-02-28T02:16:30.977Z" }, + { url = "https://files.pythonhosted.org/packages/2c/73/a72820f47ca5abf2b5d911d0407ba5178fc52cf9780191ed3a54f5f419a2/regex-2026.2.28-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7ce83654d1ab701cb619285a18a8e5a889c1216d746ddc710c914ca5fd71022", size = 800234, upload-time = "2026-02-28T02:16:32.55Z" }, + { url = "https://files.pythonhosted.org/packages/34/b3/6e6a4b7b31fa998c4cf159a12cbeaf356386fbd1a8be743b1e80a3da51e4/regex-2026.2.28-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2791948f7c70bb9335a9102df45e93d428f4b8128020d85920223925d73b9e1", size = 772803, upload-time = "2026-02-28T02:16:34.029Z" }, + { url = "https://files.pythonhosted.org/packages/10/e7/5da0280c765d5a92af5e1cd324b3fe8464303189cbaa449de9a71910e273/regex-2026.2.28-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03a83cc26aa2acda6b8b9dfe748cf9e84cbd390c424a1de34fdcef58961a297a", size = 781117, upload-time = "2026-02-28T02:16:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/76/39/0b8d7efb256ae34e1b8157acc1afd8758048a1cf0196e1aec2e71fd99f4b/regex-2026.2.28-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ec6f5674c5dc836994f50f1186dd1fafde4be0666aae201ae2fcc3d29d8adf27", size = 854224, upload-time = "2026-02-28T02:16:38.119Z" }, + { url = "https://files.pythonhosted.org/packages/21/ff/a96d483ebe8fe6d1c67907729202313895d8de8495569ec319c6f29d0438/regex-2026.2.28-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:50c2fc924749543e0eacc93ada6aeeb3ea5f6715825624baa0dccaec771668ae", size = 761898, upload-time = "2026-02-28T02:16:40.333Z" }, + { url = "https://files.pythonhosted.org/packages/89/bd/d4f2e75cb4a54b484e796017e37c0d09d8a0a837de43d17e238adf163f4e/regex-2026.2.28-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ba55c50f408fb5c346a3a02d2ce0ebc839784e24f7c9684fde328ff063c3cdea", size = 844832, upload-time = "2026-02-28T02:16:41.875Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a7/428a135cf5e15e4e11d1e696eb2bf968362f8ea8a5f237122e96bc2ae950/regex-2026.2.28-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:edb1b1b3a5576c56f08ac46f108c40333f222ebfd5cf63afdfa3aab0791ebe5b", size = 788347, upload-time = "2026-02-28T02:16:43.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/59/68691428851cf9c9c3707217ab1d9b47cfeec9d153a49919e6c368b9e926/regex-2026.2.28-cp311-cp311-win32.whl", hash = "sha256:948c12ef30ecedb128903c2c2678b339746eb7c689c5c21957c4a23950c96d15", size = 266033, upload-time = "2026-02-28T02:16:45.094Z" }, + { url = "https://files.pythonhosted.org/packages/42/8b/1483de1c57024e89296cbcceb9cccb3f625d416ddb46e570be185c9b05a9/regex-2026.2.28-cp311-cp311-win_amd64.whl", hash = "sha256:fd63453f10d29097cc3dc62d070746523973fb5aa1c66d25f8558bebd47fed61", size = 277978, upload-time = "2026-02-28T02:16:46.75Z" }, + { url = "https://files.pythonhosted.org/packages/a4/36/abec45dc6e7252e3dbc797120496e43bb5730a7abf0d9cb69340696a2f2d/regex-2026.2.28-cp311-cp311-win_arm64.whl", hash = "sha256:00f2b8d9615aa165fdff0a13f1a92049bfad555ee91e20d246a51aa0b556c60a", size = 270340, upload-time = "2026-02-28T02:16:48.626Z" }, + { url = "https://files.pythonhosted.org/packages/07/42/9061b03cf0fc4b5fa2c3984cbbaed54324377e440a5c5a29d29a72518d62/regex-2026.2.28-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fcf26c3c6d0da98fada8ae4ef0aa1c3405a431c0a77eb17306d38a89b02adcd7", size = 489574, upload-time = "2026-02-28T02:16:50.455Z" }, + { url = "https://files.pythonhosted.org/packages/77/83/0c8a5623a233015595e3da499c5a1c13720ac63c107897a6037bb97af248/regex-2026.2.28-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02473c954af35dd2defeb07e44182f5705b30ea3f351a7cbffa9177beb14da5d", size = 291426, upload-time = "2026-02-28T02:16:52.52Z" }, + { url = "https://files.pythonhosted.org/packages/9e/06/3ef1ac6910dc3295ebd71b1f9bfa737e82cfead211a18b319d45f85ddd09/regex-2026.2.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b65d33a17101569f86d9c5966a8b1d7fbf8afdda5a8aa219301b0a80f58cf7d", size = 289200, upload-time = "2026-02-28T02:16:54.08Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c9/8cc8d850b35ab5650ff6756a1cb85286e2000b66c97520b29c1587455344/regex-2026.2.28-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71dcecaa113eebcc96622c17692672c2d104b1d71ddf7adeda90da7ddeb26fc", size = 796765, upload-time = "2026-02-28T02:16:55.905Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5d/57702597627fc23278ebf36fbb497ac91c0ce7fec89ac6c81e420ca3e38c/regex-2026.2.28-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:481df4623fa4969c8b11f3433ed7d5e3dc9cec0f008356c3212b3933fb77e3d8", size = 863093, upload-time = "2026-02-28T02:16:58.094Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/f3ecad537ca2811b4d26b54ca848cf70e04fcfc138667c146a9f3157779c/regex-2026.2.28-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64e7c6ad614573e0640f271e811a408d79a9e1fe62a46adb602f598df42a818d", size = 909455, upload-time = "2026-02-28T02:17:00.918Z" }, + { url = "https://files.pythonhosted.org/packages/9e/40/bb226f203caa22c1043c1ca79b36340156eca0f6a6742b46c3bb222a3a57/regex-2026.2.28-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b08a06976ff4fb0d83077022fde3eca06c55432bb997d8c0495b9a4e9872f4", size = 802037, upload-time = "2026-02-28T02:17:02.842Z" }, + { url = "https://files.pythonhosted.org/packages/44/7c/c6d91d8911ac6803b45ca968e8e500c46934e58c0903cbc6d760ee817a0a/regex-2026.2.28-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:864cdd1a2ef5716b0ab468af40139e62ede1b3a53386b375ec0786bb6783fc05", size = 775113, upload-time = "2026-02-28T02:17:04.506Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8d/4a9368d168d47abd4158580b8c848709667b1cd293ff0c0c277279543bd0/regex-2026.2.28-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:511f7419f7afab475fd4d639d4aedfc54205bcb0800066753ef68a59f0f330b5", size = 784194, upload-time = "2026-02-28T02:17:06.888Z" }, + { url = "https://files.pythonhosted.org/packages/cc/bf/2c72ab5d8b7be462cb1651b5cc333da1d0068740342f350fcca3bca31947/regex-2026.2.28-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b42f7466e32bf15a961cf09f35fa6323cc72e64d3d2c990b10de1274a5da0a59", size = 856846, upload-time = "2026-02-28T02:17:09.11Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f4/6b65c979bb6d09f51bb2d2a7bc85de73c01ec73335d7ddd202dcb8cd1c8f/regex-2026.2.28-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8710d61737b0c0ce6836b1da7109f20d495e49b3809f30e27e9560be67a257bf", size = 763516, upload-time = "2026-02-28T02:17:11.004Z" }, + { url = "https://files.pythonhosted.org/packages/8e/32/29ea5e27400ee86d2cc2b4e80aa059df04eaf78b4f0c18576ae077aeff68/regex-2026.2.28-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4390c365fd2d45278f45afd4673cb90f7285f5701607e3ad4274df08e36140ae", size = 849278, upload-time = "2026-02-28T02:17:12.693Z" }, + { url = "https://files.pythonhosted.org/packages/1d/91/3233d03b5f865111cd517e1c95ee8b43e8b428d61fa73764a80c9bb6f537/regex-2026.2.28-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cb3b1db8ff6c7b8bf838ab05583ea15230cb2f678e569ab0e3a24d1e8320940b", size = 790068, upload-time = "2026-02-28T02:17:14.9Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/abc706c1fb03b4580a09645b206a3fc032f5a9f457bc1a8038ac555658ab/regex-2026.2.28-cp312-cp312-win32.whl", hash = "sha256:f8ed9a5d4612df9d4de15878f0bc6aa7a268afbe5af21a3fdd97fa19516e978c", size = 266416, upload-time = "2026-02-28T02:17:17.15Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/2a6f7dff190e5fa9df9fb4acf2fdf17a1aa0f7f54596cba8de608db56b3a/regex-2026.2.28-cp312-cp312-win_amd64.whl", hash = "sha256:01d65fd24206c8e1e97e2e31b286c59009636c022eb5d003f52760b0f42155d4", size = 277297, upload-time = "2026-02-28T02:17:18.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/f0/58a2484851fadf284458fdbd728f580d55c1abac059ae9f048c63b92f427/regex-2026.2.28-cp312-cp312-win_arm64.whl", hash = "sha256:c0b5ccbb8ffb433939d248707d4a8b31993cb76ab1a0187ca886bf50e96df952", size = 270408, upload-time = "2026-02-28T02:17:20.328Z" }, + { url = "https://files.pythonhosted.org/packages/87/f6/dc9ef48c61b79c8201585bf37fa70cd781977da86e466cd94e8e95d2443b/regex-2026.2.28-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6d63a07e5ec8ce7184452cb00c41c37b49e67dc4f73b2955b5b8e782ea970784", size = 489311, upload-time = "2026-02-28T02:17:22.591Z" }, + { url = "https://files.pythonhosted.org/packages/95/c8/c20390f2232d3f7956f420f4ef1852608ad57aa26c3dd78516cb9f3dc913/regex-2026.2.28-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e59bc8f30414d283ae8ee1617b13d8112e7135cb92830f0ec3688cb29152585a", size = 291285, upload-time = "2026-02-28T02:17:24.355Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a6/ba1068a631ebd71a230e7d8013fcd284b7c89c35f46f34a7da02082141b1/regex-2026.2.28-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:de0cf053139f96219ccfabb4a8dd2d217c8c82cb206c91d9f109f3f552d6b43d", size = 289051, upload-time = "2026-02-28T02:17:26.722Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1b/7cc3b7af4c244c204b7a80924bd3d85aecd9ba5bc82b485c5806ee8cda9e/regex-2026.2.28-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb4db2f17e6484904f986c5a657cec85574c76b5c5e61c7aae9ffa1bc6224f95", size = 796842, upload-time = "2026-02-28T02:17:29.064Z" }, + { url = "https://files.pythonhosted.org/packages/24/87/26bd03efc60e0d772ac1e7b60a2e6325af98d974e2358f659c507d3c76db/regex-2026.2.28-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52b017b35ac2214d0db5f4f90e303634dc44e4aba4bd6235a27f97ecbe5b0472", size = 863083, upload-time = "2026-02-28T02:17:31.363Z" }, + { url = "https://files.pythonhosted.org/packages/ae/54/aeaf4afb1aa0a65e40de52a61dc2ac5b00a83c6cb081c8a1d0dda74f3010/regex-2026.2.28-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:69fc560ccbf08a09dc9b52ab69cacfae51e0ed80dc5693078bdc97db2f91ae96", size = 909412, upload-time = "2026-02-28T02:17:33.248Z" }, + { url = "https://files.pythonhosted.org/packages/12/2f/049901def913954e640d199bbc6a7ca2902b6aeda0e5da9d17f114100ec2/regex-2026.2.28-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e61eea47230eba62a31f3e8a0e3164d0f37ef9f40529fb2c79361bc6b53d2a92", size = 802101, upload-time = "2026-02-28T02:17:35.053Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/512fb9ff7f5b15ea204bb1967ebb649059446decacccb201381f9fa6aad4/regex-2026.2.28-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4f5c0b182ad4269e7381b7c27fdb0408399881f7a92a4624fd5487f2971dfc11", size = 775260, upload-time = "2026-02-28T02:17:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/9a92935878aba19bd72706b9db5646a6f993d99b3f6ed42c02ec8beb1d61/regex-2026.2.28-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:96f6269a2882fbb0ee76967116b83679dc628e68eaea44e90884b8d53d833881", size = 784311, upload-time = "2026-02-28T02:17:39.855Z" }, + { url = "https://files.pythonhosted.org/packages/09/d3/fc51a8a738a49a6b6499626580554c9466d3ea561f2b72cfdc72e4149773/regex-2026.2.28-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b5acd4b6a95f37c3c3828e5d053a7d4edaedb85de551db0153754924cb7c83e3", size = 856876, upload-time = "2026-02-28T02:17:42.317Z" }, + { url = "https://files.pythonhosted.org/packages/08/b7/2e641f3d084b120ca4c52e8c762a78da0b32bf03ef546330db3e2635dc5f/regex-2026.2.28-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2234059cfe33d9813a3677ef7667999caea9eeaa83fef98eb6ce15c6cf9e0215", size = 763632, upload-time = "2026-02-28T02:17:45.073Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6d/0009021d97e79ee99f3d8641f0a8d001eed23479ade4c3125a5480bf3e2d/regex-2026.2.28-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c15af43c72a7fb0c97cbc66fa36a43546eddc5c06a662b64a0cbf30d6ac40944", size = 849320, upload-time = "2026-02-28T02:17:47.192Z" }, + { url = "https://files.pythonhosted.org/packages/05/7a/51cfbad5758f8edae430cb21961a9c8d04bce1dae4d2d18d4186eec7cfa1/regex-2026.2.28-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9185cc63359862a6e80fe97f696e04b0ad9a11c4ac0a4a927f979f611bfe3768", size = 790152, upload-time = "2026-02-28T02:17:49.067Z" }, + { url = "https://files.pythonhosted.org/packages/90/3d/a83e2b6b3daa142acb8c41d51de3876186307d5cb7490087031747662500/regex-2026.2.28-cp313-cp313-win32.whl", hash = "sha256:fb66e5245db9652abd7196ace599b04d9c0e4aa7c8f0e2803938377835780081", size = 266398, upload-time = "2026-02-28T02:17:50.744Z" }, + { url = "https://files.pythonhosted.org/packages/85/4f/16e9ebb1fe5425e11b9596c8d57bf8877dcb32391da0bfd33742e3290637/regex-2026.2.28-cp313-cp313-win_amd64.whl", hash = "sha256:71a911098be38c859ceb3f9a9ce43f4ed9f4c6720ad8684a066ea246b76ad9ff", size = 277282, upload-time = "2026-02-28T02:17:53.074Z" }, + { url = "https://files.pythonhosted.org/packages/07/b4/92851335332810c5a89723bf7a7e35c7209f90b7d4160024501717b28cc9/regex-2026.2.28-cp313-cp313-win_arm64.whl", hash = "sha256:39bb5727650b9a0275c6a6690f9bb3fe693a7e6cc5c3155b1240aedf8926423e", size = 270382, upload-time = "2026-02-28T02:17:54.888Z" }, + { url = "https://files.pythonhosted.org/packages/24/07/6c7e4cec1e585959e96cbc24299d97e4437a81173217af54f1804994e911/regex-2026.2.28-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:97054c55db06ab020342cc0d35d6f62a465fa7662871190175f1ad6c655c028f", size = 492541, upload-time = "2026-02-28T02:17:56.813Z" }, + { url = "https://files.pythonhosted.org/packages/7c/13/55eb22ada7f43d4f4bb3815b6132183ebc331c81bd496e2d1f3b8d862e0d/regex-2026.2.28-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d25a10811de831c2baa6aef3c0be91622f44dd8d31dd12e69f6398efb15e48b", size = 292984, upload-time = "2026-02-28T02:17:58.538Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/c301f8cb29ce9644a5ef85104c59244e6e7e90994a0f458da4d39baa8e17/regex-2026.2.28-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d6cfe798d8da41bb1862ed6e0cba14003d387c3c0c4a5d45591076ae9f0ce2f8", size = 291509, upload-time = "2026-02-28T02:18:00.208Z" }, + { url = "https://files.pythonhosted.org/packages/b5/43/aabe384ec1994b91796e903582427bc2ffaed9c4103819ed3c16d8e749f3/regex-2026.2.28-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd0ce43e71d825b7c0661f9c54d4d74bd97c56c3fd102a8985bcfea48236bacb", size = 809429, upload-time = "2026-02-28T02:18:02.328Z" }, + { url = "https://files.pythonhosted.org/packages/04/b8/8d2d987a816720c4f3109cee7c06a4b24ad0e02d4fc74919ab619e543737/regex-2026.2.28-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00945d007fd74a9084d2ab79b695b595c6b7ba3698972fadd43e23230c6979c1", size = 869422, upload-time = "2026-02-28T02:18:04.23Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ad/2c004509e763c0c3719f97c03eca26473bffb3868d54c5f280b8cd4f9e3d/regex-2026.2.28-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bec23c11cbbf09a4df32fe50d57cbdd777bc442269b6e39a1775654f1c95dee2", size = 915175, upload-time = "2026-02-28T02:18:06.791Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/fd429066da487ef555a9da73bf214894aec77fc8c66a261ee355a69871a8/regex-2026.2.28-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5cdcc17d935c8f9d3f4db5c2ebe2640c332e3822ad5d23c2f8e0228e6947943a", size = 812044, upload-time = "2026-02-28T02:18:08.736Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ca/feedb7055c62a3f7f659971bf45f0e0a87544b6b0cf462884761453f97c5/regex-2026.2.28-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a448af01e3d8031c89c5d902040b124a5e921a25c4e5e07a861ca591ce429341", size = 782056, upload-time = "2026-02-28T02:18:10.777Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/1aa959ed0d25c1dd7dd5047ea8ba482ceaef38ce363c401fd32a6b923e60/regex-2026.2.28-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:10d28e19bd4888e4abf43bd3925f3c134c52fdf7259219003588a42e24c2aa25", size = 798743, upload-time = "2026-02-28T02:18:13.025Z" }, + { url = "https://files.pythonhosted.org/packages/3b/1f/dadb9cf359004784051c897dcf4d5d79895f73a1bbb7b827abaa4814ae80/regex-2026.2.28-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:99985a2c277dcb9ccb63f937451af5d65177af1efdeb8173ac55b61095a0a05c", size = 864633, upload-time = "2026-02-28T02:18:16.84Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f1/b9a25eb24e1cf79890f09e6ec971ee5b511519f1851de3453bc04f6c902b/regex-2026.2.28-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e1e7b24cb3ae9953a560c563045d1ba56ee4749fbd05cf21ba571069bd7be81b", size = 770862, upload-time = "2026-02-28T02:18:18.892Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/c5cb10b7aa6f182f9247a30cc9527e326601f46f4df864ac6db588d11fcd/regex-2026.2.28-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d8511a01d0e4ee1992eb3ba19e09bc1866fe03f05129c3aec3fdc4cbc77aad3f", size = 854788, upload-time = "2026-02-28T02:18:21.475Z" }, + { url = "https://files.pythonhosted.org/packages/0a/50/414ba0731c4bd40b011fa4703b2cc86879ec060c64f2a906e65a56452589/regex-2026.2.28-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aaffaecffcd2479ce87aa1e74076c221700b7c804e48e98e62500ee748f0f550", size = 800184, upload-time = "2026-02-28T02:18:23.492Z" }, + { url = "https://files.pythonhosted.org/packages/69/50/0c7290987f97e7e6830b0d853f69dc4dc5852c934aae63e7fdcd76b4c383/regex-2026.2.28-cp313-cp313t-win32.whl", hash = "sha256:ef77bdde9c9eba3f7fa5b58084b29bbcc74bcf55fdbeaa67c102a35b5bd7e7cc", size = 269137, upload-time = "2026-02-28T02:18:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/68/80/ef26ff90e74ceb4051ad6efcbbb8a4be965184a57e879ebcbdef327d18fa/regex-2026.2.28-cp313-cp313t-win_amd64.whl", hash = "sha256:98adf340100cbe6fbaf8e6dc75e28f2c191b1be50ffefe292fb0e6f6eefdb0d8", size = 280682, upload-time = "2026-02-28T02:18:27.205Z" }, + { url = "https://files.pythonhosted.org/packages/69/8b/fbad9c52e83ffe8f97e3ed1aa0516e6dff6bb633a41da9e64645bc7efdc5/regex-2026.2.28-cp313-cp313t-win_arm64.whl", hash = "sha256:2fb950ac1d88e6b6a9414381f403797b236f9fa17e1eee07683af72b1634207b", size = 271735, upload-time = "2026-02-28T02:18:29.015Z" }, + { url = "https://files.pythonhosted.org/packages/cf/03/691015f7a7cb1ed6dacb2ea5de5682e4858e05a4c5506b2839cd533bbcd6/regex-2026.2.28-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:78454178c7df31372ea737996fb7f36b3c2c92cccc641d251e072478afb4babc", size = 489497, upload-time = "2026-02-28T02:18:30.889Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ba/8db8fd19afcbfa0e1036eaa70c05f20ca8405817d4ad7a38a6b4c2f031ac/regex-2026.2.28-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:5d10303dd18cedfd4d095543998404df656088240bcfd3cd20a8f95b861f74bd", size = 291295, upload-time = "2026-02-28T02:18:33.426Z" }, + { url = "https://files.pythonhosted.org/packages/5a/79/9aa0caf089e8defef9b857b52fc53801f62ff868e19e5c83d4a96612eba1/regex-2026.2.28-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:19a9c9e0a8f24f39d575a6a854d516b48ffe4cbdcb9de55cb0570a032556ecff", size = 289275, upload-time = "2026-02-28T02:18:35.247Z" }, + { url = "https://files.pythonhosted.org/packages/eb/26/ee53117066a30ef9c883bf1127eece08308ccf8ccd45c45a966e7a665385/regex-2026.2.28-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09500be324f49b470d907b3ef8af9afe857f5cca486f853853f7945ddbf75911", size = 797176, upload-time = "2026-02-28T02:18:37.15Z" }, + { url = "https://files.pythonhosted.org/packages/05/1b/67fb0495a97259925f343ae78b5d24d4a6624356ae138b57f18bd43006e4/regex-2026.2.28-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fb1c4ff62277d87a7335f2c1ea4e0387b8f2b3ad88a64efd9943906aafad4f33", size = 863813, upload-time = "2026-02-28T02:18:39.478Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/93ac9bbafc53618091c685c7ed40239a90bf9f2a82c983f0baa97cb7ae07/regex-2026.2.28-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b8b3f1be1738feadc69f62daa250c933e85c6f34fa378f54a7ff43807c1b9117", size = 908678, upload-time = "2026-02-28T02:18:41.619Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/a8f5e0561702b25239846a16349feece59712ae20598ebb205580332a471/regex-2026.2.28-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc8ed8c3f41c27acb83f7b6a9eb727a73fc6663441890c5cb3426a5f6a91ce7d", size = 801528, upload-time = "2026-02-28T02:18:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/96/5d/ed6d4cbde80309854b1b9f42d9062fee38ade15f7eb4909f6ef2440403b5/regex-2026.2.28-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa539be029844c0ce1114762d2952ab6cfdd7c7c9bd72e0db26b94c3c36dcc5a", size = 775373, upload-time = "2026-02-28T02:18:46.102Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e9/6e53c34e8068b9deec3e87210086ecb5b9efebdefca6b0d3fa43d66dcecb/regex-2026.2.28-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7900157786428a79615a8264dac1f12c9b02957c473c8110c6b1f972dcecaddf", size = 784859, upload-time = "2026-02-28T02:18:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/48/3c/736e1c7ca7f0dcd2ae33819888fdc69058a349b7e5e84bc3e2f296bbf794/regex-2026.2.28-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0b1d2b07614d95fa2bf8a63fd1e98bd8fa2b4848dc91b1efbc8ba219fdd73952", size = 857813, upload-time = "2026-02-28T02:18:50.576Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7c/48c4659ad9da61f58e79dbe8c05223e0006696b603c16eb6b5cbfbb52c27/regex-2026.2.28-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b389c61aa28a79c2e0527ac36da579869c2e235a5b208a12c5b5318cda2501d8", size = 763705, upload-time = "2026-02-28T02:18:52.59Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a1/bc1c261789283128165f71b71b4b221dd1b79c77023752a6074c102f18d8/regex-2026.2.28-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f467cb602f03fbd1ab1908f68b53c649ce393fde056628dc8c7e634dab6bfc07", size = 848734, upload-time = "2026-02-28T02:18:54.595Z" }, + { url = "https://files.pythonhosted.org/packages/10/d8/979407faf1397036e25a5ae778157366a911c0f382c62501009f4957cf86/regex-2026.2.28-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c8cb2deba42f5ec1ede46374e990f8adc5e6456a57ac1a261b19be6f28e4e6", size = 789871, upload-time = "2026-02-28T02:18:57.34Z" }, + { url = "https://files.pythonhosted.org/packages/03/23/da716821277115fcb1f4e3de1e5dc5023a1e6533598c486abf5448612579/regex-2026.2.28-cp314-cp314-win32.whl", hash = "sha256:9036b400b20e4858d56d117108d7813ed07bb7803e3eed766675862131135ca6", size = 271825, upload-time = "2026-02-28T02:18:59.202Z" }, + { url = "https://files.pythonhosted.org/packages/91/ff/90696f535d978d5f16a52a419be2770a8d8a0e7e0cfecdbfc31313df7fab/regex-2026.2.28-cp314-cp314-win_amd64.whl", hash = "sha256:1d367257cd86c1cbb97ea94e77b373a0bbc2224976e247f173d19e8f18b4afa7", size = 280548, upload-time = "2026-02-28T02:19:01.049Z" }, + { url = "https://files.pythonhosted.org/packages/69/f9/5e1b5652fc0af3fcdf7677e7df3ad2a0d47d669b34ac29a63bb177bb731b/regex-2026.2.28-cp314-cp314-win_arm64.whl", hash = "sha256:5e68192bb3a1d6fb2836da24aa494e413ea65853a21505e142e5b1064a595f3d", size = 273444, upload-time = "2026-02-28T02:19:03.255Z" }, + { url = "https://files.pythonhosted.org/packages/d3/eb/8389f9e940ac89bcf58d185e230a677b4fd07c5f9b917603ad5c0f8fa8fe/regex-2026.2.28-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a5dac14d0872eeb35260a8e30bac07ddf22adc1e3a0635b52b02e180d17c9c7e", size = 492546, upload-time = "2026-02-28T02:19:05.378Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c7/09441d27ce2a6fa6a61ea3150ea4639c1dcda9b31b2ea07b80d6937b24dd/regex-2026.2.28-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ec0c608b7a7465ffadb344ed7c987ff2f11ee03f6a130b569aa74d8a70e8333c", size = 292986, upload-time = "2026-02-28T02:19:07.24Z" }, + { url = "https://files.pythonhosted.org/packages/fb/69/4144b60ed7760a6bd235e4087041f487aa4aa62b45618ce018b0c14833ea/regex-2026.2.28-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7815afb0ca45456613fdaf60ea9c993715511c8d53a83bc468305cbc0ee23c7", size = 291518, upload-time = "2026-02-28T02:19:09.698Z" }, + { url = "https://files.pythonhosted.org/packages/2d/be/77e5426cf5948c82f98c53582009ca9e94938c71f73a8918474f2e2990bb/regex-2026.2.28-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b059e71ec363968671693a78c5053bd9cb2fe410f9b8e4657e88377ebd603a2e", size = 809464, upload-time = "2026-02-28T02:19:12.494Z" }, + { url = "https://files.pythonhosted.org/packages/45/99/2c8c5ac90dc7d05c6e7d8e72c6a3599dc08cd577ac476898e91ca787d7f1/regex-2026.2.28-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8cf76f1a29f0e99dcfd7aef1551a9827588aae5a737fe31442021165f1920dc", size = 869553, upload-time = "2026-02-28T02:19:15.151Z" }, + { url = "https://files.pythonhosted.org/packages/53/34/daa66a342f0271e7737003abf6c3097aa0498d58c668dbd88362ef94eb5d/regex-2026.2.28-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:180e08a435a0319e6a4821c3468da18dc7001987e1c17ae1335488dfe7518dd8", size = 915289, upload-time = "2026-02-28T02:19:17.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c7/e22c2aaf0a12e7e22ab19b004bb78d32ca1ecc7ef245949935463c5567de/regex-2026.2.28-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e496956106fd59ba6322a8ea17141a27c5040e5ee8f9433ae92d4e5204462a0", size = 812156, upload-time = "2026-02-28T02:19:20.011Z" }, + { url = "https://files.pythonhosted.org/packages/7f/bb/2dc18c1efd9051cf389cd0d7a3a4d90f6804b9fff3a51b5dc3c85b935f71/regex-2026.2.28-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bba2b18d70eeb7b79950f12f633beeecd923f7c9ad6f6bae28e59b4cb3ab046b", size = 782215, upload-time = "2026-02-28T02:19:22.047Z" }, + { url = "https://files.pythonhosted.org/packages/17/1e/9e4ec9b9013931faa32226ec4aa3c71fe664a6d8a2b91ac56442128b332f/regex-2026.2.28-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6db7bfae0f8a2793ff1f7021468ea55e2699d0790eb58ee6ab36ae43aa00bc5b", size = 798925, upload-time = "2026-02-28T02:19:24.173Z" }, + { url = "https://files.pythonhosted.org/packages/71/57/a505927e449a9ccb41e2cc8d735e2abe3444b0213d1cf9cb364a8c1f2524/regex-2026.2.28-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d0b02e8b7e5874b48ae0f077ecca61c1a6a9f9895e9c6dfb191b55b242862033", size = 864701, upload-time = "2026-02-28T02:19:26.376Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ad/c62cb60cdd93e13eac5b3d9d6bd5d284225ed0e3329426f94d2552dd7cca/regex-2026.2.28-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:25b6eb660c5cf4b8c3407a1ed462abba26a926cc9965e164268a3267bcc06a43", size = 770899, upload-time = "2026-02-28T02:19:29.38Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5a/874f861f5c3d5ab99633e8030dee1bc113db8e0be299d1f4b07f5b5ec349/regex-2026.2.28-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:5a932ea8ad5d0430351ff9c76c8db34db0d9f53c1d78f06022a21f4e290c5c18", size = 854727, upload-time = "2026-02-28T02:19:31.494Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ca/d2c03b0efde47e13db895b975b2be6a73ed90b8ba963677927283d43bf74/regex-2026.2.28-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2c95e1a2b0f89d01e821ff4de1be4b5d73d1f4b0bf679fa27c1ad8d2327f1a", size = 800366, upload-time = "2026-02-28T02:19:34.248Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/ee13b20b763b8989f7c75d592bfd5de37dc1181814a2a2747fedcf97e3ba/regex-2026.2.28-cp314-cp314t-win32.whl", hash = "sha256:bbb882061f742eb5d46f2f1bd5304055be0a66b783576de3d7eef1bed4778a6e", size = 274936, upload-time = "2026-02-28T02:19:36.313Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e7/d8020e39414c93af7f0d8688eabcecece44abfd5ce314b21dfda0eebd3d8/regex-2026.2.28-cp314-cp314t-win_amd64.whl", hash = "sha256:6591f281cb44dc13de9585b552cec6fc6cf47fb2fe7a48892295ee9bc4a612f9", size = 284779, upload-time = "2026-02-28T02:19:38.625Z" }, + { url = "https://files.pythonhosted.org/packages/13/c0/ad225f4a405827486f1955283407cf758b6d2fb966712644c5f5aef33d1b/regex-2026.2.28-cp314-cp314t-win_arm64.whl", hash = "sha256:dee50f1be42222f89767b64b283283ef963189da0dda4a515aa54a5563c62dec", size = 275010, upload-time = "2026-02-28T02:19:40.65Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "responses" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/b4/b7e040379838cc71bf5aabdb26998dfbe5ee73904c92c1c161faf5de8866/responses-0.26.0.tar.gz", hash = "sha256:c7f6923e6343ef3682816ba421c006626777893cb0d5e1434f674b649bac9eb4", size = 81303, upload-time = "2026-02-19T14:38:05.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/04/7f73d05b556da048923e31a0cc878f03be7c5425ed1f268082255c75d872/responses-0.26.0-py3-none-any.whl", hash = "sha256:03ec4409088cd5c66b71ecbbbd27fe2c58ddfad801c66203457b3e6a04868c37", size = 35099, upload-time = "2026-02-19T14:38:03.847Z" }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, + { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, + { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, + { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, + { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, + { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, +] + +[[package]] +name = "s3transfer" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "types-awscrt" +version = "0.31.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/26/0aa563e229c269c528a3b8c709fc671ac2a5c564732fab0852ac6ee006cf/types_awscrt-0.31.3.tar.gz", hash = "sha256:09d3eaf00231e0f47e101bd9867e430873bc57040050e2a3bd8305cb4fc30865", size = 18178, upload-time = "2026-03-08T02:31:14.569Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/e5/47a573bbbd0a790f8f9fe452f7188ea72b212d21c9be57d5fc0cbc442075/types_awscrt-0.31.3-py3-none-any.whl", hash = "sha256:e5ce65a00a2ab4f35eacc1e3d700d792338d56e4823ee7b4dbe017f94cfc4458", size = 43340, upload-time = "2026-03-08T02:31:13.38Z" }, +] + +[[package]] +name = "types-jinja2" +version = "2.11.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/b82309bfed8195de7997672deac301bd6f5bd5cbb6a3e392b7fe780d7852/types-Jinja2-2.11.9.tar.gz", hash = "sha256:dbdc74a40aba7aed520b7e4d89e8f0fe4286518494208b35123bcf084d4b8c81", size = 13302, upload-time = "2021-11-26T06:21:17.496Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b0/e79d84748f1d34304f13191424348a719c3febaa3493835370fe9528e1e6/types_Jinja2-2.11.9-py3-none-any.whl", hash = "sha256:60a1e21e8296979db32f9374d8a239af4cb541ff66447bb915d8ad398f9c63b2", size = 18190, upload-time = "2021-11-26T06:21:16.18Z" }, +] + +[[package]] +name = "types-jsonschema" +version = "4.26.0.20260325" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/bf/97b3438f0a3834d7d8e515fbccd4e1ca957465e094f0b260162a5cf9b951/types_jsonschema-4.26.0.20260325.tar.gz", hash = "sha256:84c319ba1af5463394d99accd96db543b7cb0eeab0938c652c18129536672002", size = 16441, upload-time = "2026-03-25T04:08:12.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/ec/65a4a55a024c9eb7fe08c207c0a94a537db0db50fea61ad565fa6b39220f/types_jsonschema-4.26.0.20260325-py3-none-any.whl", hash = "sha256:032a952fd32d9e06b71bdce5a5b4005dd58a074f6cb2899e96b633cbe1c28f40", size = 16080, upload-time = "2026-03-25T04:08:11.108Z" }, +] + +[[package]] +name = "types-markupsafe" +version = "1.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/31/b5f059142d058aec41e913d8e0eff0a967e7bc46f9a2ba2f31bc11cff059/types-MarkupSafe-1.1.10.tar.gz", hash = "sha256:85b3a872683d02aea3a5ac2a8ef590193c344092032f58457287fbf8e06711b1", size = 2986, upload-time = "2021-11-27T03:18:07.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/d6/b8effb1c48539260a5eb4196afc55efac4ea1684a4991977555eb266b2ef/types_MarkupSafe-1.1.10-py3-none-any.whl", hash = "sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5", size = 3998, upload-time = "2021-11-27T03:18:06.398Z" }, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20250915" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/69/3c51b36d04da19b92f9e815be12753125bd8bc247ba0470a982e6979e71c/types_pyyaml-6.0.12.20250915.tar.gz", hash = "sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3", size = 17522, upload-time = "2025-09-15T03:01:00.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/e0/1eed384f02555dde685fff1a1ac805c1c7dcb6dd019c916fe659b1c1f9ec/types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6", size = 20338, upload-time = "2025-09-15T03:00:59.218Z" }, +] + +[[package]] +name = "types-s3transfer" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/64/42689150509eb3e6e82b33ee3d89045de1592488842ddf23c56957786d05/types_s3transfer-0.16.0.tar.gz", hash = "sha256:b4636472024c5e2b62278c5b759661efeb52a81851cde5f092f24100b1ecb443", size = 13557, upload-time = "2025-12-08T08:13:09.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/27/e88220fe6274eccd3bdf95d9382918716d312f6f6cef6a46332d1ee2feff/types_s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:1c0cd111ecf6e21437cb410f5cddb631bfb2263b77ad973e79b9c6d0cb24e0ef", size = 19247, upload-time = "2025-12-08T08:13:08.426Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/43/76ded108b296a49f52de6bac5192ca1c4be84e886f9b5c9ba8427d9694fd/werkzeug-3.1.7.tar.gz", hash = "sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351", size = 875700, upload-time = "2026-03-24T01:08:07.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/b2/0bba9bbb4596d2d2f285a16c2ab04118f6b957d8441566e1abb892e6a6b2/werkzeug-3.1.7-py3-none-any.whl", hash = "sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f", size = 226295, upload-time = "2026-03-24T01:08:06.133Z" }, +] + +[[package]] +name = "wrapt" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/d2/387594fb592d027366645f3d7cc9b4d7ca7be93845fbaba6d835a912ef3c/wrapt-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c", size = 60669, upload-time = "2026-03-06T02:52:40.671Z" }, + { url = "https://files.pythonhosted.org/packages/c9/18/3f373935bc5509e7ac444c8026a56762e50c1183e7061797437ca96c12ce/wrapt-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f", size = 61603, upload-time = "2026-03-06T02:54:21.032Z" }, + { url = "https://files.pythonhosted.org/packages/c2/7a/32758ca2853b07a887a4574b74e28843919103194bb47001a304e24af62f/wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb", size = 113632, upload-time = "2026-03-06T02:53:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d5/eeaa38f670d462e97d978b3b0d9ce06d5b91e54bebac6fbed867809216e7/wrapt-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e", size = 115644, upload-time = "2026-03-06T02:54:53.33Z" }, + { url = "https://files.pythonhosted.org/packages/e3/09/2a41506cb17affb0bdf9d5e2129c8c19e192b388c4c01d05e1b14db23c00/wrapt-2.1.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba", size = 112016, upload-time = "2026-03-06T02:54:43.274Z" }, + { url = "https://files.pythonhosted.org/packages/64/15/0e6c3f5e87caadc43db279724ee36979246d5194fa32fed489c73643ba59/wrapt-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f", size = 114823, upload-time = "2026-03-06T02:54:29.392Z" }, + { url = "https://files.pythonhosted.org/packages/56/b2/0ad17c8248f4e57bedf44938c26ec3ee194715f812d2dbbd9d7ff4be6c06/wrapt-2.1.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394", size = 111244, upload-time = "2026-03-06T02:54:02.149Z" }, + { url = "https://files.pythonhosted.org/packages/ff/04/bcdba98c26f2c6522c7c09a726d5d9229120163493620205b2f76bd13c01/wrapt-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45", size = 113307, upload-time = "2026-03-06T02:54:12.428Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1b/5e2883c6bc14143924e465a6fc5a92d09eeabe35310842a481fb0581f832/wrapt-2.1.2-cp310-cp310-win32.whl", hash = "sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d", size = 57986, upload-time = "2026-03-06T02:54:26.823Z" }, + { url = "https://files.pythonhosted.org/packages/42/5a/4efc997bccadd3af5749c250b49412793bc41e13a83a486b2b54a33e240c/wrapt-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71", size = 60336, upload-time = "2026-03-06T02:54:18Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f5/a2bb833e20181b937e87c242645ed5d5aa9c373006b0467bfe1a35c727d0/wrapt-2.1.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc", size = 58757, upload-time = "2026-03-06T02:53:51.545Z" }, + { url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" }, + { url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" }, + { url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" }, + { url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" }, + { url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" }, + { url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" }, + { url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" }, + { url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" }, + { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" }, + { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" }, + { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" }, + { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" }, + { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" }, + { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, + { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, + { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, + { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, + { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, + { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, + { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, + { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, + { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, + { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, + { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, + { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, + { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, + { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, + { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" }, + { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" }, + { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" }, + { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" }, + { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" }, + { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" }, + { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" }, + { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" }, + { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" }, + { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" }, + { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" }, + { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" }, + { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" }, + { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" }, + { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" }, + { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, +] + +[[package]] +name = "xmltodict" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/70/80f3b7c10d2630aa66414bf23d210386700aa390547278c789afa994fd7e/xmltodict-1.0.4.tar.gz", hash = "sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61", size = 26124, upload-time = "2026-02-22T02:21:22.074Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/34/98a2f52245f4d47be93b580dae5f9861ef58977d73a79eb47c58f1ad1f3a/xmltodict-1.0.4-py3-none-any.whl", hash = "sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a", size = 13580, upload-time = "2026-02-22T02:21:21.039Z" }, +] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/28efd1d371f1acd037ac64ed1c5e2b41514a6cc937dd6ab6a13ab9f0702f/zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd", size = 795256, upload-time = "2025-09-14T22:15:56.415Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/ef34ef77f1ee38fc8e4f9775217a613b452916e633c4f1d98f31db52c4a5/zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7", size = 640565, upload-time = "2025-09-14T22:15:58.177Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1b/4fdb2c12eb58f31f28c4d28e8dc36611dd7205df8452e63f52fb6261d13e/zstandard-0.25.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550", size = 5345306, upload-time = "2025-09-14T22:16:00.165Z" }, + { url = "https://files.pythonhosted.org/packages/73/28/a44bdece01bca027b079f0e00be3b6bd89a4df180071da59a3dd7381665b/zstandard-0.25.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d", size = 5055561, upload-time = "2025-09-14T22:16:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/e9/74/68341185a4f32b274e0fc3410d5ad0750497e1acc20bd0f5b5f64ce17785/zstandard-0.25.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b", size = 5402214, upload-time = "2025-09-14T22:16:04.109Z" }, + { url = "https://files.pythonhosted.org/packages/8b/67/f92e64e748fd6aaffe01e2b75a083c0c4fd27abe1c8747fee4555fcee7dd/zstandard-0.25.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0", size = 5449703, upload-time = "2025-09-14T22:16:06.312Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e5/6d36f92a197c3c17729a2125e29c169f460538a7d939a27eaaa6dcfcba8e/zstandard-0.25.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0", size = 5556583, upload-time = "2025-09-14T22:16:08.457Z" }, + { url = "https://files.pythonhosted.org/packages/d7/83/41939e60d8d7ebfe2b747be022d0806953799140a702b90ffe214d557638/zstandard-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd", size = 5045332, upload-time = "2025-09-14T22:16:10.444Z" }, + { url = "https://files.pythonhosted.org/packages/b3/87/d3ee185e3d1aa0133399893697ae91f221fda79deb61adbe998a7235c43f/zstandard-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701", size = 5572283, upload-time = "2025-09-14T22:16:12.128Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1d/58635ae6104df96671076ac7d4ae7816838ce7debd94aecf83e30b7121b0/zstandard-0.25.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1", size = 4959754, upload-time = "2025-09-14T22:16:14.225Z" }, + { url = "https://files.pythonhosted.org/packages/75/d6/57e9cb0a9983e9a229dd8fd2e6e96593ef2aa82a3907188436f22b111ccd/zstandard-0.25.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150", size = 5266477, upload-time = "2025-09-14T22:16:16.343Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a9/ee891e5edf33a6ebce0a028726f0bbd8567effe20fe3d5808c42323e8542/zstandard-0.25.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab", size = 5440914, upload-time = "2025-09-14T22:16:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/58/08/a8522c28c08031a9521f27abc6f78dbdee7312a7463dd2cfc658b813323b/zstandard-0.25.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e", size = 5819847, upload-time = "2025-09-14T22:16:20.559Z" }, + { url = "https://files.pythonhosted.org/packages/6f/11/4c91411805c3f7b6f31c60e78ce347ca48f6f16d552fc659af6ec3b73202/zstandard-0.25.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74", size = 5363131, upload-time = "2025-09-14T22:16:22.206Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d6/8c4bd38a3b24c4c7676a7a3d8de85d6ee7a983602a734b9f9cdefb04a5d6/zstandard-0.25.0-cp310-cp310-win32.whl", hash = "sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa", size = 436469, upload-time = "2025-09-14T22:16:25.002Z" }, + { url = "https://files.pythonhosted.org/packages/93/90/96d50ad417a8ace5f841b3228e93d1bb13e6ad356737f42e2dde30d8bd68/zstandard-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e", size = 506100, upload-time = "2025-09-14T22:16:23.569Z" }, + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, + { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, + { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, + { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, + { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, + { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" }, + { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" }, + { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" }, + { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" }, + { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" }, + { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" }, + { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" }, + { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" }, + { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" }, + { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, + { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" }, + { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" }, + { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" }, + { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" }, + { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" }, + { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" }, + { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" }, + { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" }, + { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" }, + { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" }, +]