rpds_py-0.30.0/Cargo.toml0000644000000006231046102023000106150ustar [package] name = "rpds-py" version = "0.30.0" edition = "2021" [lib] name = "rpds" crate-type = ["cdylib"] [dependencies] rpds = "1.2.0" archery = "1.2.2" [dependencies.pyo3] version = "0.27.2" # To build extension for PyPy on Windows, "generate-import-lib" is needed: # https://github.com/PyO3/maturin-action/issues/267#issuecomment-2106844429 features = ["extension-module", "generate-import-lib"] rpds_py-0.30.0/.github/SECURITY.md0000644000000011601046102023000120130ustar # Security Policy ## Supported Versions In general, only the latest released `rpds-py` version is supported and will receive updates. ## Reporting a Vulnerability To report a security vulnerability, please send an email to `Julian+Security` at `GrayVines.com` with subject line `SECURITY (rpds-py)`. I will do my best to respond within 48 hours to acknowledge the message and discuss further steps. If the vulnerability is accepted, an advisory will be sent out via GitHub's security advisory functionality. For non-sensitive discussion related to this policy itself, feel free to open an issue on the issue tracker. rpds_py-0.30.0/.github/dependabot.yml0000644000000004301046102023000130510ustar version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" cooldown: default-days: 7 - package-ecosystem: "cargo" directory: "/" schedule: interval: "weekly" cooldown: default-days: 7 rpds_py-0.30.0/.github/release.yml0000644000000001141046102023000123630ustar changelog: exclude: authors: - dependabot - pre-commit-ci rpds_py-0.30.0/.github/workflows/CI.yml0000644000000253631046102023000133100ustar name: CI on: push: branches-ignore: - "wip*" tags: - "v[0-9].*" pull_request: schedule: # Daily at 5:33 - cron: "33 5 * * *" workflow_dispatch: permissions: {} jobs: list: runs-on: ubuntu-latest outputs: noxenvs: ${{ steps.noxenvs-matrix.outputs.noxenvs }} steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 with: enable-cache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - id: noxenvs-matrix run: | echo >>$GITHUB_OUTPUT noxenvs=$( uvx nox --list-sessions --json | jq '[.[].session]' ) test: needs: list runs-on: ubuntu-latest strategy: fail-fast: false matrix: noxenv: ${{ fromJson(needs.list.outputs.noxenvs) }} steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y libenchant-2-dev if: runner.os == 'Linux' && startsWith(matrix.noxenv, 'docs') - name: Install dependencies run: brew install enchant if: runner.os == 'macOS' && startsWith(matrix.noxenv, 'docs') - name: Set up Python uses: actions/setup-python@v6 with: python-version: | 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11 allow-prereleases: true - uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 with: enable-cache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - name: Run nox run: uvx nox -s "${{ matrix.noxenv }}" -- ${{ matrix.posargs }} # zizmor: ignore[template-injection] manylinux: needs: test runs-on: ubuntu-latest strategy: fail-fast: false matrix: target: [ x86_64, x86, aarch64, armv7, s390x, ppc64le, riscv64gc-unknown-linux-gnu, ] steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v6 with: python-version: | 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11 allow-prereleases: true - name: Build wheels uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.target }} args: --release --out dist --interpreter '3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11' sccache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] manylinux: auto - name: Upload wheels uses: actions/upload-artifact@v5 with: name: dist-${{ github.job }}-${{ matrix.target }} path: dist musllinux: needs: test runs-on: ubuntu-latest strategy: fail-fast: false matrix: target: - aarch64-unknown-linux-musl - i686-unknown-linux-musl - x86_64-unknown-linux-musl steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v6 with: python-version: | 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11 allow-prereleases: true - name: Build wheels uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.target }} args: --release --out dist --interpreter '3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11' manylinux: musllinux_1_2 sccache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - name: Upload wheels uses: actions/upload-artifact@v5 with: name: dist-${{ github.job }}-${{ matrix.target }} path: dist windows: needs: test runs-on: windows-latest strategy: fail-fast: false matrix: target: [x64, x86] # x86 is not supported by pypy steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v6 with: python-version: | 3.10 3.11 3.12 3.13 3.14 allow-prereleases: true architecture: ${{ matrix.target }} - name: Build wheels uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.target }} args: --release --out dist --interpreter '3.10 3.11 3.12 3.13 3.14' sccache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - name: Upload wheels uses: actions/upload-artifact@v5 with: name: dist-${{ github.job }}-${{ matrix.target }} path: dist windows-arm: needs: test runs-on: windows-11-arm strategy: fail-fast: false matrix: target: - aarch64-pc-windows-msvc steps: - uses: actions/checkout@v5 with: persist-credentials: false # Install each python version seperatly so that the paths can be passed to maturin. (otherwise finds pre-installed x64 versions) - uses: actions/setup-python@v6 id: cp311 with: python-version: 3.11 allow-prereleases: true architecture: arm64 - uses: actions/setup-python@v6 id: cp312 with: python-version: 3.12 allow-prereleases: true architecture: arm64 - uses: actions/setup-python@v6 id: cp313 with: python-version: 3.13 allow-prereleases: true architecture: arm64 - uses: actions/setup-python@v6 id: cp314 with: python-version: 3.14 allow-prereleases: true architecture: arm64 # rust toolchain is not currently installed on windopws arm64 images: https://github.com/actions/partner-runner-images/issues/77 - name: Setup rust id: setup-rust run: | Invoke-WebRequest https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe -OutFile .\rustup-init.exe .\rustup-init.exe -y Add-Content $env:GITHUB_PATH "$env:USERPROFILE\.cargo\bin" - name: Build wheels uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.target }} args: --release --out dist --interpreter ${{ steps.cp311.outputs.python-path }} ${{ steps.cp312.outputs.python-path }} ${{ steps.cp313.outputs.python-path }} ${{ steps.cp314.outputs.python-path }} sccache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - name: Upload wheels uses: actions/upload-artifact@v5 with: name: dist-${{ github.job }}-${{ matrix.target }} path: dist # free-threaded and normal builds share a site-packages folder on Windows so # we must build free-threaded separately windows-free-threaded: needs: test runs-on: windows-latest strategy: fail-fast: false matrix: target: [x64, x86] # x86 is not supported by pypy steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v6 with: python-version: | 3.13t 3.14t allow-prereleases: true architecture: ${{ matrix.target }} - name: Build wheels uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.target }} args: --release --out dist --interpreter '3.13t 3.14t' sccache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - name: Upload wheels uses: actions/upload-artifact@v5 with: name: dist-${{ github.job }}-${{ matrix.target }}-free-threaded path: dist macos: needs: test runs-on: macos-latest strategy: fail-fast: false matrix: target: [x86_64, aarch64] steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v6 with: python-version: | 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11 allow-prereleases: true - name: Build wheels uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: target: ${{ matrix.target }} args: --release --out dist --interpreter '3.10 3.11 3.12 3.13 3.13t 3.14 3.14t pypy3.11' sccache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - name: Upload wheels uses: actions/upload-artifact@v5 with: name: dist-${{ github.job }}-${{ matrix.target }} path: dist sdist: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v6 with: python-version: 3.13 - name: Build an sdist uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: command: sdist args: --out dist - name: Upload sdist uses: actions/upload-artifact@v5 with: name: dist-${{ github.job }} path: dist release: needs: [manylinux, musllinux, windows, windows-arm, windows-free-threaded, macos] runs-on: ubuntu-latest if: "startsWith(github.ref, 'refs/tags/')" environment: name: PyPI url: https://pypi.org/p/rpds-py permissions: contents: write id-token: write steps: - uses: actions/download-artifact@v6 with: pattern: dist-* merge-multiple: true path: dist/ - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 with: skip-existing: true print-hash: true packages-dir: dist - name: Create a GitHub Release if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe with: files: | dist/* generate_release_notes: true rpds_py-0.30.0/.github/workflows/zizmor.yml0000644000000014671046102023000143460ustar name: GitHub Actions Security Analysis with zizmor 🌈 on: push: branches: ["main"] pull_request: branches: ["**"] permissions: {} jobs: zizmor: runs-on: ubuntu-latest permissions: security-events: write steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 - name: Run zizmor 🌈 run: uvx zizmor --format=sarif . > results.sarif env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: sarif_file: results.sarif category: zizmor rpds_py-0.30.0/.github/zizmor.yml0000644000000002151046102023000122770ustar rules: template-injection: ignore: # our matrix is dynamically generated via `nox -l` but with no user input - CI.yml:71:9 rpds_py-0.30.0/.gitignore0000644000000013241046102023000106540ustar /target # Byte-compiled / optimized / DLL files __pycache__/ .pytest_cache/ *.py[cod] # C extensions *.so # Distribution / packaging .Python .venv/ env/ bin/ build/ develop-eggs/ dist/ eggs/ lib/ lib64/ parts/ sdist/ var/ include/ man/ venv/ *.egg-info/ .installed.cfg *.egg # Installer logs pip-log.txt pip-delete-this-directory.txt pip-selfcheck.json # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Rope .ropeproject # Django stuff: *.log *.pot .DS_Store # Sphinx documentation docs/_build/ # PyCharm .idea/ # VSCode .vscode/ # Pyenv .python-version # User defined /dirhtml _cache TODO rpds_py-0.30.0/.pre-commit-config.yaml0000644000000015451046102023000131520ustar ci: skip: # pre-commit.ci doesn't have Rust installed - fmt - clippy - zizmor repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: check-ast - id: check-docstring-first - id: check-toml - id: check-vcs-permalinks - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending args: [--fix, lf] - id: trailing-whitespace - repo: https://github.com/doublify/pre-commit-rust rev: "v1.0" hooks: - id: fmt - id: clippy - repo: https://github.com/astral-sh/ruff-pre-commit rev: "v0.14.6" hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/zizmorcore/zizmor-pre-commit rev: v1.18.0 hooks: - id: zizmor rpds_py-0.30.0/.readthedocs.yml0000644000000003611046102023000117520ustar version: 2 build: os: ubuntu-22.04 tools: python: "3.11" rust: "1.70" sphinx: builder: dirhtml configuration: docs/conf.py fail_on_warning: true formats: all python: install: - requirements: docs/requirements.txt rpds_py-0.30.0/Cargo.lock0000644000000135501046102023000105750ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "archery" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" dependencies = [ "triomphe", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "cc" version = "1.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "find-msvc-tools" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indoc" version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" dependencies = [ "rustversion", ] [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d" dependencies = [ "indoc", "libc", "memoffset", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", "unindent", ] [[package]] name = "pyo3-build-config" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6" dependencies = [ "python3-dll-a", "target-lexicon", ] [[package]] name = "pyo3-ffi" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089" dependencies = [ "libc", "pyo3-build-config", ] [[package]] name = "pyo3-macros" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", "syn", ] [[package]] name = "pyo3-macros-backend" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9" dependencies = [ "heck", "proc-macro2", "pyo3-build-config", "quote", "syn", ] [[package]] name = "python3-dll-a" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d381ef313ae70b4da5f95f8a4de773c6aa5cd28f73adec4b4a31df70b66780d8" dependencies = [ "cc", ] [[package]] name = "quote" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "rpds" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e75f485e819d4d3015e6c0d55d02a4fd3db47c1993d9e603e0361fba2bffb34" dependencies = [ "archery", ] [[package]] name = "rpds-py" version = "0.30.0" dependencies = [ "archery", "pyo3", "rpds", ] [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "target-lexicon" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "triomphe" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unindent" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" rpds_py-0.30.0/LICENSE0000644000000020411046102023000076660ustar Copyright (c) 2023 Julian Berman 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. rpds_py-0.30.0/README.rst0000644000000052161046102023000103570ustar =========== ``rpds.py`` =========== |PyPI| |Pythons| |CI| .. |PyPI| image:: https://img.shields.io/pypi/v/rpds-py.svg :alt: PyPI version :target: https://pypi.org/project/rpds-py/ .. |Pythons| image:: https://img.shields.io/pypi/pyversions/rpds-py.svg :alt: Supported Python versions :target: https://pypi.org/project/rpds-py/ .. |CI| image:: https://github.com/crate-py/rpds/workflows/CI/badge.svg :alt: Build status :target: https://github.com/crate-py/rpds/actions?query=workflow%3ACI .. |ReadTheDocs| image:: https://readthedocs.org/projects/referencing/badge/?version=stable&style=flat :alt: ReadTheDocs status :target: https://referencing.readthedocs.io/en/stable/ Python bindings to the `Rust rpds crate `_ for persistent data structures. What's here is quite minimal (in transparency, it was written initially to support replacing ``pyrsistent`` in the `referencing library `_). If you see something missing (which is very likely), a PR is definitely welcome to add it. Installation ------------ The distribution on PyPI is named ``rpds.py`` (equivalently ``rpds-py``), and thus can be installed via e.g.: .. code:: sh $ pip install rpds-py Note that if you install ``rpds-py`` from source, you will need a Rust toolchain installed, as it is a build-time dependency. An example of how to do so in a ``Dockerfile`` can be found `here `_. If you believe you are on a common platform which should have wheels built (i.e. and not need to compile from source), feel free to file an issue or pull request modifying the GitHub action used here to build wheels via ``maturin``. Usage ----- Methods in general are named similarly to their ``rpds`` counterparts (rather than ``pyrsistent``\ 's conventions, though probably a full drop-in ``pyrsistent``\ -compatible wrapper module is a good addition at some point). .. code:: python >>> from rpds import HashTrieMap, HashTrieSet, List >>> m = HashTrieMap({"foo": "bar", "baz": "quux"}) >>> m.insert("spam", 37) == HashTrieMap({"foo": "bar", "baz": "quux", "spam": 37}) True >>> m.remove("foo") == HashTrieMap({"baz": "quux"}) True >>> s = HashTrieSet({"foo", "bar", "baz", "quux"}) >>> s.insert("spam") == HashTrieSet({"foo", "bar", "baz", "quux", "spam"}) True >>> s.remove("foo") == HashTrieSet({"bar", "baz", "quux"}) True >>> L = List([1, 3, 5]) >>> L.push_front(-1) == List([-1, 1, 3, 5]) True >>> L.rest == List([3, 5]) True rpds_py-0.30.0/docs/api.rst0000644000000002511046102023000111150ustar API Reference ============= .. automodule:: rpds :members: :undoc-members: :imported-members: :special-members: __iter__, __getitem__, __len__, __rmatmul__ rpds_py-0.30.0/docs/conf.py0000644000000032151046102023000111140ustar import importlib.metadata import re from url import URL GITHUB = URL.parse("https://github.com/") HOMEPAGE = GITHUB / "crate-py/rpds" project = "rpds.py" author = "Julian Berman" copyright = f"2023, {author}" release = importlib.metadata.version("rpds.py") version = release.partition("-")[0] language = "en" default_role = "any" extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosectionlabel", "sphinx.ext.coverage", "sphinx.ext.doctest", "sphinx.ext.extlinks", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", "sphinx_copybutton", "sphinxcontrib.spelling", "sphinxext.opengraph", ] pygments_style = "lovelace" pygments_dark_style = "one-dark" html_theme = "furo" def entire_domain(host): return r"http.?://" + re.escape(host) + r"($|/.*)" linkcheck_ignore = [ entire_domain("img.shields.io"), f"{GITHUB}.*#.*", str(HOMEPAGE / "actions"), str(HOMEPAGE / "workflows/CI/badge.svg"), ] # = Extensions = # -- autodoc -- autodoc_default_options = { "members": True, "member-order": "bysource", } # -- autosectionlabel -- autosectionlabel_prefix_document = True # -- intersphinx -- intersphinx_mapping = { "python": ("https://docs.python.org/", None), } # -- extlinks -- extlinks = { "gh": (str(HOMEPAGE) + "/%s", None), "github": (str(GITHUB) + "/%s", None), } extlinks_detect_hardcoded_links = True # -- sphinx-copybutton -- copybutton_prompt_text = r">>> |\.\.\. |\$" copybutton_prompt_is_regexp = True # -- sphinxcontrib-spelling -- spelling_word_list_filename = "spelling-wordlist.txt" spelling_show_suggestions = True rpds_py-0.30.0/docs/index.rst0000644000000040061046102023000114550ustar Python bindings to the `Rust rpds crate `_ for persistent data structures. What's here is quite minimal (in transparency, it was written initially to support replacing ``pyrsistent`` in the `referencing library `_). If you see something missing (which is very likely), a PR is definitely welcome to add it. Installation ------------ The distribution on PyPI is named ``rpds.py`` (equivalently ``rpds-py``), and thus can be installed via e.g.: .. code:: sh $ pip install rpds-py Note that if you install ``rpds-py`` from source, you will need a Rust toolchain installed, as it is a build-time dependency. An example of how to do so in a ``Dockerfile`` can be found `here `_. If you believe you are on a common platform which should have wheels built (i.e. and not need to compile from source), feel free to file an issue or pull request modifying the GitHub action used here to build wheels via ``maturin``. Usage ----- Methods in general are named similarly to their ``rpds`` counterparts (rather than ``pyrsistent``\ 's conventions, though probably a full drop-in ``pyrsistent``\ -compatible wrapper module is a good addition at some point). .. code:: python >>> from rpds import HashTrieMap, HashTrieSet, List >>> m = HashTrieMap({"foo": "bar", "baz": "quux"}) >>> m.insert("spam", 37) == HashTrieMap({"foo": "bar", "baz": "quux", "spam": 37}) True >>> m.remove("foo") == HashTrieMap({"baz": "quux"}) True >>> s = HashTrieSet({"foo", "bar", "baz", "quux"}) >>> s.insert("spam") == HashTrieSet({"foo", "bar", "baz", "quux", "spam"}) True >>> s.remove("foo") == HashTrieSet({"bar", "baz", "quux"}) True >>> L = List([1, 3, 5]) >>> L.push_front(-1) == List([-1, 1, 3, 5]) True >>> L.rest == List([3, 5]) True .. toctree:: :glob: :hidden: api rpds_py-0.30.0/docs/spelling-wordlist.txt0000644000000000231046102023000140320ustar iter len toolchain rpds_py-0.30.0/noxfile.py0000644000000110461046102023000107040ustar from pathlib import Path from tempfile import TemporaryDirectory import os import nox ROOT = Path(__file__).parent PYPROJECT = ROOT / "pyproject.toml" DOCS = ROOT / "docs" TESTS = ROOT / "tests" SUPPORTED = [ "3.10", "3.11", "pypy3.11", "3.12", "3.13", "3.13t", "3.14t", "3.14", ] LATEST = SUPPORTED[-1] nox.options.default_venv_backend = "uv" nox.options.sessions = [] def session(default=True, python=LATEST, **kwargs): # noqa: D103 def _session(fn): if default: nox.options.sessions.append(kwargs.get("name", fn.__name__)) return nox.session(python=python, **kwargs)(fn) return _session @session(python=SUPPORTED) def tests(session): """ Run the test suite with a corresponding Python version. """ # Really we want --profile=test here (for # https://github.com/crate-py/rpds/pull/87#issuecomment-2291409297) # but it produces strange symbol errors saying: # dynamic module does not define module export function (PyInit_rpds) # so OK, dev it is. session.run_install( "uv", "sync", "--group=test", "--config-setting", "build-args=--profile=dev", "--no-cache", f"--python={session.virtualenv.location}", env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location}, ) if session.posargs and session.posargs[0] == "coverage": if len(session.posargs) > 1 and session.posargs[1] == "github": github = Path(os.environ["GITHUB_STEP_SUMMARY"]) else: github = None session.install("coverage[toml]") session.run("coverage", "run", "-m", "pytest", TESTS) if github is None: session.run("coverage", "report") else: with github.open("a") as summary: summary.write("### Coverage\n\n") summary.flush() # without a flush, output seems out of order. session.run( "coverage", "report", "--format=markdown", stdout=summary, ) else: session.run("pytest", "--parallel-threads=10", *session.posargs, TESTS) @session(tags=["build"]) def build(session): """ Build a distribution suitable for PyPI and check its validity. """ session.install("build[uv]", "twine") with TemporaryDirectory() as tmpdir: session.run( "pyproject-build", "--installer=uv", ROOT, "--outdir", tmpdir, ) session.run("twine", "check", "--strict", tmpdir + "/*") @session(tags=["style"]) def style(session): """ Check Python code style. """ session.install("ruff") session.run("ruff", "check", TESTS, __file__) @session() def typing(session): """ Check the codebase using pyright by type checking the test suite. """ session.run_install( "uv", "sync", "--group=typing", "--config-setting", "build-args=--profile=dev", "--no-cache", f"--python={session.virtualenv.location}", env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location}, ) session.run("pyright", TESTS) @session(tags=["docs"]) @nox.parametrize( "builder", [ nox.param(name, id=name) for name in [ "dirhtml", "doctest", "linkcheck", "man", "spelling", ] ], ) def docs(session, builder): """ Build the documentation using a specific Sphinx builder. """ session.run_install( "uv", "sync", "--group=docs", "--config-setting", "build-args=--profile=dev", "--no-cache", f"--python={session.virtualenv.location}", env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location}, ) with TemporaryDirectory() as tmpdir_str: tmpdir = Path(tmpdir_str) argv = ["-n", "-T", "-W"] if builder != "spelling": argv += ["-q"] posargs = session.posargs or [tmpdir / builder] session.run( "python", "-m", "sphinx", "-b", builder, DOCS, *argv, *posargs, ) @session(tags=["docs", "style"], name="docs(style)") def docs_style(session): """ Check the documentation style. """ session.install( "doc8", "pygments", "pygments-github-lexers", ) session.run("python", "-m", "doc8", "--config", PYPROJECT, DOCS) rpds_py-0.30.0/rpds.pyi0000644000000050521046102023000103610ustar from collections.abc import ( ItemsView, Iterable, Iterator, KeysView, Mapping, ValuesView, ) from typing import ( TypeVar, ) _T = TypeVar("_T") _KT_co = TypeVar("_KT_co", covariant=True) _VT_co = TypeVar("_VT_co", covariant=True) _KU_co = TypeVar("_KU_co", covariant=True) _VU_co = TypeVar("_VU_co", covariant=True) class HashTrieMap(Mapping[_KT_co, _VT_co]): def __init__( self, value: Mapping[_KT_co, _VT_co] | Iterable[tuple[_KT_co, _VT_co]] = {}, **kwds: Mapping[_KT_co, _VT_co], ): ... def __getitem__(self, key: _KT_co) -> _VT_co: ... def __iter__(self) -> Iterator[_KT_co]: ... def __len__(self) -> int: ... def discard(self, key: _KT_co) -> HashTrieMap[_KT_co, _VT_co]: ... def items(self) -> ItemsView[_KT_co, _VT_co]: ... def keys(self) -> KeysView[_KT_co]: ... def values(self) -> ValuesView[_VT_co]: ... def remove(self, key: _KT_co) -> HashTrieMap[_KT_co, _VT_co]: ... def insert( self, key: _KT_co, val: _VT_co, ) -> HashTrieMap[_KT_co, _VT_co]: ... def update( self, *args: Mapping[_KU_co, _VU_co] | Iterable[tuple[_KU_co, _VU_co]], ) -> HashTrieMap[_KT_co | _KU_co, _VT_co | _VU_co]: ... @classmethod def convert( cls, value: Mapping[_KT_co, _VT_co] | Iterable[tuple[_KT_co, _VT_co]], ) -> HashTrieMap[_KT_co, _VT_co]: ... @classmethod def fromkeys( cls, keys: Iterable[_KT_co], value: _VT_co = None, ) -> HashTrieMap[_KT_co, _VT_co]: ... class HashTrieSet(frozenset[_T]): def __init__(self, value: Iterable[_T] = ()): ... def __iter__(self) -> Iterator[_T]: ... def __len__(self) -> int: ... def discard(self, value: _T) -> HashTrieSet[_T]: ... def remove(self, value: _T) -> HashTrieSet[_T]: ... def insert(self, value: _T) -> HashTrieSet[_T]: ... def update(self, *args: Iterable[_T]) -> HashTrieSet[_T]: ... class List(Iterable[_T]): def __init__(self, value: Iterable[_T] = (), *more: _T): ... def __iter__(self) -> Iterator[_T]: ... def __len__(self) -> int: ... def push_front(self, value: _T) -> List[_T]: ... def drop_first(self) -> List[_T]: ... class Queue(Iterable[_T]): def __init__(self, value: Iterable[_T] = (), *more: _T): ... def __iter__(self) -> Iterator[_T]: ... def __len__(self) -> int: ... def enqueue(self, value: _T) -> Queue[_T]: ... def dequeue(self, value: _T) -> Queue[_T]: ... @property def is_empty(self) -> _T: ... @property def peek(self) -> _T: ... rpds_py-0.30.0/src/lib.rs0000644000001354101046102023000105730ustar use pyo3::exceptions::{PyIndexError, PyTypeError}; use pyo3::pyclass::CompareOp; use pyo3::types::{PyDict, PyIterator, PyTuple, PyType}; use pyo3::{exceptions::PyKeyError, types::PyMapping, types::PyTupleMethods}; use pyo3::{prelude::*, BoundObject, PyTypeInfo}; use rpds::{ HashTrieMap, HashTrieMapSync, HashTrieSet, HashTrieSetSync, List, ListSync, Queue, QueueSync, Stack, StackSync, }; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; fn hash_shuffle_bits(h: usize) -> usize { ((h ^ 89869747) ^ (h << 16)).wrapping_mul(3644798167) } #[derive(Debug)] struct Key { hash: isize, inner: Py, } impl<'py> IntoPyObject<'py> for Key { type Target = PyAny; type Output = Bound<'py, Self::Target>; type Error = std::convert::Infallible; fn into_pyobject(self, py: Python<'py>) -> Result { Ok(self.inner.into_bound(py)) } } impl<'a, 'py> IntoPyObject<'py> for &'a Key { type Target = PyAny; type Output = Borrowed<'a, 'py, Self::Target>; type Error = std::convert::Infallible; fn into_pyobject(self, py: Python<'py>) -> Result { Ok(self.inner.bind_borrowed(py)) } } impl Hash for Key { fn hash(&self, state: &mut H) { state.write_isize(self.hash); } } impl Eq for Key {} impl PartialEq for Key { fn eq(&self, other: &Self) -> bool { Python::attach(|py| { self.inner .call_method1(py, "__eq__", (&other.inner,)) .and_then(|value| value.extract(py)) .expect("__eq__ failed!") }) } } impl Key { fn clone_ref(&self, py: Python<'_>) -> Self { Key { hash: self.hash, inner: self.inner.clone_ref(py), } } } impl<'py> FromPyObject<'_, 'py> for Key { type Error = PyErr; fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { Ok(Key { hash: ob.hash()?, inner: ob.unbind(), }) } } #[repr(transparent)] #[pyclass(name = "HashTrieMap", module = "rpds", frozen, mapping)] struct HashTrieMapPy { inner: HashTrieMapSync>, } impl From>> for HashTrieMapPy { fn from(map: HashTrieMapSync>) -> Self { HashTrieMapPy { inner: map } } } impl<'py> FromPyObject<'_, 'py> for HashTrieMapPy { type Error = PyErr; fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let mut ret = HashTrieMap::new_sync(); if let Ok(mapping) = ob.cast::() { for each in mapping.items()?.iter() { let (k, v): (Key, Py) = each.extract()?; ret.insert_mut(k, v); } } else { for each in ob.try_iter()? { let (k, v) = each?.extract()?; ret.insert_mut(k, v); } } Ok(HashTrieMapPy { inner: ret }) } } type PickledTypeWithVec<'a> = (Bound<'a, PyType>, (Vec<(Key, Py)>,)); #[pymethods] impl HashTrieMapPy { #[new] #[pyo3(signature = (value=None, ** kwds))] fn init(value: Option, kwds: Option<&Bound<'_, PyDict>>) -> PyResult { let mut map: HashTrieMapPy; if let Some(value) = value { map = value; } else { map = HashTrieMapPy { inner: HashTrieMap::new_sync(), }; } if let Some(kwds) = kwds { for (k, v) in kwds { map.inner .insert_mut(Key::extract(k.as_borrowed())?, v.into()); } } Ok(map) } fn __contains__(&self, key: Key) -> bool { self.inner.contains_key(&key) } fn __iter__(slf: PyRef<'_, Self>) -> KeysIterator { KeysIterator { inner: slf.inner.clone(), } } fn __getitem__(&self, key: Key, py: Python) -> PyResult> { match self.inner.get(&key) { Some(value) => Ok(value.clone_ref(py)), None => Err(PyKeyError::new_err(key)), } } fn __len__(&self) -> usize { self.inner.size() } fn __repr__(&self, py: Python) -> String { let contents = self.inner.into_iter().map(|(k, v)| { format!( "{}: {}", k.inner .call_method0(py, "__repr__") .and_then(|r| r.extract(py)) .unwrap_or("".to_owned()), v.call_method0(py, "__repr__") .and_then(|r| r.extract(py)) .unwrap_or("".to_owned()) ) }); format!( "HashTrieMap({{{}}})", contents.collect::>().join(", ") ) } fn __richcmp__<'py>( &self, other: &Self, op: CompareOp, py: Python<'py>, ) -> PyResult> { match op { CompareOp::Eq => (self.inner.size() == other.inner.size() && self .inner .iter() .map(|(k1, v1)| (v1, other.inner.get(k1))) .map(|(v1, v2)| v1.bind(py).eq(v2)) .all(|r| r.unwrap_or(false))) .into_pyobject(py) .map_err(Into::into) .map(BoundObject::into_any) .map(BoundObject::unbind), CompareOp::Ne => (self.inner.size() != other.inner.size() || self .inner .iter() .map(|(k1, v1)| (v1, other.inner.get(k1))) .map(|(v1, v2)| v1.bind(py).ne(v2)) .all(|r| r.unwrap_or(true))) .into_pyobject(py) .map_err(Into::into) .map(BoundObject::into_any) .map(BoundObject::unbind), _ => Ok(py.NotImplemented()), } } fn __hash__(&self, py: Python) -> PyResult { // modified from https://github.com/python/cpython/blob/d69529d31ccd1510843cfac1ab53bb8cb027541f/Objects/setobject.c#L715 let mut hash_val = self .inner .iter() .map(|(key, val)| { let mut hasher = DefaultHasher::new(); let val_bound = val.bind(py); let key_hash = key.hash; let val_hash = val_bound.hash().map_err(|_| { PyTypeError::new_err(format!( "Unhashable type in HashTrieMap of key {}: {}", key.inner .bind(py) .repr() .and_then(|r| r.extract()) .unwrap_or(" error".to_string()), val_bound .repr() .and_then(|r| r.extract()) .unwrap_or(" error".to_string()) )) })?; hasher.write_isize(key_hash); hasher.write_isize(val_hash); Ok(hasher.finish() as usize) }) .try_fold(0, |acc: usize, x: PyResult| { PyResult::::Ok(acc ^ hash_shuffle_bits(x?)) })?; // factor in the number of entries in the collection hash_val ^= self.inner.size().wrapping_add(1).wrapping_mul(1927868237); // dispense patterns in the hash value hash_val ^= (hash_val >> 11) ^ (hash_val >> 25); hash_val = hash_val.wrapping_mul(69069).wrapping_add(907133923); Ok(hash_val as isize) } fn __reduce__(slf: PyRef<'_, Self>) -> PickledTypeWithVec<'_> { ( HashTrieMapPy::type_object(slf.py()), (slf.inner .iter() .map(|(k, v)| (k.clone_ref(slf.py()), v.clone_ref(slf.py()))) .collect(),), ) } #[classmethod] fn convert( _cls: &Bound<'_, PyType>, value: Bound<'_, PyAny>, py: Python, ) -> PyResult> { if value.is_instance_of::() { Ok(value.unbind()) } else { HashTrieMapPy::extract(value.as_borrowed())? .into_pyobject(py) .map(BoundObject::into_any) .map(BoundObject::unbind) } } #[classmethod] #[pyo3(signature = (keys, val=None))] fn fromkeys( _cls: &Bound<'_, PyType>, keys: &Bound<'_, PyAny>, val: Option<&Bound<'_, PyAny>>, py: Python, ) -> PyResult { let mut inner = HashTrieMap::new_sync(); let none = py.None().into_bound(py); let value = val.unwrap_or(&none); for each in keys.try_iter()? { let key = Key::extract(each?.as_borrowed())?; inner.insert_mut(key, value.clone().unbind()); } Ok(HashTrieMapPy { inner }) } #[pyo3(signature = (key, default=None))] fn get(&self, key: Key, default: Option>, py: Python) -> Option> { if let Some(value) = self.inner.get(&key) { Some(value.clone_ref(py)) } else { default } } fn keys(&self) -> KeysView { KeysView { inner: self.inner.clone(), } } fn values(&self) -> ValuesView { ValuesView { inner: self.inner.clone(), } } fn items(&self) -> ItemsView { ItemsView { inner: self.inner.clone(), } } fn discard(&self, key: Key) -> PyResult { match self.inner.contains_key(&key) { true => Ok(HashTrieMapPy { inner: self.inner.remove(&key), }), false => Ok(HashTrieMapPy { inner: self.inner.clone(), }), } } fn insert(&self, key: Key, value: Bound<'_, PyAny>) -> HashTrieMapPy { HashTrieMapPy { inner: self.inner.insert(key, value.unbind()), } } fn remove(&self, key: Key) -> PyResult { match self.inner.contains_key(&key) { true => Ok(HashTrieMapPy { inner: self.inner.remove(&key), }), false => Err(PyKeyError::new_err(key)), } } #[pyo3(signature = (*maps, **kwds))] fn update( &self, maps: &Bound<'_, PyTuple>, kwds: Option<&Bound<'_, PyDict>>, ) -> PyResult { let mut inner = self.inner.clone(); for value in maps { let map = HashTrieMapPy::extract(value.as_borrowed())?; for (k, v) in &map.inner { inner.insert_mut(k.clone_ref(value.py()), v.clone_ref(value.py())); } } if let Some(kwds) = kwds { for (k, v) in kwds { inner.insert_mut(Key::extract(k.as_borrowed())?, v.extract()?); } } Ok(HashTrieMapPy { inner }) } } #[pyclass(module = "rpds")] struct KeysIterator { inner: HashTrieMapSync>, } #[pymethods] impl KeysIterator { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { let first = slf.inner.keys().next()?.clone_ref(slf.py()); slf.inner = slf.inner.remove(&first); Some(first) } } #[pyclass(module = "rpds")] struct ValuesIterator { inner: HashTrieMapSync>, } #[pymethods] impl ValuesIterator { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option> { let kv = slf.inner.iter().next()?; let value = kv.1.clone_ref(slf.py()); slf.inner = slf.inner.remove(kv.0); Some(value) } } #[pyclass(module = "rpds")] struct ItemsIterator { inner: HashTrieMapSync>, } #[pymethods] impl ItemsIterator { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<(Key, Py)> { let kv = slf.inner.iter().next()?; let key = kv.0.clone_ref(slf.py()); let value = kv.1.clone_ref(slf.py()); slf.inner = slf.inner.remove(kv.0); Some((key, value)) } } #[pyclass(module = "rpds")] struct KeysView { inner: HashTrieMapSync>, } #[pymethods] impl KeysView { fn __contains__(&self, key: Key) -> bool { self.inner.contains_key(&key) } fn __eq__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? != slf.inner.size() { return Ok(false); } for each in other.try_iter()? { if !slf.inner.contains_key(&Key::extract(each?.as_borrowed())?) { return Ok(false); } } Ok(true) } fn __lt__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? <= slf.inner.size() { return Ok(false); } for each in slf.inner.keys() { if !other.contains(each.inner.clone_ref(slf.py()))? { return Ok(false); } } Ok(true) } fn __le__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? < slf.inner.size() { return Ok(false); } for each in slf.inner.keys() { if !other.contains(each.inner.clone_ref(slf.py()))? { return Ok(false); } } Ok(true) } fn __gt__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? >= slf.inner.size() { return Ok(false); } for each in other.try_iter()? { if !slf.inner.contains_key(&Key::extract(each?.as_borrowed())?) { return Ok(false); } } Ok(true) } fn __ge__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? > slf.inner.size() { return Ok(false); } for each in other.try_iter()? { if !slf.inner.contains_key(&Key::extract(each?.as_borrowed())?) { return Ok(false); } } Ok(true) } fn __iter__(slf: PyRef<'_, Self>) -> KeysIterator { KeysIterator { inner: slf.inner.clone(), } } fn __len__(slf: PyRef<'_, Self>) -> usize { slf.inner.size() } fn __and__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>) -> PyResult { KeysView::intersection(slf, other) } fn __or__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { KeysView::union(slf, other, py) } fn __repr__(&self, py: Python) -> PyResult { let contents = self.inner.into_iter().map(|(k, _)| { Ok(k.clone_ref(py) .inner .into_pyobject(py)? .call_method0("__repr__") .and_then(|r| r.extract()) .unwrap_or("".to_owned())) }); let contents = contents.collect::, PyErr>>()?; Ok(format!("keys_view({{{}}})", contents.join(", "))) } fn intersection(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>) -> PyResult { // TODO: iterate over the shorter one if it's got a length let mut inner = HashTrieSet::new_sync(); for each in other.try_iter()? { let key = Key::extract(each?.as_borrowed())?; if slf.inner.contains_key(&key) { inner.insert_mut(key); } } Ok(HashTrieSetPy { inner }) } fn union(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { // There doesn't seem to be a low-effort way to get a HashTrieSet out of a map, // so we just keep our map and add values we'll ignore. let mut inner = slf.inner.clone(); for each in other.try_iter()? { inner.insert_mut(Key::extract(each?.as_borrowed())?, py.None()); } Ok(KeysView { inner }) } } #[pyclass(module = "rpds")] struct ValuesView { inner: HashTrieMapSync>, } #[pymethods] impl ValuesView { fn __iter__(slf: PyRef<'_, Self>) -> ValuesIterator { ValuesIterator { inner: slf.inner.clone(), } } fn __len__(slf: PyRef<'_, Self>) -> usize { slf.inner.size() } fn __repr__(&self, py: Python) -> PyResult { let contents = self.inner.into_iter().map(|(_, v)| { Ok(v.into_pyobject(py)? .call_method0("__repr__") .and_then(|r| r.extract()) .unwrap_or("".to_owned())) }); let contents = contents.collect::, PyErr>>()?; Ok(format!("values_view([{}])", contents.join(", "))) } } #[pyclass(module = "rpds")] struct ItemsView { inner: HashTrieMapSync>, } #[derive(FromPyObject)] struct ItemViewQuery(Key, Py); #[pymethods] impl ItemsView { fn __contains__(slf: PyRef<'_, Self>, item: ItemViewQuery) -> PyResult { if let Some(value) = slf.inner.get(&item.0) { return item.1.bind(slf.py()).eq(value); } Ok(false) } fn __iter__(slf: PyRef<'_, Self>) -> ItemsIterator { ItemsIterator { inner: slf.inner.clone(), } } fn __len__(slf: PyRef<'_, Self>) -> usize { slf.inner.size() } fn __eq__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? != slf.inner.size() { return Ok(false); } for (k, v) in slf.inner.iter() { if !other.contains((k.inner.clone_ref(slf.py()), v))? { return Ok(false); } } Ok(true) } fn __repr__(&self, py: Python) -> PyResult { let contents = self.inner.into_iter().map(|(k, v)| { let tuple = PyTuple::new(py, [k.inner.clone_ref(py), v.clone_ref(py)])?; Ok(format!("{:?}", tuple)) }); let contents = contents.collect::, PyErr>>()?; Ok(format!("items_view([{}])", contents.join(", "))) } fn __lt__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? <= slf.inner.size() { return Ok(false); } for (k, v) in slf.inner.iter() { let pair = PyTuple::new(py, [k.inner.clone_ref(py), v.clone_ref(py)])?; // FIXME: needs to compare if !other.contains(pair)? { return Ok(false); } } Ok(true) } fn __le__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? < slf.inner.size() { return Ok(false); } for (k, v) in slf.inner.iter() { let pair = PyTuple::new(py, [k.inner.clone_ref(py), v.clone_ref(py)])?; // FIXME: needs to compare if !other.contains(pair)? { return Ok(false); } } Ok(true) } fn __gt__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? >= slf.inner.size() { return Ok(false); } for each in other.try_iter()? { let kv = each?; let k = kv.get_item(0)?; match slf.inner.get(&Key::extract(k.as_borrowed())?) { Some(value) => { let pair = PyTuple::new(py, [k, value.bind(py).clone()])?; if !pair.eq(kv)? { return Ok(false); } } None => return Ok(false), } } Ok(true) } fn __ge__(slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? > slf.inner.size() { return Ok(false); } for each in other.try_iter()? { let kv = each?; let k = kv.get_item(0)?; match slf.inner.get(&Key::extract(k.as_borrowed())?) { Some(value) => { let pair = PyTuple::new(py, [k, value.bind(py).clone()])?; if !pair.eq(kv)? { return Ok(false); } } None => return Ok(false), } } Ok(true) } fn __and__( slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python, ) -> PyResult { ItemsView::intersection(slf, other, py) } fn __or__( slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python, ) -> PyResult { ItemsView::union(slf, other, py) } fn intersection( slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python, ) -> PyResult { // TODO: iterate over the shorter one if it's got a length let mut inner = HashTrieSet::new_sync(); for each in other.try_iter()? { let kv = each?; let k = kv.get_item(0)?; if let Some(value) = slf.inner.get(&Key::extract(k.as_borrowed())?) { let pair = PyTuple::new(py, [k, value.bind(py).clone()])?; if pair.eq(kv)? { inner.insert_mut(Key::extract(pair.as_any().as_borrowed())?); } } } Ok(HashTrieSetPy { inner }) } fn union( slf: PyRef<'_, Self>, other: &Bound<'_, PyAny>, py: Python, ) -> PyResult { // TODO: this is very inefficient, but again can't seem to get a HashTrieSet out of ourself let mut inner = HashTrieSet::new_sync(); for (k, v) in slf.inner.iter() { let pair = PyTuple::new(py, [k.inner.clone_ref(py), v.clone_ref(py)])?; inner.insert_mut(Key::extract(pair.as_any().as_borrowed())?); } for each in other.try_iter()? { inner.insert_mut(Key::extract(each?.as_borrowed())?); } Ok(HashTrieSetPy { inner }) } } #[repr(transparent)] #[pyclass(name = "HashTrieSet", module = "rpds", frozen)] struct HashTrieSetPy { inner: HashTrieSetSync, } impl<'py> FromPyObject<'_, 'py> for HashTrieSetPy { type Error = PyErr; fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let mut ret = HashTrieSet::new_sync(); for each in ob.try_iter()? { let k: Key = each?.extract()?; ret.insert_mut(k); } Ok(HashTrieSetPy { inner: ret }) } } #[pymethods] impl HashTrieSetPy { #[new] #[pyo3(signature = (value=None))] fn init(value: Option) -> Self { if let Some(value) = value { value } else { HashTrieSetPy { inner: HashTrieSet::new_sync(), } } } fn __contains__(&self, key: Key) -> bool { self.inner.contains(&key) } fn __and__(&self, other: &Self, py: Python) -> Self { self.intersection(other, py) } fn __or__(&self, other: &Self, py: Python) -> Self { self.union(other, py) } fn __sub__(&self, other: &Self) -> Self { self.difference(other) } fn __xor__(&self, other: &Self, py: Python) -> Self { self.symmetric_difference(other, py) } fn __iter__(slf: PyRef<'_, Self>) -> SetIterator { SetIterator { inner: slf.inner.clone(), } } fn __len__(&self) -> usize { self.inner.size() } fn __repr__(&self, py: Python) -> PyResult { let contents = self.inner.into_iter().map(|k| { Ok(k.clone_ref(py) .into_pyobject(py)? .call_method0("__repr__") .and_then(|r| r.extract()) .unwrap_or("".to_owned())) }); let contents = contents.collect::, PyErr>>()?; Ok(format!("HashTrieSet({{{}}})", contents.join(", "))) } fn __eq__(slf: PyRef<'_, Self>, other: Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? != slf.inner.size() { return Ok(false); } for each in other.try_iter()? { if !slf.inner.contains(&Key::extract(each?.as_borrowed())?) { return Ok(false); } } Ok(true) } fn __hash__(&self) -> PyResult { // modified from https://github.com/python/cpython/blob/d69529d31ccd1510843cfac1ab53bb8cb027541f/Objects/setobject.c#L715 let mut hash_val = self .inner .iter() .map(|k| k.hash as usize) .fold(0, |acc: usize, x: usize| acc ^ hash_shuffle_bits(x)); // factor in the number of entries in the collection hash_val ^= self.inner.size().wrapping_add(1).wrapping_mul(1927868237); // dispense patterns in the hash value hash_val ^= (hash_val >> 11) ^ (hash_val >> 25); hash_val = hash_val.wrapping_mul(69069).wrapping_add(907133923); Ok(hash_val as isize) } fn __lt__(slf: PyRef<'_, Self>, other: Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? <= slf.inner.size() { return Ok(false); } for each in slf.inner.iter() { if !other.contains(each.inner.clone_ref(py))? { return Ok(false); } } Ok(true) } fn __le__(slf: PyRef<'_, Self>, other: Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? < slf.inner.size() { return Ok(false); } for each in slf.inner.iter() { if !other.contains(each.inner.clone_ref(slf.py()))? { return Ok(false); } } Ok(true) } fn __gt__(slf: PyRef<'_, Self>, other: Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? >= slf.inner.size() { return Ok(false); } for each in other.try_iter()? { if !slf.inner.contains(&Key::extract(each?.as_borrowed())?) { return Ok(false); } } Ok(true) } fn __ge__(slf: PyRef<'_, Self>, other: Bound<'_, PyAny>, py: Python) -> PyResult { let abc = PyModule::import(py, "collections.abc")?; if !other.is_instance(&abc.getattr("Set")?)? || other.len()? > slf.inner.size() { return Ok(false); } for each in other.try_iter()? { if !slf.inner.contains(&Key::extract(each?.as_borrowed())?) { return Ok(false); } } Ok(true) } fn __reduce__(slf: PyRef<'_, Self>) -> (Bound<'_, PyType>, (Vec,)) { ( HashTrieSetPy::type_object(slf.py()), (slf.inner.iter().map(|e| e.clone_ref(slf.py())).collect(),), ) } fn insert(&self, value: Key) -> HashTrieSetPy { HashTrieSetPy { inner: self.inner.insert(value), } } fn discard(&self, value: Key) -> PyResult { match self.inner.contains(&value) { true => Ok(HashTrieSetPy { inner: self.inner.remove(&value), }), false => Ok(HashTrieSetPy { inner: self.inner.clone(), }), } } fn remove(&self, value: Key) -> PyResult { match self.inner.contains(&value) { true => Ok(HashTrieSetPy { inner: self.inner.remove(&value), }), false => Err(PyKeyError::new_err(value)), } } fn difference(&self, other: &Self) -> HashTrieSetPy { let mut inner = self.inner.clone(); for value in other.inner.iter() { inner.remove_mut(value); } HashTrieSetPy { inner } } fn intersection(&self, other: &Self, py: Python) -> HashTrieSetPy { let mut inner: HashTrieSetSync = HashTrieSet::new_sync(); let larger: &HashTrieSetSync; let iter; if self.inner.size() > other.inner.size() { larger = &self.inner; iter = other.inner.iter(); } else { larger = &other.inner; iter = self.inner.iter(); } for value in iter { if larger.contains(value) { inner.insert_mut(value.clone_ref(py)); } } HashTrieSetPy { inner } } fn symmetric_difference(&self, other: &Self, py: Python) -> HashTrieSetPy { let mut inner: HashTrieSetSync; let iter; if self.inner.size() > other.inner.size() { inner = self.inner.clone(); iter = other.inner.iter(); } else { inner = other.inner.clone(); iter = self.inner.iter(); } for value in iter { if inner.contains(value) { inner.remove_mut(value); } else { inner.insert_mut(value.clone_ref(py)); } } HashTrieSetPy { inner } } fn union(&self, other: &Self, py: Python) -> HashTrieSetPy { let mut inner: HashTrieSetSync; let iter; if self.inner.size() > other.inner.size() { inner = self.inner.clone(); iter = other.inner.iter(); } else { inner = other.inner.clone(); iter = self.inner.iter(); } for value in iter { inner.insert_mut(value.clone_ref(py)); } HashTrieSetPy { inner } } #[pyo3(signature = (*iterables))] fn update(&self, iterables: Bound<'_, PyTuple>) -> PyResult { let mut inner = self.inner.clone(); for each in iterables { let iter = each.try_iter()?; for value in iter { inner.insert_mut(Key::extract(value?.as_borrowed())?); } } Ok(HashTrieSetPy { inner }) } } #[pyclass(module = "rpds")] struct SetIterator { inner: HashTrieSetSync, } #[pymethods] impl SetIterator { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { let first = slf.inner.iter().next()?.clone_ref(slf.py()); slf.inner = slf.inner.remove(&first); Some(first) } } #[repr(transparent)] #[pyclass(name = "List", module = "rpds", frozen, sequence)] struct ListPy { inner: ListSync>, } impl From>> for ListPy { fn from(elements: ListSync>) -> Self { ListPy { inner: elements } } } impl<'py> FromPyObject<'_, 'py> for ListPy { type Error = PyErr; fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let mut ret = List::new_sync(); let reversed = PyModule::import(ob.py(), "builtins")?.getattr("reversed")?; let rob: Bound<'_, PyIterator> = reversed.call1((ob,))?.try_iter()?; for each in rob { ret.push_front_mut(each?.extract()?); } Ok(ListPy { inner: ret }) } } #[pymethods] impl ListPy { #[new] #[pyo3(signature = (*elements))] fn init(elements: &Bound<'_, PyTuple>) -> PyResult { let mut ret: ListPy; if elements.len() == 1 { ret = elements.get_item(0)?.extract()?; } else { ret = ListPy { inner: List::new_sync(), }; if elements.len() > 1 { for each in (0..elements.len()).rev() { ret.inner .push_front_mut(elements.get_item(each)?.extract()?); } } } Ok(ret) } fn __len__(&self) -> usize { self.inner.len() } fn __repr__(&self, py: Python) -> PyResult { let contents = self.inner.into_iter().map(|k| { Ok(k.into_pyobject(py)? .call_method0("__repr__") .and_then(|r| r.extract()) .unwrap_or("".to_owned())) }); let contents = contents.collect::, PyErr>>()?; Ok(format!("List([{}])", contents.join(", "))) } fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyResult> { match op { CompareOp::Eq => (self.inner.len() == other.inner.len() && self .inner .iter() .zip(other.inner.iter()) .map(|(e1, e2)| e1.bind(py).eq(e2)) .all(|r| r.unwrap_or(false))) .into_pyobject(py) .map_err(Into::into) .map(BoundObject::into_any) .map(BoundObject::unbind), CompareOp::Ne => (self.inner.len() != other.inner.len() || self .inner .iter() .zip(other.inner.iter()) .map(|(e1, e2)| e1.bind(py).ne(e2)) .any(|r| r.unwrap_or(true))) .into_pyobject(py) .map_err(Into::into) .map(BoundObject::into_any) .map(BoundObject::unbind), _ => Ok(py.NotImplemented()), } } fn __hash__(&self, py: Python) -> PyResult { let mut hasher = DefaultHasher::new(); self.inner .iter() .enumerate() .try_for_each(|(index, each)| { each.bind(py) .hash() .map_err(|_| { PyTypeError::new_err(format!( "Unhashable type at {} element in List: {}", index, each.bind(py) .repr() .and_then(|r| r.extract()) .unwrap_or(" error".to_string()) )) }) .map(|x| hasher.write_isize(x)) })?; Ok(hasher.finish()) } fn __iter__(slf: PyRef<'_, Self>) -> ListIterator { ListIterator { inner: slf.inner.clone(), } } fn __reversed__(&self) -> ListPy { ListPy { inner: self.inner.reverse(), } } fn __reduce__(slf: PyRef<'_, Self>) -> (Bound<'_, PyType>, (Vec>,)) { ( ListPy::type_object(slf.py()), (slf.inner.iter().map(|e| e.clone_ref(slf.py())).collect(),), ) } #[getter] fn first(&self) -> PyResult<&Py> { self.inner .first() .ok_or_else(|| PyIndexError::new_err("empty list has no first element")) } #[getter] fn rest(&self) -> ListPy { let mut inner = self.inner.clone(); inner.drop_first_mut(); ListPy { inner } } fn push_front(&self, other: Py) -> ListPy { ListPy { inner: self.inner.push_front(other), } } fn drop_first(&self) -> PyResult { if let Some(inner) = self.inner.drop_first() { Ok(ListPy { inner }) } else { Err(PyIndexError::new_err("empty list has no first element")) } } } #[pyclass(module = "rpds")] struct ListIterator { inner: ListSync>, } #[pymethods] impl ListIterator { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option> { let first_op = slf.inner.first()?; let first = first_op.clone_ref(slf.py()); slf.inner = slf.inner.drop_first()?; Some(first) } } #[repr(transparent)] #[pyclass(name = "Stack", module = "rpds", frozen, sequence)] struct StackPy { inner: StackSync>, } impl From>> for StackPy { fn from(elements: StackSync>) -> Self { StackPy { inner: elements } } } #[pymethods] impl StackPy { #[new] #[pyo3(signature = (*args))] fn init(args: &Bound<'_, PyTuple>) -> PyResult { let mut inner = Stack::new_sync(); if args.len() == 1 { for each in args.get_item(0)?.try_iter()? { inner.push_mut(each?.extract()?); } } else { for each in args { inner.push_mut(each.extract()?); } } Ok(StackPy { inner }) } fn __hash__(&self, py: Python<'_>) -> PyResult { let mut hasher = DefaultHasher::new(); self.inner .iter() .enumerate() .try_for_each(|(index, each)| { each.bind(py) .hash() .map_err(|_| { PyTypeError::new_err(format!( "Unhashable type at {} element in Stack: {}", index, each.bind(py) .repr() .and_then(|r| r.extract()) .unwrap_or(" error".to_string()) )) }) .map(|x| hasher.write_isize(x)) })?; Ok(hasher.finish()) } fn __iter__(slf: PyRef<'_, Self>) -> StackIterator { StackIterator { inner: slf.inner.clone(), } } fn __len__(&self) -> usize { self.inner.size() } fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyResult> { match op { CompareOp::Eq => (self.inner.size() == other.inner.size() && self .inner .iter() .zip(other.inner.iter()) .map(|(e1, e2)| e1.bind(py).eq(e2)) .all(|r| r.unwrap_or(false))) .into_pyobject(py) .map_err(Into::into) .map(BoundObject::into_any) .map(BoundObject::unbind), CompareOp::Ne => (self.inner.size() != other.inner.size() || self .inner .iter() .zip(other.inner.iter()) .map(|(e1, e2)| e1.bind(py).ne(e2)) .any(|r| r.unwrap_or(true))) .into_pyobject(py) .map_err(Into::into) .map(BoundObject::into_any) .map(BoundObject::unbind), _ => Ok(py.NotImplemented()), } } fn __repr__(&self, py: Python) -> PyResult { let contents = self.inner.into_iter().map(|k| { Ok(k.into_pyobject(py)? .call_method0("__repr__") .and_then(|r| r.extract()) .unwrap_or("".to_owned())) }); let mut contents = contents.collect::, PyErr>>()?; contents.reverse(); Ok(format!("Stack([{}])", contents.join(", "))) } fn peek(&self, py: Python) -> PyResult> { if let Some(peeked) = self.inner.peek() { Ok(peeked.clone_ref(py)) } else { Err(PyIndexError::new_err("peeked an empty stack")) } } fn pop(&self) -> PyResult { if let Some(popped) = self.inner.pop() { Ok(StackPy { inner: popped }) } else { Err(PyIndexError::new_err("popped an empty stack")) } } fn push(&self, other: Py) -> StackPy { StackPy { inner: self.inner.push(other), } } } #[pyclass(module = "rpds")] struct StackIterator { inner: StackSync>, } #[pymethods] impl StackIterator { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option> { let first_op = slf.inner.peek()?; let first = first_op.clone_ref(slf.py()); slf.inner = slf.inner.pop()?; Some(first) } } #[pyclass(module = "rpds")] struct QueueIterator { inner: QueueSync>, } #[pymethods] impl QueueIterator { fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(mut slf: PyRefMut<'_, Self>) -> Option> { let first_op = slf.inner.peek()?; let first = first_op.clone_ref(slf.py()); slf.inner = slf.inner.dequeue()?; Some(first) } } #[repr(transparent)] #[pyclass(name = "Queue", module = "rpds", frozen, sequence)] struct QueuePy { inner: QueueSync>, } impl From>> for QueuePy { fn from(elements: QueueSync>) -> Self { QueuePy { inner: elements } } } impl<'py> FromPyObject<'_, 'py> for QueuePy { type Error = PyErr; fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let mut ret = Queue::new_sync(); for each in ob.try_iter()? { ret.enqueue_mut(each?.extract()?); } Ok(QueuePy { inner: ret }) } } #[pymethods] impl QueuePy { #[new] #[pyo3(signature = (*elements))] fn init(elements: &Bound<'_, PyTuple>, py: Python<'_>) -> PyResult { let mut ret: QueuePy; if elements.len() == 1 { ret = elements.get_item(0)?.extract()?; } else { ret = QueuePy { inner: Queue::new_sync(), }; if elements.len() > 1 { for each in elements { ret.inner.enqueue_mut(each.into_pyobject(py)?.unbind()); } } } Ok(ret) } fn __eq__(&self, other: &Self, py: Python<'_>) -> bool { (self.inner.len() == other.inner.len()) && self .inner .iter() .zip(other.inner.iter()) .map(|(e1, e2)| e1.bind(py).eq(e2)) .all(|r| r.unwrap_or(false)) } fn __hash__(&self, py: Python<'_>) -> PyResult { let mut hasher = DefaultHasher::new(); self.inner .iter() .enumerate() .try_for_each(|(index, each)| { each.bind(py) .hash() .map_err(|_| { PyTypeError::new_err(format!( "Unhashable type at {} element in Queue: {}", index, each.bind(py) .repr() .and_then(|r| r.extract()) .unwrap_or(" error".to_string()) )) }) .map(|x| hasher.write_isize(x)) })?; Ok(hasher.finish()) } fn __ne__(&self, other: &Self, py: Python<'_>) -> bool { (self.inner.len() != other.inner.len()) || self .inner .iter() .zip(other.inner.iter()) .map(|(e1, e2)| e1.bind(py).ne(e2)) .any(|r| r.unwrap_or(true)) } fn __iter__(slf: PyRef<'_, Self>) -> QueueIterator { QueueIterator { inner: slf.inner.clone(), } } fn __len__(&self) -> usize { self.inner.len() } fn __repr__(&self, py: Python) -> PyResult { let contents = self.inner.into_iter().map(|k| { Ok(k.into_pyobject(py)? .call_method0("__repr__") .and_then(|r| r.extract()) .unwrap_or("".to_owned())) }); let contents = contents.collect::, PyErr>>()?; Ok(format!("Queue([{}])", contents.join(", "))) } fn peek(&self, py: Python) -> PyResult> { if let Some(peeked) = self.inner.peek() { Ok(peeked.clone_ref(py)) } else { Err(PyIndexError::new_err("peeked an empty queue")) } } fn is_empty(&self) -> bool { self.inner.is_empty() } fn enqueue(&self, value: Bound<'_, PyAny>) -> Self { QueuePy { inner: self.inner.enqueue(value.into()), } } fn dequeue(&self) -> PyResult { if let Some(inner) = self.inner.dequeue() { Ok(QueuePy { inner }) } else { Err(PyIndexError::new_err("dequeued an empty queue")) } } } #[pymodule(gil_used = false)] #[pyo3(name = "rpds")] fn rpds_py(py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; PyMapping::register::(py)?; let abc = PyModule::import(py, "collections.abc")?; abc.getattr("Set")? .call_method1("register", (HashTrieSetPy::type_object(py),))?; abc.getattr("MappingView")? .call_method1("register", (KeysView::type_object(py),))?; abc.getattr("MappingView")? .call_method1("register", (ValuesView::type_object(py),))?; abc.getattr("MappingView")? .call_method1("register", (ItemsView::type_object(py),))?; abc.getattr("KeysView")? .call_method1("register", (KeysView::type_object(py),))?; abc.getattr("ValuesView")? .call_method1("register", (ValuesView::type_object(py),))?; abc.getattr("ItemsView")? .call_method1("register", (ItemsView::type_object(py),))?; Ok(()) } rpds_py-0.30.0/tests/__init__.py0000644000000000001046102023000121250ustar rpds_py-0.30.0/tests/test_hash_trie_map.py0000644000000357761046102023000142640ustar """ Modified from the pyrsistent test suite. Pre-modification, these were MIT licensed, and are copyright: Copyright (c) 2022 Tobias Gustafsson 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. """ from collections import abc from operator import methodcaller import pickle import sysconfig import pytest from rpds import HashTrieMap # see https://github.com/python/cpython/issues/127065, # remove this when the CPython bug is fixed in a released version if bool(sysconfig.get_config_var("Py_GIL_DISABLED")): def methodcaller(name, /, *args, **kwargs): def caller(obj): return getattr(obj, name)(*args, **kwargs) return caller def test_instance_of_hashable(): assert isinstance(HashTrieMap(), abc.Hashable) def test_instance_of_map(): assert isinstance(HashTrieMap(), abc.Mapping) def test_literalish_works(): assert HashTrieMap() == HashTrieMap() assert HashTrieMap(a=1, b=2) == HashTrieMap({"a": 1, "b": 2}) def test_empty_initialization(): a_map = HashTrieMap() assert len(a_map) == 0 def test_initialization_with_one_element(): the_map = HashTrieMap({"a": 2}) assert len(the_map) == 1 assert the_map["a"] == 2 assert "a" in the_map empty_map = the_map.remove("a") assert len(empty_map) == 0 assert "a" not in empty_map def test_index_non_existing_raises_key_error(): m1 = HashTrieMap() with pytest.raises(KeyError) as error: m1["foo"] assert str(error.value) == "'foo'" def test_remove_non_existing_element_raises_key_error(): m1 = HashTrieMap(a=1) with pytest.raises(KeyError) as error: m1.remove("b") assert str(error.value) == "'b'" def test_various_iterations(): assert {"a", "b"} == set(HashTrieMap(a=1, b=2)) assert ["a", "b"] == sorted(HashTrieMap(a=1, b=2).keys()) assert [1, 2] == sorted(HashTrieMap(a=1, b=2).values()) assert {("a", 1), ("b", 2)} == set(HashTrieMap(a=1, b=2).items()) pm = HashTrieMap({k: k for k in range(100)}) assert len(pm) == len(pm.keys()) assert len(pm) == len(pm.values()) assert len(pm) == len(pm.items()) ks = pm.keys() assert all(k in pm for k in ks) assert all(k in ks for k in ks) us = pm.items() assert all(pm[k] == v for (k, v) in us) vs = pm.values() assert all(v in vs for v in vs) def test_initialization_with_two_elements(): map1 = HashTrieMap({"a": 2, "b": 3}) assert len(map1) == 2 assert map1["a"] == 2 assert map1["b"] == 3 map2 = map1.remove("a") assert "a" not in map2 assert map2["b"] == 3 def test_initialization_with_many_elements(): init_dict = {str(x): x for x in range(1700)} the_map = HashTrieMap(init_dict) assert len(the_map) == 1700 assert the_map["16"] == 16 assert the_map["1699"] == 1699 assert the_map.insert("256", 256) == the_map new_map = the_map.remove("1600") assert len(new_map) == 1699 assert "1600" not in new_map assert new_map["1601"] == 1601 # Some NOP properties assert new_map.discard("18888") == new_map assert "19999" not in new_map assert new_map["1500"] == 1500 assert new_map.insert("1500", new_map["1500"]) == new_map def test_access_non_existing_element(): map1 = HashTrieMap() assert len(map1) == 0 map2 = map1.insert("1", 1) assert "1" not in map1 assert map2["1"] == 1 assert "2" not in map2 def test_overwrite_existing_element(): map1 = HashTrieMap({"a": 2}) map2 = map1.insert("a", 3) assert len(map2) == 1 assert map2["a"] == 3 def test_hashing(): o = object() assert hash(HashTrieMap([(o, o), (1, o)])) == hash( HashTrieMap([(o, o), (1, o)]), ) assert hash(HashTrieMap([(o, o), (1, o)])) == hash( HashTrieMap([(1, o), (o, o)]), ) assert hash(HashTrieMap([(o, "foo")])) == hash(HashTrieMap([(o, "foo")])) assert hash(HashTrieMap()) == hash(HashTrieMap([])) assert hash(HashTrieMap({1: 2})) != hash(HashTrieMap({1: 3})) assert hash(HashTrieMap({o: 1})) != hash(HashTrieMap({o: o})) assert hash(HashTrieMap([])) != hash(HashTrieMap([(o, 1)])) assert hash(HashTrieMap({1: 2, 3: 4})) != hash(HashTrieMap({1: 3, 2: 4})) def test_same_hash_when_content_the_same_but_underlying_vector_size_differs(): x = HashTrieMap({x: x for x in range(1000)}) y = HashTrieMap({10: 10, 200: 200, 700: 700}) for z in x: if z not in y: x = x.remove(z) assert x == y # assert hash(x) == hash(y) # noqa: ERA001 class HashabilityControlled: hashable = True def __hash__(self): if self.hashable: return 4 # Proven random raise ValueError("I am not currently hashable.") def test_map_does_not_hash_values_on_second_hash_invocation(): hashable = HashabilityControlled() x = HashTrieMap(dict(el=hashable)) hash(x) hashable.hashable = False with pytest.raises( TypeError, match=r"Unhashable type in HashTrieMap of key 'el'", ): hash(x) def test_equal(): x = HashTrieMap(a=1, b=2, c=3) y = HashTrieMap(a=1, b=2, c=3) assert x == y assert not (x != y) assert y == x assert not (y != x) def test_equal_with_different_insertion_order(): x = HashTrieMap([(i, i) for i in range(50)]) y = HashTrieMap([(i, i) for i in range(49, -1, -1)]) assert x == y assert not (x != y) assert y == x assert not (y != x) def test_not_equal(): x = HashTrieMap(a=1, b=2, c=3) y = HashTrieMap(a=1, b=2) assert x != y assert not (x == y) assert y != x assert not (y == x) def test_not_equal_to_dict(): x = HashTrieMap(a=1, b=2, c=3) y = dict(a=1, b=2, d=4) assert x != y assert not (x == y) assert y != x assert not (y == x) def test_update_with_multiple_arguments(): # If same value is present in multiple sources, the rightmost is used. x = HashTrieMap(a=1, b=2, c=3) y = x.update(HashTrieMap(b=4, c=5), {"c": 6}) assert y == HashTrieMap(a=1, b=4, c=6) def test_update_one_argument(): x = HashTrieMap(a=1) assert x.update({"b": 2}) == HashTrieMap(a=1, b=2) def test_update_no_arguments(): x = HashTrieMap(a=1) assert x.update() == x class HashDummy: def __hash__(self): return 6528039219058920 # Hash of '33' def __eq__(self, other): return self is other def test_iteration_with_many_elements(): values = list(range(2000)) keys = [str(x) for x in values] init_dict = dict(zip(keys, values)) hash_dummy1 = HashDummy() hash_dummy2 = HashDummy() # Throw in a couple of hash collision nodes to tests # those properly as well init_dict[hash_dummy1] = 12345 init_dict[hash_dummy2] = 54321 a_map = HashTrieMap(init_dict) actual_values = set() actual_keys = set() for k, v in a_map.items(): actual_values.add(v) actual_keys.add(k) assert actual_keys == {*keys, hash_dummy1, hash_dummy2} assert actual_values == {*values, 12345, 54321} def test_repr(): rep = repr(HashTrieMap({"foo": "12", "": 37})) assert rep in { "HashTrieMap({'foo': '12', '': 37})", "HashTrieMap({'': 37, 'foo': '12'})", } def test_str(): s = str(HashTrieMap({1: 2, 3: 4})) assert s == "HashTrieMap({1: 2, 3: 4})" or s == "HashTrieMap({3: 4, 1: 2})" def test_empty_truthiness(): assert HashTrieMap(a=1) assert not HashTrieMap() def test_iterable(): m = HashTrieMap((i, i * 2) for i in range(3)) assert m == HashTrieMap({0: 0, 1: 2, 2: 4}) def test_convert_hashtriemap(): m = HashTrieMap({i: i * 2 for i in range(3)}) assert HashTrieMap.convert({i: i * 2 for i in range(3)}) == m def test_fast_convert_hashtriemap(): m = HashTrieMap({i: i * 2 for i in range(3)}) assert HashTrieMap.convert(m) is m # Non-pyrsistent-test-suite tests def test_more_eq(): o = object() assert HashTrieMap([(o, o), (1, o)]) == HashTrieMap([(o, o), (1, o)]) assert HashTrieMap([(o, "foo")]) == HashTrieMap([(o, "foo")]) assert HashTrieMap() == HashTrieMap([]) assert HashTrieMap({1: 2}) != HashTrieMap({1: 3}) assert HashTrieMap({o: 1}) != HashTrieMap({o: o}) assert HashTrieMap([]) != HashTrieMap([(o, 1)]) def test_pickle(): assert pickle.loads( pickle.dumps(HashTrieMap([(1, 2), (3, 4)])), ) == HashTrieMap([(1, 2), (3, 4)]) def test_get(): m1 = HashTrieMap({"foo": "bar"}) assert m1.get("foo") == "bar" assert m1.get("baz") is None assert m1.get("spam", "eggs") == "eggs" @pytest.mark.parametrize( "view", [pytest.param(methodcaller(p), id=p) for p in ["keys", "values", "items"]], ) @pytest.mark.parametrize( "cls", [ abc.Set, abc.MappingView, abc.KeysView, abc.ValuesView, abc.ItemsView, ], ) def test_views_abc(view, cls): m, d = HashTrieMap(), {} assert isinstance(view(m), cls) == isinstance(view(d), cls) def test_keys(): d = HashTrieMap({1: 2, 3: 4}) k = d.keys() assert 1 in k assert 2 not in k assert object() not in k assert len(k) == 2 assert k == d.keys() assert k == HashTrieMap({1: 2, 3: 4}).keys() assert k == {1, 3} assert k != iter({1, 3}) assert k != {1, 2, 3} assert k != {1, 4} assert not k == {1, 4} assert k != object() def test_keys_setlike(): assert {1: 2, 3: 4}.keys() & HashTrieMap({1: 2}).keys() == {1} assert {1: 2, 3: 4}.keys() & HashTrieMap({1: 2}).keys() != {1, 2} assert HashTrieMap({1: 2}).keys() & {1: 2, 3: 4}.keys() == {1} assert HashTrieMap({1: 2}).keys() & {1: 2, 3: 4}.keys() != {2} assert not HashTrieMap({1: 2}).keys() & {}.keys() assert HashTrieMap({1: 2}).keys() & {1} == {1} assert HashTrieMap({1: 2}).keys() & [1] == {1} assert HashTrieMap({1: 2}).keys() | {3} == {1, 3} assert HashTrieMap({1: 2}).keys() | [3] == {1, 3} # these don't really exist on the KeysView protocol but it's nice to have s = (1, "foo") assert HashTrieMap({1: 2, "foo": 7}).keys().intersection(s) == set(s) assert not HashTrieMap({1: 2}).keys().intersection({}) assert HashTrieMap({1: 2}).keys().union({3}) == {1, 3} assert HashTrieMap({1: 2, 3: 4}).keys() < {1, 2, 3} assert HashTrieMap({1: 2, 3: 4}).keys() <= {1, 2, 3} assert not HashTrieMap({1: 2}).keys() < {1} assert HashTrieMap({1: 2}).keys() > set() assert HashTrieMap({1: 2}).keys() >= set() def test_keys_repr(): m = HashTrieMap({"foo": 3, 37: "bar"}) assert repr(m.keys()) in { "keys_view({'foo', 37})", "keys_view({37, 'foo'})", } def test_values(): d = HashTrieMap({1: 2, 3: 4}) v = d.values() assert 2 in v assert 3 not in v assert object() not in v assert len(v) == 2 assert v == v # https://bugs.python.org/issue12445 which was WONTFIXed assert v != HashTrieMap({1: 2, 3: 4}).values() assert v != [2, 4] assert set(v) == {2, 4} def test_values_repr(): m = HashTrieMap({"foo": 3, 37: "bar", "baz": 3}) assert repr(m.values()) in { "values_view(['bar', 3, 3])", "values_view([3, 'bar', 3])", "values_view([3, 3, 'bar'])", } def test_items(): d = HashTrieMap({1: 2, 3: 4}) i = d.items() assert (1, 2) in i assert (1, 4) not in i assert len(i) == 2 assert i == d.items() assert i == HashTrieMap({1: 2, 3: 4}).items() assert i == {(1, 2), (3, 4)} assert i != iter({(1, 2), (3, 4)}) assert i != {(1, 2, 3), (3, 4, 5)} assert i == {1: 2, 3: 4}.items() assert i != {(1, 2), (3, 4), (5, 6)} assert i != {(1, 2)} assert not i == {1, 4} assert i != object() def test_items_setlike(): assert {1: 2, 3: 4}.items() & HashTrieMap({1: 2}).items() == {(1, 2)} assert {1: 2, 3: 4}.items() & HashTrieMap({1: 2}).items() != {(1, 2), 3} assert HashTrieMap({1: 2}).items() & {1: 2, 3: 4}.items() == {(1, 2)} assert HashTrieMap({1: 2}).items() & {1: 2, 3: 4}.items() != {(3, 4)} assert not HashTrieMap({1: 2}).items() & {}.items() assert HashTrieMap({1: 2}).items() & [(1, 2)] == {(1, 2)} assert HashTrieMap({1: 2}).items() & [[1, 2]] == set() assert HashTrieMap({1: 2}).items() | {(3, 4)} == {(1, 2), (3, 4)} assert HashTrieMap({1: 2}).items() | [7] == {(1, 2), 7} s = ((1, 2), ("foo", 37)) assert HashTrieMap({1: 2, "foo": 7}).items().intersection(s) == {(1, 2)} assert not HashTrieMap({1: 2}).items().intersection({}) assert HashTrieMap({1: 2}).items().union({3}) == {(1, 2), 3} assert HashTrieMap({1: 2, 3: 4}).items() < {(1, 2), (3, 4), ("foo", "bar")} assert HashTrieMap({1: 2, 3: 4}).items() <= {(1, 2), (3, 4)} assert not HashTrieMap({1: 2}).keys() < {1} assert HashTrieMap({1: 2}).items() > set() assert HashTrieMap({1: 2}).items() >= set() def test_items_repr(): m = HashTrieMap({"foo": 3, 37: "bar", "baz": 3}) assert repr(m.items()) in { "items_view([('foo', 3), (37, 'bar'), ('baz', 3)])", "items_view([('foo', 3), ('baz', 3), (37, 'bar')])", "items_view([(37, 'bar'), ('foo', 3), ('baz', 3)])", "items_view([(37, 'bar'), ('baz', 3), ('foo', 3)])", "items_view([('baz', 3), (37, 'bar'), ('foo', 3)])", "items_view([('baz', 3), ('foo', 3), (37, 'bar')])", } def test_fromkeys(): keys = list(range(10)) got = HashTrieMap.fromkeys(keys) expected = HashTrieMap((i, None) for i in keys) assert got == HashTrieMap(dict.fromkeys(keys)) == expected def test_fromkeys_explicit_value(): keys = list(range(10)) expected = HashTrieMap((i, "foo") for i in keys) got = HashTrieMap.fromkeys(keys, "foo") expected = HashTrieMap((i, "foo") for i in keys) assert got == HashTrieMap(dict.fromkeys(keys, "foo")) == expected def test_fromkeys_explicit_value_not_copied(): keys = list(range(5)) got = HashTrieMap.fromkeys(keys, []) got[3].append(1) assert got == HashTrieMap((i, [1]) for i in keys) def test_update_with_iterable_of_kvs(): assert HashTrieMap({1: 2}).update(iter([(3, 4), ("5", 6)])) == HashTrieMap( { 1: 2, 3: 4, "5": 6, }, ) rpds_py-0.30.0/tests/test_hash_trie_set.py0000644000000137161046102023000142700ustar """ Modified from the pyrsistent test suite. Pre-modification, these were MIT licensed, and are copyright: Copyright (c) 2022 Tobias Gustafsson 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. """ from collections import abc import pickle import pytest from rpds import HashTrieSet def test_key_is_tuple(): with pytest.raises(KeyError): HashTrieSet().remove((1, 1)) def test_key_is_not_tuple(): with pytest.raises(KeyError): HashTrieSet().remove("asdf") def test_hashing(): o = object() assert hash(HashTrieSet([o])) == hash(HashTrieSet([o])) assert hash(HashTrieSet([o, o])) == hash(HashTrieSet([o, o])) assert hash(HashTrieSet([])) == hash(HashTrieSet([])) assert hash(HashTrieSet([1, 2])) == hash(HashTrieSet([1, 2])) assert hash(HashTrieSet([1, 2])) == hash(HashTrieSet([2, 1])) assert not (HashTrieSet([1, 2]) == HashTrieSet([1, 3])) assert not (HashTrieSet([]) == HashTrieSet([o])) assert hash(HashTrieSet([1, 2])) != hash(HashTrieSet([1, 3])) assert hash(HashTrieSet([1, o])) != hash(HashTrieSet([1, 2])) assert hash(HashTrieSet([1, 2])) != hash(HashTrieSet([2, 1, 3])) assert not (HashTrieSet([o]) != HashTrieSet([o, o])) assert not (HashTrieSet([o, o]) != HashTrieSet([o, o])) assert not (HashTrieSet() != HashTrieSet([])) def test_empty_truthiness(): assert HashTrieSet([1]) assert not HashTrieSet() def test_contains_elements_that_it_was_initialized_with(): initial = [1, 2, 3] s = HashTrieSet(initial) assert set(s) == set(initial) assert len(s) == len(set(initial)) def test_is_immutable(): s1 = HashTrieSet([1]) s2 = s1.insert(2) assert s1 == HashTrieSet([1]) assert s2 == HashTrieSet([1, 2]) s3 = s2.remove(1) assert s2 == HashTrieSet([1, 2]) assert s3 == HashTrieSet([2]) def test_remove_when_not_present(): s1 = HashTrieSet([1, 2, 3]) with pytest.raises(KeyError): s1.remove(4) def test_discard(): s1 = HashTrieSet((1, 2, 3)) assert s1.discard(3) == HashTrieSet((1, 2)) assert s1.discard(4) == s1 def test_is_iterable(): assert sum(HashTrieSet([1, 2, 3])) == 6 def test_contains(): s = HashTrieSet([1, 2, 3]) assert 2 in s assert 4 not in s def test_supports_set_operations(): s1 = HashTrieSet([1, 2, 3]) s2 = HashTrieSet([3, 4, 5]) assert s1 | s2 == HashTrieSet([1, 2, 3, 4, 5]) assert s1.union(s2) == s1 | s2 assert s1 & s2 == HashTrieSet([3]) assert s1.intersection(s2) == s1 & s2 assert s1 - s2 == HashTrieSet([1, 2]) assert s1.difference(s2) == s1 - s2 assert s1 ^ s2 == HashTrieSet([1, 2, 4, 5]) assert s1.symmetric_difference(s2) == s1 ^ s2 def test_supports_set_comparisons(): s1 = HashTrieSet([1, 2, 3]) s3 = HashTrieSet([1, 2]) s4 = HashTrieSet([1, 2, 3]) assert HashTrieSet([1, 2, 3, 3, 5]) == HashTrieSet([1, 2, 3, 5]) assert s1 != s3 assert s3 < s1 assert s3 <= s1 assert s3 <= s4 assert s1 > s3 assert s1 >= s3 assert s4 >= s3 def test_repr(): rep = repr(HashTrieSet([1, 2])) assert rep == "HashTrieSet({1, 2})" or rep == "HashTrieSet({2, 1})" rep = repr(HashTrieSet(["1", "2"])) assert rep == "HashTrieSet({'1', '2'})" or rep == "HashTrieSet({'2', '1'})" def test_update(): assert HashTrieSet([1, 2, 3]).update([3, 4, 4, 5]) == HashTrieSet( [1, 2, 3, 4, 5], ) def test_update_no_elements(): s1 = HashTrieSet([1, 2]) assert s1.update([]) == s1 def test_iterable(): assert HashTrieSet(iter("a")) == HashTrieSet(iter("a")) def test_more_eq(): # Non-pyrsistent-test-suite test o = object() assert HashTrieSet([o]) == HashTrieSet([o]) assert HashTrieSet([o, o]) == HashTrieSet([o, o]) assert HashTrieSet([o]) == HashTrieSet([o, o]) assert HashTrieSet() == HashTrieSet([]) assert not (HashTrieSet([1, 2]) == HashTrieSet([1, 3])) assert not (HashTrieSet([o, 1]) == HashTrieSet([o, o])) assert not (HashTrieSet([]) == HashTrieSet([o])) assert HashTrieSet([1, 2]) != HashTrieSet([1, 3]) assert HashTrieSet([]) != HashTrieSet([o]) assert not (HashTrieSet([o]) != HashTrieSet([o])) assert not (HashTrieSet([o, o]) != HashTrieSet([o, o])) assert not (HashTrieSet([o]) != HashTrieSet([o, o])) assert not (HashTrieSet() != HashTrieSet([])) assert HashTrieSet([1, 2]) == {1, 2} assert HashTrieSet([1, 2]) != {1, 2, 3} assert HashTrieSet([1, 2]) != [1, 2] def test_more_set_comparisons(): s = HashTrieSet([1, 2, 3]) assert s == s assert not (s < s) assert s <= s assert not (s > s) assert s >= s def test_pickle(): assert pickle.loads( pickle.dumps(HashTrieSet([1, 2, 3, 4])), ) == HashTrieSet([1, 2, 3, 4]) def test_instance_of_set(): assert isinstance(HashTrieSet(), abc.Set) def test_lt_le_gt_ge(): assert HashTrieSet({}) < {1} assert HashTrieSet({}) <= {1} assert HashTrieSet({1}) > set() assert HashTrieSet({1}) >= set() rpds_py-0.30.0/tests/test_list.py0000644000000103431046102023000124130ustar """ Modified from the pyrsistent test suite. Pre-modification, these were MIT licensed, and are copyright: Copyright (c) 2022 Tobias Gustafsson 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. """ import pickle import pytest from rpds import List def test_literalish_works(): assert List(1, 2, 3) == List([1, 2, 3]) def test_first_and_rest(): pl = List([1, 2]) assert pl.first == 1 assert pl.rest.first == 2 assert pl.rest.rest == List() def test_instantiate_large_list(): assert List(range(1000)).first == 0 def test_iteration(): assert list(List()) == [] assert list(List([1, 2, 3])) == [1, 2, 3] def test_push_front(): assert List([1, 2, 3]).push_front(0) == List([0, 1, 2, 3]) def test_push_front_empty_list(): assert List().push_front(0) == List([0]) def test_truthiness(): assert List([1]) assert not List() def test_len(): assert len(List([1, 2, 3])) == 3 assert len(List()) == 0 def test_first_illegal_on_empty_list(): with pytest.raises(IndexError): List().first def test_rest_return_self_on_empty_list(): assert List().rest == List() def test_reverse(): assert reversed(List([1, 2, 3])) == List([3, 2, 1]) assert reversed(List()) == List() def test_inequality(): assert List([1, 2]) != List([1, 3]) assert List([1, 2]) != List([1, 2, 3]) assert List() != List([1, 2, 3]) def test_repr(): assert str(List()) == "List([])" assert str(List([1, 2, 3])) in "List([1, 2, 3])" def test_hashing(): o = object() assert hash(List([o, o])) == hash(List([o, o])) assert hash(List([o])) == hash(List([o])) assert hash(List()) == hash(List([])) assert not (hash(List([1, 2])) == hash(List([1, 3]))) assert not (hash(List([1, 2])) == hash(List([2, 1]))) assert not (hash(List([o])) == hash(List([o, o]))) assert not (hash(List([])) == hash(List([o]))) assert hash(List([1, 2])) != hash(List([1, 3])) assert hash(List([1, 2])) != hash(List([2, 1])) assert hash(List([o])) != hash(List([o, o])) assert hash(List([])) != hash(List([o])) assert not (hash(List([o, o])) != hash(List([o, o]))) assert not (hash(List([o])) != hash(List([o]))) assert not (hash(List([])) != hash(List([]))) def test_sequence(): m = List("asdf") assert m == List(["a", "s", "d", "f"]) # Non-pyrsistent-test-suite tests def test_drop_first(): assert List([1, 2, 3]).drop_first() == List([2, 3]) def test_drop_first_empty(): """ rpds itself returns an Option here but we try IndexError instead. """ with pytest.raises(IndexError): List([]).drop_first() def test_more_eq(): o = object() assert List([o, o]) == List([o, o]) assert List([o]) == List([o]) assert List() == List([]) assert not (List([1, 2]) == List([1, 3])) assert not (List([o]) == List([o, o])) assert not (List([]) == List([o])) assert List([1, 2]) != List([1, 3]) assert List([o]) != List([o, o]) assert List([]) != List([o]) assert not (List([o, o]) != List([o, o])) assert not (List([o]) != List([o])) assert not (List() != List([])) def test_pickle(): assert pickle.loads(pickle.dumps(List([1, 2, 3, 4]))) == List([1, 2, 3, 4]) rpds_py-0.30.0/tests/test_queue.py0000644000000071221046102023000125650ustar """ Modified from the pyrsistent test suite. Pre-modification, these were MIT licensed, and are copyright: Copyright (c) 2022 Tobias Gustafsson 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. """ import pytest from rpds import Queue def test_literalish_works(): assert Queue(1, 2, 3) == Queue([1, 2, 3]) def test_peek_dequeue(): pl = Queue([1, 2]) assert pl.peek() == 1 assert pl.dequeue().peek() == 2 assert pl.dequeue().dequeue().is_empty with pytest.raises(IndexError): pl.dequeue().dequeue().dequeue() def test_instantiate_large_list(): assert Queue(range(1000)).peek() == 0 def test_iteration(): assert list(Queue()) == [] assert list(Queue([1, 2, 3])) == [1, 2, 3] def test_enqueue(): assert Queue([1, 2, 3]).enqueue(4) == Queue([1, 2, 3, 4]) def test_enqueue_empty_list(): assert Queue().enqueue(0) == Queue([0]) def test_truthiness(): assert Queue([1]) assert not Queue() def test_len(): assert len(Queue([1, 2, 3])) == 3 assert len(Queue()) == 0 def test_peek_illegal_on_empty_list(): with pytest.raises(IndexError): Queue().peek() def test_inequality(): assert Queue([1, 2]) != Queue([1, 3]) assert Queue([1, 2]) != Queue([1, 2, 3]) assert Queue() != Queue([1, 2, 3]) def test_repr(): assert str(Queue()) == "Queue([])" assert str(Queue([1, 2, 3])) in "Queue([1, 2, 3])" def test_sequence(): m = Queue("asdf") assert m == Queue(["a", "s", "d", "f"]) # Non-pyrsistent-test-suite tests def test_dequeue(): assert Queue([1, 2, 3]).dequeue() == Queue([2, 3]) def test_dequeue_empty(): """ rpds itself returns an Option here but we try IndexError instead. """ with pytest.raises(IndexError): Queue([]).dequeue() def test_more_eq(): o = object() assert Queue([o, o]) == Queue([o, o]) assert Queue([o]) == Queue([o]) assert Queue() == Queue([]) assert not (Queue([1, 2]) == Queue([1, 3])) assert not (Queue([o]) == Queue([o, o])) assert not (Queue([]) == Queue([o])) assert Queue([1, 2]) != Queue([1, 3]) assert Queue([o]) != Queue([o, o]) assert Queue([]) != Queue([o]) assert not (Queue([o, o]) != Queue([o, o])) assert not (Queue([o]) != Queue([o])) assert not (Queue() != Queue([])) def test_hashing(): assert hash(Queue([1, 2])) == hash(Queue([1, 2])) assert hash(Queue([1, 2])) != hash(Queue([2, 1])) assert len({Queue([1, 2]), Queue([1, 2])}) == 1 def test_unhashable_contents(): q = Queue([1, {1}]) with pytest.raises(TypeError): hash(q) rpds_py-0.30.0/tests/test_stack.py0000644000000101201046102023000125360ustar """ Modified from the pyrsistent test suite. Pre-modification, these were MIT licensed, and are copyright: Copyright (c) 2022 Tobias Gustafsson 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. """ import pytest from rpds import Stack def test_literalish_works(): assert Stack(1, 2, 3) == Stack([1, 2, 3]) def test_pop_and_peek(): ps = Stack([1, 2]) assert ps.peek() == 2 assert ps.pop().peek() == 1 assert ps.pop().pop() == Stack() def test_instantiate_large_stack(): assert Stack(range(1000)).peek() == 999 def test_iteration(): assert list(Stack()) == [] assert list(Stack([1, 2, 3]))[::-1] == [1, 2, 3] def test_push(): assert Stack([1, 2, 3]).push(4) == Stack([1, 2, 3, 4]) def test_push_empty_stack(): assert Stack().push(0) == Stack([0]) def test_truthiness(): assert Stack([1]) assert not Stack() def test_len(): assert len(Stack([1, 2, 3])) == 3 assert len(Stack()) == 0 def test_peek_illegal_on_empty_stack(): with pytest.raises(IndexError): Stack().peek() def test_pop_illegal_on_empty_stack(): with pytest.raises(IndexError): Stack().pop() def test_inequality(): assert Stack([1, 2]) != Stack([1, 3]) assert Stack([1, 2]) != Stack([1, 2, 3]) assert Stack() != Stack([1, 2, 3]) def test_repr(): assert str(Stack()) == "Stack([])" assert str(Stack([1, 2, 3])) in "Stack([1, 2, 3])" def test_hashing(): o = object() assert hash(Stack([o, o])) == hash(Stack([o, o])) assert hash(Stack([o])) == hash(Stack([o])) assert hash(Stack()) == hash(Stack([])) assert not (hash(Stack([1, 2])) == hash(Stack([1, 3]))) assert not (hash(Stack([1, 2])) == hash(Stack([2, 1]))) assert not (hash(Stack([o])) == hash(Stack([o, o]))) assert not (hash(Stack([])) == hash(Stack([o]))) assert hash(Stack([1, 2])) != hash(Stack([1, 3])) assert hash(Stack([1, 2])) != hash(Stack([2, 1])) assert hash(Stack([o])) != hash(Stack([o, o])) assert hash(Stack([])) != hash(Stack([o])) assert not (hash(Stack([o, o])) != hash(Stack([o, o]))) assert not (hash(Stack([o])) != hash(Stack([o]))) assert not (hash(Stack([])) != hash(Stack([]))) def test_sequence(): m = Stack("asdf") assert m == Stack(["a", "s", "d", "f"]) # Non-pyrsistent-test-suite tests def test_more_eq(): o = object() assert Stack([o, o]) == Stack([o, o]) assert Stack([o]) == Stack([o]) assert Stack() == Stack([]) assert not (Stack([1, 2]) == Stack([1, 3])) assert not (Stack([o]) == Stack([o, o])) assert not (Stack([]) == Stack([o])) assert Stack([1, 2]) != Stack([1, 3]) assert Stack([o]) != Stack([o, o]) assert Stack([]) != Stack([o]) assert not (Stack([o, o]) != Stack([o, o])) assert not (Stack([o]) != Stack([o])) assert not (Stack() != Stack([])) def test_rpds_doc(): """ From the rpds docs. """ stack = Stack().push("stack") assert stack.peek() == "stack" a_stack = stack.push("a") assert a_stack.peek() == "a" stack_popped = a_stack.pop() assert stack_popped == stack rpds_py-0.30.0/uv.lock0000644000004301431046102023000101750ustar version = 1 revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.11'", "python_full_version < '3.11'", ] [[package]] name = "accessible-pygments" version = "0.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, ] [[package]] name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, ] [[package]] name = "babel" version = "2.17.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, ] [[package]] name = "beautifulsoup4" version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, ] [[package]] name = "certifi" version = "2025.11.12" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] [[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 = "docutils" version = "0.21.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] [[package]] name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] [[package]] name = "furo" version = "2025.9.25" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "accessible-pygments" }, { name = "beautifulsoup4" }, { name = "pygments" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-basic-ng" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4e/29/ff3b83a1ffce74676043ab3e7540d398e0b1ce7660917a00d7c4958b93da/furo-2025.9.25.tar.gz", hash = "sha256:3eac05582768fdbbc2bdfa1cdbcdd5d33cfc8b4bd2051729ff4e026a1d7e0a98", size = 1662007, upload-time = "2025-09-25T21:37:19.221Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ba/69/964b55f389c289e16ba2a5dfe587c3c462aac09e24123f09ddf703889584/furo-2025.9.25-py3-none-any.whl", hash = "sha256:2937f68e823b8e37b410c972c371bc2b1d88026709534927158e0cb3fac95afe", size = 340409, upload-time = "2025-09-25T21:37:17.244Z" }, ] [[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 = "imagesize" version = "1.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, ] [[package]] name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] [[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 = "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 = "nodeenv" version = "1.9.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "pyenchant" version = "3.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/36/ad/64925c937e41be75c7067c85757b3d45b148e9111187b37693269f583156/pyenchant-3.3.0.tar.gz", hash = "sha256:825288246b5debc9436f91967650974ef0d5636458502619e322c476f1283891", size = 60696, upload-time = "2025-09-14T16:23:12.113Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/38/b0/35926bad6885fb7bc24aa7e1b45e6d86540c6c57ee4abc4fed1ef58d4ec0/pyenchant-3.3.0-py3-none-any.whl", hash = "sha256:3da00b1d01314d85aac733bb997415d7a3e875666dc81735ddcf320aa36b7a70", size = 58363, upload-time = "2025-09-14T16:23:04.297Z" }, { url = "https://files.pythonhosted.org/packages/d6/7f/1d7b8ad86c2a841d940df7b965fa727e052b95d539e4c563da685c25d0d2/pyenchant-3.3.0-py3-none-win32.whl", hash = "sha256:1d55e075645a6edbb3c590fb42f9e02b4d455e4affe28a2227d5cb6d4868e626", size = 37787278, upload-time = "2025-09-14T16:23:06.629Z" }, { url = "https://files.pythonhosted.org/packages/ad/ae/5624803b62ecb0a20248f0d28ed3f78c78746a032582a016d4b2890c7899/pyenchant-3.3.0-py3-none-win_amd64.whl", hash = "sha256:04a5bd0e022ebe2e8c6d9e498ec3d650602e264ec5486e9c6a1b7f99c9507c49", size = 37427576, upload-time = "2025-09-14T16:23:09.574Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pygments-github-lexers" version = "0.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/04/d41eaf70152a8db863f57376660e772b3a1221469dbaeafe142ba8fdcd01/pygments-github-lexers-0.0.5.tar.gz", hash = "sha256:aaca57e77cd6fcfce8d6ee97a998962eebf7fbb810519a8ebde427c62823e133", size = 5303, upload-time = "2015-01-21T02:11:47.046Z" } [[package]] name = "pyright" version = "1.1.407" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, ] [[package]] name = "pytest" version = "9.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, ] [[package]] name = "pytest-run-parallel" version = "0.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/83/d8/51206a0a30fa19607552625160deeeb29503e0fc7fc43bf229026084057d/pytest_run_parallel-0.7.1.tar.gz", hash = "sha256:ba53069f6c16147d6f5c275f5123947d1d0c31bd6ba57efc27e845a79afbe79e", size = 65445, upload-time = "2025-10-23T12:49:19.327Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/64/30/14493dd5282439661a09649685f04e45434c750f7301a1bb5f6e1e2d5308/pytest_run_parallel-0.7.1-py3-none-any.whl", hash = "sha256:91cf4b1760a4f1f93b5e707bb715909858c803debc2d46e4991f2805b0538292", size = 19107, upload-time = "2025-10-23T12:49:17.992Z" }, ] [[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 = "roman-numerals-py" version = "3.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, ] [[package]] name = "rpds-py" source = { editable = "." } [package.dev-dependencies] docs = [ { name = "furo" }, { name = "pygments-github-lexers" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-copybutton" }, { name = "sphinxcontrib-spelling" }, { name = "sphinxext-opengraph" }, { name = "url-py" }, ] test = [ { name = "pytest" }, { name = "pytest-run-parallel" }, ] typing = [ { name = "pyright" }, { name = "pytest" }, { name = "pytest-run-parallel" }, ] [package.metadata] [package.metadata.requires-dev] docs = [ { name = "furo", specifier = ">=2025.9.25" }, { name = "pygments-github-lexers", specifier = ">=0.0.5" }, { name = "sphinx", specifier = ">5" }, { name = "sphinx-copybutton", specifier = ">=0.5.2" }, { name = "sphinxcontrib-spelling", specifier = ">5" }, { name = "sphinxext-opengraph", specifier = ">=0.13.0" }, { name = "url-py", specifier = ">=0.18.0" }, ] test = [ { name = "pytest", specifier = ">=9.0.1" }, { name = "pytest-run-parallel", specifier = ">=0.7.1" }, ] typing = [ { name = "pyright", specifier = ">=1.1.407" }, { name = "pytest", specifier = ">=9.0.1" }, { name = "pytest-run-parallel", specifier = ">=0.7.1" }, ] [[package]] name = "snowballstemmer" version = "3.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, ] [[package]] name = "soupsieve" version = "2.8" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, ] [[package]] name = "sphinx" version = "8.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ { name = "alabaster", marker = "python_full_version < '3.11'" }, { name = "babel", marker = "python_full_version < '3.11'" }, { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, { name = "docutils", marker = "python_full_version < '3.11'" }, { name = "imagesize", marker = "python_full_version < '3.11'" }, { name = "jinja2", marker = "python_full_version < '3.11'" }, { name = "packaging", marker = "python_full_version < '3.11'" }, { name = "pygments", marker = "python_full_version < '3.11'" }, { name = "requests", marker = "python_full_version < '3.11'" }, { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, ] [[package]] name = "sphinx" version = "8.2.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", ] dependencies = [ { name = "alabaster", marker = "python_full_version >= '3.11'" }, { name = "babel", marker = "python_full_version >= '3.11'" }, { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, { name = "docutils", marker = "python_full_version >= '3.11'" }, { name = "imagesize", marker = "python_full_version >= '3.11'" }, { name = "jinja2", marker = "python_full_version >= '3.11'" }, { name = "packaging", marker = "python_full_version >= '3.11'" }, { name = "pygments", marker = "python_full_version >= '3.11'" }, { name = "requests", marker = "python_full_version >= '3.11'" }, { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, ] [[package]] name = "sphinx-basic-ng" version = "1.0.0b2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/0b/a866924ded68efec7a1759587a4e478aec7559d8165fac8b2ad1c0e774d6/sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9", size = 20736, upload-time = "2023-07-08T18:40:54.166Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3c/dd/018ce05c532a22007ac58d4f45232514cd9d6dd0ee1dc374e309db830983/sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b", size = 22496, upload-time = "2023-07-08T18:40:52.659Z" }, ] [[package]] name = "sphinx-copybutton" version = "0.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, ] [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, ] [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, ] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, ] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, ] [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, ] [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, ] [[package]] name = "sphinxcontrib-spelling" version = "8.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyenchant" }, { name = "requests" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/04/099b55abd934cacccaedab9680c8238042eb3c722bdd420bc752d0eddb78/sphinxcontrib_spelling-8.0.1.tar.gz", hash = "sha256:f0447b6413c78b613b916c7891e36be85a105d1919c99784c53dfea2d8f8040f", size = 36005, upload-time = "2024-12-19T17:07:54.062Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/03/30/05efe7261eac789cf3ba28ef5dfb76d719df30baae6881cb54a6801c0e8f/sphinxcontrib_spelling-8.0.1-py3-none-any.whl", hash = "sha256:21704857c1b5e26e06bb07d15927df41c9d7ecfc1843169ecd22cb59f24069ac", size = 14617, upload-time = "2024-12-19T17:07:52.799Z" }, ] [[package]] name = "sphinxext-opengraph" version = "0.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/c0/eb6838e3bae624ce6c8b90b245d17e84252863150e95efdb88f92c8aa3fb/sphinxext_opengraph-0.13.0.tar.gz", hash = "sha256:103335d08567ad8468faf1425f575e3b698e9621f9323949a6c8b96d9793e80b", size = 1026875, upload-time = "2025-08-29T12:20:31.066Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/bf/a4/66c1fd4f8fab88faf71cee04a945f9806ba0fef753f2cfc8be6353f64508/sphinxext_opengraph-0.13.0-py3-none-any.whl", hash = "sha256:936c07828edc9ad9a7b07908b29596dc84ed0b3ceaa77acdf51282d232d4d80e", size = 1004152, upload-time = "2025-08-29T12:20:29.072Z" }, ] [[package]] name = "tomli" version = "2.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] [[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 = "url-py" version = "0.18.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/8c/e0/79e54af7445d5a4e51b3d8fba8c2aea072291ede82f0e62f43a276508426/url_py-0.18.0.tar.gz", hash = "sha256:4c1773cb4f0eadd72f93d3662a6515aa98f98211b1c57bc2c21865efc344473d", size = 19309, upload-time = "2025-09-07T16:20:49.936Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d5/db/19e92edf440b860a370b5c9ff0af0b7c03daefbc3d7612dc74ea023ba07e/url_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e57d2f786562d31d5b3ee6feb389e2e4ecfe370d28d73ef8ab627d4177642019", size = 370626, upload-time = "2025-09-07T16:17:01.189Z" }, { url = "https://files.pythonhosted.org/packages/88/b6/a269a391c8fb0db6659a9c8c313bd4b370ad15eac59d126aee44b20a3615/url_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bfbb6233b49e2f5b15cf8ecbdb4db75556f149623fdce8f7c295ca91e8dceb2a", size = 359602, upload-time = "2025-09-07T16:17:03.21Z" }, { url = "https://files.pythonhosted.org/packages/fc/e3/ba72d8986f083f1d5aadeb1ddf8e87eb9c151a4e2ea9f74cdec562f688c2/url_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6de9776876e6ca668fc72d942ecfdca65c4a74ad81bace259b3b1f82853c2287", size = 394501, upload-time = "2025-09-07T16:17:04.918Z" }, { url = "https://files.pythonhosted.org/packages/aa/5f/3bcacf42203b81c5209d8bf5b303a9bba321b4da587f7c1f3146858f705d/url_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e59064ab111ec4262f7be6b2aa4a3ae07bcb7048b9228df20dbbf81ed24643f7", size = 401697, upload-time = "2025-09-07T16:17:06.154Z" }, { url = "https://files.pythonhosted.org/packages/b3/b9/96fd093dfd6bf6f8a53922cce3968a3a54aa14ff52d39066b0ff9133b7bd/url_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440801c1b4c009b0af05371d9d21d5141747c20c8d39cdb8457b6acd8ca096dd", size = 543752, upload-time = "2025-09-07T16:17:07.833Z" }, { url = "https://files.pythonhosted.org/packages/6f/00/16d8511f61e2eae588cf38da71df623fd9618fd93a8782f3b354816fabab/url_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84aef94c43fcdd60614ed218599afe4ed79b064cd1160cfe7b00a803dd022bf5", size = 435046, upload-time = "2025-09-07T16:17:09.274Z" }, { url = "https://files.pythonhosted.org/packages/c2/35/f556ab46e04223109f0e37a352ce804df0d27008a17489d3078ebf7a2675/url_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d1aef6f4e3bfdbe98ad76da684cdba09eaafb0a043d4ffc3a45463f1b178dd", size = 399981, upload-time = "2025-09-07T16:17:10.897Z" }, { url = "https://files.pythonhosted.org/packages/57/dc/700341406c0cb6bb02bc85b252ae347bc3fa0079113148464b74693053d1/url_py-0.18.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:dcf715c735038731688434c1a2ef4aa33345c094ef9c088c047b6a05863a883a", size = 414344, upload-time = "2025-09-07T16:17:12.193Z" }, { url = "https://files.pythonhosted.org/packages/28/df/6212bc870acd7f4c3456df3c3bf5a5a2e284c8a08e4126f04c2ba71fc3cb/url_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d165dec44f96c07417defe0b8e68c50fffc3a6225dd75b45a7ca6176d3c65251", size = 426301, upload-time = "2025-09-07T16:17:13.534Z" }, { url = "https://files.pythonhosted.org/packages/b5/89/654fd072f23a76d20ab6ebc59115f5ad5ebec5e6c654ec0f3f65a44ae7ad/url_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ce0578cdb1a8158e4ee0aefb574a7f35c45cbae18e61727888101c91fe4bfe0d", size = 572986, upload-time = "2025-09-07T16:17:14.731Z" }, { url = "https://files.pythonhosted.org/packages/c4/f0/afd23224429aa93b36688e51baaea2c96120fe7efa1d4c0f48d291b4526a/url_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b9b66c880c9b74c67579abac9797dc1d7ce96cea6d74c1e9efc031d5c982fbc7", size = 599047, upload-time = "2025-09-07T16:17:16.15Z" }, { url = "https://files.pythonhosted.org/packages/c7/0d/0c901d8b785167f2952123c158a2c3aa1c077c2785e52d83ffd56c0f0a0e/url_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1a5e22a221c5adec7ff1967b7ac0a1823ff4b139ffd6ab789f134b3e8ce888", size = 569716, upload-time = "2025-09-07T16:17:17.475Z" }, { url = "https://files.pythonhosted.org/packages/b8/7e/769e74f48c5e3d3b63f7dd123b5859f9a688e2550d3ae0aff26da01fccaf/url_py-0.18.0-cp310-cp310-win32.whl", hash = "sha256:f0fbbea2a42c1e1640a8bf2764883e4cc688917f7ff704f5fba5911c915f858e", size = 240663, upload-time = "2025-09-07T16:17:18.903Z" }, { url = "https://files.pythonhosted.org/packages/56/d3/b74004a393968bbc47476ccdd194e9488f595e499eec57a3e09c4d000d5e/url_py-0.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:0aaf37777df348fb4c1cc27954e3f0919e36272542e040e906d08b6577084ece", size = 249799, upload-time = "2025-09-07T16:17:20.44Z" }, { url = "https://files.pythonhosted.org/packages/bd/a3/042a97f78c450e29d45f544fed1d4152373951b01b3ef27e1565483481ce/url_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:73ad8d292b684bda195da062b07adb155632bc95973bbdb74d9fc6c772ab6a8a", size = 370448, upload-time = "2025-09-07T16:17:22.005Z" }, { url = "https://files.pythonhosted.org/packages/b2/97/2a908db0922ae2a37cdfe77de310c15ac25cd4bea61bad23abfd526c3df6/url_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f09c7fb7b670fd66a04bb6fcfb42917c6fc11d0d2ac8527d9a7f226f292811ec", size = 359333, upload-time = "2025-09-07T16:17:23.234Z" }, { url = "https://files.pythonhosted.org/packages/89/65/591294e60c422b11c7bb70faaeb5ffaa82e3005c7137b9db9d29bd20b9cd/url_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a669250bd5237082444f69a09ca51112274107b41c835fc9cbc7bb7ce281772", size = 394283, upload-time = "2025-09-07T16:17:25.233Z" }, { url = "https://files.pythonhosted.org/packages/ce/2a/0d2e691b91429f25864ce9845671a5fc4253bc543c68a8a8d0c1c4b7305e/url_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87666cc3ca3e19a90d8f2089236663d44466594247b0a633e45ffba58e46416a", size = 401685, upload-time = "2025-09-07T16:17:26.909Z" }, { url = "https://files.pythonhosted.org/packages/1f/86/28f008358fe55c6a8a7f6c6019ae72db30b634c072c02f28a5311419f535/url_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ff5ac6bd1663d02f316f011dac329f1c3b8895005f6dcd5e77b20827d758a3c", size = 543728, upload-time = "2025-09-07T16:17:28.374Z" }, { url = "https://files.pythonhosted.org/packages/8f/2f/d126d1061a630d21460e99819740e54152745357785a26f5c090e9671cc8/url_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeeb4e8d86f30725297ea9c8ec8da5e1a0945d678e0ecb0957634f31812e155", size = 434915, upload-time = "2025-09-07T16:17:29.895Z" }, { url = "https://files.pythonhosted.org/packages/d6/31/edb6dc66e6c153d55b79683b1677cf761579416b84b33ca77f667c3ee90b/url_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afb93c96667b8b0ecfe47a6c6a857ed093b379fb89184a922fc68b0a9170ed4", size = 399620, upload-time = "2025-09-07T16:17:32.082Z" }, { url = "https://files.pythonhosted.org/packages/8e/f4/514efa477d41844c534e07cc05dd2b989aee55e7c3154889032782043fd2/url_py-0.18.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:001c10bc898349dd6a8e7275b70b04b989f1deaf840a4ca0c556a6e3df8cc7a9", size = 414169, upload-time = "2025-09-07T16:17:33.37Z" }, { url = "https://files.pythonhosted.org/packages/02/50/a3f1a0f312d98e817407a069a9e961b97844b72c6d1cb6a5b1d9bca4aebf/url_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bc5a2293160fdecbb3dad0fcaef074effd7d298bfe2866cef82904436674314a", size = 425985, upload-time = "2025-09-07T16:17:35.019Z" }, { url = "https://files.pythonhosted.org/packages/24/14/4704a414259bf957b8d64dfa33a3d65fb4815b8c9f341e23577dc2da7a80/url_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8a5b3bd0cd8ba67535e40e09663ccd5a0ee8f9c60679c834cb32b6743b74f8d6", size = 572814, upload-time = "2025-09-07T16:17:36.606Z" }, { url = "https://files.pythonhosted.org/packages/70/03/70028764bceb9fbfc0ef44e0d38f8a51afa0b50e7bd32e9ed2f19a5542ed/url_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3d9ad85ea5cbf7c92c3fb0f3fdbbbc263fc07b5878e350bfbe95dbb35ea11bdb", size = 598859, upload-time = "2025-09-07T16:17:38.412Z" }, { url = "https://files.pythonhosted.org/packages/14/96/517030f674d7cbe0f689805b1f065ab49ad893f026474f54897cf5eae7ee/url_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7653e2423cfc8e587ee9b4ccde27b1485fdc70c20400e12e83910133b67f40f6", size = 569496, upload-time = "2025-09-07T16:17:40.063Z" }, { url = "https://files.pythonhosted.org/packages/b1/20/ab19612719e8a9eb3293be6071e6c449942c3293e9540e02d6b6f6cb90c9/url_py-0.18.0-cp311-cp311-win32.whl", hash = "sha256:dbee23a9ce503566c3aab44fbf2300b1cbedcb39b0d7008161bc06cebccfeb7a", size = 240006, upload-time = "2025-09-07T16:17:41.523Z" }, { url = "https://files.pythonhosted.org/packages/5a/40/197105f07cb12ab6a3b7c94e3a4b40a76c6fadb10aeb50c9555387b1f06c/url_py-0.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:8a8bbbb5426741ae2026579dcaf234f7dc93351ad3708a2c36aa5306c61024c3", size = 249704, upload-time = "2025-09-07T16:17:42.736Z" }, { url = "https://files.pythonhosted.org/packages/e8/76/57d9b4f9636a80b600a36ff0be3aad9b1f0a6224755339379ed7198abdee/url_py-0.18.0-cp311-cp311-win_arm64.whl", hash = "sha256:35fcd5c848d3fedf3dc48d13e2ccd41878166c386863aa46e6ce7d1e70e7569f", size = 240691, upload-time = "2025-09-07T16:17:43.957Z" }, { url = "https://files.pythonhosted.org/packages/a2/f9/59751d3b3d40793724f39a8e92689997ca030a028c0e850cc8c9931e53b6/url_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e3050a866b86c3a5459ae68460fff219000379638bde4a69678c858cc361a3bc", size = 366916, upload-time = "2025-09-07T16:17:45.217Z" }, { url = "https://files.pythonhosted.org/packages/f5/be/d306ecad98d89a8fa6391bb13366e6c155027c8e245d5d96b11f5eec247c/url_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:188eb695c6af47c7de3f821c62e4074b1f3178e10ed83394e251a2a598314724", size = 355780, upload-time = "2025-09-07T16:17:46.892Z" }, { url = "https://files.pythonhosted.org/packages/d4/03/b66d06e438211fb135fcdc44bbaca1894bde9b9c786cf3469831c4d20d9a/url_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3806aec15e8a33e061efc232b976b986ebc8534357eeb440623eb2f86b9b9c4", size = 394416, upload-time = "2025-09-07T16:17:48.092Z" }, { url = "https://files.pythonhosted.org/packages/56/f7/915d139d15ce7c06d1edd311bc9e0b88b363714cef79865c30787fcc61f9/url_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6e4046ccff77769b417ce772ef3e1ebfda236b7503a76e4697b35b6a6dcfbde1", size = 402309, upload-time = "2025-09-07T16:17:49.356Z" }, { url = "https://files.pythonhosted.org/packages/b2/5f/a7972a2f9a16f48e822bf4e6771a1fa765c302079bb952dbd3288d382fb7/url_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f47e8b6e1907e67f3d16107fb7af3f7fd6aa270ac1be93ef2265550113bece", size = 543074, upload-time = "2025-09-07T16:17:50.892Z" }, { url = "https://files.pythonhosted.org/packages/f6/95/a1695320f3a9c3cace47ecc403f773a8ed339f1a2455381245d8cdb877f1/url_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:221da28ec986385d7b81a5b30e50db584013a6c687957a14f52dedbc26000d17", size = 436339, upload-time = "2025-09-07T16:17:52.176Z" }, { url = "https://files.pythonhosted.org/packages/55/65/7e24bbe06921b05c83789261e9692b061fb87a43629bbe482908cd662cb7/url_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce8a71b391eb4426d124dad34680581fe3920f028513b9604989eba9eca4a2b0", size = 400539, upload-time = "2025-09-07T16:17:53.494Z" }, { url = "https://files.pythonhosted.org/packages/23/5a/844d8a2d50185940bd008afefdbd62b5f1c7183e825803922cc33b79a977/url_py-0.18.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:4addbb913ed4fb456f079e86140b214b4f83e4fa902a260a25085eecc90afdad", size = 415626, upload-time = "2025-09-07T16:17:54.764Z" }, { url = "https://files.pythonhosted.org/packages/c2/fe/90c8951cd4f7bf60c38fdbf9487e431d27ede25c304911e57790818f5673/url_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e534e22af96c47a09c0493adf75affb0f5e3f2e3ca6d80e01608fb4cbb416bd", size = 426669, upload-time = "2025-09-07T16:17:56.04Z" }, { url = "https://files.pythonhosted.org/packages/d3/10/e653c2ab5553f6dc3ab7a176352215a98c85101ae15f192a83b4f29a3add/url_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36f500a395e229bd7391e15dd33b3af38feb382aebc6c43562bc6150cc52f788", size = 572832, upload-time = "2025-09-07T16:17:57.395Z" }, { url = "https://files.pythonhosted.org/packages/58/1e/844e572871991352a98a6957bb887320bcc6c99750dc32843f9a1f8d4cfb/url_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cf7c4c219b5a4c49d6229344e8241cd646a38ccd9d7791004f2240ee9d2a9364", size = 599310, upload-time = "2025-09-07T16:17:58.68Z" }, { url = "https://files.pythonhosted.org/packages/22/70/fef4e8f43595870d3fa89db19164d37c58754b2c713347f39743809e5a2c/url_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:400bda5baf70ce7e36f150803c9515fb336e4a61fdc1ba705dcd3f53ab784cd0", size = 570347, upload-time = "2025-09-07T16:18:00.142Z" }, { url = "https://files.pythonhosted.org/packages/30/e1/409f89eec5061560b90a0cc7758dcc5f0ad944f8e6d494f8c11256c94468/url_py-0.18.0-cp312-cp312-win32.whl", hash = "sha256:9a95ef51266c1738c6beb0686b81e873c265eaa81c453f5cca96dd92f36b8bde", size = 241839, upload-time = "2025-09-07T16:18:01.43Z" }, { url = "https://files.pythonhosted.org/packages/f0/c9/bffd6f526ed6500350cbf19eccb2812cdfa77e6ccdfd222194b37f11ba68/url_py-0.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:9c6666c2f047cc786e487d27b18f1064c0eea47ae2a3aa9ca6ee34f8ede01894", size = 250988, upload-time = "2025-09-07T16:18:02.668Z" }, { url = "https://files.pythonhosted.org/packages/32/5f/841bfacbeaaad8bc6d4bace815a86908ee4c5a5a7e0ac240c986b9ab36ff/url_py-0.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:514326c6d9816376f9b9bd20451bc0393ee34e281ab219b2f4e70a22d2b3b5c7", size = 240990, upload-time = "2025-09-07T16:18:04.846Z" }, { url = "https://files.pythonhosted.org/packages/5c/0f/daeb47ff1be77228c348341e870cde0927a5dc049345bb39267d8ecd1c3f/url_py-0.18.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4130c07918d2e6c63775e1c895ca0c9e1c86ce25626e44508370131ffc1a316e", size = 367220, upload-time = "2025-09-07T16:18:06.469Z" }, { url = "https://files.pythonhosted.org/packages/05/d6/c9c774d7994149535d75900abf985d8e2c27b8d70d817ad4948190e63572/url_py-0.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4e88066483eaf70d14883313956b053753a12326f4ef85d7bb903d6d26db7cd", size = 356257, upload-time = "2025-09-07T16:18:07.708Z" }, { url = "https://files.pythonhosted.org/packages/7d/e9/0389b6bb555aa1b3cea2b052a97acad281a54f5ce575b4c0ba0a0aa63dd7/url_py-0.18.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:145b595cc76bd32fac5f9b578cf25a9cba63d723968f544a7f450954d520e8dd", size = 394571, upload-time = "2025-09-07T16:18:09.312Z" }, { url = "https://files.pythonhosted.org/packages/47/11/65fa4ce422ae2d15f2d9ef613bb3af0daa61b704edc5fc6638409ecd8762/url_py-0.18.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9bf1f2d67dd4f1a4fbc0a97507cce147bef536415c1b54691d083749c6f7fab6", size = 402677, upload-time = "2025-09-07T16:18:10.532Z" }, { url = "https://files.pythonhosted.org/packages/10/41/2725e5d7d3a2cbd08804a776c7bcb7ded3c222912cbf452b9bf3f36e678c/url_py-0.18.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ae005980399a5a622c9b6646150726df07ca2af7c03174055aaa8cfe1abd83", size = 545108, upload-time = "2025-09-07T16:18:12.259Z" }, { url = "https://files.pythonhosted.org/packages/12/e0/47f3bddaea3a5586f5219cc54d72ec1af5e88e6bd75b3f1110c03e68c88c/url_py-0.18.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd79c5a7f63ffe2d56ce6e5cd6ee570ace4f8c9244324405a995a382c29ebfff", size = 436686, upload-time = "2025-09-07T16:18:13.588Z" }, { url = "https://files.pythonhosted.org/packages/b7/c4/1080aa80d0a7f79a7bce9987949c4b24699ce204efb6d98e5d9bfe09d7a1/url_py-0.18.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69154730d91a8afeb29fe58a37fc292e1977737fceb5f0dc9cf009039d861e05", size = 400809, upload-time = "2025-09-07T16:18:14.859Z" }, { url = "https://files.pythonhosted.org/packages/ad/bd/090c6eada00ee62e8c3b5920c83e201f4089b90022ebbbbbe0ffbda5b4e7/url_py-0.18.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8ba6730c0ab4a5fdd90de0aed74d4a820a9c6241c4450790133725a2700e4d0d", size = 415005, upload-time = "2025-09-07T16:18:16.053Z" }, { url = "https://files.pythonhosted.org/packages/2b/3b/eb61e14b81e85adfc3556c2b4d5c3a0450dc49958a2a05c9e3439deb3e06/url_py-0.18.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:83f9366225fb29ada733d18dddc9d2c0ae3a8e5d31c0d91ba5d4b0edb908bd7d", size = 427132, upload-time = "2025-09-07T16:18:17.686Z" }, { url = "https://files.pythonhosted.org/packages/59/d3/9eb2aa144ca2fccbdf88ce877a42c3fa423ed2265faf780897103aafb5ce/url_py-0.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:25ac96a7ff0ae6625dc53bc46c8afbd6f819284e6f543f46a7f2961c69482945", size = 572834, upload-time = "2025-09-07T16:18:19.374Z" }, { url = "https://files.pythonhosted.org/packages/52/a3/98f2a79840bdb243c5ff5f51b78b1f1465a3cff2554e09be0236ce6bd131/url_py-0.18.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b9594779e81fcc0e015b09e5c6017c69fc1c99e4dd78e191fc64d057414d1e8", size = 599412, upload-time = "2025-09-07T16:18:20.804Z" }, { url = "https://files.pythonhosted.org/packages/df/35/7e8a35f3860cab3d9b8472ae6ab69df4f3a88148ce4103170c6a51ad4e6a/url_py-0.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1d1020876d9834615f0208735bb7988bc9fc30b8a599b4224bce422e0f5c7829", size = 570699, upload-time = "2025-09-07T16:18:23.176Z" }, { url = "https://files.pythonhosted.org/packages/32/82/547082fecbd3864236bff43cafd647b570430d0b12c77e6173d6074a02ed/url_py-0.18.0-cp313-cp313-win32.whl", hash = "sha256:955f735a4b4f4709dc9e1e8d2ac09e52c288686069af8811478759496c00e947", size = 242043, upload-time = "2025-09-07T16:18:24.74Z" }, { url = "https://files.pythonhosted.org/packages/63/41/734280a22374f0090128452cf83821b7b33880978f23a72222c590e8ad78/url_py-0.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:6dfa6694253d5f900c18265f4ebc9684d179bca0c14fbbd8b99042757bc4fc7b", size = 251555, upload-time = "2025-09-07T16:18:26.35Z" }, { url = "https://files.pythonhosted.org/packages/5e/66/d17a8fc71de7e916bd5af4df127184564e1f4df2725b33b433e989e833a8/url_py-0.18.0-cp313-cp313-win_arm64.whl", hash = "sha256:7222ca76cb2b1fed633d2b63f42b1d625fe9cd7cee42e1bae26293068d6e2f10", size = 241164, upload-time = "2025-09-07T16:18:27.566Z" }, { url = "https://files.pythonhosted.org/packages/7b/11/23c5664e08133b491683f75dbcd2b9a9061a2d220963d12d8c58d16c04a3/url_py-0.18.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:be47ef865d04dbc67b829b8e6828c9148c6affc28e56158e373fe8b3170eb840", size = 367277, upload-time = "2025-09-07T16:18:28.818Z" }, { url = "https://files.pythonhosted.org/packages/22/2d/0e36aad92644032d6ca2bbe333fcc6edfde9a5d7827c3d1b91ae6fad8a0b/url_py-0.18.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2eff5c2f55542a98096ab60a41e9b67c8e5923847c3b02fecb724aa606f88449", size = 356060, upload-time = "2025-09-07T16:18:30.094Z" }, { url = "https://files.pythonhosted.org/packages/9f/c1/08a96bc7de9f9556c29eecb9515c2bcad3d64dec4c2b862762f6703d40ad/url_py-0.18.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:212dea4210ab8aef36b0f5f29e0522e9e28dddbaba52c483df98e75b231f3394", size = 394476, upload-time = "2025-09-07T16:18:31.887Z" }, { url = "https://files.pythonhosted.org/packages/cf/d8/c8bbd8c5e5aa7546fee3eb379c83eb25c9bc05dde59f23c0d485dec9e8c5/url_py-0.18.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be2d989c8228cd4518d406dcbbe9bf3c90a5de10eb9961171067648f91ae8368", size = 402035, upload-time = "2025-09-07T16:18:33.209Z" }, { url = "https://files.pythonhosted.org/packages/44/9d/425395df63a9e6fd2f94012e6ac4770c4af8c124f8b2cd3c49acc52680be/url_py-0.18.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2cede2c6c27c5c2743591205e7d17c1aaad96cd5713764159ece9a03db935d0", size = 542917, upload-time = "2025-09-07T16:18:34.613Z" }, { url = "https://files.pythonhosted.org/packages/a7/e0/fc241f5bd153ecfdaecc5218fd133d273aa5c9cdbe95acb5912e01fb73cc/url_py-0.18.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b46aa959f8b43d315ca11c9f12f210ee18f147d0aab6a175d14696497a5c94c5", size = 436371, upload-time = "2025-09-07T16:18:36.111Z" }, { url = "https://files.pythonhosted.org/packages/4e/c5/4a6a11e3a57cbc4a4321879b33f3c3e3891f5730240ca56973757433419b/url_py-0.18.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:327ef1d6f3c20fe9f8cfbeb251156d933baa56d436a7dd1c6007802b2e601192", size = 400875, upload-time = "2025-09-07T16:18:37.391Z" }, { url = "https://files.pythonhosted.org/packages/dc/f4/9160939040d2bdbdcc7bdc0c51b71665521a9785947d6098e421db218ff2/url_py-0.18.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:3b3f41715f41f7b6020bff3954dba2b19af0dd9d7b04505eb4453880e17e4b31", size = 414435, upload-time = "2025-09-07T16:18:38.842Z" }, { url = "https://files.pythonhosted.org/packages/8a/b1/f1c14a963686e4db2ab62ad1005ef36d3670c1447e1ed07ae9138c175ea5/url_py-0.18.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a73ca47bef112e2033b20d257b7952e9b5b01ae2a7b1bcb9b55d5782f164bf1", size = 427151, upload-time = "2025-09-07T16:18:40.175Z" }, { url = "https://files.pythonhosted.org/packages/e9/51/9550401ecc4bf8f5b3f3726f4d2e892e9889a5b32dc365e19e38c083b624/url_py-0.18.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:23ddcaf5ea1ccfd1d158c32dd71f58968b020836df0a5ace07f3e8ae33e34ed1", size = 573009, upload-time = "2025-09-07T16:18:41.981Z" }, { url = "https://files.pythonhosted.org/packages/69/5a/b5b22045d55effcbfd4c7f3a94d683849060100b47c5ba7fbddc0679a26c/url_py-0.18.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2e5c79a54b331f6f289505c5fee8732b72b3f75fdde337c17757fad180260736", size = 599441, upload-time = "2025-09-07T16:18:43.902Z" }, { url = "https://files.pythonhosted.org/packages/13/04/018d409a8c3ce841ef20ca3b46947b167378906913a83aeab49de1665160/url_py-0.18.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5d05246b54448c0793bf97f7f025b1eb691f0db8e26af54fc752c43af4ece8d7", size = 570624, upload-time = "2025-09-07T16:18:45.303Z" }, { url = "https://files.pythonhosted.org/packages/05/8c/60287e745ba615b815c0b0fbddc1fab7ab325d45559f78d03d739d97da90/url_py-0.18.0-cp313-cp313t-win32.whl", hash = "sha256:3c87fb957fe894abd1388187bbcd1ff84d64c9a031b677e4508a93d3e346f63f", size = 241685, upload-time = "2025-09-07T16:18:46.553Z" }, { url = "https://files.pythonhosted.org/packages/fb/6a/1ba61b30a1111d6b8a16953dab40ac05193eacfa61705a2cc8c0d71a276c/url_py-0.18.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9ae720e3861982e5a9831037c3b9d7b565439c76903dabf2d1bfae08c22e1c87", size = 251198, upload-time = "2025-09-07T16:18:47.785Z" }, { url = "https://files.pythonhosted.org/packages/db/cc/f9835f17870d5b7052fc27cacc1d3ba5a0f2cbf3d5086d657f2a663fdfad/url_py-0.18.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:bbd071930a1fabb8c1f1fdefc0f4a9dfdcded89c62231b6ea5a894e022b947f9", size = 365050, upload-time = "2025-09-07T16:18:49.447Z" }, { url = "https://files.pythonhosted.org/packages/16/c0/104f852c6f5656757e8c49396de65eb4a7c13db3f60339af4e81493611c3/url_py-0.18.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:41c11ac1282cf73e7158f7d05a6f06a591b147348fd5cd374448261f59d363e8", size = 355201, upload-time = "2025-09-07T16:18:50.673Z" }, { url = "https://files.pythonhosted.org/packages/3c/fe/5c90d506d49795967b278a600397009f593618f0bcfe9019df8a35141238/url_py-0.18.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b11d99c64f3e49d352a6e2aa2c814967345817b44d759fd179b80131a6ca1440", size = 393237, upload-time = "2025-09-07T16:18:52.003Z" }, { url = "https://files.pythonhosted.org/packages/a4/fd/0878c0b9f2405ce09671dbaa37110e34ba34d10571cd964bc173c6143447/url_py-0.18.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1b01dbaa76f2d7c522bbd11a1356dd9cb933edffb795a7c731ee05ebaae98eb", size = 400337, upload-time = "2025-09-07T16:18:53.269Z" }, { url = "https://files.pythonhosted.org/packages/03/85/43101c716aae6d9783f79e6efa678025b4d413919d54ed7f2e5580fd9447/url_py-0.18.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab87b12039311694e7b55adfd53767ceec181e3b86f0ce687174093b3741ed4b", size = 541643, upload-time = "2025-09-07T16:18:55.023Z" }, { url = "https://files.pythonhosted.org/packages/99/84/209e8eb47af6bb8af9b83e747613560010d43f774b2e1295ccde12bc6194/url_py-0.18.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:216b9b2dbaedc73f0b444c15325c5867b0145a4f905f9c4964127c9d0d3965e2", size = 435196, upload-time = "2025-09-07T16:18:56.478Z" }, { url = "https://files.pythonhosted.org/packages/b8/bf/564352c9d0632f77507ed8591cc46a30b107355ae8d8601d9ad98d2e33f6/url_py-0.18.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703dbe202444af13059f9495e48d00413bd4555cc101585c8a54a6d2bdfadba6", size = 398557, upload-time = "2025-09-07T16:18:57.895Z" }, { url = "https://files.pythonhosted.org/packages/c4/ac/035169ba221d4d16d5542427af0cd6fc53075f26d3fa8bda8a6eea9d3997/url_py-0.18.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e193c61a9700daf48882eae828c3c7ce869e2aef47f5d0edad1b44cb1a7026b8", size = 413242, upload-time = "2025-09-07T16:18:59.158Z" }, { url = "https://files.pythonhosted.org/packages/4f/26/f8a3f79a190eec41541db2ce7645bb0011a7adee8eb143000f7705b3337d/url_py-0.18.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56e2345a6d1f085fc8fe928ed1ec26d5e7153cb67971ca3d375bf8f58102f819", size = 425614, upload-time = "2025-09-07T16:19:00.442Z" }, { url = "https://files.pythonhosted.org/packages/cd/bb/74737ca320a6becc896ed5cc85076c454ef0fc3d8522cb049337b594c428/url_py-0.18.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c530aae5313c7c14941e7a2aef4ab2f9e2ba6c10588bbd50feec654a1dd88fe", size = 572267, upload-time = "2025-09-07T16:19:02.296Z" }, { url = "https://files.pythonhosted.org/packages/69/ff/4e042e95fc086241a9ca7d2fd12637d96c369e676805e5d4c2b065584de0/url_py-0.18.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a50a998f6e4c7eb62a275e8ffa956ab120c6712d247b4ee5b17b2fd3c2970510", size = 598682, upload-time = "2025-09-07T16:19:04.06Z" }, { url = "https://files.pythonhosted.org/packages/fc/87/589f1d18d627d1dc51dfa10a6d3b9d3073e3b1c5af5bffa2fccd4954e74c/url_py-0.18.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59f73e56177742e3ce6bf3652e9fb83ccbe2315dc1fa24152b60c9cb5136fe87", size = 568306, upload-time = "2025-09-07T16:19:05.402Z" }, { url = "https://files.pythonhosted.org/packages/66/4a/41d924a22ab8378b16d26a963668e7d2375b975a50b919c1aefa81ebc63e/url_py-0.18.0-cp314-cp314-win32.whl", hash = "sha256:560fc92cf8ac78d8b594992b0eaefd63002aae367a8b27e96089c4b5bb2150ec", size = 241263, upload-time = "2025-09-07T16:19:06.744Z" }, { url = "https://files.pythonhosted.org/packages/c5/07/24d01fc2bb39b2f7c029d014bbf23fe9e4c5605e96dec3d0fa89c69ebd3e/url_py-0.18.0-cp314-cp314-win_amd64.whl", hash = "sha256:9154527dfb7c48211d853f628a20e35de8732412a75a9d64be55a2da5a04921d", size = 250078, upload-time = "2025-09-07T16:19:08.394Z" }, { url = "https://files.pythonhosted.org/packages/7f/50/244fa7aba97e8d5f76600bbfde44181c629380c0755373ce636b9571bd21/url_py-0.18.0-cp314-cp314-win_arm64.whl", hash = "sha256:f64fd91bb747a96046ad4e117332e6b4c0c1ecbbeaf279b5a0c9461bffe93891", size = 241215, upload-time = "2025-09-07T16:19:09.589Z" }, { url = "https://files.pythonhosted.org/packages/ae/07/0af9d36a3faf2d9b833ad6d17ebc420b0cbd15f1e686c091b6ae9f4a5bb9/url_py-0.18.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:b4ba81802f3f725ef172a256100812d6c5ba6a4f600b4a91f986fa5535e448a5", size = 365603, upload-time = "2025-09-07T16:19:10.956Z" }, { url = "https://files.pythonhosted.org/packages/55/17/ee3c1b6286ed5c140a5978a124a705a9255d4b1f74c4bf1d987b140eae40/url_py-0.18.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ce5a329cf028bb1dd9bb806baf14c362178c595cbe8edcb38e52f3e16a623e36", size = 355408, upload-time = "2025-09-07T16:19:12.525Z" }, { url = "https://files.pythonhosted.org/packages/aa/08/07095a7ead1c3ce46c3b884ae3feea707ccd8005a951b32266765faee278/url_py-0.18.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8b0df46f80b38d66eecfbf4dd0649363a738649046e05348d574c605c15326d", size = 393661, upload-time = "2025-09-07T16:19:13.809Z" }, { url = "https://files.pythonhosted.org/packages/0f/ff/60cebe57d821a2100f1d3131740a0875107c2e557c349978c0bc0e84df1a/url_py-0.18.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3825a3f2127e65ebf41efc0f503f892ad92f360b0581c93781c0fb5422fe1b5b", size = 400537, upload-time = "2025-09-07T16:19:15.172Z" }, { url = "https://files.pythonhosted.org/packages/51/56/8561b6985b06aba7086688a52d029f84f8eb455575ac5f47aec41a4a4ea2/url_py-0.18.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd880dd68a562cdc4be6a0bd2308d9b1272b9e62a86e877413b31975d32a5be2", size = 541529, upload-time = "2025-09-07T16:19:16.541Z" }, { url = "https://files.pythonhosted.org/packages/bc/cd/53ee149d3e3ac4d88e009c5356cf46c8ef5e00ec7657fc47fb3579b0b4f1/url_py-0.18.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0771dba049fb140db9ee52bd348e7ba6127fbac13d77c55449198c13b0293581", size = 435087, upload-time = "2025-09-07T16:19:17.864Z" }, { url = "https://files.pythonhosted.org/packages/18/28/c3610e67a00c0c082eb0dce493753253d6fcfdc88473109ccfb03e92b3b9/url_py-0.18.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:951a6b860be6f26120cf18fbba7eab32fe2392c23e9bda00089553f5375eeae3", size = 399368, upload-time = "2025-09-07T16:19:19.18Z" }, { url = "https://files.pythonhosted.org/packages/30/31/25cc95571a7299f22e1f418765406c68d7d23510943e0245eec037b591d9/url_py-0.18.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:da3a837ccd2d0e6c3ca71a0b3b5aaab300ee3a862171327822c17861cd6b509b", size = 412942, upload-time = "2025-09-07T16:19:21.07Z" }, { url = "https://files.pythonhosted.org/packages/52/99/ceceb6b18677d1e3c9c9d9d16e4d5ffc18f1fff05edfccd7ebf0c85a678b/url_py-0.18.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3f44215dc1ab6f2db4322fb7b525e273dc60d4bc1785ea2aee8a00931ffb202f", size = 425982, upload-time = "2025-09-07T16:19:22.573Z" }, { url = "https://files.pythonhosted.org/packages/ea/d8/e2393fd9151e25aedd0b1c49e71e41543b974c397770477055b80cfbc020/url_py-0.18.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:08214df1fdd16e8813e806525592efe6f84d03488b12ef5d6594e3e81deb2c8c", size = 572398, upload-time = "2025-09-07T16:19:24.021Z" }, { url = "https://files.pythonhosted.org/packages/d7/9c/07da6a6a3ab056f48e95ddff06edeac07bbb436a6179950922157d2d9583/url_py-0.18.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1a892b0a4b8824dd04e076aaabaaaeaee702373c6e0b25af6e08cd247b8e20c0", size = 598105, upload-time = "2025-09-07T16:19:25.358Z" }, { url = "https://files.pythonhosted.org/packages/fc/64/fd4af7b2606d3ce2985bf607d16251b222f31f41db7931f3bf703eaa599d/url_py-0.18.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7540258af33e35e94d9f65a4349da3b6fa618a723079f03100a0b3d36948758b", size = 569106, upload-time = "2025-09-07T16:19:26.757Z" }, { url = "https://files.pythonhosted.org/packages/41/2d/108e5981c699b40fabf0d242775fd5df92106fe1f165c437a94f6269cda4/url_py-0.18.0-cp314-cp314t-win32.whl", hash = "sha256:24a097e064ec4f29712e575f5012cb5edc09d61d4cc61f3d7a32d86d03db23b4", size = 240826, upload-time = "2025-09-07T16:19:28.111Z" }, { url = "https://files.pythonhosted.org/packages/05/43/e3bab4ddbd7c0cd9a644343404c2eb7e3e447913d5fdcee6e4fc4267d393/url_py-0.18.0-cp314-cp314t-win_amd64.whl", hash = "sha256:976f71f958bd1ce9dcf89f0f722608bab4d0e12815dd9be1ba36838b1fb1d7e0", size = 249588, upload-time = "2025-09-07T16:19:29.411Z" }, { url = "https://files.pythonhosted.org/packages/4f/11/4659369699b9dad6ee0556cdcb1364f54070f695d8b10762f4bae318fd3b/url_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ccd9d5871ddab202fad390d6ef4c1027fb37fadbf7dba6762c1968b5bd142622", size = 372124, upload-time = "2025-09-07T16:19:52.231Z" }, { url = "https://files.pythonhosted.org/packages/02/7c/013fc39442bd3a24d743f3e080122b8183f306704f8b402800a033b54672/url_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1db840731b0da5a8cc0786f66a1c70cd30dd05e2ea3acc04e18e1604bb34e8d8", size = 361413, upload-time = "2025-09-07T16:19:53.607Z" }, { url = "https://files.pythonhosted.org/packages/ed/0e/71b665f339e45428260ae421869e2484e8f5a6f9662fbd2866d9a5b40e53/url_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0573ddde73add05d8a39a8fa7a7472774439ceb4f31edacd4ccd60f31661a580", size = 395602, upload-time = "2025-09-07T16:19:54.992Z" }, { url = "https://files.pythonhosted.org/packages/05/63/ff6ee0243a1b71ef233508520174b43d6114bb1c435010474329e9cf8454/url_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4fcd905f648ff733f004b2524ffa02d1e2ab2e6429914547dd175b0a42615c67", size = 404248, upload-time = "2025-09-07T16:19:56.403Z" }, { url = "https://files.pythonhosted.org/packages/64/e6/c0b3ec8355649eee9316031f2fbbd72d56bc496f0b8bb5e5667685a8d5ca/url_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6688affa51c210640331b02e872d36e98e768a07a40379495beb05f880b00cc1", size = 543644, upload-time = "2025-09-07T16:19:57.867Z" }, { url = "https://files.pythonhosted.org/packages/0b/4b/38d3538870bd03fc86c54aae0e5b022a34dc08b6dde46a6797a0f5a6c58c/url_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4838f1ffb54558d43c84e130fbb0f2937c90dd0127d2d2c983ada1f460f03a9", size = 436645, upload-time = "2025-09-07T16:19:59.405Z" }, { url = "https://files.pythonhosted.org/packages/cd/b0/934d80f9125bb2a946e02561c66ad66fd801d8341ebf849eb48fe5a303e5/url_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:182e4c78e7103b979352e5c9a357346385841392c5910954296282913560a030", size = 402182, upload-time = "2025-09-07T16:20:00.762Z" }, { url = "https://files.pythonhosted.org/packages/ee/93/c71bd2cb5e699df80cf71ade93db1ff717a580d6c2d2c7dd3f6158cf65de/url_py-0.18.0-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:bdea2de957f4a4ad74f7e9dbeb2239171b74974128cacc5462d405f50e9bb8b6", size = 416096, upload-time = "2025-09-07T16:20:02.292Z" }, { url = "https://files.pythonhosted.org/packages/27/97/5621f82f2cab3bc0f697d31a9f1b1be73edbadbfaa9bab25e30b317a1487/url_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8172e8fe2ea6624d6f883018f5cbb3d324692684a995123417bab68fb9352032", size = 428905, upload-time = "2025-09-07T16:20:03.975Z" }, { url = "https://files.pythonhosted.org/packages/12/42/dbbde0ec328409996e806722600424c583d28121106387491076df019c84/url_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4ee2bbfb7fa6ad4a9cd2fb186564c60a6db816e2e8ef52261549ac3126222f46", size = 573655, upload-time = "2025-09-07T16:20:05.391Z" }, { url = "https://files.pythonhosted.org/packages/8a/28/8a0ac5ad103d2025d72d039dd332ef55ebed0c246c39722ef0a039a117d3/url_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3668d11b20cdcba342804d6bba1ce368a361a1bcea09a6b7764e2317710ce083", size = 601411, upload-time = "2025-09-07T16:20:06.792Z" }, { url = "https://files.pythonhosted.org/packages/ed/a6/fce32c2896493b1942dd63501e927729f1b04a1e23fb966441cce0360645/url_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:b0f443dd06d8fd8b5526dfc9bd2c6856911f2e0e348c0e5b109a58fe6e3afa8f", size = 571823, upload-time = "2025-09-07T16:20:08.633Z" }, { url = "https://files.pythonhosted.org/packages/47/2d/0b2c341ef4c5886ab4d916add439925019644fe40d6c06a5c284c6ff0c9c/url_py-0.18.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48e2890e26a299aceb969fa385973ce0c8f5ce8d72c1dd38a069aacbe56b4083", size = 251532, upload-time = "2025-09-07T16:20:10.07Z" }, { url = "https://files.pythonhosted.org/packages/db/cc/5a59b3e334da4775e2069fe0347df82623130db03a27a3e9d0f97a33397b/url_py-0.18.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e52193a3dd63490f4c1a9f1da5db4f53974b11f214c9aee87050b6251623ebbf", size = 370449, upload-time = "2025-09-07T16:20:11.74Z" }, { url = "https://files.pythonhosted.org/packages/ce/cc/4dc520fd774a94c4e29a6fff7ff6ba8828f506fb67fc39c8b769b302c68b/url_py-0.18.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fad160242aa62641c489a2780be9ec5e2c4903c42e80c1da262a262168c21755", size = 359613, upload-time = "2025-09-07T16:20:13.273Z" }, { url = "https://files.pythonhosted.org/packages/16/40/9a926b987528561997dc9e60fa5ea25ed008190ebc91c91f6695caca13ca/url_py-0.18.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e246646e3f803a086390e131434241626aa7653faab72a4dc2ae171a2100bdc6", size = 394010, upload-time = "2025-09-07T16:20:14.647Z" }, { url = "https://files.pythonhosted.org/packages/c2/16/79e888e3d63b0c2c99a6776289122deefc1a0735033aae84550033ee80d7/url_py-0.18.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f09fe7de1c84b43577f72112157f5c944a6fc2303f3e8dda696cd6a2384faf7", size = 402413, upload-time = "2025-09-07T16:20:16.324Z" }, { url = "https://files.pythonhosted.org/packages/0e/9f/434819ad5a6c3b61c888b4c5b2f60a79ec12248fb3699c9534dab48ab1d3/url_py-0.18.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32c198e2f9aae8e541f65d6d86f8135967a64cc19d158d182edc62a6c591f487", size = 541855, upload-time = "2025-09-07T16:20:17.717Z" }, { url = "https://files.pythonhosted.org/packages/69/5c/560b76b9dbd39c4a027ff8b8c3661b6ac1152f2507b53d46aec4b7ddd1bb/url_py-0.18.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59ca1ee7c4c340f8ef0ce4364e2c48fc6dca5244614bb7013a2c41970447a859", size = 435138, upload-time = "2025-09-07T16:20:19.14Z" }, { url = "https://files.pythonhosted.org/packages/76/09/514291eb09f95b46acc8f9fa9e82cc1594e8c1f8e241bee63885a43dbc02/url_py-0.18.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b012ef89f982bc6b2eeb0784c1a79ba11373fee71cea959db8286a5167e3fa69", size = 400533, upload-time = "2025-09-07T16:20:20.6Z" }, { url = "https://files.pythonhosted.org/packages/18/31/630f4e871f1756a6632d52127b00d34359e97842016a8d86f199d1655c45/url_py-0.18.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:8017ff3964990c6e93d4811f2ff452dd84e57eb67201f03aeac0aac57baaac3f", size = 414148, upload-time = "2025-09-07T16:20:22.074Z" }, { url = "https://files.pythonhosted.org/packages/ac/c1/08f8c9702a1f94649420a3837568544053b1f868c2782eb91b4f281f716c/url_py-0.18.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac5366c584aca99fa3ab1913c14362105328e84625c57fdb82edca74817c066a", size = 427364, upload-time = "2025-09-07T16:20:23.458Z" }, { url = "https://files.pythonhosted.org/packages/9d/69/439d8f935da17361cf56fc8f6bc08bede1375c29b387bce1f48b2e97b5be/url_py-0.18.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ba0e3796ca42c2c5b03b6b2f9ecea2e119773d6cc222aa6f85786127ae9b5ee2", size = 572386, upload-time = "2025-09-07T16:20:24.939Z" }, { url = "https://files.pythonhosted.org/packages/24/70/ad6d2e345f507154511ec456c129dd42100a50b5c1dcd55e02513527d0d8/url_py-0.18.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:65207da494770fcf41bc95a50dd75532f85dffdc775a92806fc731df52f2abaf", size = 599960, upload-time = "2025-09-07T16:20:26.857Z" }, { url = "https://files.pythonhosted.org/packages/d4/4f/c757307a4df0e83cb72294989784eda06f2a7c8e43a427b3d0aaedc6aabe/url_py-0.18.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:bc36ffb743a34553756331e586b68619c52b946197b146554476e90ee34634e2", size = 570022, upload-time = "2025-09-07T16:20:28.499Z" }, ] [[package]] name = "urllib3" version = "2.5.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, ] rpds_py-0.30.0/pyproject.toml0000644000000114271046102023000116050ustar [build-system] requires = ["maturin>=1.9,<2.0"] build-backend = "maturin" [project] name = "rpds-py" description = "Python bindings to Rust's persistent data structures (rpds)" requires-python = ">=3.10" readme = "README.rst" license = "MIT" license-files = ["LICENSE"] keywords = ["data structures", "rust", "persistent"] authors = [ { name = "Julian Berman", email = "Julian+rpds@GrayVines.com" }, ] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Rust", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] dynamic = ["version"] [project.urls] Documentation = "https://rpds.readthedocs.io/" Homepage = "https://github.com/crate-py/rpds" Issues = "https://github.com/crate-py/rpds/issues/" Funding = "https://github.com/sponsors/Julian" Tidelift = "https://tidelift.com/subscription/pkg/pypi-rpds-py?utm_source=pypi-rpds-py&utm_medium=referral&utm_campaign=pypi-link" Source = "https://github.com/crate-py/rpds" Upstream = "https://github.com/orium/rpds" [dependency-groups] docs = [ "furo>=2025.9.25", "pygments-github-lexers>=0.0.5", "sphinx>5", "sphinx-copybutton>=0.5.2", "sphinxcontrib-spelling>5", "sphinxext-opengraph>=0.13.0", "url-py>=0.18.0", ] test = [ "pytest>=9.0.1", "pytest-run-parallel>=0.7.1", ] typing = [ { include-group = "test" }, "pyright>=1.1.407", ] [tool.coverage.html] show_contexts = true skip_covered = false [tool.coverage.run] branch = true dynamic_context = "test_function" [tool.coverage.report] exclude_also = [ "if TYPE_CHECKING:", "\\s*\\.\\.\\.\\s*", ] fail_under = 100 show_missing = true skip_covered = true [tool.doc8] ignore = [ "D000", # see PyCQA/doc8#125 "D001", # one sentence per line, so max length doesn't make sense ] [tool.maturin] features = ["pyo3/extension-module"] [tool.pyright] reportUnnecessaryTypeIgnoreComment = true strict = ["**/*"] exclude = [ "**/tests/__init__.py", "**/tests/test_*.py", ] [tool.ruff] line-length = 79 [tool.ruff.lint] select = ["ALL"] ignore = [ "A001", # It's fine to shadow builtins "A002", "A003", "A005", "ARG", # This is all wrong whenever an interface is involved "ANN", # Just let the type checker do this "B006", # Mutable arguments require care but are OK if you don't abuse them "B008", # It's totally OK to call functions for default arguments. "B904", # raise SomeException(...) is fine. "B905", # No need for explicit strict, this is simply zip's default behavior "C408", # Calling dict is fine when it saves quoting the keys "C901", # Not really something to focus on "D105", # It's fine to not have docstrings for magic methods. "D107", # __init__ especially doesn't need a docstring "D200", # This rule makes diffs uglier when expanding docstrings "D203", # No blank lines before docstrings. "D212", # Start docstrings on the second line. "D400", # This rule misses sassy docstrings ending with ! or ? "D401", # This rule is too flaky. "D406", # Section headers should end with a colon not a newline "D407", # Underlines aren't needed "D412", # Plz spaces after section headers "EM101", # These don't bother me, it's fine there's some duplication. "EM102", "FBT", # It's worth avoiding boolean args but I don't care to enforce it "FIX", # Yes thanks, if I could it wouldn't be there "N", # These naming rules are silly "PLR0912", # These metrics are fine to be aware of but not to enforce "PLR0913", "PLR0915", "PLW2901", # Shadowing for loop variables is occasionally fine. "PT006", # pytest parametrize takes strings as well "PYI025", # wat, I'm not confused, thanks. "RET502", # Returning None implicitly is fine "RET503", "RET505", # These push you to use `if` instead of `elif`, but for no reason "RET506", "RSE102", # Ha, what, who even knew you could leave the parens off. But no. "SIM300", # Not sure what heuristic this uses, but it's easily incorrect "SLF001", # Private usage within this package itself is fine "TD", # These TODO style rules are also silly ] [tool.ruff.lint.flake8-pytest-style] mark-parentheses = false [tool.ruff.lint.flake8-quotes] docstring-quotes = "double" [tool.ruff.lint.isort] combine-as-imports = true from-first = true known-first-party = ["rpds"] [tool.ruff.lint.per-file-ignores] "noxfile.py" = ["ANN", "D100", "S101", "T201"] "docs/*" = ["ANN", "D", "INP001"] "tests/*" = ["ANN", "B018", "D", "PLR", "RUF012", "S", "SIM", "TRY"] rpds_py-0.30.0/PKG-INFO0000644000000100611046102023000077570ustar Metadata-Version: 2.4 Name: rpds-py Version: 0.30.0 Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: Programming Language :: Rust Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3.14 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy License-File: LICENSE Summary: Python bindings to Rust's persistent data structures (rpds) Keywords: data structures,rust,persistent Author-email: Julian Berman License-Expression: MIT Requires-Python: >=3.10 Description-Content-Type: text/x-rst; charset=UTF-8 Project-URL: Documentation, https://rpds.readthedocs.io/ Project-URL: Homepage, https://github.com/crate-py/rpds Project-URL: Issues, https://github.com/crate-py/rpds/issues/ Project-URL: Funding, https://github.com/sponsors/Julian Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-rpds-py?utm_source=pypi-rpds-py&utm_medium=referral&utm_campaign=pypi-link Project-URL: Source, https://github.com/crate-py/rpds Project-URL: Upstream, https://github.com/orium/rpds =========== ``rpds.py`` =========== |PyPI| |Pythons| |CI| .. |PyPI| image:: https://img.shields.io/pypi/v/rpds-py.svg :alt: PyPI version :target: https://pypi.org/project/rpds-py/ .. |Pythons| image:: https://img.shields.io/pypi/pyversions/rpds-py.svg :alt: Supported Python versions :target: https://pypi.org/project/rpds-py/ .. |CI| image:: https://github.com/crate-py/rpds/workflows/CI/badge.svg :alt: Build status :target: https://github.com/crate-py/rpds/actions?query=workflow%3ACI .. |ReadTheDocs| image:: https://readthedocs.org/projects/referencing/badge/?version=stable&style=flat :alt: ReadTheDocs status :target: https://referencing.readthedocs.io/en/stable/ Python bindings to the `Rust rpds crate `_ for persistent data structures. What's here is quite minimal (in transparency, it was written initially to support replacing ``pyrsistent`` in the `referencing library `_). If you see something missing (which is very likely), a PR is definitely welcome to add it. Installation ------------ The distribution on PyPI is named ``rpds.py`` (equivalently ``rpds-py``), and thus can be installed via e.g.: .. code:: sh $ pip install rpds-py Note that if you install ``rpds-py`` from source, you will need a Rust toolchain installed, as it is a build-time dependency. An example of how to do so in a ``Dockerfile`` can be found `here `_. If you believe you are on a common platform which should have wheels built (i.e. and not need to compile from source), feel free to file an issue or pull request modifying the GitHub action used here to build wheels via ``maturin``. Usage ----- Methods in general are named similarly to their ``rpds`` counterparts (rather than ``pyrsistent``\ 's conventions, though probably a full drop-in ``pyrsistent``\ -compatible wrapper module is a good addition at some point). .. code:: python >>> from rpds import HashTrieMap, HashTrieSet, List >>> m = HashTrieMap({"foo": "bar", "baz": "quux"}) >>> m.insert("spam", 37) == HashTrieMap({"foo": "bar", "baz": "quux", "spam": 37}) True >>> m.remove("foo") == HashTrieMap({"baz": "quux"}) True >>> s = HashTrieSet({"foo", "bar", "baz", "quux"}) >>> s.insert("spam") == HashTrieSet({"foo", "bar", "baz", "quux", "spam"}) True >>> s.remove("foo") == HashTrieSet({"bar", "baz", "quux"}) True >>> L = List([1, 3, 5]) >>> L.push_front(-1) == List([-1, 1, 3, 5]) True >>> L.rest == List([3, 5]) True