├── src ├── deflate │ ├── py.typed │ ├── __init__.py │ └── _deflate.pyi └── _deflate.c ├── .clang-format ├── .gitmodules ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── build.yml ├── .pre-commit-config.yaml ├── LICENSE ├── CMakeLists.txt ├── tests ├── test_crc32.py ├── test_adler32.py └── test_compress.py ├── pyproject.toml ├── README.md ├── CHANGELOG.md └── uv.lock /src/deflate/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | ColumnLimit: 88 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libdeflate"] 2 | path = libdeflate 3 | url = https://github.com/ebiggers/libdeflate 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | *.so 4 | __pycache__ 5 | .DS_Store 6 | /dist 7 | /build 8 | /wheelhouse 9 | /pip-wheel-metadata 10 | .vscode 11 | .venv 12 | .nova 13 | .python-version 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "monthly" 8 | groups: 9 | actions: 10 | patterns: 11 | - "*" 12 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/astral-sh/ruff-pre-commit 3 | # Ruff version. 4 | rev: v0.4.3 5 | hooks: 6 | # Run the linter. 7 | - id: ruff 8 | args: ["--fix"] 9 | # Run the formatter. 10 | - id: ruff-format 11 | - repo: https://github.com/pre-commit/mirrors-clang-format 12 | rev: v18.1.5 13 | hooks: 14 | - id: clang-format 15 | -------------------------------------------------------------------------------- /src/deflate/__init__.py: -------------------------------------------------------------------------------- 1 | from ._deflate import ( 2 | DeflateError, 3 | __version__, 4 | adler32, 5 | crc32, 6 | deflate_compress, 7 | deflate_decompress, 8 | gzip_compress, 9 | gzip_decompress, 10 | zlib_compress, 11 | zlib_decompress, 12 | ) 13 | 14 | __all__ = [ 15 | "DeflateError", 16 | "__version__", 17 | "adler32", 18 | "crc32", 19 | "deflate_compress", 20 | "deflate_decompress", 21 | "gzip_compress", 22 | "gzip_decompress", 23 | "zlib_compress", 24 | "zlib_decompress", 25 | ] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dan Watson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.26...3.29) 2 | project(${SKBUILD_PROJECT_NAME} LANGUAGES C) 3 | 4 | find_package(Python REQUIRED COMPONENTS Interpreter Development.Module ${SKBUILD_SABI_COMPONENT}) 5 | 6 | if("${SKBUILD_SABI_COMPONENT}" STREQUAL "") 7 | python_add_library(_deflate MODULE WITH_SOABI src/_deflate.c) 8 | else() 9 | python_add_library(_deflate MODULE WITH_SOABI USE_SABI 3.11 src/_deflate.c) 10 | endif() 11 | 12 | if(DEFINED ENV{LIBDEFLATE_PREFIX}) 13 | message(STATUS "Finding libdeflate in $ENV{LIBDEFLATE_PREFIX}") 14 | target_include_directories(_deflate PUBLIC "$ENV{LIBDEFLATE_PREFIX}/include") 15 | target_link_directories(_deflate PUBLIC "$ENV{LIBDEFLATE_PREFIX}/lib") 16 | target_link_libraries(_deflate PRIVATE deflate) 17 | else() 18 | message(STATUS "Building and linking bundled libdeflate") 19 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 20 | set(LIBDEFLATE_BUILD_SHARED_LIB OFF) 21 | add_subdirectory(libdeflate EXCLUDE_FROM_ALL) 22 | target_link_libraries(_deflate PRIVATE libdeflate_static) 23 | endif() 24 | 25 | install(TARGETS _deflate LIBRARY DESTINATION ${SKBUILD_PROJECT_NAME}) 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # badge: https://github.com/dcwatson/deflate/workflows/CI/badge.svg?branch=main 2 | 3 | name: CI 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | env: 14 | UV_SYSTEM_PYTHON: 1 15 | UV_PYTHON_DOWNLOADS: never 16 | UV_PYTHON_PREFERENCE: only-system 17 | 18 | jobs: 19 | checks: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v5 23 | - uses: astral-sh/ruff-action@v3 24 | 25 | test: 26 | needs: checks 27 | strategy: 28 | matrix: 29 | os: ["ubuntu-latest"] 30 | python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "pypy3.10"] 31 | runs-on: ${{ matrix.os }} 32 | steps: 33 | - uses: actions/checkout@v5 34 | with: 35 | submodules: 'recursive' 36 | - name: Set up Python ${{ matrix.python-version }} 37 | uses: actions/setup-python@v6 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | allow-prereleases: true 41 | - name: Setup uv 42 | uses: astral-sh/setup-uv@v6 43 | - name: Run Tests 44 | run: uv run pytest 45 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build_wheels: 11 | name: Build wheels on ${{ matrix.os }} 12 | runs-on: ${{ matrix.os }} 13 | timeout-minutes: 30 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-14] 17 | 18 | steps: 19 | - uses: actions/checkout@v5 20 | with: 21 | submodules: 'recursive' 22 | 23 | - name: Build wheels 24 | uses: pypa/cibuildwheel@v3.2.1 25 | 26 | - uses: actions/upload-artifact@v4 27 | with: 28 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} 29 | path: ./wheelhouse/*.whl 30 | 31 | build_sdist: 32 | name: Build source distribution 33 | runs-on: ubuntu-latest 34 | timeout-minutes: 10 35 | steps: 36 | - uses: actions/checkout@v5 37 | with: 38 | submodules: 'recursive' 39 | 40 | - name: Build sdist 41 | run: pipx run build --sdist 42 | 43 | - uses: actions/upload-artifact@v4 44 | with: 45 | name: cibw-sdist 46 | path: dist/*.tar.gz 47 | -------------------------------------------------------------------------------- /tests/test_crc32.py: -------------------------------------------------------------------------------- 1 | # test crc32 function 2 | 3 | import os 4 | import zlib 5 | 6 | import pytest 7 | 8 | import deflate 9 | 10 | 11 | @pytest.mark.parametrize( 12 | "data", 13 | [ 14 | b"", 15 | b"\0", 16 | b"\0" * 23, 17 | b"foobar", 18 | b"foobar" * 42, 19 | os.urandom(50), 20 | ], 21 | ) 22 | def test_crc32_simple(data): 23 | """test whether crc32 function computes correctly (reference: zlib.crc32)""" 24 | assert deflate.crc32(data) == zlib.crc32(data) 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "data", 29 | [ 30 | b"", 31 | b"\0", 32 | b"\0" * 23, 33 | b"foobar", 34 | b"foobar" * 42, 35 | os.urandom(50), 36 | ], 37 | ) 38 | def test_crc32_with_start_value(data): 39 | """test whether crc32 function computes correctly, 40 | with start value (reference: zlib.crc32)""" 41 | # 0 is the default start value 42 | assert deflate.crc32(data, 0) == deflate.crc32(data) 43 | # 0 start value behaviour as in zlib 44 | assert deflate.crc32(data, 0) == zlib.crc32(data, 0) 45 | # "random" start value behaviour as in zlib 46 | assert deflate.crc32(data, deflate.crc32(data, 0)) == zlib.crc32( 47 | data, zlib.crc32(data, 0) 48 | ) 49 | # continued crc32 computation yields same result as in one go 50 | assert deflate.crc32(data, deflate.crc32(data, 0)) == deflate.crc32(data * 2, 0) 51 | -------------------------------------------------------------------------------- /tests/test_adler32.py: -------------------------------------------------------------------------------- 1 | # test adler32 function 2 | 3 | import os 4 | import zlib 5 | 6 | import pytest 7 | 8 | import deflate 9 | 10 | 11 | @pytest.mark.parametrize( 12 | "data", 13 | [ 14 | b"", 15 | b"\0", 16 | b"\0" * 23, 17 | b"foobar", 18 | b"foobar" * 42, 19 | os.urandom(50), 20 | ], 21 | ) 22 | def test_adler_simple(data): 23 | """test whether adler32 function computes correctly (reference: zlib.adler32)""" 24 | assert deflate.adler32(data) == zlib.adler32(data) 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "data", 29 | [ 30 | b"", 31 | b"\0", 32 | b"\0" * 23, 33 | b"foobar", 34 | b"foobar" * 42, 35 | os.urandom(50), 36 | ], 37 | ) 38 | def test_adler32_with_start_value(data): 39 | """test whether adler32 function computes correctly, 40 | with start value (reference: zlib.adler32)""" 41 | # 1 is the default start value 42 | assert deflate.adler32(data, 1) == deflate.adler32(data) 43 | # 1 start value behaviour as in zlib 44 | assert deflate.adler32(data, 1) == zlib.adler32(data, 1) 45 | # "random" start value behaviour as in zlib 46 | assert deflate.adler32(data, deflate.adler32(data, 0)) == zlib.adler32( 47 | data, zlib.adler32(data, 0) 48 | ) 49 | # continued adler32 computation yields same result as in one go 50 | assert deflate.adler32(data, deflate.adler32(data, 0)) == deflate.adler32( 51 | data * 2, 0 52 | ) 53 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["scikit-build-core>=0.9"] 3 | build-backend = "scikit_build_core.build" 4 | 5 | [project] 6 | name = "deflate" 7 | dynamic = ["version"] 8 | description = "Python wrapper for libdeflate." 9 | readme = "README.md" 10 | requires-python = ">=3.9" 11 | dependencies = [] 12 | authors = [ 13 | { name = "Dan Watson", email = "dcwatson@gmail.com" }, 14 | ] 15 | classifiers = [ 16 | "Development Status :: 4 - Beta", 17 | "Intended Audience :: Developers", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: MacOS :: MacOS X", 20 | "Operating System :: Microsoft :: Windows", 21 | "Operating System :: POSIX :: Linux", 22 | "Programming Language :: Python", 23 | "Programming Language :: Python :: 3", 24 | "Programming Language :: Python :: 3.9", 25 | "Programming Language :: Python :: 3.10", 26 | "Programming Language :: Python :: 3.11", 27 | "Programming Language :: Python :: 3.12", 28 | "Programming Language :: Python :: 3.13", 29 | "Topic :: Software Development :: Libraries", 30 | "Topic :: System :: Archiving :: Compression", 31 | ] 32 | 33 | [project.urls] 34 | Homepage = "https://github.com/dcwatson/deflate" 35 | 36 | [project.optional-dependencies] 37 | test = ["pytest>=7"] 38 | 39 | [dependency-groups] 40 | dev = [ 41 | "pytest>=7", 42 | "pre-commit~=3.5.0", 43 | ] 44 | 45 | [tool.scikit-build] 46 | minimum-version = "0.9" 47 | wheel.py-api = "cp311" 48 | cmake.version = ">=3.26" 49 | 50 | [tool.scikit-build.metadata.version] 51 | provider = "scikit_build_core.metadata.regex" 52 | input = "src/_deflate.c" 53 | regex = '.*#define MODULE_VERSION "(?P.*)"' 54 | 55 | [tool.ruff] 56 | extend-exclude = ["libdeflate"] 57 | 58 | [tool.ruff.lint] 59 | extend-select = ["B", "I"] 60 | isort.known-first-party = ["deflate"] 61 | 62 | [tool.cibuildwheel] 63 | test-extras = ["test"] 64 | test-command = "pytest {package}/tests" 65 | -------------------------------------------------------------------------------- /src/deflate/_deflate.pyi: -------------------------------------------------------------------------------- 1 | from collections.abc import Buffer 2 | 3 | # This is what it was before 3.12 4 | # Buffer = Union[bytearray, memoryview, array.array[Any], mmap.mmap, ctypes._CData] 5 | 6 | __version__: str 7 | 8 | class DeflateError(Exception): ... 9 | 10 | def adler32(data: Buffer, initial: int = 1) -> int: 11 | """ 12 | Computes the Adler-32 checksum of `data`, using `initial` as the initial value. 13 | """ 14 | ... 15 | 16 | def crc32(data: Buffer, initial: int = 0) -> int: 17 | """ 18 | Computes the CRC-32 checksum of `data`, using `initial` as the initial value. 19 | """ 20 | ... 21 | 22 | def deflate_compress(data: Buffer, compresslevel: int = 6) -> bytearray: 23 | """ 24 | Compresses `data` using DEFLATE. `compresslevel` must be between 1 and 12. 25 | """ 26 | ... 27 | 28 | def deflate_decompress(data: Buffer, originalsize: int) -> bytearray: 29 | """ 30 | Decompresses `data` using DEFLATE. `originalsize` must be (at least) the length of 31 | the original uncompressed data. 32 | """ 33 | ... 34 | 35 | def gzip_compress(data: Buffer, compresslevel: int = 6) -> bytearray: 36 | """ 37 | Compresses `data` using GZip. `compresslevel` must be between 1 and 12. 38 | """ 39 | ... 40 | 41 | def gzip_decompress(data: Buffer, originalsize: int = 0) -> bytearray: 42 | """ 43 | Decompresses `data` using GZip. `originalsize`, if specified, must be (at least) 44 | the length of the original uncompressed data. If unspecified or 0, the original size 45 | will be parsed from the GZip data footer. 46 | """ 47 | ... 48 | 49 | def zlib_compress(data: Buffer, compresslevel: int = 6) -> bytearray: 50 | """ 51 | Compresses `data` using zlib. `compresslevel` must be between 1 and 12. 52 | """ 53 | ... 54 | 55 | def zlib_decompress(data: Buffer, originalsize: int) -> bytearray: 56 | """ 57 | Decompresses `data` using zlib. `originalsize` must be (at least) the length of 58 | the original uncompressed data. 59 | """ 60 | ... 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deflate API 2 | 3 | This is a very thin Python wrapper Eric Biggers' excellent 4 | [libdeflate](https://github.com/ebiggers/libdeflate). 5 | 6 | Currently, it handles: 7 | 8 | ## Compression and decompression of gzip data, with a very basic API 9 | 10 | ```python 11 | import deflate 12 | level = 6 # The default; may be 1-12 for libdeflate. 13 | compressed = deflate.gzip_compress(b"hello world!" * 1000, level) 14 | original = deflate.gzip_decompress(compressed) 15 | ``` 16 | 17 | ## Compression and decompression of raw DEFLATE or zlib data 18 | 19 | The original size of the decompressed data needs to be kept through additional logic. 20 | 21 | ```python 22 | import deflate 23 | level = 6 # The default; may be 1-12 for libdeflate. 24 | data = b"hello world!" * 1000 25 | # DEFLATE 26 | compressed = deflate.deflate_compress(data, level) 27 | original = deflate.deflate_decompress(compressed, len(data)) 28 | # zlib 29 | compressed = deflate.zlib_compress(data, level) 30 | original = deflate.zlib_decompress(compressed, len(data)) 31 | ``` 32 | 33 | ## CRC32 computation 34 | 35 | ```python 36 | import deflate 37 | crc32 = deflate.crc32(b"hello world! ") # initial 38 | crc32 = deflate.crc32(b"hello universe!", crc32) # continued 39 | ``` 40 | 41 | ## Adler-32 computation 42 | 43 | ```python 44 | import deflate 45 | adler32 = deflate.adler32(b"hello world! ") # initial 46 | adler32 = deflate.adler32(b"hello universe!", adler32) # continued 47 | ``` 48 | 49 | # Installation 50 | 51 | ```bash 52 | pip install deflate 53 | ``` 54 | 55 | By default, `deflate` will compile and statically link the bundled `libdeflate` when you 56 | build from source (or use the pre-compiled wheels). To link to a system-installed `libdeflate`, set the 57 | `LIBDEFLATE_PREFIX` environment variable and build from source: 58 | 59 | ``` 60 | LIBDEFLATE_PREFIX=/opt/homebrew/Cellar/libdeflate/1.20 pip install deflate --no-binary=deflate 61 | ``` 62 | 63 | Be warned: you can't use this wheel on a system without the referenced libdeflate. 64 | 65 | # Testing 66 | 67 | ```bash 68 | pip install -r requirements-dev.lock 69 | python -m pytest 70 | ``` 71 | 72 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.9.0 (in development) 2 | 3 | * Build and test on Python 3.14 4 | * Drop support for Python 3.9 5 | 6 | 7 | ## 0.8.1 (2025-07-18) 8 | 9 | * Updated bundled libdeflate to v1.24 10 | * Allow `compresslevel=0` (since `libdeflate` does) 11 | 12 | 13 | ## 0.8.0 (2025-03-29) 14 | 15 | * Use only the [limited Python API](https://docs.python.org/3/c-api/stable.html) 16 | * Use the Stable ABI for Python 3.11+ 17 | * Return [bytearray](https://docs.python.org/3/library/stdtypes.html#bytearray) from compression and decompression methods to avoid copying (resizing bytes is private API) 18 | * Added typing stubs (and a shell python package to hold them and the renamed `_deflate` compiled extension) (#49) 19 | * Added support and compiled wheels for PyPy. 20 | * Updated bundled libdeflate to v1.23 21 | * Drop support for Python 3.8 22 | 23 | 24 | ## 0.7.0 (2024-05-09) 25 | 26 | * Switched to [scikit-build-core](https://github.com/scikit-build/scikit-build-core) and CMake (#42, #43) - huge thanks to @henryiii 27 | * Raise `ValueError` instead of `DeflateError` on invalid gzip data 28 | * Lots of internal refactoring 29 | 30 | 31 | ## 0.6.1 (2024-05-06) 32 | 33 | * Fixed broken wheels on macOS arm64 platforms 34 | 35 | 36 | ## 0.6.0 (2024-04-20, yanked) 37 | 38 | * Require `originalsize` argument for `deflate_decompress` and `zlib_decompress` 39 | * Updated bundled libdeflate to v1.20 40 | 41 | 42 | ## 0.5.0 (2023-12-20) 43 | 44 | * Added raw DEFLATE functionality from libdeflate (#39) 45 | * Added zlib functionality from libdeflate (#2) 46 | * Updated bundled libdeflate to v1.19 47 | * Test on and build wheels for Python 3.12 (#40) 48 | 49 | 50 | ## 0.4.0 (2023-06-29) 51 | 52 | * Eliminate unnecessary allocation/copy in compression and decompression (#10) 53 | * add DeflateError object to module, fixes related ImportError (#14) 54 | * add deflate.crc32 api, docs, tests (#11) 55 | * update bundled code to libdeflate v1.18 (#38) 56 | * prefer system libdeflate (via pkgconfig) over bundled code, see the docs 57 | about how to influence this behaviour via environment variables (#29) 58 | * setup.py: add pypi metadata, require python >= 3.8 59 | * benchmark deflate.crc32 against zlib.crc32 using pytest-benchmark 60 | * add tests (using pytest), flake8 linter, CI via github actions (#16, #13) 61 | 62 | 63 | ## 0.3.0 (2020-12-14) 64 | 65 | * Compile libdeflate directly instead of trying to build/link it 66 | * Actual working Windows binary wheels 67 | * Change the default compression level to 6 to match zlib 68 | 69 | 70 | ## 0.2.0 (2020-11-19) 71 | 72 | * Compile libdeflate with `-fPIC` (#5) 73 | * Automatically build wheels for macOS, Linux, and Windows (#3) 74 | * Fixed a memory leak (#6) 75 | * Experimental support for building a universal library on Apple Silicon 76 | 77 | 78 | ## 0.1.0 (2020-06-17) 79 | 80 | * Initial release 81 | -------------------------------------------------------------------------------- /tests/test_compress.py: -------------------------------------------------------------------------------- 1 | # test compress / decompress functionality 2 | 3 | import array 4 | import gzip 5 | import os 6 | import zlib 7 | 8 | import pytest 9 | 10 | import deflate 11 | 12 | 13 | @pytest.mark.parametrize( 14 | "data", 15 | [ 16 | b"", 17 | b"\0", 18 | b"\0" * 23, 19 | b"foobar", 20 | b"foobar" * 42, 21 | os.urandom(500), 22 | bytearray(b"hello world") * 100, 23 | memoryview(b"hello world" * 100), 24 | ], 25 | ) 26 | def test_roundtrip(data): 27 | """test whether decompressing compressed data yields the original data""" 28 | assert deflate.gzip_decompress(deflate.gzip_compress(data)) == data 29 | assert deflate.gzip_decompress(gzip.compress(data)) == data 30 | assert gzip.decompress(deflate.gzip_compress(data)) == data 31 | 32 | assert deflate.zlib_decompress(deflate.zlib_compress(data), len(data)) == data 33 | assert deflate.zlib_decompress(zlib.compress(data), len(data)) == data 34 | assert zlib.decompress(deflate.zlib_compress(data)) == data 35 | 36 | assert deflate.deflate_decompress(deflate.deflate_compress(data), len(data)) == data 37 | 38 | 39 | def test_array(): 40 | """test roundtripping compressed array.array values""" 41 | a = array.array("d", [-42.0, 1.0, 2.0, 3.14, float("inf"), float("-inf")]) 42 | c = deflate.gzip_compress(a) 43 | b = array.array("d", deflate.gzip_decompress(c)) 44 | assert a == b 45 | 46 | 47 | @pytest.mark.parametrize("compresslevel", range(0, 12 + 1)) 48 | def test_shorter(compresslevel): 49 | """tests whether compressed data is actually shorter than original data and 50 | also uses all supported compresslevels""" 51 | data = b"foobar" * 123 52 | len_original = len(data) 53 | len_compressed = len(deflate.gzip_compress(data, compresslevel)) 54 | if compresslevel > 0: 55 | assert len_compressed < len_original 56 | else: 57 | assert len_compressed > len_original 58 | 59 | 60 | @pytest.mark.parametrize("compresslevel", [-1, 13, 256, 65536]) 61 | def test_unsupported_compresslevel(compresslevel): 62 | """test if compresslevels outside of supported range raise ValueError""" 63 | with pytest.raises(ValueError): 64 | deflate.gzip_compress(b"foobar", compresslevel) 65 | 66 | 67 | @pytest.mark.parametrize( 68 | "compressed", 69 | [ 70 | b"", 71 | b"\0", 72 | b"\0" * 23, 73 | b"foobar", 74 | b"foobar" * 42, 75 | ], 76 | ) 77 | def test_invalid_decompression(compressed): 78 | """test whether giving invalid data to decompress raises ValueError""" 79 | with pytest.raises(ValueError): 80 | deflate.gzip_decompress(compressed) 81 | 82 | 83 | def test_decompress_resize(): 84 | """test that decompressing with overestimated originalsize works, and fails when 85 | originalsize is too small""" 86 | block = os.urandom(4096) 87 | compressed = deflate.gzip_compress(block) 88 | result = deflate.gzip_decompress(compressed, 5000) 89 | assert block == result 90 | with pytest.raises(deflate.DeflateError): 91 | deflate.gzip_decompress(compressed, 3000) 92 | -------------------------------------------------------------------------------- /src/_deflate.c: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | // If Py_LIMITED_API defined (to 0x030b00f0), will use Stable ABI 3 | 4 | #include 5 | 6 | #include "libdeflate.h" 7 | 8 | #define MODULE_VERSION "0.9.0" 9 | 10 | static PyObject *DeflateError; 11 | 12 | typedef size_t (*CompressFunc)(struct libdeflate_compressor *, const void *, size_t, 13 | void *, size_t); 14 | typedef enum libdeflate_result (*DecompressFunc)(struct libdeflate_decompressor *, 15 | const void *, size_t, void *, size_t, 16 | size_t *); 17 | typedef size_t (*BoundFunc)(struct libdeflate_compressor *, size_t); 18 | 19 | static PyObject *compress(Py_buffer *data, int compression_level, 20 | CompressFunc compressfunc, BoundFunc boundfunc) { 21 | if (compression_level < 0 || compression_level > 12) { 22 | PyErr_SetString(PyExc_ValueError, "compresslevel must be between 0 and 12"); 23 | return NULL; 24 | } 25 | 26 | struct libdeflate_compressor *compressor = 27 | libdeflate_alloc_compressor(compression_level); 28 | size_t bound = (*boundfunc)(compressor, data->len); 29 | 30 | PyObject *bytes = PyByteArray_FromStringAndSize(NULL, bound); 31 | if (bytes == NULL) { 32 | libdeflate_free_compressor(compressor); 33 | return PyErr_NoMemory(); 34 | } 35 | 36 | size_t compressed_size = (*compressfunc)(compressor, data->buf, data->len, 37 | PyByteArray_AsString(bytes), bound); 38 | libdeflate_free_compressor(compressor); 39 | 40 | if (compressed_size == 0) { 41 | Py_DecRef(bytes); 42 | PyErr_SetString(DeflateError, "Compression failed"); 43 | return NULL; 44 | } 45 | 46 | if (compressed_size != bound) { 47 | PyByteArray_Resize(bytes, compressed_size); 48 | } 49 | 50 | return bytes; 51 | } 52 | 53 | static PyObject *decompress(Py_buffer *data, unsigned int originalsize, 54 | DecompressFunc decompressfunc) { 55 | // Nothing in, nothing out. 56 | if (originalsize == 0) { 57 | return PyByteArray_FromStringAndSize(NULL, 0); 58 | } 59 | 60 | PyObject *output = PyByteArray_FromStringAndSize(NULL, originalsize); 61 | if (output == NULL) { 62 | return PyErr_NoMemory(); 63 | } 64 | 65 | size_t decompressed_size; 66 | struct libdeflate_decompressor *decompressor = libdeflate_alloc_decompressor(); 67 | enum libdeflate_result result = (*decompressfunc)( 68 | decompressor, data->buf, data->len, PyByteArray_AsString(output), originalsize, 69 | &decompressed_size); 70 | libdeflate_free_decompressor(decompressor); 71 | 72 | if (result != LIBDEFLATE_SUCCESS) { 73 | Py_DecRef(output); 74 | PyErr_SetString(DeflateError, "Decompression failed"); 75 | return NULL; 76 | } 77 | 78 | if (decompressed_size != originalsize) { 79 | PyByteArray_Resize(output, decompressed_size); 80 | } 81 | 82 | return output; 83 | } 84 | 85 | /* GZIP */ 86 | 87 | static int read_gzip_size(Py_buffer *data, unsigned int *outsize) { 88 | uint8_t *bytes = (uint8_t *)data->buf; 89 | if ((data->len < 6) || (bytes[0] != 0x1F || bytes[1] != 0x8B)) { 90 | return -1; 91 | } 92 | 93 | // The last 4 bytes of a gzip archive are the original data size, in little endian. 94 | bytes += (data->len - 4); 95 | (*outsize) = ((uint32_t)bytes[0] << 0) | ((uint32_t)bytes[1] << 8) | 96 | ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); 97 | 98 | return 0; 99 | } 100 | 101 | static PyObject *deflate_gzip_compress(PyObject *self, PyObject *args, 102 | PyObject *kwargs) { 103 | static char *keywords[] = {"data", "compresslevel", NULL}; 104 | Py_buffer data; 105 | int compression_level = 6; 106 | 107 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|i", keywords, &data, 108 | &compression_level)) { 109 | return NULL; 110 | } 111 | 112 | PyObject *obj = compress(&data, compression_level, libdeflate_gzip_compress, 113 | libdeflate_gzip_compress_bound); 114 | PyBuffer_Release(&data); 115 | return obj; 116 | } 117 | 118 | static PyObject *deflate_gzip_decompress(PyObject *self, PyObject *args, 119 | PyObject *kwargs) { 120 | static char *keywords[] = {"data", "originalsize", NULL}; 121 | Py_buffer data; 122 | unsigned int originalsize = 0; 123 | 124 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|I", keywords, &data, 125 | &originalsize)) { 126 | return NULL; 127 | } 128 | 129 | if (originalsize == 0) { 130 | if (read_gzip_size(&data, &originalsize) != 0) { 131 | PyBuffer_Release(&data); 132 | PyErr_SetString(PyExc_ValueError, "Invalid gzip data"); 133 | return NULL; 134 | } 135 | } 136 | 137 | PyObject *obj = decompress(&data, originalsize, libdeflate_gzip_decompress); 138 | PyBuffer_Release(&data); 139 | return obj; 140 | } 141 | 142 | /* DEFLATE */ 143 | 144 | static PyObject *deflate_deflate_compress(PyObject *self, PyObject *args, 145 | PyObject *kwargs) { 146 | static char *keywords[] = {"data", "compresslevel", NULL}; 147 | Py_buffer data; 148 | int compression_level = 6; 149 | 150 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|i", keywords, &data, 151 | &compression_level)) { 152 | return NULL; 153 | } 154 | 155 | PyObject *obj = compress(&data, compression_level, libdeflate_deflate_compress, 156 | libdeflate_deflate_compress_bound); 157 | PyBuffer_Release(&data); 158 | return obj; 159 | } 160 | 161 | static PyObject *deflate_deflate_decompress(PyObject *self, PyObject *args, 162 | PyObject *kwargs) { 163 | static char *keywords[] = {"data", "originalsize", NULL}; 164 | Py_buffer data; 165 | unsigned int originalsize = 0; 166 | 167 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*I", keywords, &data, 168 | &originalsize)) { 169 | return NULL; 170 | } 171 | 172 | PyObject *obj = decompress(&data, originalsize, libdeflate_deflate_decompress); 173 | PyBuffer_Release(&data); 174 | return obj; 175 | } 176 | 177 | /* ZLIB */ 178 | 179 | static PyObject *deflate_zlib_compress(PyObject *self, PyObject *args, 180 | PyObject *kwargs) { 181 | static char *keywords[] = {"data", "compresslevel", NULL}; 182 | Py_buffer data; 183 | int compression_level = 6; 184 | 185 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|i", keywords, &data, 186 | &compression_level)) { 187 | return NULL; 188 | } 189 | 190 | PyObject *obj = compress(&data, compression_level, libdeflate_zlib_compress, 191 | libdeflate_zlib_compress_bound); 192 | PyBuffer_Release(&data); 193 | return obj; 194 | } 195 | 196 | static PyObject *deflate_zlib_decompress(PyObject *self, PyObject *args, 197 | PyObject *kwargs) { 198 | static char *keywords[] = {"data", "originalsize", NULL}; 199 | Py_buffer data; 200 | unsigned int originalsize = 0; 201 | 202 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*I", keywords, &data, 203 | &originalsize)) { 204 | return NULL; 205 | } 206 | 207 | PyObject *obj = decompress(&data, originalsize, libdeflate_zlib_decompress); 208 | PyBuffer_Release(&data); 209 | return obj; 210 | } 211 | 212 | /* CRC-32/Adler-32 */ 213 | 214 | static PyObject *deflate_crc32(PyObject *self, PyObject *args) { 215 | Py_buffer data; 216 | unsigned int crc = 0; 217 | 218 | if (!PyArg_ParseTuple(args, "y*|I", &data, &crc)) { 219 | return NULL; 220 | } 221 | 222 | crc = libdeflate_crc32(crc, data.buf, data.len); 223 | PyBuffer_Release(&data); 224 | 225 | return Py_BuildValue("I", crc); 226 | } 227 | 228 | static PyObject *deflate_adler32(PyObject *self, PyObject *args) { 229 | Py_buffer data; 230 | unsigned int adler = 1; 231 | 232 | if (!PyArg_ParseTuple(args, "y*|I", &data, &adler)) { 233 | return NULL; 234 | } 235 | 236 | adler = libdeflate_adler32(adler, data.buf, data.len); 237 | PyBuffer_Release(&data); 238 | 239 | return Py_BuildValue("I", adler); 240 | } 241 | 242 | static PyMethodDef deflate_methods[] = { 243 | {"gzip_compress", (PyCFunction)deflate_gzip_compress, METH_VARARGS | METH_KEYWORDS, 244 | "Compress data using gzip."}, 245 | {"gzip_decompress", (PyCFunction)deflate_gzip_decompress, 246 | METH_VARARGS | METH_KEYWORDS, "Decompress gzip data."}, 247 | {"deflate_compress", (PyCFunction)deflate_deflate_compress, 248 | METH_VARARGS | METH_KEYWORDS, "Compress data using raw DEFLATE."}, 249 | {"deflate_decompress", (PyCFunction)deflate_deflate_decompress, 250 | METH_VARARGS | METH_KEYWORDS, "Decompress raw DEFLATE data."}, 251 | {"zlib_compress", (PyCFunction)deflate_zlib_compress, METH_VARARGS | METH_KEYWORDS, 252 | "Compress data using zlib."}, 253 | {"zlib_decompress", (PyCFunction)deflate_zlib_decompress, 254 | METH_VARARGS | METH_KEYWORDS, "Decompress zlib data."}, 255 | {"crc32", (PyCFunction)deflate_crc32, METH_VARARGS, 256 | "CRC32 algorithm from libdeflate"}, 257 | {"adler32", (PyCFunction)deflate_adler32, METH_VARARGS, 258 | "adler32 algorithm from libdeflate"}, 259 | {NULL, NULL, 0, NULL}}; 260 | 261 | static struct PyModuleDef deflate_module = {PyModuleDef_HEAD_INIT, "_deflate", 262 | "Python wrapper module for libdeflate.", -1, 263 | deflate_methods}; 264 | 265 | PyMODINIT_FUNC PyInit__deflate(void) { 266 | PyObject *module = PyModule_Create(&deflate_module); 267 | if (module == NULL) 268 | return NULL; 269 | 270 | PyModule_AddStringConstant(module, "__version__", MODULE_VERSION); 271 | 272 | DeflateError = PyErr_NewException("deflate.DeflateError", NULL, NULL); 273 | Py_IncRef(DeflateError); 274 | PyModule_AddObject(module, "DeflateError", DeflateError); 275 | 276 | return module; 277 | } 278 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 3 3 | requires-python = ">=3.9" 4 | resolution-markers = [ 5 | "python_full_version >= '3.10'", 6 | "python_full_version < '3.10'", 7 | ] 8 | 9 | [[package]] 10 | name = "cfgv" 11 | version = "3.4.0" 12 | source = { registry = "https://pypi.org/simple" } 13 | sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } 14 | wheels = [ 15 | { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, 16 | ] 17 | 18 | [[package]] 19 | name = "colorama" 20 | version = "0.4.6" 21 | source = { registry = "https://pypi.org/simple" } 22 | 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" } 23 | wheels = [ 24 | { 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" }, 25 | ] 26 | 27 | [[package]] 28 | name = "deflate" 29 | source = { editable = "." } 30 | 31 | [package.optional-dependencies] 32 | test = [ 33 | { name = "pytest" }, 34 | ] 35 | 36 | [package.dev-dependencies] 37 | dev = [ 38 | { name = "pre-commit" }, 39 | { name = "pytest" }, 40 | ] 41 | 42 | [package.metadata] 43 | requires-dist = [{ name = "pytest", marker = "extra == 'test'", specifier = ">=7" }] 44 | provides-extras = ["test"] 45 | 46 | [package.metadata.requires-dev] 47 | dev = [ 48 | { name = "pre-commit", specifier = "~=3.5.0" }, 49 | { name = "pytest", specifier = ">=7" }, 50 | ] 51 | 52 | [[package]] 53 | name = "distlib" 54 | version = "0.4.0" 55 | source = { registry = "https://pypi.org/simple" } 56 | sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } 57 | wheels = [ 58 | { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, 59 | ] 60 | 61 | [[package]] 62 | name = "exceptiongroup" 63 | version = "1.3.0" 64 | source = { registry = "https://pypi.org/simple" } 65 | dependencies = [ 66 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 67 | ] 68 | 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" } 69 | wheels = [ 70 | { 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" }, 71 | ] 72 | 73 | [[package]] 74 | name = "filelock" 75 | version = "3.19.1" 76 | source = { registry = "https://pypi.org/simple" } 77 | resolution-markers = [ 78 | "python_full_version < '3.10'", 79 | ] 80 | sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } 81 | wheels = [ 82 | { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, 83 | ] 84 | 85 | [[package]] 86 | name = "filelock" 87 | version = "3.20.0" 88 | source = { registry = "https://pypi.org/simple" } 89 | resolution-markers = [ 90 | "python_full_version >= '3.10'", 91 | ] 92 | sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } 93 | wheels = [ 94 | { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, 95 | ] 96 | 97 | [[package]] 98 | name = "identify" 99 | version = "2.6.15" 100 | source = { registry = "https://pypi.org/simple" } 101 | sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } 102 | wheels = [ 103 | { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, 104 | ] 105 | 106 | [[package]] 107 | name = "iniconfig" 108 | version = "2.1.0" 109 | source = { registry = "https://pypi.org/simple" } 110 | resolution-markers = [ 111 | "python_full_version < '3.10'", 112 | ] 113 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } 114 | wheels = [ 115 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, 116 | ] 117 | 118 | [[package]] 119 | name = "iniconfig" 120 | version = "2.3.0" 121 | source = { registry = "https://pypi.org/simple" } 122 | resolution-markers = [ 123 | "python_full_version >= '3.10'", 124 | ] 125 | 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" } 126 | wheels = [ 127 | { 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" }, 128 | ] 129 | 130 | [[package]] 131 | name = "nodeenv" 132 | version = "1.9.1" 133 | source = { registry = "https://pypi.org/simple" } 134 | 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" } 135 | wheels = [ 136 | { 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" }, 137 | ] 138 | 139 | [[package]] 140 | name = "packaging" 141 | version = "25.0" 142 | source = { registry = "https://pypi.org/simple" } 143 | 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" } 144 | wheels = [ 145 | { 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" }, 146 | ] 147 | 148 | [[package]] 149 | name = "platformdirs" 150 | version = "4.4.0" 151 | source = { registry = "https://pypi.org/simple" } 152 | resolution-markers = [ 153 | "python_full_version < '3.10'", 154 | ] 155 | sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } 156 | wheels = [ 157 | { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, 158 | ] 159 | 160 | [[package]] 161 | name = "platformdirs" 162 | version = "4.5.0" 163 | source = { registry = "https://pypi.org/simple" } 164 | resolution-markers = [ 165 | "python_full_version >= '3.10'", 166 | ] 167 | sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } 168 | wheels = [ 169 | { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, 170 | ] 171 | 172 | [[package]] 173 | name = "pluggy" 174 | version = "1.6.0" 175 | source = { registry = "https://pypi.org/simple" } 176 | 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" } 177 | wheels = [ 178 | { 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" }, 179 | ] 180 | 181 | [[package]] 182 | name = "pre-commit" 183 | version = "3.5.0" 184 | source = { registry = "https://pypi.org/simple" } 185 | dependencies = [ 186 | { name = "cfgv" }, 187 | { name = "identify" }, 188 | { name = "nodeenv" }, 189 | { name = "pyyaml" }, 190 | { name = "virtualenv" }, 191 | ] 192 | sdist = { url = "https://files.pythonhosted.org/packages/04/b3/4ae08d21eb097162f5aad37f4585f8069a86402ed7f5362cc9ae097f9572/pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32", size = 177079, upload-time = "2023-10-13T15:57:48.334Z" } 193 | wheels = [ 194 | { url = "https://files.pythonhosted.org/packages/6c/75/526915fedf462e05eeb1c75ceaf7e3f9cde7b5ce6f62740fe5f7f19a0050/pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660", size = 203698, upload-time = "2023-10-13T15:57:46.378Z" }, 195 | ] 196 | 197 | [[package]] 198 | name = "pygments" 199 | version = "2.19.2" 200 | source = { registry = "https://pypi.org/simple" } 201 | 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" } 202 | wheels = [ 203 | { 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" }, 204 | ] 205 | 206 | [[package]] 207 | name = "pytest" 208 | version = "8.4.2" 209 | source = { registry = "https://pypi.org/simple" } 210 | dependencies = [ 211 | { name = "colorama", marker = "sys_platform == 'win32'" }, 212 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 213 | { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 214 | { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 215 | { name = "packaging" }, 216 | { name = "pluggy" }, 217 | { name = "pygments" }, 218 | { name = "tomli", marker = "python_full_version < '3.11'" }, 219 | ] 220 | sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } 221 | wheels = [ 222 | { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, 223 | ] 224 | 225 | [[package]] 226 | name = "pyyaml" 227 | version = "6.0.3" 228 | source = { registry = "https://pypi.org/simple" } 229 | sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } 230 | wheels = [ 231 | { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, 232 | { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, 233 | { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, 234 | { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, 235 | { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, 236 | { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, 237 | { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, 238 | { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, 239 | { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, 240 | { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, 241 | { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, 242 | { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, 243 | { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, 244 | { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, 245 | { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, 246 | { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, 247 | { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, 248 | { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, 249 | { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, 250 | { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, 251 | { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, 252 | { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, 253 | { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, 254 | { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, 255 | { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, 256 | { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, 257 | { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, 258 | { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, 259 | { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, 260 | { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, 261 | { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, 262 | { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, 263 | { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, 264 | { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, 265 | { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, 266 | { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, 267 | { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, 268 | { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, 269 | { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, 270 | { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, 271 | { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, 272 | { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, 273 | { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, 274 | { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, 275 | { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, 276 | { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, 277 | { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, 278 | { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, 279 | { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, 280 | { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, 281 | { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, 282 | { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, 283 | { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, 284 | { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, 285 | { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, 286 | { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, 287 | { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450, upload-time = "2025-09-25T21:33:00.618Z" }, 288 | { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319, upload-time = "2025-09-25T21:33:02.086Z" }, 289 | { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631, upload-time = "2025-09-25T21:33:03.25Z" }, 290 | { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795, upload-time = "2025-09-25T21:33:05.014Z" }, 291 | { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767, upload-time = "2025-09-25T21:33:06.398Z" }, 292 | { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982, upload-time = "2025-09-25T21:33:08.708Z" }, 293 | { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677, upload-time = "2025-09-25T21:33:09.876Z" }, 294 | { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592, upload-time = "2025-09-25T21:33:10.983Z" }, 295 | { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" }, 296 | ] 297 | 298 | [[package]] 299 | name = "tomli" 300 | version = "2.3.0" 301 | source = { registry = "https://pypi.org/simple" } 302 | 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" } 303 | wheels = [ 304 | { 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" }, 305 | { 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" }, 306 | { 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" }, 307 | { 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" }, 308 | { 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" }, 309 | { 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" }, 310 | { 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" }, 311 | { 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" }, 312 | { 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" }, 313 | { 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" }, 314 | { 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" }, 315 | { 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" }, 316 | { 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" }, 317 | { 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" }, 318 | { 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" }, 319 | { 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" }, 320 | { 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" }, 321 | { 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" }, 322 | { 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" }, 323 | { 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" }, 324 | { 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" }, 325 | { 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" }, 326 | { 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" }, 327 | { 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" }, 328 | { 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" }, 329 | { 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" }, 330 | { 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" }, 331 | { 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" }, 332 | { 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" }, 333 | { 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" }, 334 | { 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" }, 335 | { 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" }, 336 | { 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" }, 337 | { 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" }, 338 | { 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" }, 339 | { 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" }, 340 | { 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" }, 341 | { 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" }, 342 | { 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" }, 343 | { 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" }, 344 | { 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" }, 345 | ] 346 | 347 | [[package]] 348 | name = "typing-extensions" 349 | version = "4.15.0" 350 | source = { registry = "https://pypi.org/simple" } 351 | 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" } 352 | wheels = [ 353 | { 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" }, 354 | ] 355 | 356 | [[package]] 357 | name = "virtualenv" 358 | version = "20.35.4" 359 | source = { registry = "https://pypi.org/simple" } 360 | dependencies = [ 361 | { name = "distlib" }, 362 | { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 363 | { name = "filelock", version = "3.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 364 | { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 365 | { name = "platformdirs", version = "4.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 366 | { name = "typing-extensions", marker = "python_full_version < '3.11'" }, 367 | ] 368 | sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } 369 | wheels = [ 370 | { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, 371 | ] 372 | --------------------------------------------------------------------------------