├── test ├── __init__.py ├── data │ ├── trashcan.raw │ ├── trashcan-1200-decoded-macos.raw │ ├── trashcan-1200-decoded-ubuntu.raw │ └── trashcan-1200-decoded-macos15-m3.raw └── test_trashcan.py ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── custom-issue.md │ └── bug-report.md └── workflows │ └── build.yml ├── pycodec2 ├── __init__.py ├── codec2.pxd └── pycodec2.pyx ├── setup.cfg ├── .envrc ├── MANIFEST.in ├── commitlint.config.js ├── codec2-integration-tests ├── test.sh ├── README.md └── Dockerfile ├── .gitignore ├── dev ├── bin │ ├── test │ └── release-new-version-tag └── cut-off-new-version ├── Pipfile ├── example.py ├── lefthook.yml ├── pyproject.toml ├── LICENSE ├── DEV.md ├── setup.py ├── README.md └── Pipfile.lock /test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [gregorias] 2 | -------------------------------------------------------------------------------- /pycodec2/__init__.py: -------------------------------------------------------------------------------- 1 | from .pycodec2 import * 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description_file = README.md 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | layout pipenv 2 | PATH_add dev/bin 3 | export PYTHONDEVMODE=1 4 | -------------------------------------------------------------------------------- /test/data/trashcan.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregorias/pycodec2/HEAD/test/data/trashcan.raw -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pycodec2/pycodec2.c 2 | include pycodec2/pycodec2.pyx 3 | include pycodec2/codec2.pxd 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | "@commitlint/config-conventional" 4 | ], 5 | } 6 | -------------------------------------------------------------------------------- /test/data/trashcan-1200-decoded-macos.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregorias/pycodec2/HEAD/test/data/trashcan-1200-decoded-macos.raw -------------------------------------------------------------------------------- /test/data/trashcan-1200-decoded-ubuntu.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregorias/pycodec2/HEAD/test/data/trashcan-1200-decoded-ubuntu.raw -------------------------------------------------------------------------------- /test/data/trashcan-1200-decoded-macos15-m3.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregorias/pycodec2/HEAD/test/data/trashcan-1200-decoded-macos15-m3.raw -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue 3 | about: Non-bug tickets 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /codec2-integration-tests/test.sh: -------------------------------------------------------------------------------- 1 | cd pycodec2 2 | rm dist/* 3 | pipenv run python -m build --wheel 4 | pipenv run pip install dist/*.whl 5 | cd test 6 | pipenv run python -m unittest discover -s . 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pycodec2/pycodec2.c 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # Distribution / packaging 9 | .Python 10 | build/ 11 | develop-eggs/ 12 | dist/ 13 | downloads/ 14 | eggs/ 15 | .eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | pip-wheel-metadata/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | -------------------------------------------------------------------------------- /dev/bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This test builds a package, installs it in a temporary virtual environment, 4 | # and runs the unit tests. 5 | 6 | python -m build --wheel && \ 7 | TEST_VENV=$(mktemp -d) && \ 8 | python -m venv $TEST_VENV && \ 9 | $TEST_VENV/bin/pip install dist/*.whl && \ 10 | cd test && \ 11 | $TEST_VENV/bin/python -m unittest discover -s . 12 | TEST_RESULT=$? 13 | rm -r $TEST_VENV 14 | exit $TEST_RESULT 15 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | # Keep these in sync with pyproject.toml. 7 | [dev-packages] 8 | Cython = "==3.*,>=3.0.11" 9 | numpy = "==2.3.*" 10 | setuptools = "*" 11 | build = "==1.*" 12 | mypy = "*" 13 | twine = ">=6.0.1" 14 | # For fetching the current version from pyproject.toml. 15 | toml = "*" 16 | # These are for types for development only. 17 | types-setuptools = "*" 18 | 19 | [packages] 20 | 21 | [requires] 22 | python_version = "3.13" 23 | -------------------------------------------------------------------------------- /codec2-integration-tests/README.md: -------------------------------------------------------------------------------- 1 | # Codec2 Integration Tests 2 | 3 | This is a Docker container setup for testing pycodec2 with different versions 4 | of codec2. 5 | 6 | Right now there's only one codec2 version pycodec2 supports, 1.2.0, so the test 7 | is straightforward. 8 | 9 | ## Build & Run 10 | 11 | To build and run this container, execute the following steps. 12 | 13 | 1. Build the Docker image. 14 | 15 | ```shell 16 | docker build -t codec2-integration-tests -f - .. < Dockerfile 17 | ``` 18 | 19 | 1. Run the test: 20 | 21 | ```shell 22 | docker run codec2-integration-tests:latest bash ./pycodec2/codec2-integration-tests/test.sh 23 | ``` 24 | -------------------------------------------------------------------------------- /codec2-integration-tests/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use Ubuntu instead of Alpine, because it has more familiar tools included. 2 | FROM ubuntu:24.04 3 | WORKDIR /root 4 | RUN apt-get update 5 | RUN apt-get install -y cmake git 6 | RUN apt-get install -y curl python3 pipenv python3-pip python3-venv 7 | RUN curl https://pyenv.run | bash 8 | 9 | RUN git clone https://github.com/drowe67/codec2 10 | WORKDIR /root/codec2 11 | RUN git checkout 1.2.0 12 | RUN mkdir build_linux 13 | WORKDIR /root/codec2/build_linux 14 | RUN cmake .. 15 | RUN make install 16 | RUN ldconfig 17 | WORKDIR .. 18 | WORKDIR .. 19 | 20 | RUN mkdir pycodec2 21 | COPY ../../ pycodec2/ 22 | WORKDIR pycodec2 23 | RUN pipenv install --dev 24 | WORKDIR .. 25 | -------------------------------------------------------------------------------- /dev/cut-off-new-version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | # 3 | # This script creates a new version of the project on GitHub. 4 | 5 | set OLD_VERSION (poetry version | cut -d' ' -f2) 6 | or begin 7 | echo "Could not get the current version. Aborting." 8 | exit 0 9 | end 10 | 11 | echo "Bump up the new version in pyproject.toml. Once done, confirm [y/n]": 12 | read confirm 13 | switch $confirm 14 | case y 15 | case '*' 16 | echo "Aborting due to lack of confirmation (user input was $confirm)." 17 | exit 0 18 | end 19 | set NEW_VERSION (poetry version | cut -d' ' -f2) 20 | and echo "New version: $NEW_VERSION." 21 | git add pyproject.toml 22 | and echo "Committing the new version." 23 | and git commit -m 'chore: release' 24 | and git push 25 | and echo "Tagging the new version." 26 | and git tag v$NEW_VERSION 27 | and git push origin v$NEW_VERSION 28 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | # See README.md for documentation. 2 | import struct 3 | import sys 4 | 5 | import numpy as np 6 | import pycodec2 7 | 8 | if __name__ == '__main__': 9 | input = sys.argv[1] 10 | c2 = pycodec2.Codec2(1200) 11 | INT16_BYTE_SIZE = 2 12 | PACKET_SIZE = c2.samples_per_frame() * INT16_BYTE_SIZE 13 | STRUCT_FORMAT = '{}h'.format(c2.samples_per_frame()) 14 | 15 | with open(sys.argv[1], 'rb') as input,\ 16 | open('output.raw', 'wb') as output: 17 | while True: 18 | packet = input.read(PACKET_SIZE) 19 | if len(packet) != PACKET_SIZE: 20 | break 21 | packet = np.array(struct.unpack(STRUCT_FORMAT, packet), 22 | dtype=np.int16) 23 | encoded = c2.encode(packet) 24 | packet = c2.decode(encoded) 25 | output.write(struct.pack(STRUCT_FORMAT, *packet)) 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Checklist 11 | 12 | Complete this checklist before filing the bug ticket: 13 | 14 | - [ ] I have verified that I'm using [compatible](https://github.com/gregorias/pycodec2#codec-2-compatibility) Pycodec2 and Codec2 versions. 15 | 16 | ## Bug description 17 | 18 | A clear and concise description of what the bug is. 19 | 20 | ## Reproduction steps 21 | 22 | Steps to reproduce the behavior: 23 | 24 | 1. Do A 25 | 2. Check B 26 | 27 | ## Expected behavior 28 | 29 | A clear and concise description of what you expected to happen. 30 | 31 | 32 | ## Additional information 33 | 34 | - OS version: [e.g. macOS 13.2.1] 35 | - Codec2 version: 36 | - Pycodec2 version: 37 | 38 | ## Additional context 39 | 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | tests: 5 | run: "./dev/bin/test" 6 | codec2-integration-tests: 7 | run: | 8 | cd codec2-integration-tests 9 | docker build -t codec2-integration-tests -f - .. < Dockerfile 10 | docker run codec2-integration-tests:latest bash ./pycodec2/codec2-integration-tests/test.sh 11 | markdownlint: 12 | tags: documentation style 13 | glob: "*.md" 14 | run: markdownlint {staged_files} 15 | toml-taplo-lint: 16 | tags: style 17 | glob: "*.toml" 18 | run: taplo check {staged_files} 19 | yaml-prettier: 20 | tags: style 21 | glob: "*.{yml,yaml}" 22 | run: prettier -c {staged_files} 23 | yapf: 24 | tags: style 25 | glob: "*.py" 26 | run: yapf -i -r {staged_files} && git add {staged_files} 27 | commit-msg: 28 | commands: 29 | commitlint: 30 | run: commitlint --edit={1} 31 | -------------------------------------------------------------------------------- /dev/bin/release-new-version-tag: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | # 3 | # This script releases a new version of the project. 4 | 5 | function echo_version 6 | python -c "import toml; print(toml.load('pyproject.toml')['project']['version'])" 7 | end 8 | 9 | 10 | set OLD_VERSION (echo_version) 11 | or begin 12 | echo "Could not get the current version. Aborting." 13 | exit 0 14 | end 15 | 16 | echo "The current version is $OLD_VERSION." 17 | echo "Bump up the new version in pyproject.toml. Once done, confirm [y/n]:" 18 | read confirm 19 | switch $confirm 20 | case y 21 | case '*' 22 | echo "Aborting due to lack of confirmation (user input was $confirm)." 23 | exit 0 24 | end 25 | set NEW_VERSION (echo_version) 26 | and echo "The new version is $NEW_VERSION." 27 | 28 | echo "Tagging and pushing the new version." 29 | git add pyproject.toml 30 | and echo "Committing the new version." 31 | and git commit -m "chore: release $NEW_VERSION" 32 | and git push 33 | and echo "Tagging the new version." 34 | and git tag v$NEW_VERSION 35 | and git push origin v$NEW_VERSION 36 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # This file is necessary for the build system as it specifies the build-time 2 | # dependencies. 3 | # You need to keep it in sync with development dependencies specified in 4 | # Pipfile. 5 | [build-system] 6 | requires = [ 7 | "Cython==3.*", 8 | "numpy==2.3.*", 9 | "setuptools>=79" 10 | ] 11 | 12 | # Required table with required fields: https://peps.python.org/pep-0621/#table-name. 13 | [project] 14 | name = "pycodec2" 15 | version = "4.1.1" 16 | description = "A Cython wrapper for codec2" 17 | readme = "README.md" 18 | 19 | # Authors are defined in setup.py. 20 | # https://discuss.python.org/t/adding-extra-fields-in-the-pyproject-toml-authors-maintainers-list/16848/3 21 | dynamic = ["authors"] 22 | 23 | keywords = ["codec2", "audio", "voice", "speech"] 24 | license = "BSD-3-Clause" 25 | classifiers=[ 26 | 'Topic :: Multimedia :: Sound/Audio :: Speech', 27 | 'Programming Language :: Python :: 3', 28 | 'Development Status :: 5 - Production/Stable', 29 | ] 30 | 31 | # Necessary for cibuildwheel not to use an old python that may have old 32 | # libraries. E.g., python 3.6 does not have numpy 1.26. 33 | # https://cibuildwheel.readthedocs.io/en/stable/options/#requires-python 34 | # 35 | # Using at least Python 3.11, because Numpy 2.3.0 requires it. 36 | requires-python = ">=3.11" 37 | 38 | # Use-time dependencies. 39 | dependencies = [ 40 | # The package will error out on import if these are not installed. 41 | "numpy>=2.00, <3.0.0", 42 | ] 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Grzegorz Milka 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | # Developer Documentation 2 | 3 | This file is meant for developers. It provides instructions on how to 4 | work with the repository. 5 | 6 | ## Development environment setup 7 | 8 | This section explains how to setup your development environment upon cloning 9 | this repository. 10 | 11 | 1. Run 12 | 13 | ```bash 14 | lefthook install 15 | pipenv install --dev 16 | ``` 17 | 18 | ## Local Build 19 | 20 | This section explains how to build 21 | [a wheel](https://realpython.com/python-wheels/) of this package on your local 22 | machine. 23 | 24 | 1. Run 25 | 26 | ```bash 27 | python -m build --wheel 28 | ``` 29 | 30 | This builds a wheel file and saves it in `dist/`. 31 | 32 | ## Build, Release & Publish 33 | 34 | A version release consists of producing the following artifacts: 35 | 36 | 1. Tagging a commit with a version string (`vx.y.z`). 37 | 2. Uploading the package's wheels and an sdist archive to: 38 | 1. PyPI 39 | 2. GitHub Releases 40 | 41 | To create a release, run the following steps: 42 | 43 | 1. Run `dev/bin/release-new-version-tag` 44 | 2. Run the build GitHub action. 45 | 3. Fetch the built artifacts and unpack. 46 | 4. Run `twine upload pycodec2-$VERSION*`. 47 | 5. Run `gh release create v$VERSION pycodec2-$VERSION*`. 48 | 49 | ## ADRs 50 | 51 | ### Pipenv vs Poetry 52 | 53 | This project uses Pipenv to simplify managing development environment 54 | dependencies. It does not use Poetry, because Poetry does not support building 55 | impure Python wheels (those with Cython), so it would be quite surprising to 56 | have Poetry AND use setuptools directly to build this package. 57 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import Cython.Build 2 | from Cython.Build import cythonize 3 | from setuptools import Extension, setup 4 | import numpy as np 5 | import sys 6 | 7 | # Extension API reference: 8 | # https://setuptools.pypa.io/en/latest/userguide/ext_modules.html. 9 | ext_modules = [ 10 | Extension( 11 | "pycodec2.pycodec2", 12 | [ 13 | "pycodec2/pycodec2.pyx", 14 | ], 15 | # We need to include the numpy headers, because we use the numpy API 16 | # for array types. Without this line, the build will fail. 17 | include_dirs=[np.get_include()], 18 | # This line guarantees that we do not use the numpy API 19 | # deprecated in 1.26. 20 | define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_26_API_VERSION")], 21 | # The list of libraries to link against. 22 | # Not including this line, will cause an error upon using pycodec2: 23 | # > ImportError: dlopen(/Users/grzesiek/Code/pycodec2-202310/build/lib.macosx-13.3-arm64-cpython-311/pycodec2.cpython-311-darwin.so, 0x0002): 24 | # > symbol not found in flat namespace '_codec2_700c_eq' 25 | library_dirs=["lib_win"] if sys.platform == "win32" else None, 26 | libraries=["libcodec2"] if sys.platform == "win32" else ["codec2"], 27 | ) 28 | ] 29 | 30 | setup( 31 | packages=["pycodec2"], 32 | ext_modules=cythonize(ext_modules), 33 | author="Grzegorz Milka", 34 | author_email="grzegorzmilka@gmail.com", 35 | url="https://github.com/gregorias/pycodec2", 36 | cmdclass={"build_ext": Cython.Build.build_ext}, 37 | data_files=( 38 | [("Lib/site-packages/pycodec2", ["lib_win/libcodec2.dll", "lib_win/libgcc_s_seh-1.dll"])] 39 | if sys.platform == "win32" 40 | else None 41 | ), 42 | ) 43 | -------------------------------------------------------------------------------- /test/test_trashcan.py: -------------------------------------------------------------------------------- 1 | """E2e test cases that encode and decodes a trashcan file.""" 2 | import io 3 | import os.path as path 4 | import struct 5 | import unittest 6 | from pathlib import Path 7 | 8 | import numpy as np 9 | 10 | import pycodec2 11 | 12 | TEST_DATA_DIR = path.join(path.dirname(path.realpath(__file__)), 'data') 13 | TRASHCAN_GOLDEN_FILES = [ 14 | path.join(TEST_DATA_DIR, f) for f in [ 15 | './trashcan-1200-decoded-macos.raw', 16 | './trashcan-1200-decoded-macos15-m3.raw', 17 | './trashcan-1200-decoded-ubuntu.raw' 18 | ] 19 | ] 20 | 21 | 22 | def read_file(filename: str) -> bytes: 23 | with open(filename, 'rb') as f: 24 | return f.read() 25 | 26 | 27 | class TrashcanTestCase(unittest.TestCase): 28 | 29 | def test_encodes_and_decodes_trashcan(self): 30 | c2 = pycodec2.Codec2(1200) 31 | with io.BytesIO() as output: 32 | with open(path.join(TEST_DATA_DIR, 'trashcan.raw'), 'rb') as input: 33 | INT16_BYTE_SIZE = 2 34 | PACKET_SIZE = c2.samples_per_frame() * INT16_BYTE_SIZE 35 | STRUCT_FORMAT = '{}h'.format(c2.samples_per_frame()) 36 | while True: 37 | packet = input.read(PACKET_SIZE) 38 | if len(packet) != PACKET_SIZE: 39 | break 40 | packet = np.array(struct.unpack(STRUCT_FORMAT, packet), 41 | dtype=np.int16) 42 | encoded = c2.encode(packet) 43 | packet = c2.decode(encoded) 44 | output.write(struct.pack(STRUCT_FORMAT, *packet)) 45 | output.seek(0) 46 | 47 | self.assertIn(output.read(), 48 | [read_file(f) for f in TRASHCAN_GOLDEN_FILES]) 49 | -------------------------------------------------------------------------------- /pycodec2/codec2.pxd: -------------------------------------------------------------------------------- 1 | # Synced with 2 | # https://github.com/drowe67/codec2/commits/25014a290752fb830390ff24a612a9694319a47a/src/codec2.h 3 | cdef extern from 'codec2/codec2.h': 4 | cdef enum: 5 | CODEC2_MODE_3200 = 0 6 | CODEC2_MODE_2400 = 1 7 | CODEC2_MODE_1600 = 2 8 | CODEC2_MODE_1400 = 3 9 | CODEC2_MODE_1300 = 4 10 | CODEC2_MODE_1200 = 5 11 | CODEC2_MODE_700C = 8 12 | cdef struct CODEC2: 13 | pass 14 | 15 | CODEC2* codec2_create(int mode) 16 | void codec2_destroy(CODEC2* codec2_state) 17 | void codec2_encode(CODEC2 *codec2_state, 18 | unsigned char *bytes, 19 | short speech_in[]) 20 | void codec2_decode(CODEC2 *codec2_state, 21 | short speech_out[], 22 | const unsigned char *bytes) 23 | void codec2_decode_ber(CODEC2 *codec2_state, 24 | short speech_out[], 25 | const unsigned char *bytes, 26 | float ber_est) 27 | int codec2_samples_per_frame(CODEC2 *codec2_state) 28 | int codec2_bits_per_frame(CODEC2 *codec2_state) 29 | int codec2_bytes_per_frame(CODEC2 *codec2_state); 30 | void codec2_set_lpc_post_filter(CODEC2 *codec2_state, 31 | int enable, 32 | int bass_boost, 33 | float beta, 34 | float gamma) 35 | int codec2_get_spare_bit_index(CODEC2 *codec2_state) 36 | int codec2_rebuild_spare_bit(CODEC2 *codec2_state, char unpacked_bits[]) 37 | void codec2_set_natural_or_gray(CODEC2 *codec2_state, int gray) 38 | void codec2_set_softdec(CODEC2 *c2, float *softdec); 39 | float codec2_get_energy(CODEC2 *codec2_state, const unsigned char *bits); 40 | 41 | # support for ML and VQ experiments 42 | void codec2_open_mlfeat(CODEC2 *codec2_state, char *feat_filename, char *model_filename); 43 | void codec2_load_codebook(CODEC2 *codec2_state, int num, char *filename); 44 | float codec2_get_var(CODEC2 *codec2_state); 45 | float *codec2_enable_user_ratek(CODEC2 *codec2_state, int *K); 46 | 47 | # 700C post filter and equaliser 48 | void codec2_700c_post_filter(CODEC2 *codec2_state, int en); 49 | void codec2_700c_eq(CODEC2 *codec2_state, int en); 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pycodec2 2 | 3 | Pycodec2 is a Cython wrapper for [Codec 2][codec2]. 4 | 5 | In other words, Pycodec2 allows using the Codec 2 C library from Python. 6 | 7 | ## Installation 8 | 9 | ### Prerequisites 10 | 11 | Pycodec2 requires [Codec 2][codec2]. 12 | 13 | On Linux, I recommend using the distro's package-manager, e.g., on 14 | Arch/Manjaro: `pacman -Syuu codec2`, so that Codec2's assets land in standard 15 | searchable directories for `python setup.py` to use. 16 | 17 | On macOS, you may use [the Homebrew 18 | formula](https://formulae.brew.sh/formula/codec2#default). 19 | 20 | ### Instructions 21 | 22 | You can install the library using PyPI (the easiest option) or from source. 23 | 24 | #### From PyPI 25 | 26 | To install the library from PyPI, run: 27 | 28 | ```bash 29 | pip install pycodec2 30 | ``` 31 | 32 | ### From Source 33 | 34 | To install the library from source, see building instructions in `DEV.md`. You 35 | can then install the wheel with: 36 | 37 | ```bash 38 | pip install dist/*.whl 39 | ``` 40 | 41 | ### Codec 2 Compatibility 42 | 43 | Pycodec2 4.\* is compatible with Codec 2 1.2.\* and Numpy 2.\*. 44 | 45 | Pycodec2 3.\* is compatible with Codec 2 1.2.\*. 46 | 47 | Pycodec2 2.\* is compatible with Codec 2 1.0.\*. 48 | 49 | Pycodec2 1.0.\* is compatible with Codec 2 at 0.9.2+ versions. 50 | 51 | If your Codec 2 version is older than 0.9.2, then try 52 | [pycodec2-old](https://pypi.org/project/pycodec2-old/) package. 53 | 54 | For more information on potential compatibility problems, check out [this 55 | issue](https://github.com/gregorias/pycodec2/issues/8). 56 | 57 | ## Usage 58 | 59 | ### Example 60 | 61 | `example.py` implements a basic script that uses Codec 2 to encode and deencode 62 | a sample. Use the following steps to run an end-2-end scenario (dev/rune2etest 63 | implements steps 2-5). 64 | 65 | 1. Download a sample .wav file, e.g., [trashcan](https://freesound.org/people/InspectorJ/sounds/431158/). 66 | 67 | 2. Convert the .wav to a raw mono-channel 8kHz format, e.g., 68 | 69 | sox trashcan.wav -e signed-integer -b 16 trashcan.raw channels 1 rate 8000 70 | 71 | 3. Compile pycodec2 72 | 73 | python setup.py build_ext --inplace 74 | 75 | 4. Run `example.py` 76 | 77 | python example.py trashcan.raw 78 | 79 | 5. Convert `output.raw` 80 | 81 | sox -r 8000 -e signed-integer -b 16 output.raw output.wav 82 | 83 | Now you can listen to `output.wav`. 84 | 85 | ### Expected Input Format 86 | 87 | [Codec 2][codec2] assumes that input files use: 88 | 89 | * 8kHz bitrate, 90 | * 16-bit width samples, 91 | * a single channel. 92 | 93 | ### Available Modes 94 | 95 | For a list of currently supported modes, look for `_modes` in 96 | `pycodec2/pycodec2.pyx`. 97 | 98 | ## Remarks 99 | 100 | This library is considered complete. Please notify me or send a pull request on 101 | GitHub if you notice any bugs. 102 | 103 | [codec2]: http://www.rowetel.com/blog/?page_id=452 104 | -------------------------------------------------------------------------------- /pycodec2/pycodec2.pyx: -------------------------------------------------------------------------------- 1 | #cython: language_level=3 2 | # The language level specification is needed. I don't have it in my build file, 3 | # so I use it per-module. 4 | from .codec2 cimport * 5 | 6 | import math 7 | 8 | import numpy as np 9 | cimport numpy as cnp 10 | ctypedef cnp.int8_t CHAR_DTYPE_t 11 | ctypedef cnp.int16_t SHORT_DTYPE_t 12 | 13 | cnp.import_array() 14 | 15 | _modes = { 16 | 700 : CODEC2_MODE_700C, 17 | 1200 : CODEC2_MODE_1200, 18 | 1300 : CODEC2_MODE_1300, 19 | 1400 : CODEC2_MODE_1400, 20 | 1600 : CODEC2_MODE_1600, 21 | 2400 : CODEC2_MODE_2400, 22 | 3200 : CODEC2_MODE_3200, 23 | } 24 | 25 | cdef class Codec2: 26 | '''Wrapper for codec2 state and its functions. 27 | 28 | Initialization method expects an integer defining expected bitrate per 29 | second.''' 30 | cdef CODEC2 *_c_codec2_state 31 | 32 | def __cinit__(self, mode): 33 | self._c_codec2_state = codec2_create(_modes[mode]) 34 | if self._c_codec2_state is NULL: 35 | raise MemoryError() 36 | 37 | def __dealloc__(self): 38 | if self._c_codec2_state is not NULL: 39 | codec2_destroy(self._c_codec2_state) 40 | 41 | def encode(self, cnp.ndarray[SHORT_DTYPE_t, ndim=1] speech_in): 42 | '''Encode samples with codec2. 43 | 44 | Encode the given ndarray of samples to bits represented as a byte array.''' 45 | assert len(speech_in) % self.samples_per_frame() == 0 46 | frames = len(speech_in) // self.samples_per_frame() 47 | bit_count = frames * self.bits_per_frame() 48 | bits = b'\x00' * int(math.ceil(bit_count / 8.0)) 49 | codec2_encode(self._c_codec2_state, bits, cnp.PyArray_DATA(speech_in)) 50 | return bits 51 | 52 | def decode(self, bytes frames): 53 | '''Decode a byte array into an ndarray of samples''' 54 | assert len(frames) >= self.bytes_per_frame() 55 | cdef cnp.ndarray[SHORT_DTYPE_t, ndim=1] speech_out 56 | frame_count = int(math.floor(len(frames) / self.bytes_per_frame())) 57 | sample_count = frame_count * self.samples_per_frame() 58 | speech_out = np.empty(sample_count, dtype=np.int16, order='C') 59 | codec2_decode(self._c_codec2_state, (cnp.PyArray_DATA(speech_out)), frames) 60 | return speech_out 61 | 62 | def decode_ber(self, bytes frames, float ber_est): 63 | assert len(frames) >= self.bytes_per_frame() 64 | cdef cnp.ndarray[SHORT_DTYPE_t, ndim=1] speech_out 65 | frame_count = len(frames) // self.bytes_per_frame() 66 | sample_count = frame_count * self.samples_per_frame() 67 | speech_out = np.empty(sample_count, dtype=np.int16, order='C') 68 | codec2_decode_ber(self._c_codec2_state, 69 | (cnp.PyArray_DATA(speech_out)), 70 | frames, 71 | ber_est) 72 | return speech_out 73 | 74 | def samples_per_frame(self): 75 | return codec2_samples_per_frame(self._c_codec2_state) 76 | 77 | def bits_per_frame(self): 78 | return codec2_bits_per_frame(self._c_codec2_state) 79 | 80 | def bytes_per_frame(self): 81 | return codec2_bytes_per_frame(self._c_codec2_state) 82 | 83 | def set_lpc_post_filter(self, 84 | int enable, 85 | int bass_boost, 86 | float beta, 87 | float gamma): 88 | codec2_set_lpc_post_filter(self._c_codec2_state, 89 | enable, 90 | bass_boost, 91 | beta, 92 | gamma) 93 | 94 | def get_spare_bit_index(self): 95 | return codec2_get_spare_bit_index(self._c_codec2_state) 96 | 97 | def rebuild_spare_bit(self, cnp.ndarray[char, ndim=1] unpacked_bits): 98 | return codec2_rebuild_spare_bit(self._c_codec2_state, cnp.PyArray_DATA(unpacked_bits)) 99 | 100 | def set_natural_or_gray(self, int gray): 101 | codec2_set_natural_or_gray(self._c_codec2_state, gray) 102 | 103 | def set_softdec(self, float softdec): 104 | codec2_set_softdec(self._c_codec2_state, &softdec) 105 | 106 | def get_energy(self, cnp.ndarray[unsigned char, ndim=1] bits): 107 | return codec2_get_energy(self._c_codec2_state, (cnp.PyArray_DATA(bits))) 108 | 109 | # support for ML and VQ experiments 110 | def open_mlfeat(self, bytearray feat_filename, bytearray model_filename): 111 | codec2_open_mlfeat(self._c_codec2_state, feat_filename, model_filename) 112 | 113 | def load_codebook(self, bytearray feat_filename, int num, bytearray filename): 114 | codec2_load_codebook(self._c_codec2_state, num, filename) 115 | 116 | def get_var(self): 117 | return codec2_get_var(self._c_codec2_state) 118 | 119 | def enable_user_ratek(self): 120 | cdef int K 121 | cdef float *user_rate_k = codec2_enable_user_ratek(self._c_codec2_state, &K) 122 | return (user_rate_k[0], K) 123 | 124 | # 700C post filter and equaliser 125 | def post_filter_700c(self, int en): 126 | codec2_700c_post_filter(self._c_codec2_state, en) 127 | 128 | def eq_700c(self, int en): 129 | codec2_700c_eq(self._c_codec2_state, en) 130 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: workflow_dispatch 3 | env: 4 | # Codec2 is required for the build process, so instruct cibuildwheel to 5 | # install the currently supported version. 6 | CIBW_BEFORE_ALL_LINUX: > 7 | git clone https://github.com/drowe67/codec2 && 8 | cd codec2 && 9 | git checkout 1.2.0 && 10 | mkdir build_linux && 11 | cd build_linux && 12 | cmake .. && 13 | make && 14 | make install 15 | CIBW_ARCHS_LINUX: native 16 | # auditwheel gives library "libcodec2.so.1.2" could not be located without it. 17 | # https://github.com/pypa/cibuildwheel/issues/1981#issuecomment-3592353976 18 | CIBW_ENVIRONMENT_LINUX: LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64 19 | 20 | # A smoke test for each wheel. 21 | CIBW_TEST_COMMAND: > 22 | python -c "import pycodec2" 23 | jobs: 24 | build_sdist: 25 | name: Build sdist 26 | runs-on: ubuntu-24.04 27 | steps: 28 | - uses: actions/checkout@v6 29 | - uses: actions/setup-python@v6 30 | with: 31 | python-version: "3.14" 32 | - name: Install build 33 | run: python -m pip install build==1.3.0 34 | - name: Build sdist 35 | run: python -m build --sdist 36 | - uses: actions/upload-artifact@v4 37 | with: 38 | name: pycodec2-sdist 39 | path: ./dist/*.tar.gz 40 | 41 | build_wheels: 42 | name: Build wheels on ${{ matrix.os }} 43 | runs-on: ${{ matrix.os }} 44 | strategy: 45 | matrix: 46 | # Supported runners: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources 47 | # Not supporting Windows, because there's no codec2 package for Windows' package manager. Use cross compilation to support windows 48 | os: [ubuntu-24.04, ubuntu-24.04-arm, macos-14, macos-15] 49 | 50 | steps: 51 | - uses: actions/checkout@v6 52 | 53 | # Used to host cibuildwheel 54 | # https://github.com/actions/setup-python 55 | - uses: actions/setup-python@v6 56 | with: 57 | python-version: "3.14" 58 | 59 | - name: Install cibuildwheel 60 | run: python -m pip install cibuildwheel==3.1.4 61 | 62 | - name: Install codec2 on macOS 63 | if: contains(matrix.os, 'macos') 64 | run: | 65 | git clone https://github.com/drowe67/codec2 && 66 | cd codec2 && 67 | git checkout 1.2.0 && 68 | mkdir build_linux && 69 | cd build_linux && 70 | cmake .. && 71 | make && 72 | sudo make install 73 | # brew update 74 | # brew install codec2 75 | 76 | - name: Build wheels 77 | run: python -m cibuildwheel --output-dir wheelhouse 78 | 79 | - uses: actions/upload-artifact@v4 80 | with: 81 | name: pycodec2-${{ matrix.os }} 82 | path: ./wheelhouse/*.whl 83 | 84 | cross_compile_windows: 85 | name: cross compile windows on ubuntu 86 | runs-on: ubuntu-24.04 87 | steps: 88 | - uses: actions/checkout@v6 89 | 90 | - name: build_linux 91 | run: | 92 | git clone https://github.com/drowe67/codec2 && 93 | cd codec2 && 94 | git checkout 1.2.0 && 95 | mkdir build_linux && 96 | cd build_linux && 97 | cmake .. && 98 | make && 99 | sudo make install 100 | 101 | - name: build_windows 102 | run: | 103 | sudo apt-get update && 104 | sudo apt-get install -y mingw-w64 && 105 | cd codec2 && 106 | mkdir build_windows && 107 | echo "set(CMAKE_SYSTEM_NAME Windows)" > Toolchain-Ubuntu-mingw32.cmake && 108 | echo "set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)" >> Toolchain-Ubuntu-mingw32.cmake && 109 | echo "set(CMAKE_C_COMPILER \${TOOLCHAIN_PREFIX}-gcc)" >> Toolchain-Ubuntu-mingw32.cmake && 110 | echo "set(CMAKE_CXX_COMPILER \${TOOLCHAIN_PREFIX}-g++)" >> Toolchain-Ubuntu-mingw32.cmake && 111 | echo "set(CMAKE_RC_COMPILER \${TOOLCHAIN_PREFIX}-windres)" >> Toolchain-Ubuntu-mingw32.cmake && 112 | echo "set(CMAKE_FIND_ROOT_PATH /usr/\${TOOLCHAIN_PREFIX})" >> Toolchain-Ubuntu-mingw32.cmake && 113 | echo "set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)" >> Toolchain-Ubuntu-mingw32.cmake && 114 | echo "set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)" >> Toolchain-Ubuntu-mingw32.cmake && 115 | echo "set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)" >> Toolchain-Ubuntu-mingw32.cmake && 116 | echo "set(CMAKE_C_FLAGS \"\${CMAKE_C_FLAGS} -m64\")" >> Toolchain-Ubuntu-mingw32.cmake && 117 | echo "set(CMAKE_CXX_FLAGS \"\${CMAKE_CXX_FLAGS} -m64\")" >> Toolchain-Ubuntu-mingw32.cmake && 118 | echo "set(CMAKE_SIZEOF_VOID_P 8)" >> Toolchain-Ubuntu-mingw32.cmake && 119 | cd build_windows && 120 | cmake -DCMAKE_TOOLCHAIN_FILE=../Toolchain-Ubuntu-mingw32.cmake -DUNITTEST=FALSE -DGENERATE_CODEBOOK=../build_linux/src/generate_codebook -DCMAKE_BUILD_TYPE=Release .. && 121 | make 122 | 123 | - name: Upload libs for windows 124 | uses: actions/upload-artifact@v4 125 | with: 126 | name: windows-libs 127 | path: | 128 | codec2/build_windows/src/libcodec2.dll 129 | codec2/build_windows/src/libcodec2.dll.a 130 | codec2/build_windows/codec2/version.h 131 | codec2/src/codec2.h 132 | /usr/lib/gcc/x86_64-w64-mingw32/13-win32/libgcc_s_seh-1.dll 133 | 134 | build_wheels_windows: 135 | needs: 136 | - cross_compile_windows 137 | name: Build wheels on windows-latest 138 | runs-on: windows-latest 139 | steps: 140 | - uses: actions/checkout@v6 141 | 142 | # Used to host cibuildwheel 143 | # https://github.com/actions/setup-python 144 | - uses: actions/setup-python@v6 145 | with: 146 | python-version: "3.14" 147 | 148 | - name: Install cibuildwheel 149 | run: python -m pip install cibuildwheel==3.1.4 150 | 151 | - name: Download libs for windows 152 | uses: actions/download-artifact@v6 153 | with: 154 | name: windows-libs 155 | path: temp/ 156 | pattern: true 157 | 158 | - name: Copy libs for windows 159 | run: | 160 | mkdir lib_win && 161 | mkdir pycodec2/codec2 && 162 | cp .\temp\home\runner\work\pycodec2\pycodec2\codec2\build_windows\src\libcodec2.dll lib_win\libcodec2.dll && 163 | cp .\temp\home\runner\work\pycodec2\pycodec2\codec2\build_windows\src\libcodec2.dll.a lib_win\libcodec2.lib && 164 | cp .\temp\home\runner\work\pycodec2\pycodec2\codec2\build_windows\codec2\version.h pycodec2\codec2\version.h && 165 | cp .\temp\home\runner\work\pycodec2\pycodec2\codec2\src\codec2.h pycodec2\codec2\codec2.h && 166 | cp .\temp\usr\lib\gcc\x86_64-w64-mingw32\13-win32\libgcc_s_seh-1.dll lib_win\libgcc_s_seh-1.dll && 167 | ls lib_win 168 | 169 | - name: Build wheels 170 | run: python -m cibuildwheel --output-dir wheelhouse --archs AMD64 171 | 172 | - uses: actions/upload-artifact@v4 173 | with: 174 | name: pycodec2-windows 175 | path: ./wheelhouse/*.whl 176 | 177 | merge: 178 | runs-on: ubuntu-latest 179 | needs: 180 | - build_sdist 181 | - build_wheels 182 | - build_wheels_windows 183 | steps: 184 | - name: Merge artifacts 185 | uses: actions/upload-artifact/merge@v4 186 | with: 187 | name: pycodec2 188 | pattern: pycodec2-* 189 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "f5e1af7a7392ead4a10e09d307acaeca70fadacf38a0ed583d218741f13dd41e" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.13" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": {}, 19 | "develop": { 20 | "build": { 21 | "hashes": [ 22 | "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397", 23 | "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4" 24 | ], 25 | "index": "pypi", 26 | "markers": "python_version >= '3.9'", 27 | "version": "==1.3.0" 28 | }, 29 | "certifi": { 30 | "hashes": [ 31 | "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", 32 | "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316" 33 | ], 34 | "markers": "python_version >= '3.7'", 35 | "version": "==2025.11.12" 36 | }, 37 | "charset-normalizer": { 38 | "hashes": [ 39 | "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", 40 | "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", 41 | "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", 42 | "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", 43 | "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", 44 | "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", 45 | "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63", 46 | "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", 47 | "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", 48 | "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", 49 | "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", 50 | "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", 51 | "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", 52 | "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af", 53 | "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", 54 | "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", 55 | "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", 56 | "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", 57 | "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", 58 | "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", 59 | "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576", 60 | "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", 61 | "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", 62 | "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", 63 | "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", 64 | "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", 65 | "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", 66 | "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", 67 | "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", 68 | "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", 69 | "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", 70 | "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", 71 | "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a", 72 | "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", 73 | "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", 74 | "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", 75 | "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", 76 | "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", 77 | "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7", 78 | "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", 79 | "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", 80 | "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", 81 | "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", 82 | "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", 83 | "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", 84 | "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2", 85 | "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", 86 | "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", 87 | "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", 88 | "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", 89 | "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", 90 | "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", 91 | "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", 92 | "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", 93 | "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa", 94 | "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", 95 | "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", 96 | "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", 97 | "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", 98 | "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", 99 | "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", 100 | "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", 101 | "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", 102 | "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", 103 | "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", 104 | "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", 105 | "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", 106 | "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", 107 | "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", 108 | "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", 109 | "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3", 110 | "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", 111 | "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", 112 | "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", 113 | "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", 114 | "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", 115 | "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", 116 | "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf", 117 | "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", 118 | "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", 119 | "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac", 120 | "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", 121 | "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", 122 | "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", 123 | "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", 124 | "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", 125 | "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", 126 | "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4", 127 | "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84", 128 | "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", 129 | "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", 130 | "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", 131 | "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", 132 | "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", 133 | "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", 134 | "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", 135 | "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", 136 | "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", 137 | "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074", 138 | "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3", 139 | "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", 140 | "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", 141 | "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", 142 | "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d", 143 | "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", 144 | "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", 145 | "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", 146 | "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", 147 | "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", 148 | "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", 149 | "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", 150 | "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", 151 | "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608" 152 | ], 153 | "markers": "python_version >= '3.7'", 154 | "version": "==3.4.4" 155 | }, 156 | "cython": { 157 | "hashes": [ 158 | "sha256:034ab96cb8bc8e7432bc27491f8d66f51e435b1eb21ddc03aa844be8f21ad847", 159 | "sha256:098590c1dc309f8a0406ade031963a95a87714296b425539f9920aebf924560d", 160 | "sha256:0e35ff0f1bb3a7a5c40afb8fb540e4178b6551909f10748bf39d323f8140ccf3", 161 | "sha256:13b99ecb9482aff6a6c12d1ca6feef6940c507af909914b49f568de74fa965fb", 162 | "sha256:14432d7f207245a3c35556155873f494784169297b28978a6204f1c60d31553e", 163 | "sha256:177faf4d61e9f2d4d2db61194ac9ec16d3fe3041c1b6830f871a01935319eeb3", 164 | "sha256:2b910b89a2a71004064c5e890b9512a251eda63fae252caa0feb9835057035f9", 165 | "sha256:3de43a5786033a27fae1c882feb5ff0d023c38b83356e6800c1be0bcd6cf9f11", 166 | "sha256:436eb562d0affbc0b959f62f3f9c1ed251b9499e4f29c1d19514ae859894b6bf", 167 | "sha256:50bbaabee733fd2780985e459fc20f655e02def83e8eff10220ad88455a34622", 168 | "sha256:5f818d40bbcf17e2089e2de7840f0de1c0ca527acf9b044aba79d5f5d8a5bdba", 169 | "sha256:60f4aa425e1ff98abf8d965ae7020f06dd2cbc01dbd945137d2f9cca4ff0524a", 170 | "sha256:692a41c8fe06fb2dc55ca2c8d71c80c469fd16fe69486ed99f3b3cbb2d3af83f", 171 | "sha256:820c4a99dbf6b3e6c0300be42b4040b501eff0e1feeb80cfa52c48a346fb0df2", 172 | "sha256:826cad0ad43ab05a26e873b5d625f64d458dc739ec6fdeecab848b60a91c4252", 173 | "sha256:86b1d39a1ea974dd16fe3bcef0df7b64dadd0bd38d05a339f287b48d37cb109f", 174 | "sha256:8c9265b3e84ae2d999b7c3165c683e366bbbbbe4346468055ca2366fe013f2df", 175 | "sha256:8db28aef793c81dc69383b619ca508668998aaf099cd839d3cbae85184cce744", 176 | "sha256:8e72ee88a9a5381d30a6da116a3c8352730b9b038a49ed9bc5c3d0ed6d69b06c", 177 | "sha256:9cd2ede6af225499ad22888dbfb13b92d71fc1016f401ee637559a5831b177c2", 178 | "sha256:a3898c076e9c458bcb3e4936187919fda5f5365fe4c567d35d2b003444b6f3fe", 179 | "sha256:a473df474ba89e9fee81ee82b31062a267f9e598096b222783477e56d02ad12c", 180 | "sha256:a6387e3ad31342443916db9a419509935fddd8d4cbac34aab9c895ae55326a56", 181 | "sha256:a9509f1e9c41c86b790cff745bb31927bbc861662a3b462596d71d3d2a578abb", 182 | "sha256:aa24cd0bdab27ca099b2467806c684404add597c1108e07ddf7b6471653c85d7", 183 | "sha256:aff11412ed5fc78bd8b148621f4d1034fcad6cfcba468c20cd9f327b4f61ec3e", 184 | "sha256:b223c1f84c3420c24f6a4858e979524bd35a79437a5839e29d41201c87ed119d", 185 | "sha256:b4df52101209817fde7284cf779156f79142fb639b1d7840f11680ff4bb30604", 186 | "sha256:b4fe499eed7cd70b2aa4e096b9ce2588f5e6fdf049b46d40a5e55efcde6e4904", 187 | "sha256:b5afac4e77e71a9010dc7fd3191ced00f9b12b494dd7525c140781054ce63a73", 188 | "sha256:c3add3d483acc73129a61d105389344d792c17e7c1cee24863f16416bd071634", 189 | "sha256:ca18d9d53c0e2f0c9347478b37532b46e0dc34c704e052ab1b0d8b21a290fc0f", 190 | "sha256:d140c2701cbb8cf960300cf1b67f3b4fa9d294d32e51b85f329bff56936a82fd", 191 | "sha256:d7b3447b2005dffc5f276d420a480d2b57d15091242652d410b6a46fb00ed251", 192 | "sha256:d8c93fe128b58942832b1fcac96e48f93c2c69b569eff0d38d30fb5995fecfa0", 193 | "sha256:e7200309b81f4066cf36a96efeec646716ca74afd73d159045169263db891133", 194 | "sha256:f560ff3aea5b5df93853ec7bf1a1e9623d6d511f4192f197559aca18fca43392", 195 | "sha256:fed44d0ab2d36f1b0301c770b0dafec23bcb9700d58e7769cd6d9136b3304c11", 196 | "sha256:ff07e784ea748225bbdea07fec0ac451379e9e41a0a84cb57b36db19dd01ae71" 197 | ], 198 | "index": "pypi", 199 | "markers": "python_version >= '3.8'", 200 | "version": "==3.2.2" 201 | }, 202 | "docutils": { 203 | "hashes": [ 204 | "sha256:21486ae730e4ca9f622677b1412b879af1791efcfba517e4c6f60be543fc8cdd", 205 | "sha256:bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb" 206 | ], 207 | "markers": "python_version >= '3.9'", 208 | "version": "==0.22.3" 209 | }, 210 | "id": { 211 | "hashes": [ 212 | "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", 213 | "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658" 214 | ], 215 | "markers": "python_version >= '3.8'", 216 | "version": "==1.5.0" 217 | }, 218 | "idna": { 219 | "hashes": [ 220 | "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", 221 | "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" 222 | ], 223 | "markers": "python_version >= '3.8'", 224 | "version": "==3.11" 225 | }, 226 | "jaraco.classes": { 227 | "hashes": [ 228 | "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", 229 | "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" 230 | ], 231 | "markers": "python_version >= '3.8'", 232 | "version": "==3.4.0" 233 | }, 234 | "jaraco.context": { 235 | "hashes": [ 236 | "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", 237 | "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" 238 | ], 239 | "markers": "python_version >= '3.8'", 240 | "version": "==6.0.1" 241 | }, 242 | "jaraco.functools": { 243 | "hashes": [ 244 | "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8", 245 | "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294" 246 | ], 247 | "markers": "python_version >= '3.9'", 248 | "version": "==4.3.0" 249 | }, 250 | "keyring": { 251 | "hashes": [ 252 | "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", 253 | "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b" 254 | ], 255 | "markers": "python_version >= '3.9'", 256 | "version": "==25.7.0" 257 | }, 258 | "librt": { 259 | "hashes": [ 260 | "sha256:04f8ce401d4f6380cfc42af0f4e67342bf34c820dae01343f58f472dbac75dcf", 261 | "sha256:05f385a414de3f950886ea0aad8f109650d4b712cf9cc14cc17f5f62a9ab240b", 262 | "sha256:0765b0fe0927d189ee14b087cd595ae636bef04992e03fe6dfdaa383866c8a46", 263 | "sha256:078cd77064d1640cb7b0650871a772956066174d92c8aeda188a489b58495179", 264 | "sha256:09262cb2445b6f15d09141af20b95bb7030c6f13b00e876ad8fdd1a9045d6aa5", 265 | "sha256:0c74c26736008481c9f6d0adf1aedb5a52aff7361fea98276d1f965c0256ee70", 266 | "sha256:0e0f2b79993fec23a685b3e8107ba5f8675eeae286675a216da0b09574fa1e47", 267 | "sha256:10a95ad074e2a98c9e4abc7f5b7d40e5ecbfa84c04c6ab8a70fabf59bd429b88", 268 | "sha256:14b345eb7afb61b9fdcdfda6738946bd11b8e0f6be258666b0646af3b9bb5916", 269 | "sha256:17000df14f552e86877d67e4ab7966912224efc9368e998c96a6974a8d609bf9", 270 | "sha256:1b51ba7d9d5d9001494769eca8c0988adce25d0a970c3ba3f2eb9df9d08036fc", 271 | "sha256:1ef42ff4edd369e84433ce9b188a64df0837f4f69e3d34d3b34d4955c599d03f", 272 | "sha256:25b1b60cb059471c0c0c803e07d0dfdc79e41a0a122f288b819219ed162672a3", 273 | "sha256:26b8026393920320bb9a811b691d73c5981385d537ffc5b6e22e53f7b65d4122", 274 | "sha256:324462fe7e3896d592b967196512491ec60ca6e49c446fe59f40743d08c97917", 275 | "sha256:349b6873ebccfc24c9efd244e49da9f8a5c10f60f07575e248921aae2123fc42", 276 | "sha256:36a8e337461150b05ca2c7bdedb9e591dfc262c5230422cea398e89d0c746cdc", 277 | "sha256:36b2ec8c15030002c7f688b4863e7be42820d7c62d9c6eece3db54a2400f0530", 278 | "sha256:38320386a48a15033da295df276aea93a92dfa94a862e06893f75ea1d8bbe89d", 279 | "sha256:3ac2a7835434b31def8ed5355dd9b895bbf41642d61967522646d1d8b9681106", 280 | "sha256:3caa0634c02d5ff0b2ae4a28052e0d8c5f20d497623dc13f629bd4a9e2a6efad", 281 | "sha256:3e84a4121a7ae360ca4da436548a9c1ca8ca134a5ced76c893cc5944426164bd", 282 | "sha256:3f0e4bd9bcb0ee34fa3dbedb05570da50b285f49e52c07a241da967840432513", 283 | "sha256:4018904c83eab49c814e2494b4e22501a93cdb6c9f9425533fe693c3117126f9", 284 | "sha256:408a36ddc75e91918cb15b03460bdc8a015885025d67e68c6f78f08c3a88f522", 285 | "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1", 286 | "sha256:4aa4a93a353ccff20df6e34fa855ae8fd788832c88f40a9070e3ddd3356a9f0e", 287 | "sha256:4bca9e4c260233fba37b15c4ec2f78aa99c1a79fbf902d19dd4a763c5c3fb751", 288 | "sha256:514f3f363d1ebc423357d36222c37e5c8e6674b6eae8d7195ac9a64903722057", 289 | "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf", 290 | "sha256:57705e8eec76c5b77130d729c0f70190a9773366c555c5457c51eace80afd873", 291 | "sha256:5cc22f7f5c0cc50ed69f4b15b9c51d602aabc4500b433aaa2ddd29e578f452f7", 292 | "sha256:61348cc488b18d1b1ff9f3e5fcd5ac43ed22d3e13e862489d2267c2337285c08", 293 | "sha256:64645b757d617ad5f98c08e07620bc488d4bced9ced91c6279cec418f16056fa", 294 | "sha256:669ff2495728009a96339c5ad2612569c6d8be4474e68f3f3ac85d7c3261f5f5", 295 | "sha256:6bac97e51f66da2ca012adddbe9fd656b17f7368d439de30898f24b39512f40f", 296 | "sha256:6d46aa46aa29b067f0b8b84f448fd9719aaf5f4c621cc279164d76a9dc9ab3e8", 297 | "sha256:71f0a5918aebbea1e7db2179a8fe87e8a8732340d9e8b8107401fb407eda446e", 298 | "sha256:74418f718083009108dc9a42c21bf2e4802d49638a1249e13677585fcc9ca176", 299 | "sha256:760c25ed6ac968e24803eb5f7deb17ce026902d39865e83036bacbf5cf242aa8", 300 | "sha256:822ca79e28720a76a935c228d37da6579edef048a17cd98d406a2484d10eda78", 301 | "sha256:86605d5bac340beb030cbc35859325982a79047ebdfba1e553719c7126a2389d", 302 | "sha256:87597e3d57ec0120a3e1d857a708f80c02c42ea6b00227c728efbc860f067c45", 303 | "sha256:8983c5c06ac9c990eac5eb97a9f03fe41dc7e9d7993df74d9e8682a1056f596c", 304 | "sha256:8c659f9fb8a2f16dc4131b803fa0144c1dadcb3ab24bb7914d01a6da58ae2457", 305 | "sha256:8e695f25d1a425ad7a272902af8ab8c8d66c1998b177e4b5f5e7b4e215d0c88a", 306 | "sha256:8f8ed5053ef9fb08d34f1fd80ff093ccbd1f67f147633a84cf4a7d9b09c0f089", 307 | "sha256:92267f865c7bbd12327a0d394666948b9bf4b51308b52947c0cc453bfa812f5d", 308 | "sha256:98e4bbecbef8d2a60ecf731d735602feee5ac0b32117dbbc765e28b054bac912", 309 | "sha256:9e716f9012148a81f02f46a04fc4c663420c6fbfeacfac0b5e128cf43b4413d3", 310 | "sha256:9f2a6623057989ebc469cd9cc8fe436c40117a0147627568d03f84aef7854c55", 311 | "sha256:a218f85081fc3f70cddaed694323a1ad7db5ca028c379c214e3a7c11c0850523", 312 | "sha256:aa346e202e6e1ebc01fe1c69509cffe486425884b96cb9ce155c99da1ecbe0e9", 313 | "sha256:ad8ba80cdcea04bea7b78fcd4925bfbf408961e9d8397d2ee5d3ec121e20c08c", 314 | "sha256:afb39550205cc5e5c935762c6bf6a2bb34f7d21a68eadb25e2db7bf3593fecc0", 315 | "sha256:b2922a0e8fa97395553c304edc3bd36168d8eeec26b92478e292e5d4445c1ef0", 316 | "sha256:b47395091e7e0ece1e6ebac9b98bf0c9084d1e3d3b2739aa566be7e56e3f7bf2", 317 | "sha256:c0ecf4786ad0404b072196b5df774b1bb23c8aacdcacb6c10b4128bc7b00bd01", 318 | "sha256:c5b31bed2c2f2fa1fcb4815b75f931121ae210dc89a3d607fb1725f5907f1437", 319 | "sha256:c724a884e642aa2bbad52bb0203ea40406ad742368a5f90da1b220e970384aae", 320 | "sha256:cb92741c2b4ea63c09609b064b26f7f5d9032b61ae222558c55832ec3ad0bcaf", 321 | "sha256:ced0925a18fddcff289ef54386b2fc230c5af3c83b11558571124bfc485b8c07", 322 | "sha256:cf1115207a5049d1f4b7b4b72de0e52f228d6c696803d94843907111cbf80610", 323 | "sha256:d3c9a07eafdc70556f8c220da4a538e715668c0c63cabcc436a026e4e89950bf", 324 | "sha256:d7769c579663a6f8dbf34878969ac71befa42067ce6bf78e6370bf0d1194997c", 325 | "sha256:d8f89c8d20dfa648a3f0a56861946eb00e5b00d6b00eea14bc5532b2fcfa8ef1", 326 | "sha256:d998b432ed9ffccc49b820e913c8f327a82026349e9c34fa3690116f6b70770f", 327 | "sha256:dcbe48f6a03979384f27086484dc2a14959be1613cb173458bd58f714f2c48f3", 328 | "sha256:e17b5b42c8045867ca9d1f54af00cc2275198d38de18545edaa7833d7e9e4ac8", 329 | "sha256:e18875e17ef69ba7dfa9623f2f95f3eda6f70b536079ee6d5763ecdfe6cc9040", 330 | "sha256:e61ab234624c9ffca0248a707feffe6fac2343758a36725d8eb8a6efef0f8c30", 331 | "sha256:ecc2c526547eacd20cb9fbba19a5268611dbc70c346499656d6cf30fae328977", 332 | "sha256:f33462b19503ba68d80dac8a1354402675849259fb3ebf53b67de86421735a3a", 333 | "sha256:fbedeb9b48614d662822ee514567d2d49a8012037fc7b4cd63f282642c2f4b7d", 334 | "sha256:fd98cacf4e0fabcd4005c452cb8a31750258a85cab9a59fb3559e8078da408d7", 335 | "sha256:fdcd095b1b812d756fa5452aca93b962cf620694c0cadb192cec2bb77dcca9a2" 336 | ], 337 | "markers": "python_version >= '3.9'", 338 | "version": "==0.6.3" 339 | }, 340 | "markdown-it-py": { 341 | "hashes": [ 342 | "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", 343 | "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3" 344 | ], 345 | "markers": "python_version >= '3.10'", 346 | "version": "==4.0.0" 347 | }, 348 | "mdurl": { 349 | "hashes": [ 350 | "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", 351 | "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" 352 | ], 353 | "markers": "python_version >= '3.7'", 354 | "version": "==0.1.2" 355 | }, 356 | "more-itertools": { 357 | "hashes": [ 358 | "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", 359 | "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd" 360 | ], 361 | "markers": "python_version >= '3.9'", 362 | "version": "==10.8.0" 363 | }, 364 | "mypy": { 365 | "hashes": [ 366 | "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9", 367 | "sha256:0dde5cb375cb94deff0d4b548b993bec52859d1651e073d63a1386d392a95495", 368 | "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018", 369 | "sha256:0ea4fd21bb48f0da49e6d3b37ef6bd7e8228b9fe41bbf4d80d9364d11adbd43c", 370 | "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d", 371 | "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab", 372 | "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b", 373 | "sha256:16f76ff3f3fd8137aadf593cb4607d82634fca675e8211ad75c43d86033ee6c6", 374 | "sha256:1cf9c59398db1c68a134b0b5354a09a1e124523f00bacd68e553b8bd16ff3299", 375 | "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e", 376 | "sha256:3210d87b30e6af9c8faed61be2642fcbe60ef77cec64fa1ef810a630a4cf671c", 377 | "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7", 378 | "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364", 379 | "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1", 380 | "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee", 381 | "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2", 382 | "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8", 383 | "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835", 384 | "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e", 385 | "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7", 386 | "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f", 387 | "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18", 388 | "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106", 389 | "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39", 390 | "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6", 391 | "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e", 392 | "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134", 393 | "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53", 394 | "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7", 395 | "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3", 396 | "sha256:e2c1101ab41d01303103ab6ef82cbbfedb81c1a060c868fa7cc013d573d37ab5", 397 | "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431", 398 | "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d", 399 | "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760", 400 | "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528", 401 | "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7", 402 | "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba", 403 | "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d" 404 | ], 405 | "index": "pypi", 406 | "markers": "python_version >= '3.9'", 407 | "version": "==1.19.0" 408 | }, 409 | "mypy-extensions": { 410 | "hashes": [ 411 | "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", 412 | "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558" 413 | ], 414 | "markers": "python_version >= '3.8'", 415 | "version": "==1.1.0" 416 | }, 417 | "nh3": { 418 | "hashes": [ 419 | "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80", 420 | "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5", 421 | "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31", 422 | "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99", 423 | "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131", 424 | "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b", 425 | "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0", 426 | "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93", 427 | "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131", 428 | "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b", 429 | "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5", 430 | "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130", 431 | "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe", 432 | "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87", 433 | "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e", 434 | "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866", 435 | "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7", 436 | "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6", 437 | "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868", 438 | "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8", 439 | "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", 440 | "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d", 441 | "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13", 442 | "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07", 443 | "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376", 444 | "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a" 445 | ], 446 | "markers": "python_version >= '3.8'", 447 | "version": "==0.3.2" 448 | }, 449 | "numpy": { 450 | "hashes": [ 451 | "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", 452 | "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", 453 | "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", 454 | "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", 455 | "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", 456 | "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", 457 | "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", 458 | "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", 459 | "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", 460 | "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", 461 | "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", 462 | "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", 463 | "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", 464 | "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", 465 | "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", 466 | "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", 467 | "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", 468 | "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", 469 | "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", 470 | "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", 471 | "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", 472 | "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", 473 | "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", 474 | "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", 475 | "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", 476 | "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", 477 | "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", 478 | "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", 479 | "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", 480 | "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", 481 | "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", 482 | "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", 483 | "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", 484 | "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", 485 | "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", 486 | "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", 487 | "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", 488 | "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", 489 | "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", 490 | "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", 491 | "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", 492 | "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", 493 | "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", 494 | "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", 495 | "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", 496 | "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", 497 | "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", 498 | "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", 499 | "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", 500 | "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", 501 | "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", 502 | "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", 503 | "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", 504 | "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", 505 | "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", 506 | "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", 507 | "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", 508 | "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", 509 | "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", 510 | "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", 511 | "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", 512 | "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", 513 | "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", 514 | "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", 515 | "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", 516 | "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", 517 | "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", 518 | "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", 519 | "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", 520 | "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", 521 | "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", 522 | "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", 523 | "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", 524 | "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520" 525 | ], 526 | "index": "pypi", 527 | "markers": "python_version >= '3.11'", 528 | "version": "==2.3.5" 529 | }, 530 | "packaging": { 531 | "hashes": [ 532 | "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", 533 | "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" 534 | ], 535 | "markers": "python_version >= '3.8'", 536 | "version": "==25.0" 537 | }, 538 | "pathspec": { 539 | "hashes": [ 540 | "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", 541 | "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" 542 | ], 543 | "markers": "python_version >= '3.8'", 544 | "version": "==0.12.1" 545 | }, 546 | "pygments": { 547 | "hashes": [ 548 | "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", 549 | "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" 550 | ], 551 | "markers": "python_version >= '3.8'", 552 | "version": "==2.19.2" 553 | }, 554 | "pyproject-hooks": { 555 | "hashes": [ 556 | "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", 557 | "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913" 558 | ], 559 | "markers": "python_version >= '3.7'", 560 | "version": "==1.2.0" 561 | }, 562 | "readme-renderer": { 563 | "hashes": [ 564 | "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", 565 | "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1" 566 | ], 567 | "markers": "python_version >= '3.9'", 568 | "version": "==44.0" 569 | }, 570 | "requests": { 571 | "hashes": [ 572 | "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", 573 | "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" 574 | ], 575 | "markers": "python_version >= '3.9'", 576 | "version": "==2.32.5" 577 | }, 578 | "requests-toolbelt": { 579 | "hashes": [ 580 | "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", 581 | "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06" 582 | ], 583 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 584 | "version": "==1.0.0" 585 | }, 586 | "rfc3986": { 587 | "hashes": [ 588 | "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", 589 | "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c" 590 | ], 591 | "markers": "python_version >= '3.7'", 592 | "version": "==2.0.0" 593 | }, 594 | "rich": { 595 | "hashes": [ 596 | "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", 597 | "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd" 598 | ], 599 | "markers": "python_full_version >= '3.8.0'", 600 | "version": "==14.2.0" 601 | }, 602 | "setuptools": { 603 | "hashes": [ 604 | "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", 605 | "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" 606 | ], 607 | "index": "pypi", 608 | "markers": "python_version >= '3.9'", 609 | "version": "==80.9.0" 610 | }, 611 | "toml": { 612 | "hashes": [ 613 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 614 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 615 | ], 616 | "index": "pypi", 617 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", 618 | "version": "==0.10.2" 619 | }, 620 | "twine": { 621 | "hashes": [ 622 | "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", 623 | "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf" 624 | ], 625 | "index": "pypi", 626 | "markers": "python_version >= '3.9'", 627 | "version": "==6.2.0" 628 | }, 629 | "types-setuptools": { 630 | "hashes": [ 631 | "sha256:070ea7716968ec67a84c7f7768d9952ff24d28b65b6594797a464f1b3066f965", 632 | "sha256:53bf881cb9d7e46ed12c76ef76c0aaf28cfe6211d3fab12e0b83620b1a8642c3" 633 | ], 634 | "index": "pypi", 635 | "markers": "python_version >= '3.9'", 636 | "version": "==80.9.0.20250822" 637 | }, 638 | "typing-extensions": { 639 | "hashes": [ 640 | "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", 641 | "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" 642 | ], 643 | "markers": "python_version >= '3.9'", 644 | "version": "==4.15.0" 645 | }, 646 | "urllib3": { 647 | "hashes": [ 648 | "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", 649 | "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" 650 | ], 651 | "markers": "python_version >= '3.9'", 652 | "version": "==2.5.0" 653 | } 654 | } 655 | } 656 | --------------------------------------------------------------------------------