├── src └── fiobank │ ├── py.typed │ ├── __init__.py │ ├── exceptions.py │ ├── utils.py │ └── fiobank.py ├── token.png ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── copilot-setup-steps.yml │ ├── test.yml │ └── release.yml ├── LICENSE ├── tests ├── test_coerce_date.py ├── test_sanitize_value.py ├── transactions.json └── test_fiobank.py ├── pyproject.toml ├── .gitignore ├── README.rst └── uv.lock /src/fiobank/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/honzajavorek/fiobank/HEAD/token.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: honzajavorek 2 | custom: "https://junior.guru/love/" 3 | -------------------------------------------------------------------------------- /src/fiobank/__init__.py: -------------------------------------------------------------------------------- 1 | from .exceptions import ThrottlingError 2 | from .fiobank import FioBank 3 | 4 | 5 | __all__ = ("FioBank", "ThrottlingError") 6 | -------------------------------------------------------------------------------- /src/fiobank/exceptions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | 4 | class ThrottlingError(Exception): 5 | """Throttling error raised when the API is being used too fast.""" 6 | 7 | def __str__(self) -> str: 8 | return "Token can be used only once per 30s" 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 1 8 | labels: 9 | - dependencies 10 | - package-ecosystem: github-actions 11 | directory: "/" 12 | schedule: 13 | interval: daily 14 | open-pull-requests-limit: 1 15 | labels: 16 | - dependencies 17 | -------------------------------------------------------------------------------- /.github/workflows/copilot-setup-steps.yml: -------------------------------------------------------------------------------- 1 | name: "Copilot Setup Steps" 2 | 3 | # Automatically run the setup steps when they are changed to allow for easy validation, and 4 | # allow manual testing through the repository's "Actions" tab 5 | on: 6 | workflow_dispatch: 7 | push: 8 | paths: 9 | - .github/workflows/copilot-setup-steps.yml 10 | pull_request: 11 | paths: 12 | - .github/workflows/copilot-setup-steps.yml 13 | 14 | jobs: 15 | copilot-setup-steps: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v6 22 | 23 | - uses: astral-sh/setup-uv@v7 24 | 25 | - name: Install 26 | run: uv sync 27 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | python-version: 13 | - "3.9" 14 | - "3.10" 15 | - "3.11" 16 | - "3.12" 17 | - "3.13" 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v6 21 | 22 | - name: Setup Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v6 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | 27 | - uses: astral-sh/setup-uv@v7 28 | 29 | - name: Install 30 | run: uv sync 31 | 32 | - name: Test 33 | run: uv run pytest 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025, Honza Javorek 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /tests/test_coerce_date.py: -------------------------------------------------------------------------------- 1 | from datetime import date, datetime 2 | 3 | import pytest 4 | 5 | from fiobank.utils import coerce_date 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "test_input", 10 | [ 11 | date(2016, 8, 3), 12 | datetime(2016, 8, 3, 21, 3, 42), 13 | "2016-08-03T21:03:42", 14 | ], 15 | ) 16 | def test_coerce_date(test_input: "date | datetime | str"): 17 | assert coerce_date(test_input) == date(2016, 8, 3) 18 | 19 | 20 | @pytest.mark.parametrize("test_input", [42, True]) 21 | def test_coerce_date_invalid_type(test_input: "int | bool"): 22 | with pytest.raises(TypeError): 23 | coerce_date(test_input) # type: ignore 24 | 25 | 26 | @pytest.mark.parametrize("test_input", ["21:03:42", "fio@fio.cz"]) 27 | def test_coerce_date_invalid_value(test_input: str): 28 | with pytest.raises(ValueError): 29 | coerce_date(test_input) 30 | -------------------------------------------------------------------------------- /src/fiobank/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import date, datetime 4 | from decimal import Decimal 5 | from typing import Any, Callable 6 | 7 | 8 | def coerce_amount(value: int | float) -> Decimal: 9 | if isinstance(value, int): 10 | return Decimal(value) 11 | if isinstance(value, float): 12 | return Decimal(str(value)) 13 | raise ValueError(value) 14 | 15 | 16 | def coerce_date(value: datetime | date | str) -> date: 17 | if isinstance(value, datetime): 18 | return value.date() 19 | if isinstance(value, date): 20 | return value 21 | return datetime.strptime(value[:10], "%Y-%m-%d").date() 22 | 23 | 24 | def sanitize_value(value: Any, convert: Callable | None = None) -> Any: 25 | if isinstance(value, str): 26 | value = value.strip() or None 27 | if convert and value is not None: 28 | return convert(value) 29 | return value 30 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | environment: 11 | name: pypi 12 | url: https://pypi.org/p/fiobank 13 | permissions: 14 | id-token: write 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v6 18 | 19 | - uses: astral-sh/setup-uv@v7 20 | 21 | - name: Build 22 | run: uv build 23 | 24 | - name: Smoke Test 25 | run: | 26 | uv venv .smoketest 27 | uv pip install --python=.smoketest/bin/python ./dist/fiobank-*.tar.gz 28 | .smoketest/bin/python -c 'from fiobank import FioBank' 29 | rm -r .smoketest 30 | 31 | - name: Publish package 32 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'honzajavorek/fiobank' 33 | run: uv publish --trusted-publishing always 34 | -------------------------------------------------------------------------------- /tests/test_sanitize_value.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable 2 | 3 | import pytest 4 | 5 | from fiobank.utils import sanitize_value 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "test_input, expected", 10 | [ 11 | ("fio", "fio"), 12 | (0, 0), 13 | (False, False), 14 | (None, None), 15 | (123, 123), 16 | (30.8, 30.8), 17 | ], 18 | ) 19 | def test_sanitize_value_no_effect(test_input: Any, expected: Any): 20 | assert sanitize_value(test_input) == expected 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "test_input, expected", 25 | [ 26 | ("", None), 27 | (" \n ", None), 28 | ("\nfio ", "fio"), 29 | ], 30 | ) 31 | def test_sanitize_value_strip(test_input: str, expected: "str | None"): 32 | assert sanitize_value(test_input) == expected 33 | 34 | 35 | @pytest.mark.parametrize( 36 | "test_input, convert, expected", 37 | [ 38 | ("abc", str, "abc"), 39 | ("žluťoučký kůň", str, "žluťoučký kůň"), 40 | (None, str, None), 41 | (None, bool, None), 42 | (123, str, "123"), 43 | (30.8, str, "30.8"), 44 | (0, int, 0), 45 | (30.8, int, 30), 46 | (False, bool, False), 47 | ], 48 | ) 49 | def test_sanitize_value_convert(test_input: Any, convert: Callable, expected: Any): 50 | assert sanitize_value(test_input, convert) == expected 51 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "fiobank" 3 | version = "4.0.0" 4 | description = "Fio Bank API in Python (unofficial)" 5 | readme = "README.rst" 6 | authors = [{name = "Honza Javorek", email = "mail@honzajavorek.cz"}] 7 | license = {text = "ICS"} 8 | requires-python = "<4.0,>=3.9" 9 | dependencies = [ 10 | "requests<3.0.0,>=2.28.2", 11 | "tenacity>=9.0.0", 12 | ] 13 | classifiers = [ 14 | "Development Status :: 5 - Production/Stable", 15 | "Intended Audience :: Developers", 16 | "License :: OSI Approved :: ISC License (ISCL)", 17 | "Programming Language :: Python", 18 | "Programming Language :: Python :: 3", 19 | "Topic :: Software Development :: Libraries :: Python Modules", 20 | "Topic :: Internet", 21 | ] 22 | 23 | [project.urls] 24 | repository = "https://github.com/honzajavorek/fiobank" 25 | 26 | [dependency-groups] 27 | dev = [ 28 | "mock>=5.0.1", 29 | "pytest-cov>=6.0.0", 30 | "pytest-ruff>=0.4.1", 31 | "pytest>=7.2.1", 32 | "responses>=0.22.0", 33 | "ruff>=0.8.4", 34 | ] 35 | 36 | [tool.pytest.ini_options] 37 | pythonpath = ["."] 38 | testpaths = "tests" 39 | norecursedirs = "env venv .git" 40 | addopts = "--ff --ruff --ruff-format --cov=fiobank --cov-report=term-missing:skip-covered --cov-context=test" 41 | filterwarnings = ["ignore:Using float for money can cause inaccuracies:DeprecationWarning"] 42 | 43 | [tool.ruff.lint] 44 | extend-select = ["I", "UP"] 45 | 46 | [tool.ruff.lint.isort] 47 | combine-as-imports = true 48 | lines-after-imports = 2 49 | 50 | [build-system] 51 | requires = ["hatchling"] 52 | build-backend = "hatchling.build" 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | .smoketest/ 133 | 134 | # Spyder project settings 135 | .spyderproject 136 | .spyproject 137 | 138 | # Rope project settings 139 | .ropeproject 140 | 141 | # mkdocs documentation 142 | /site 143 | 144 | # mypy 145 | .mypy_cache/ 146 | .dmypy.json 147 | dmypy.json 148 | 149 | # Pyre type checker 150 | .pyre/ 151 | 152 | # pytype static type analyzer 153 | .pytype/ 154 | 155 | # Cython debug symbols 156 | cython_debug/ 157 | 158 | # pytest, ruff 159 | .pytest_cache 160 | .ruff_cache 161 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | fiobank 2 | ======= 3 | 4 | `Fio Bank API `_ 5 | in Python. 6 | 7 | Installation 8 | ------------ 9 | 10 | From PyPI:: 11 | 12 | pip install fiobank 13 | 14 | In case you have an adventurous mind, give a try to the source:: 15 | 16 | pip install git+https://github.com/honzajavorek/fiobank.git#egg=fiobank 17 | 18 | Usage 19 | ----- 20 | 21 | First, get your API token. 22 | 23 | .. image:: token.png 24 | 25 | Initialization of the client: 26 | 27 | .. code:: python 28 | 29 | >>> from fiobank import FioBank 30 | >>> client = FioBank(token='...', decimal=True) 31 | 32 | Account information: 33 | 34 | .. code:: python 35 | 36 | >>> client.info() 37 | { 38 | 'currency': 'CZK', 39 | 'account_number_full': 'XXXXXXXXXX/2010', 40 | 'balance': Decimal('42.00'), 41 | 'account_number': 'XXXXXXXXXX', 42 | 'bank_code': '2010' 43 | } 44 | 45 | Listing transactions within a period: 46 | 47 | .. code:: python 48 | 49 | >>> gen = client.period('2013-01-20', '2013-03-20') 50 | >>> list(gen)[0] 51 | { 52 | 'comment': 'N\xe1kup: IKEA CR, BRNO, CZ, dne 17.1.2013, \u010d\xe1stka 2769.00 CZK', 53 | 'recipient_message': 'N\xe1kup: IKEA CR, BRNO, CZ, dne 17.1.2013, \u010d\xe1stka 2769.00 CZK', 54 | 'user_identification': 'N\xe1kup: IKEA CR, BRNO, CZ, dne 17.1.2013, \u010d\xe1stka 2769.00 CZK', 55 | 'currency': 'CZK', 56 | 'amount': Decimal('-2769.0'), 57 | 'instruction_id': 'XXXXXXXXXX', 58 | 'executor': 'Vilém Fusek', 59 | 'date': datetime.date(2013, 1, 20), 60 | 'type': 'Platba kartou', 61 | 'transaction_id': 'XXXXXXXXXX' 62 | } 63 | 64 | Getting transactions with account information in one request: 65 | 66 | .. code:: python 67 | 68 | >>> client.transactions('2013-01-20', '2013-03-20') 69 | ( 70 | {'currency': 'CZK', 'account_number_full': 'XXXXXXXXXX/2010', 'balance': 42.00, 'account_number': 'XXXXXXXXXX', 'bank_code': '2010'}, 71 | 'transactions': 72 | ) 73 | 74 | Listing transactions from a single account statement: 75 | 76 | .. code:: python 77 | 78 | >>> client.statement(2013, 1) # 1 is January only by coincidence - arguments mean 'first statement of 2013' 79 | 80 | Listing the latest transactions: 81 | 82 | .. code:: python 83 | 84 | >>> client.last() # return transactions added from last listing 85 | >>> client.last(from_id='...') # sets cursor to given transaction_id and returns following transactions 86 | >>> client.last(from_date='2013-03-01') # sets cursor to given date and returns following transactions 87 | 88 | Getting the latest transactions with account information in one request: 89 | 90 | .. code:: python 91 | 92 | >>> client.last_transactions() 93 | ( 94 | {'currency': 'CZK', 'account_number_full': 'XXXXXXXXXX/2010', 'balance': 42.00, 'account_number': 'XXXXXXXXXX', 'bank_code': '2010'}, 95 | 'transactions': 96 | ) 97 | 98 | Conflict Error 99 | -------------- 100 | 101 | `Fio API documentation `_ 102 | (Section 8.3) states that a single token should be used only once per 103 | 30s. Otherwise, an HTTP 409 Conflict will be returned and 104 | ``fiobank.ThrottlingError`` will be raised. 105 | 106 | Development 107 | ----------- 108 | 109 | Install using `uv `_:: 110 | 111 | git clone git@github.com:honzajavorek/fiobank.git 112 | cd fiobank 113 | uv sync 114 | 115 | Then run tests:: 116 | 117 | uv run pytest 118 | 119 | License: ISC 120 | ------------ 121 | 122 | © 2025 Honza Javorek 123 | 124 | This work is licensed under the `ISC 125 | license `_. 126 | -------------------------------------------------------------------------------- /tests/transactions.json: -------------------------------------------------------------------------------- 1 | { 2 | "accountStatement": { 3 | "info": { 4 | "dateStart": "2016-08-03+0200", 5 | "idList": null, 6 | "idLastDownload": null, 7 | "closingBalance": 2060.52, 8 | "bic": "FIOBCZPPXXX", 9 | "yearList": null, 10 | "idTo": 10000000001, 11 | "currency": "CZK", 12 | "openingBalance": 2543.81, 13 | "iban": "CZ1220100000001234567890", 14 | "idFrom": 10000000002, 15 | "bankId": "2010", 16 | "dateEnd": "2016-08-03+0200", 17 | "accountId": "1234567890" 18 | }, 19 | "transactionList": { 20 | "transaction": [ 21 | { 22 | "column18": null, 23 | "column26": null, 24 | "column10": null, 25 | "column12": null, 26 | "column14": { 27 | "name": "M\u011bna", 28 | "value": "CZK", 29 | "id": 14 30 | }, 31 | "column17": { 32 | "name": "ID pokynu", 33 | "value": 12210748893, 34 | "id": 17 35 | }, 36 | "column16": { 37 | "name": "Zpr\u00e1va pro p\u0159\u00edjemce", 38 | "value": "N\u00e1kup: ORDR, PRAGUE, CZ, dne 1.8.2016, \u010d\u00e1stka 130.00 CZK", 39 | "id": 16 40 | }, 41 | "column22": { 42 | "name": "ID pohybu", 43 | "value": 10000000002, 44 | "id": 22 45 | }, 46 | "column9": { 47 | "name": "Provedl", 48 | "value": "Javorek, Jan", 49 | "id": 9 50 | }, 51 | "column8": { 52 | "name": "Typ", 53 | "value": "Platba kartou", 54 | "id": 8 55 | }, 56 | "column25": { 57 | "name": "Koment\u00e1\u0159", 58 | "value": "N\u00e1kup: ORDR, PRAGUE, CZ, dne 1.8.2016, \u010d\u00e1stka 130.00 CZK", 59 | "id": 25 60 | }, 61 | "column5": { 62 | "name": "VS", 63 | "value": "5678", 64 | "id": 5 65 | }, 66 | "column4": null, 67 | "column7": { 68 | "name": "U\u017eivatelsk\u00e1 identifikace", 69 | "value": "N\u00e1kup: ORDR, PRAGUE, CZ, dne 1.8.2016, \u010d\u00e1stka 130.00 CZK", 70 | "id": 7 71 | }, 72 | "column6": null, 73 | "column1": { 74 | "name": "Objem", 75 | "value": -130.0, 76 | "id": 1 77 | }, 78 | "column0": { 79 | "name": "Datum", 80 | "value": "2016-08-03+0200", 81 | "id": 0 82 | }, 83 | "column3": null, 84 | "column2": null 85 | }, 86 | { 87 | "column18": null, 88 | "column26": null, 89 | "column10": null, 90 | "column12": null, 91 | "column14": { 92 | "name": "M\u011bna", 93 | "value": "CZK", 94 | "id": 14 95 | }, 96 | "column17": { 97 | "name": "ID pokynu", 98 | "value": 12210832097, 99 | "id": 17 100 | }, 101 | "column16": { 102 | "name": "Zpr\u00e1va pro p\u0159\u00edjemce", 103 | "value": "N\u00e1kup: Billa Ul. Konevova, Praha - Vitko, CZ, dne 1.8.2016, \u010d\u00e1stka 353.29 CZK", 104 | "id": 16 105 | }, 106 | "column22": { 107 | "name": "ID pohybu", 108 | "value": 10000000001, 109 | "id": 22 110 | }, 111 | "column9": { 112 | "name": "Provedl", 113 | "value": "Javorek, Jan", 114 | "id": 9 115 | }, 116 | "column8": { 117 | "name": "Typ", 118 | "value": "Platba kartou", 119 | "id": 8 120 | }, 121 | "column25": { 122 | "name": "Koment\u00e1\u0159", 123 | "value": "N\u00e1kup: Billa Ul. Konevova, Praha - Vitko, CZ, dne 1.8.2016, \u010d\u00e1stka 353.29 CZK", 124 | "id": 25 125 | }, 126 | "column5": { 127 | "name": "VS", 128 | "value": "1234", 129 | "id": 5 130 | }, 131 | "column4": null, 132 | "column7": { 133 | "name": "U\u017eivatelsk\u00e1 identifikace", 134 | "value": "N\u00e1kup: Billa Ul. Konevova, Praha - Vitko, CZ, dne 1.8.2016, \u010d\u00e1stka 353.29 CZK", 135 | "id": 7 136 | }, 137 | "column6": null, 138 | "column1": { 139 | "name": "Objem", 140 | "value": -353.29, 141 | "id": 1 142 | }, 143 | "column0": { 144 | "name": "Datum", 145 | "value": "2016-08-03+0200", 146 | "id": 0 147 | }, 148 | "column3": null, 149 | "column2": null 150 | } 151 | ] 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/fiobank/fiobank.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import re 4 | import warnings 5 | from collections.abc import Generator 6 | from datetime import date, datetime 7 | from decimal import Decimal 8 | 9 | import requests 10 | from tenacity import ( 11 | retry, 12 | retry_if_exception_type, 13 | stop_after_attempt, 14 | wait_random_exponential, 15 | ) 16 | 17 | from .exceptions import ThrottlingError 18 | from .utils import coerce_date, sanitize_value 19 | 20 | 21 | class FioBank: 22 | base_url = "https://fioapi.fio.cz/v1/rest/" 23 | 24 | actions = { 25 | "periods": "periods/{token}/{from_date}/{to_date}/transactions.json", 26 | "by-id": "by-id/{token}/{year}/{number}/transactions.json", 27 | "last": "last/{token}/transactions.json", 28 | "set-last-id": "set-last-id/{token}/{from_id}/", 29 | "set-last-date": "set-last-date/{token}/{from_date}/", 30 | } 31 | 32 | _amount_re = re.compile(r"\-?\d+(\.\d+)? [A-Z]{3}") 33 | 34 | def __init__(self, token: str, *, decimal: bool = False): 35 | self.token = token 36 | 37 | if decimal: 38 | self.float_type = Decimal 39 | else: 40 | warnings.warn( 41 | ( 42 | "Using float for money can cause inaccuracies. " 43 | "Use FioBank(..., decimal=True) for Decimal objects instead. " 44 | "This will be the default in the future versions." 45 | ), 46 | DeprecationWarning, 47 | ) 48 | self.float_type = float 49 | 50 | # http://www.fio.cz/xsd/IBSchema.xsd 51 | self.transaction_schema = { 52 | "column0": ("date", coerce_date), 53 | "column1": ("amount", self.float_type), 54 | "column2": ("account_number", str), 55 | "column3": ("bank_code", str), 56 | "column4": ("constant_symbol", str), 57 | "column5": ("variable_symbol", str), 58 | "column6": ("specific_symbol", str), 59 | "column7": ("user_identification", str), 60 | "column8": ("type", str), 61 | "column9": ("executor", str), 62 | "column10": ("account_name", str), 63 | "column12": ("bank_name", str), 64 | "column14": ("currency", str), 65 | "column16": ("recipient_message", str), 66 | "column17": ("instruction_id", str), 67 | "column18": ("specification", str), 68 | "column22": ("transaction_id", str), 69 | "column25": ("comment", str), 70 | "column26": ("bic", str), 71 | "column27": ("reference", str), 72 | } 73 | self.info_schema = { 74 | "accountid": ("account_number", str), 75 | "bankid": ("bank_code", str), 76 | "currency": ("currency", str), 77 | "iban": ("iban", str), 78 | "bic": ("bic", str), 79 | "closingbalance": ("balance", self.float_type), 80 | } 81 | 82 | @retry( 83 | retry=retry_if_exception_type(ThrottlingError), 84 | reraise=True, 85 | stop=stop_after_attempt(3), 86 | wait=wait_random_exponential(max=2 * 60), 87 | ) 88 | def _request(self, action: str, **params) -> dict | None: 89 | url_template = self.base_url + self.actions[action] 90 | url = url_template.format(token=self.token, **params) 91 | 92 | response = requests.get(url) 93 | if response.status_code == requests.codes["conflict"]: 94 | raise ThrottlingError() 95 | response.raise_for_status() 96 | 97 | if response.content: 98 | return response.json(parse_float=self.float_type) 99 | return None 100 | 101 | def _parse_info(self, data: dict) -> dict: 102 | # parse data from API 103 | info = {} 104 | for key, value in data["accountStatement"]["info"].items(): 105 | key = key.lower() 106 | if key in self.info_schema: 107 | field_name, convert = self.info_schema[key] 108 | value = sanitize_value(value, convert) 109 | info[field_name] = value 110 | 111 | # make some refinements 112 | self._add_account_number_full(info) 113 | 114 | # return data 115 | return info 116 | 117 | def _parse_transactions(self, data: dict) -> Generator[dict, None, None]: 118 | schema = self.transaction_schema 119 | try: 120 | entries = data["accountStatement"]["transactionList"]["transaction"] 121 | except TypeError: 122 | entries = [] 123 | 124 | for entry in entries: 125 | # parse entry from API 126 | trans = {} 127 | for column_name, column_data in entry.items(): 128 | if not column_data: 129 | continue 130 | field_name, convert = schema[column_name.lower()] 131 | value = sanitize_value(column_data["value"], convert) 132 | trans[field_name] = value 133 | 134 | # add missing fileds with None values 135 | for column_data_name, (field_name, convert) in schema.items(): 136 | trans.setdefault(field_name, None) 137 | 138 | # make some refinements 139 | specification = trans.get("specification") 140 | is_amount = self._amount_re.match 141 | if specification is not None and is_amount(specification): 142 | amount, currency = trans["specification"].split(" ") 143 | trans["original_amount"] = self.float_type(amount) 144 | trans["original_currency"] = currency 145 | else: 146 | trans["original_amount"] = None 147 | trans["original_currency"] = None 148 | 149 | self._add_account_number_full(trans) 150 | 151 | # generate transaction data 152 | yield trans 153 | 154 | def _add_account_number_full(self, obj: dict) -> None: 155 | account_number = obj.get("account_number") 156 | bank_code = obj.get("bank_code") 157 | 158 | if account_number is not None and bank_code is not None: 159 | account_number_full = f"{account_number}/{bank_code}" 160 | else: 161 | account_number_full = None 162 | 163 | obj["account_number_full"] = account_number_full 164 | 165 | def info(self) -> dict: 166 | today = date.today() 167 | if data := self._request("periods", from_date=today, to_date=today): 168 | return self._parse_info(data) 169 | raise ValueError("No data available") 170 | 171 | def _fetch_period( 172 | self, from_date: str | date | datetime, to_date: str | date | datetime 173 | ) -> dict: 174 | if data := self._request( 175 | "periods", from_date=coerce_date(from_date), to_date=coerce_date(to_date) 176 | ): 177 | return data 178 | raise ValueError("No data available") 179 | 180 | def period( 181 | self, from_date: str | date | datetime, to_date: str | date | datetime 182 | ) -> Generator[dict]: 183 | data = self._fetch_period(from_date, to_date) 184 | return self._parse_transactions(data) 185 | 186 | def transactions( 187 | self, from_date: str | date | datetime, to_date: str | date | datetime 188 | ) -> tuple[dict, Generator[dict]]: 189 | if data := self._fetch_period(from_date, to_date): 190 | return (self._parse_info(data), self._parse_transactions(data)) 191 | raise ValueError("No data available") 192 | 193 | def statement(self, year: int, number: int) -> Generator[dict]: 194 | if data := self._request("by-id", year=year, number=number): 195 | return self._parse_transactions(data) 196 | raise ValueError("No data available") 197 | 198 | def _fetch_last( 199 | self, from_id: str | None = None, from_date: str | date | datetime | None = None 200 | ) -> dict: 201 | if from_id and from_date: 202 | raise ValueError("Only one constraint is allowed.") 203 | 204 | if from_id: 205 | self._request("set-last-id", from_id=from_id) 206 | elif from_date: 207 | self._request("set-last-date", from_date=coerce_date(from_date)) 208 | 209 | if data := self._request("last"): 210 | return data 211 | raise ValueError("No data available") 212 | 213 | def last( 214 | self, from_id: str | None = None, from_date: str | date | datetime | None = None 215 | ) -> Generator[dict]: 216 | return self._parse_transactions(self._fetch_last(from_id, from_date)) 217 | 218 | def last_transactions( 219 | self, from_id: str | None = None, from_date: str | date | datetime | None = None 220 | ) -> tuple[dict, Generator[dict]]: 221 | data = self._fetch_last(from_id, from_date) 222 | return (self._parse_info(data), self._parse_transactions(data)) 223 | -------------------------------------------------------------------------------- /tests/test_fiobank.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import json 4 | import os 5 | import re 6 | import uuid 7 | from datetime import date 8 | from decimal import Decimal 9 | from unittest import mock 10 | 11 | import pytest 12 | import requests 13 | import responses 14 | from responses.registries import OrderedRegistry 15 | 16 | from fiobank import FioBank 17 | 18 | 19 | @pytest.fixture() 20 | def token() -> str: 21 | return str(uuid.uuid4()) 22 | 23 | 24 | @pytest.fixture() 25 | def transactions_text() -> str: 26 | with open(os.path.dirname(__file__) + "/transactions.json") as f: 27 | return f.read() 28 | 29 | 30 | @pytest.fixture() 31 | def transactions_json() -> dict: 32 | with open(os.path.dirname(__file__) + "/transactions.json") as f: 33 | return json.load(f) 34 | 35 | 36 | @pytest.fixture() 37 | def client_float(token: str, transactions_text: str): 38 | with responses.RequestsMock(assert_all_requests_are_fired=False) as resps: 39 | url = re.compile( 40 | re.escape(FioBank.base_url) + rf"[^/]+/{token}/([^/]+/)*transactions\.json" 41 | ) 42 | resps.add(responses.GET, url, body=transactions_text) 43 | 44 | url = re.compile(re.escape(FioBank.base_url) + rf"set-last-\w+/{token}/[^/]+/") 45 | resps.add(responses.GET, url) 46 | 47 | yield FioBank(token) 48 | 49 | 50 | @pytest.fixture() 51 | def client_decimal(token: str, transactions_text: str): 52 | with responses.RequestsMock(assert_all_requests_are_fired=False) as resps: 53 | url = re.compile( 54 | re.escape(FioBank.base_url) + rf"[^/]+/{token}/([^/]+/)*transactions\.json" 55 | ) 56 | resps.add(responses.GET, url, body=transactions_text) 57 | 58 | url = re.compile(re.escape(FioBank.base_url) + rf"set-last-\w+/{token}/[^/]+/") 59 | resps.add(responses.GET, url) 60 | 61 | yield FioBank(token, decimal=True) 62 | 63 | 64 | def test_client_decimal(client_decimal: FioBank): 65 | transaction = next(client_decimal.last()) 66 | info = client_decimal.info() 67 | 68 | assert client_decimal.float_type is Decimal 69 | assert transaction["amount"] == Decimal("-130.0") 70 | assert info["balance"] == Decimal("2060.52") 71 | 72 | 73 | def test_info_integration(client_float: FioBank): 74 | assert frozenset(client_float.info().keys()) == frozenset( 75 | [ 76 | "account_number_full", 77 | "account_number", 78 | "bank_code", 79 | "currency", 80 | "iban", 81 | "bic", 82 | "balance", 83 | ] 84 | ) 85 | 86 | 87 | def test_info_uses_today(transactions_json: dict): 88 | client = FioBank("...") 89 | today = date.today() 90 | 91 | with mock.patch.object(client, "_request", return_value=transactions_json) as stub: 92 | client.info() 93 | stub.assert_called_once_with("periods", from_date=today, to_date=today) 94 | 95 | 96 | def test_info_is_case_insensitive(transactions_json): 97 | client = FioBank("...") 98 | 99 | api_info = transactions_json["accountStatement"]["info"] 100 | value = api_info["accountId"] 101 | del api_info["accountId"] 102 | api_info["acCOUNTid"] = value 103 | 104 | sdk_info = client._parse_info(transactions_json) 105 | 106 | assert sdk_info["account_number"] == value 107 | 108 | 109 | @pytest.mark.parametrize( 110 | "api_key, sdk_key", 111 | [ 112 | ("accountId", "account_number"), 113 | ("bankId", "bank_code"), 114 | ("currency", "currency"), 115 | ("iban", "iban"), 116 | ("bic", "bic"), 117 | ("closingBalance", "balance"), 118 | ], 119 | ) 120 | def test_info_parse(transactions_json, api_key, sdk_key): 121 | client = FioBank("...") 122 | 123 | api_info = transactions_json["accountStatement"]["info"] 124 | sdk_info = client._parse_info(transactions_json) 125 | 126 | assert sdk_info[sdk_key] == api_info[api_key] 127 | 128 | 129 | def test_info_parse_account_number_full(transactions_json): 130 | client = FioBank("...") 131 | 132 | api_info = transactions_json["accountStatement"]["info"] 133 | sdk_info = client._parse_info(transactions_json) 134 | 135 | expected_value = "{}/{}".format(api_info["accountId"], api_info["bankId"]) 136 | assert sdk_info["account_number_full"] == expected_value 137 | 138 | 139 | def test_info_parse_no_account_number_full(transactions_json): 140 | client = FioBank("...") 141 | 142 | api_info = transactions_json["accountStatement"]["info"] 143 | del api_info["bankId"] 144 | 145 | sdk_info = client._parse_info(transactions_json) 146 | 147 | assert sdk_info["account_number_full"] is None 148 | 149 | 150 | @pytest.mark.parametrize( 151 | "method, args, kwargs", 152 | [ 153 | ("period", [date(2016, 8, 4), date(2016, 8, 30)], {}), 154 | ("period", ["2016-08-04", "2016-08-30"], {}), 155 | ("statement", [2016, 308], {}), 156 | ("last", [], {"from_id": 308}), 157 | ("last", [], {"from_date": date(2016, 8, 4)}), 158 | ("last", [], {"from_date": "2016-08-04"}), 159 | ], 160 | ) 161 | def test_transactions_integration(client_float, method, args, kwargs): 162 | gen = getattr(client_float, method)(*args, **kwargs) 163 | 164 | count = 0 165 | for record in gen: 166 | count += 1 167 | assert frozenset(record.keys()) == frozenset( 168 | [ 169 | "transaction_id", 170 | "date", 171 | "amount", 172 | "currency", 173 | "account_number", 174 | "account_name", 175 | "bank_code", 176 | "bic", 177 | "bank_name", 178 | "constant_symbol", 179 | "variable_symbol", 180 | "specific_symbol", 181 | "user_identification", 182 | "recipient_message", 183 | "type", 184 | "executor", 185 | "specification", 186 | "comment", 187 | "instruction_id", 188 | "account_number_full", 189 | "original_amount", 190 | "original_currency", 191 | "reference", 192 | ] 193 | ) 194 | 195 | assert count > 0 196 | 197 | 198 | @pytest.mark.parametrize( 199 | "args,kwargs", 200 | [ 201 | ([date(2016, 8, 4), date(2016, 8, 30)], {}), 202 | (["2016-08-04", "2016-08-30"], {}), 203 | ], 204 | ) 205 | def test_transactions(client_decimal, args, kwargs): 206 | info, transactions = client_decimal.transactions(*args, **kwargs) 207 | transaction = next(transactions) 208 | assert transaction["amount"] == Decimal("-130.0") 209 | assert info["balance"] == Decimal("2060.52") 210 | 211 | 212 | @pytest.mark.parametrize( 213 | "args,kwargs", 214 | [ 215 | ([], {"from_id": 308}), 216 | ([], {"from_date": date(2016, 8, 4)}), 217 | ([], {"from_date": "2016-08-04"}), 218 | ], 219 | ) 220 | def test_last_transactions(client_decimal, args, kwargs): 221 | info, transactions = client_decimal.last_transactions(*args, **kwargs) 222 | transaction = next(transactions) 223 | assert transaction["amount"] == Decimal("-130.0") 224 | assert info["balance"] == Decimal("2060.52") 225 | 226 | 227 | def test_period_coerces_date(transactions_json): 228 | client = FioBank("...") 229 | 230 | from_date = "2016-08-04T09:36:42" 231 | to_date = "2016-08-30T11:45:38" 232 | 233 | options = {"return_value": transactions_json} 234 | with mock.patch.object(client, "_request", **options) as stub: 235 | client.period(from_date, to_date) 236 | stub.assert_called_once_with( 237 | "periods", from_date=date(2016, 8, 4), to_date=date(2016, 8, 30) 238 | ) 239 | 240 | 241 | def test_statement(transactions_json): 242 | client = FioBank("...") 243 | 244 | options = {"return_value": transactions_json} 245 | with mock.patch.object(client, "_request", **options) as stub: 246 | client.statement(2016, 308) 247 | stub.assert_called_once_with("by-id", year=2016, number=308) 248 | 249 | 250 | def test_last_conflicting_params(): 251 | client = FioBank("...") 252 | with pytest.raises(ValueError): 253 | client.last(from_id=308, from_date=date(2016, 8, 30)) 254 | 255 | 256 | def test_last_from_id(transactions_json): 257 | client = FioBank("...") 258 | 259 | options = {"return_value": transactions_json} 260 | with mock.patch.object(client, "_request", **options) as stub: 261 | client.last(from_id=308) 262 | stub.assert_has_calls( 263 | [ 264 | mock.call("set-last-id", from_id=308), 265 | mock.call("last"), 266 | ] 267 | ) 268 | 269 | 270 | @pytest.mark.parametrize("test_input", [date(2016, 8, 30), "2016-08-30"]) 271 | def test_last_from_date(transactions_json, test_input): 272 | client = FioBank("...") 273 | 274 | options = {"return_value": transactions_json} 275 | with mock.patch.object(client, "_request", **options) as stub: 276 | client.last(from_date=test_input) 277 | stub.assert_has_calls( 278 | [ 279 | mock.call("set-last-date", from_date=date(2016, 8, 30)), 280 | mock.call("last"), 281 | ] 282 | ) 283 | 284 | 285 | def test_transaction_schema_is_complete(): 286 | response = requests.get("http://www.fio.cz/xsd/IBSchema.xsd") 287 | response.raise_for_status() 288 | 289 | columns_in_xsd = set() 290 | 291 | element_re = re.compile(r'<\w+:element[^>]+name="column_(\d+)') 292 | for match in element_re.finditer(response.text): 293 | column_name = f"column{match.group(1)}" 294 | columns_in_xsd.add(column_name) 295 | 296 | assert frozenset(FioBank("...").transaction_schema.keys()) == columns_in_xsd 297 | 298 | 299 | @pytest.mark.parametrize( 300 | "api_key, sdk_key, sdk_type", 301 | [ 302 | (api_key, sdk_key, sdk_type) 303 | for api_key, (sdk_key, sdk_type) in FioBank("...").transaction_schema.items() 304 | ], 305 | ) 306 | def test_transactions_parse(transactions_json, api_key, sdk_key, sdk_type): 307 | client = FioBank("...") 308 | 309 | api_transactions = transactions_json["accountStatement"]["transactionList"][ 310 | "transaction" 311 | ] # NOQA 312 | 313 | # The 'transactions.json' file is based on real data, so it doesn't 314 | # contain some values. To test all values, we use dummy data here. 315 | dummy_mapping = {"column0": "2015-08-30"} 316 | dummy_default = 30.8 317 | for api_transaction in api_transactions: 318 | dummy_value = dummy_mapping.get(api_key, dummy_default) 319 | api_transaction[api_key] = {"value": dummy_value} 320 | 321 | sdk_transactions = list(client._parse_transactions(transactions_json)) 322 | assert len(sdk_transactions) == len(api_transactions) 323 | 324 | for i in range(len(api_transactions)): 325 | api_transaction = api_transactions[i] 326 | sdk_transaction = sdk_transactions[i] 327 | 328 | assert sdk_transaction[sdk_key] == sdk_type(api_transaction[api_key]["value"]) 329 | 330 | 331 | def test_transactions_parse_unsanitized(transactions_json): 332 | client = FioBank("...") 333 | 334 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 335 | "transaction" 336 | ][0] # NOQA 337 | api_transaction["column10"] = {"value": " Honza\n"} 338 | 339 | sdk_transaction = next(client._parse_transactions(transactions_json)) 340 | 341 | assert sdk_transaction["account_name"] == "Honza" 342 | 343 | 344 | def test_transactions_parse_convert(transactions_json): 345 | client = FioBank("...") 346 | 347 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 348 | "transaction" 349 | ][0] # NOQA 350 | api_transaction["column0"] = {"value": "2015-08-30"} 351 | 352 | sdk_transaction = next(client._parse_transactions(transactions_json)) 353 | 354 | assert sdk_transaction["date"] == date(2015, 8, 30) 355 | 356 | 357 | def test_transactions_parse_none(transactions_json): 358 | client = FioBank("...") 359 | 360 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 361 | "transaction" 362 | ][0] # NOQA 363 | sdk_transaction = next(client._parse_transactions(transactions_json)) 364 | 365 | assert api_transaction["column10"] is None 366 | assert sdk_transaction["account_name"] is None 367 | 368 | 369 | def test_transactions_parse_missing(transactions_json): 370 | client = FioBank("...") 371 | 372 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 373 | "transaction" 374 | ][0] # NOQA 375 | del api_transaction["column10"] 376 | 377 | sdk_transaction = next(client._parse_transactions(transactions_json)) 378 | 379 | assert "column10" not in api_transaction 380 | assert sdk_transaction["account_name"] is None 381 | 382 | 383 | @pytest.mark.parametrize( 384 | "test_input", 385 | [ 386 | "650.00 HRK", 387 | "-308 EUR", 388 | "46052.01 HUF", 389 | ], 390 | ) 391 | def test_amount_re(test_input): 392 | assert FioBank._amount_re.match(test_input) 393 | 394 | 395 | @pytest.mark.parametrize( 396 | "test_input, amount, currency", 397 | [ 398 | ("650.00 HRK", 650.0, "HRK"), 399 | ("-308 EUR", -308.0, "EUR"), 400 | ("46052.01 HUF", 46052.01, "HUF"), 401 | ], 402 | ) 403 | def test_transactions_parse_amount_as_float( 404 | transactions_json, test_input, amount, currency 405 | ): 406 | client = FioBank("...") 407 | 408 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 409 | "transaction" 410 | ][0] # NOQA 411 | api_transaction["column18"] = {"value": test_input} 412 | 413 | sdk_transaction = next(client._parse_transactions(transactions_json)) 414 | 415 | assert sdk_transaction["specification"] == test_input 416 | assert sdk_transaction["original_amount"] == amount 417 | assert sdk_transaction["original_currency"] == currency 418 | 419 | 420 | @pytest.mark.parametrize( 421 | "test_input, amount, currency", 422 | [ 423 | ("650.00 HRK", Decimal("650.0"), "HRK"), 424 | ("-308 EUR", Decimal("-308.0"), "EUR"), 425 | ("46052.01 HUF", Decimal("46052.01"), "HUF"), 426 | ], 427 | ) 428 | def test_transactions_parse_amount_as_decimal( 429 | transactions_json, test_input, amount, currency 430 | ): 431 | client = FioBank("...", decimal=True) 432 | 433 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 434 | "transaction" 435 | ][0] # NOQA 436 | api_transaction["column18"] = {"value": test_input} 437 | 438 | sdk_transaction = next(client._parse_transactions(transactions_json)) 439 | 440 | assert sdk_transaction["specification"] == test_input 441 | assert sdk_transaction["original_amount"] == amount 442 | assert sdk_transaction["original_currency"] == currency 443 | 444 | 445 | def test_transactions_parse_account_number_full(transactions_json): 446 | client = FioBank("...") 447 | 448 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 449 | "transaction" 450 | ][0] # NOQA 451 | api_transaction["column2"] = {"value": 10000000002} 452 | api_transaction["column3"] = {"value": "2010"} 453 | 454 | sdk_transaction = next(client._parse_transactions(transactions_json)) 455 | 456 | assert sdk_transaction["account_number_full"] == "10000000002/2010" 457 | 458 | 459 | def test_transactions_parse_no_account_number_full(transactions_json): 460 | client = FioBank("...") 461 | 462 | api_transaction = transactions_json["accountStatement"]["transactionList"][ 463 | "transaction" 464 | ][0] # NOQA 465 | api_transaction["column2"] = {"value": 10000000002} 466 | api_transaction["column3"] = {"value": None} 467 | 468 | sdk_transaction = next(client._parse_transactions(transactions_json)) 469 | 470 | assert sdk_transaction["account_number_full"] is None 471 | 472 | 473 | def test_409_conflict(token: str, transactions_text: str): 474 | with responses.RequestsMock(registry=OrderedRegistry) as resps: 475 | url = re.compile( 476 | re.escape(FioBank.base_url) + rf"[^/]+/{token}/([^/]+/)*transactions\.json" 477 | ) 478 | resps.add(responses.GET, url, status=409) 479 | resps.add(responses.GET, url, body=transactions_text) 480 | client = FioBank(token, decimal=True) 481 | transaction = next(client.last()) 482 | 483 | assert transaction["amount"] == Decimal("-130.0") 484 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.9, <4.0" 3 | 4 | [[package]] 5 | name = "certifi" 6 | version = "2024.12.14" 7 | source = { registry = "https://pypi.org/simple" } 8 | sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } 9 | wheels = [ 10 | { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, 11 | ] 12 | 13 | [[package]] 14 | name = "charset-normalizer" 15 | version = "3.4.1" 16 | source = { registry = "https://pypi.org/simple" } 17 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } 18 | wheels = [ 19 | { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, 20 | { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, 21 | { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, 22 | { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, 23 | { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, 24 | { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, 25 | { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, 26 | { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, 27 | { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, 28 | { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, 29 | { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, 30 | { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, 31 | { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, 32 | { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, 33 | { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, 34 | { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, 35 | { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, 36 | { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, 37 | { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, 38 | { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, 39 | { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, 40 | { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, 41 | { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, 42 | { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, 43 | { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, 44 | { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, 45 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, 46 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, 47 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, 48 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, 49 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, 50 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, 51 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, 52 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, 53 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, 54 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, 55 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, 56 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, 57 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, 58 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, 59 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, 60 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, 61 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, 62 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, 63 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, 64 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, 65 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, 66 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, 67 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, 68 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, 69 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, 70 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, 71 | { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 }, 72 | { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 }, 73 | { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 }, 74 | { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 }, 75 | { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 }, 76 | { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 }, 77 | { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 }, 78 | { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 }, 79 | { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 }, 80 | { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 }, 81 | { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 }, 82 | { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 }, 83 | { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 }, 84 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, 85 | ] 86 | 87 | [[package]] 88 | name = "colorama" 89 | version = "0.4.6" 90 | source = { registry = "https://pypi.org/simple" } 91 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 92 | wheels = [ 93 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 94 | ] 95 | 96 | [[package]] 97 | name = "coverage" 98 | version = "7.6.10" 99 | source = { registry = "https://pypi.org/simple" } 100 | sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868 } 101 | wheels = [ 102 | { url = "https://files.pythonhosted.org/packages/c5/12/2a2a923edf4ddabdffed7ad6da50d96a5c126dae7b80a33df7310e329a1e/coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78", size = 207982 }, 103 | { url = "https://files.pythonhosted.org/packages/ca/49/6985dbca9c7be3f3cb62a2e6e492a0c88b65bf40579e16c71ae9c33c6b23/coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c", size = 208414 }, 104 | { url = "https://files.pythonhosted.org/packages/35/93/287e8f1d1ed2646f4e0b2605d14616c9a8a2697d0d1b453815eb5c6cebdb/coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a", size = 236860 }, 105 | { url = "https://files.pythonhosted.org/packages/de/e1/cfdb5627a03567a10031acc629b75d45a4ca1616e54f7133ca1fa366050a/coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165", size = 234758 }, 106 | { url = "https://files.pythonhosted.org/packages/6d/85/fc0de2bcda3f97c2ee9fe8568f7d48f7279e91068958e5b2cc19e0e5f600/coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988", size = 235920 }, 107 | { url = "https://files.pythonhosted.org/packages/79/73/ef4ea0105531506a6f4cf4ba571a214b14a884630b567ed65b3d9c1975e1/coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5", size = 234986 }, 108 | { url = "https://files.pythonhosted.org/packages/c6/4d/75afcfe4432e2ad0405c6f27adeb109ff8976c5e636af8604f94f29fa3fc/coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3", size = 233446 }, 109 | { url = "https://files.pythonhosted.org/packages/86/5b/efee56a89c16171288cafff022e8af44f8f94075c2d8da563c3935212871/coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5", size = 234566 }, 110 | { url = "https://files.pythonhosted.org/packages/f2/db/67770cceb4a64d3198bf2aa49946f411b85ec6b0a9b489e61c8467a4253b/coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244", size = 210675 }, 111 | { url = "https://files.pythonhosted.org/packages/8d/27/e8bfc43f5345ec2c27bc8a1fa77cdc5ce9dcf954445e11f14bb70b889d14/coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e", size = 211518 }, 112 | { url = "https://files.pythonhosted.org/packages/85/d2/5e175fcf6766cf7501a8541d81778fd2f52f4870100e791f5327fd23270b/coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3", size = 208088 }, 113 | { url = "https://files.pythonhosted.org/packages/4b/6f/06db4dc8fca33c13b673986e20e466fd936235a6ec1f0045c3853ac1b593/coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43", size = 208536 }, 114 | { url = "https://files.pythonhosted.org/packages/0d/62/c6a0cf80318c1c1af376d52df444da3608eafc913b82c84a4600d8349472/coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132", size = 240474 }, 115 | { url = "https://files.pythonhosted.org/packages/a3/59/750adafc2e57786d2e8739a46b680d4fb0fbc2d57fbcb161290a9f1ecf23/coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f", size = 237880 }, 116 | { url = "https://files.pythonhosted.org/packages/2c/f8/ef009b3b98e9f7033c19deb40d629354aab1d8b2d7f9cfec284dbedf5096/coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994", size = 239750 }, 117 | { url = "https://files.pythonhosted.org/packages/a6/e2/6622f3b70f5f5b59f705e680dae6db64421af05a5d1e389afd24dae62e5b/coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99", size = 238642 }, 118 | { url = "https://files.pythonhosted.org/packages/2d/10/57ac3f191a3c95c67844099514ff44e6e19b2915cd1c22269fb27f9b17b6/coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd", size = 237266 }, 119 | { url = "https://files.pythonhosted.org/packages/ee/2d/7016f4ad9d553cabcb7333ed78ff9d27248ec4eba8dd21fa488254dff894/coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377", size = 238045 }, 120 | { url = "https://files.pythonhosted.org/packages/a7/fe/45af5c82389a71e0cae4546413266d2195c3744849669b0bab4b5f2c75da/coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8", size = 210647 }, 121 | { url = "https://files.pythonhosted.org/packages/db/11/3f8e803a43b79bc534c6a506674da9d614e990e37118b4506faf70d46ed6/coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609", size = 211508 }, 122 | { url = "https://files.pythonhosted.org/packages/86/77/19d09ea06f92fdf0487499283b1b7af06bc422ea94534c8fe3a4cd023641/coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", size = 208281 }, 123 | { url = "https://files.pythonhosted.org/packages/b6/67/5479b9f2f99fcfb49c0d5cf61912a5255ef80b6e80a3cddba39c38146cf4/coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", size = 208514 }, 124 | { url = "https://files.pythonhosted.org/packages/15/d1/febf59030ce1c83b7331c3546d7317e5120c5966471727aa7ac157729c4b/coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", size = 241537 }, 125 | { url = "https://files.pythonhosted.org/packages/4b/7e/5ac4c90192130e7cf8b63153fe620c8bfd9068f89a6d9b5f26f1550f7a26/coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", size = 238572 }, 126 | { url = "https://files.pythonhosted.org/packages/dc/03/0334a79b26ecf59958f2fe9dd1f5ab3e2f88db876f5071933de39af09647/coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", size = 240639 }, 127 | { url = "https://files.pythonhosted.org/packages/d7/45/8a707f23c202208d7b286d78ad6233f50dcf929319b664b6cc18a03c1aae/coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", size = 240072 }, 128 | { url = "https://files.pythonhosted.org/packages/66/02/603ce0ac2d02bc7b393279ef618940b4a0535b0868ee791140bda9ecfa40/coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", size = 238386 }, 129 | { url = "https://files.pythonhosted.org/packages/04/62/4e6887e9be060f5d18f1dd58c2838b2d9646faf353232dec4e2d4b1c8644/coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", size = 240054 }, 130 | { url = "https://files.pythonhosted.org/packages/5c/74/83ae4151c170d8bd071924f212add22a0e62a7fe2b149edf016aeecad17c/coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", size = 210904 }, 131 | { url = "https://files.pythonhosted.org/packages/c3/54/de0893186a221478f5880283119fc40483bc460b27c4c71d1b8bba3474b9/coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", size = 211692 }, 132 | { url = "https://files.pythonhosted.org/packages/25/6d/31883d78865529257bf847df5789e2ae80e99de8a460c3453dbfbe0db069/coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", size = 208308 }, 133 | { url = "https://files.pythonhosted.org/packages/70/22/3f2b129cc08de00c83b0ad6252e034320946abfc3e4235c009e57cfeee05/coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", size = 208565 }, 134 | { url = "https://files.pythonhosted.org/packages/97/0a/d89bc2d1cc61d3a8dfe9e9d75217b2be85f6c73ebf1b9e3c2f4e797f4531/coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", size = 241083 }, 135 | { url = "https://files.pythonhosted.org/packages/4c/81/6d64b88a00c7a7aaed3a657b8eaa0931f37a6395fcef61e53ff742b49c97/coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", size = 238235 }, 136 | { url = "https://files.pythonhosted.org/packages/9a/0b/7797d4193f5adb4b837207ed87fecf5fc38f7cc612b369a8e8e12d9fa114/coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", size = 240220 }, 137 | { url = "https://files.pythonhosted.org/packages/65/4d/6f83ca1bddcf8e51bf8ff71572f39a1c73c34cf50e752a952c34f24d0a60/coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", size = 239847 }, 138 | { url = "https://files.pythonhosted.org/packages/30/9d/2470df6aa146aff4c65fee0f87f58d2164a67533c771c9cc12ffcdb865d5/coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", size = 237922 }, 139 | { url = "https://files.pythonhosted.org/packages/08/dd/723fef5d901e6a89f2507094db66c091449c8ba03272861eaefa773ad95c/coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", size = 239783 }, 140 | { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965 }, 141 | { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719 }, 142 | { url = "https://files.pythonhosted.org/packages/ab/c9/f2857a135bcff4330c1e90e7d03446b036b2363d4ad37eb5e3a47bbac8a6/coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", size = 209050 }, 143 | { url = "https://files.pythonhosted.org/packages/aa/b3/f840e5bd777d8433caa9e4a1eb20503495709f697341ac1a8ee6a3c906ad/coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", size = 209321 }, 144 | { url = "https://files.pythonhosted.org/packages/85/7d/125a5362180fcc1c03d91850fc020f3831d5cda09319522bcfa6b2b70be7/coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", size = 252039 }, 145 | { url = "https://files.pythonhosted.org/packages/a9/9c/4358bf3c74baf1f9bddd2baf3756b54c07f2cfd2535f0a47f1e7757e54b3/coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", size = 247758 }, 146 | { url = "https://files.pythonhosted.org/packages/cf/c7/de3eb6fc5263b26fab5cda3de7a0f80e317597a4bad4781859f72885f300/coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", size = 250119 }, 147 | { url = "https://files.pythonhosted.org/packages/3e/e6/43de91f8ba2ec9140c6a4af1102141712949903dc732cf739167cfa7a3bc/coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", size = 249597 }, 148 | { url = "https://files.pythonhosted.org/packages/08/40/61158b5499aa2adf9e37bc6d0117e8f6788625b283d51e7e0c53cf340530/coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", size = 247473 }, 149 | { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737 }, 150 | { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 }, 151 | { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 }, 152 | { url = "https://files.pythonhosted.org/packages/40/41/473617aadf9a1c15bc2d56be65d90d7c29bfa50a957a67ef96462f7ebf8e/coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a", size = 207978 }, 153 | { url = "https://files.pythonhosted.org/packages/10/f6/480586607768b39a30e6910a3c4522139094ac0f1677028e1f4823688957/coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27", size = 208415 }, 154 | { url = "https://files.pythonhosted.org/packages/f1/af/439bb760f817deff6f4d38fe7da08d9dd7874a560241f1945bc3b4446550/coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4", size = 236452 }, 155 | { url = "https://files.pythonhosted.org/packages/d0/13/481f4ceffcabe29ee2332e60efb52e4694f54a402f3ada2bcec10bb32e43/coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f", size = 234374 }, 156 | { url = "https://files.pythonhosted.org/packages/c5/59/4607ea9d6b1b73e905c7656da08d0b00cdf6e59f2293ec259e8914160025/coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25", size = 235505 }, 157 | { url = "https://files.pythonhosted.org/packages/85/60/d66365723b9b7f29464b11d024248ed3523ce5aab958e4ad8c43f3f4148b/coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315", size = 234616 }, 158 | { url = "https://files.pythonhosted.org/packages/74/f8/2cf7a38e7d81b266f47dfcf137fecd8fa66c7bdbd4228d611628d8ca3437/coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90", size = 233099 }, 159 | { url = "https://files.pythonhosted.org/packages/50/2b/bff6c1c6b63c4396ea7ecdbf8db1788b46046c681b8fcc6ec77db9f4ea49/coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d", size = 234089 }, 160 | { url = "https://files.pythonhosted.org/packages/bf/b5/baace1c754d546a67779358341aa8d2f7118baf58cac235db457e1001d1b/coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18", size = 210701 }, 161 | { url = "https://files.pythonhosted.org/packages/b1/bf/9e1e95b8b20817398ecc5a1e8d3e05ff404e1b9fb2185cd71561698fe2a2/coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59", size = 211482 }, 162 | { url = "https://files.pythonhosted.org/packages/a1/70/de81bfec9ed38a64fc44a77c7665e20ca507fc3265597c28b0d989e4082e/coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f", size = 200223 }, 163 | ] 164 | 165 | [package.optional-dependencies] 166 | toml = [ 167 | { name = "tomli", marker = "python_full_version <= '3.11'" }, 168 | ] 169 | 170 | [[package]] 171 | name = "exceptiongroup" 172 | version = "1.2.2" 173 | source = { registry = "https://pypi.org/simple" } 174 | sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } 175 | wheels = [ 176 | { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, 177 | ] 178 | 179 | [[package]] 180 | name = "fiobank" 181 | version = "4.0.0" 182 | source = { virtual = "." } 183 | dependencies = [ 184 | { name = "requests" }, 185 | { name = "tenacity" }, 186 | ] 187 | 188 | [package.dev-dependencies] 189 | dev = [ 190 | { name = "mock" }, 191 | { name = "pytest" }, 192 | { name = "pytest-cov" }, 193 | { name = "pytest-ruff" }, 194 | { name = "responses" }, 195 | { name = "ruff" }, 196 | ] 197 | 198 | [package.metadata] 199 | requires-dist = [ 200 | { name = "requests", specifier = ">=2.28.2,<3.0.0" }, 201 | { name = "tenacity", specifier = ">=9.0.0" }, 202 | ] 203 | 204 | [package.metadata.requires-dev] 205 | dev = [ 206 | { name = "mock", specifier = ">=5.0.1" }, 207 | { name = "pytest", specifier = ">=7.2.1" }, 208 | { name = "pytest-cov", specifier = ">=6.0.0" }, 209 | { name = "pytest-ruff", specifier = ">=0.4.1" }, 210 | { name = "responses", specifier = ">=0.22.0" }, 211 | { name = "ruff", specifier = ">=0.8.4" }, 212 | ] 213 | 214 | [[package]] 215 | name = "idna" 216 | version = "3.10" 217 | source = { registry = "https://pypi.org/simple" } 218 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 219 | wheels = [ 220 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 221 | ] 222 | 223 | [[package]] 224 | name = "iniconfig" 225 | version = "2.0.0" 226 | source = { registry = "https://pypi.org/simple" } 227 | sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } 228 | wheels = [ 229 | { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, 230 | ] 231 | 232 | [[package]] 233 | name = "mock" 234 | version = "5.1.0" 235 | source = { registry = "https://pypi.org/simple" } 236 | sdist = { url = "https://files.pythonhosted.org/packages/66/ab/41d09a46985ead5839d8be987acda54b5bb93f713b3969cc0be4f81c455b/mock-5.1.0.tar.gz", hash = "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d", size = 80232 } 237 | wheels = [ 238 | { url = "https://files.pythonhosted.org/packages/6b/20/471f41173930550f279ccb65596a5ac19b9ac974a8d93679bcd3e0c31498/mock-5.1.0-py3-none-any.whl", hash = "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", size = 30938 }, 239 | ] 240 | 241 | [[package]] 242 | name = "packaging" 243 | version = "24.2" 244 | source = { registry = "https://pypi.org/simple" } 245 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 246 | wheels = [ 247 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 248 | ] 249 | 250 | [[package]] 251 | name = "pluggy" 252 | version = "1.5.0" 253 | source = { registry = "https://pypi.org/simple" } 254 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } 255 | wheels = [ 256 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, 257 | ] 258 | 259 | [[package]] 260 | name = "pytest" 261 | version = "7.4.4" 262 | source = { registry = "https://pypi.org/simple" } 263 | dependencies = [ 264 | { name = "colorama", marker = "sys_platform == 'win32'" }, 265 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 266 | { name = "iniconfig" }, 267 | { name = "packaging" }, 268 | { name = "pluggy" }, 269 | { name = "tomli", marker = "python_full_version < '3.11'" }, 270 | ] 271 | sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116 } 272 | wheels = [ 273 | { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287 }, 274 | ] 275 | 276 | [[package]] 277 | name = "pytest-cov" 278 | version = "6.0.0" 279 | source = { registry = "https://pypi.org/simple" } 280 | dependencies = [ 281 | { name = "coverage", extra = ["toml"] }, 282 | { name = "pytest" }, 283 | ] 284 | sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } 285 | wheels = [ 286 | { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, 287 | ] 288 | 289 | [[package]] 290 | name = "pytest-ruff" 291 | version = "0.4.1" 292 | source = { registry = "https://pypi.org/simple" } 293 | dependencies = [ 294 | { name = "pytest" }, 295 | { name = "ruff" }, 296 | ] 297 | sdist = { url = "https://files.pythonhosted.org/packages/d1/28/4d4e312a16c8969f475f6e18741bc6a2f8ee70d78e16dc3b6deba9f94e6d/pytest_ruff-0.4.1.tar.gz", hash = "sha256:2c9a30f15f384c229c881b52ec86cfaf1e79d39530dd7dd5f2d6aebe278f7eb7", size = 3919 } 298 | wheels = [ 299 | { url = "https://files.pythonhosted.org/packages/0f/7a/ed1015e97fadfde96b2c439390dc5a75542a77982d775baf2ada9f536cac/pytest_ruff-0.4.1-py3-none-any.whl", hash = "sha256:69acd5b2ba68d65998c730b5b4d656788193190e45f61a53aa66ef8b390634a4", size = 4589 }, 300 | ] 301 | 302 | [[package]] 303 | name = "pyyaml" 304 | version = "6.0.2" 305 | source = { registry = "https://pypi.org/simple" } 306 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } 307 | wheels = [ 308 | { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, 309 | { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, 310 | { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, 311 | { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, 312 | { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, 313 | { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, 314 | { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, 315 | { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, 316 | { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, 317 | { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, 318 | { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, 319 | { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, 320 | { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, 321 | { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, 322 | { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, 323 | { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, 324 | { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, 325 | { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, 326 | { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, 327 | { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, 328 | { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, 329 | { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, 330 | { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, 331 | { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, 332 | { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, 333 | { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, 334 | { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, 335 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, 336 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, 337 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, 338 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, 339 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, 340 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, 341 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, 342 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, 343 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, 344 | { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, 345 | { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, 346 | { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, 347 | { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, 348 | { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, 349 | { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, 350 | { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, 351 | { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, 352 | { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, 353 | ] 354 | 355 | [[package]] 356 | name = "requests" 357 | version = "2.32.3" 358 | source = { registry = "https://pypi.org/simple" } 359 | dependencies = [ 360 | { name = "certifi" }, 361 | { name = "charset-normalizer" }, 362 | { name = "idna" }, 363 | { name = "urllib3" }, 364 | ] 365 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } 366 | wheels = [ 367 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, 368 | ] 369 | 370 | [[package]] 371 | name = "responses" 372 | version = "0.25.3" 373 | source = { registry = "https://pypi.org/simple" } 374 | dependencies = [ 375 | { name = "pyyaml" }, 376 | { name = "requests" }, 377 | { name = "urllib3" }, 378 | ] 379 | sdist = { url = "https://files.pythonhosted.org/packages/67/24/1d67c8974daa502e860b4a5b57ad6de0d7dbc0b1160ef7148189a24a40e1/responses-0.25.3.tar.gz", hash = "sha256:617b9247abd9ae28313d57a75880422d55ec63c29d33d629697590a034358dba", size = 77798 } 380 | wheels = [ 381 | { url = "https://files.pythonhosted.org/packages/12/24/93293d0be0db9da1ed8dfc5e6af700fdd40e8f10a928704dd179db9f03c1/responses-0.25.3-py3-none-any.whl", hash = "sha256:521efcbc82081ab8daa588e08f7e8a64ce79b91c39f6e62199b19159bea7dbcb", size = 55238 }, 382 | ] 383 | 384 | [[package]] 385 | name = "ruff" 386 | version = "0.8.4" 387 | source = { registry = "https://pypi.org/simple" } 388 | sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 } 389 | wheels = [ 390 | { url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 }, 391 | { url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 }, 392 | { url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 }, 393 | { url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 }, 394 | { url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 }, 395 | { url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 }, 396 | { url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 }, 397 | { url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 }, 398 | { url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 }, 399 | { url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 }, 400 | { url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 }, 401 | { url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 }, 402 | { url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 }, 403 | { url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 }, 404 | { url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 }, 405 | { url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 }, 406 | { url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 }, 407 | ] 408 | 409 | [[package]] 410 | name = "tenacity" 411 | version = "9.0.0" 412 | source = { registry = "https://pypi.org/simple" } 413 | sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } 414 | wheels = [ 415 | { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, 416 | ] 417 | 418 | [[package]] 419 | name = "tomli" 420 | version = "2.2.1" 421 | source = { registry = "https://pypi.org/simple" } 422 | sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } 423 | wheels = [ 424 | { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, 425 | { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, 426 | { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, 427 | { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, 428 | { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, 429 | { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, 430 | { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, 431 | { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, 432 | { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, 433 | { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, 434 | { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, 435 | { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, 436 | { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, 437 | { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, 438 | { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, 439 | { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, 440 | { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, 441 | { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, 442 | { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, 443 | { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, 444 | { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, 445 | { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, 446 | { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, 447 | { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, 448 | { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, 449 | { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, 450 | { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, 451 | { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, 452 | { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, 453 | { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, 454 | { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, 455 | ] 456 | 457 | [[package]] 458 | name = "urllib3" 459 | version = "2.3.0" 460 | source = { registry = "https://pypi.org/simple" } 461 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } 462 | wheels = [ 463 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, 464 | ] 465 | --------------------------------------------------------------------------------