├── tests ├── __init__.py ├── test_import.py └── test_parse_methods.py ├── data └── .gitignore ├── _config.yml ├── pdf_statement_reader ├── validate.py ├── config │ ├── za │ │ └── absa │ │ │ ├── cheque.json │ │ │ └── cheque-sample.json │ └── psr_config.schema.json ├── decrypt.py ├── parse.py └── __init__.py ├── .github └── workflows │ ├── test.yml │ └── publish.yml ├── LICENSE ├── pyproject.toml ├── .gitignore ├── CONTRIBUTING.md ├── README.md └── uv.lock /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /tests/test_import.py: -------------------------------------------------------------------------------- 1 | import pdf_statement_reader # noqa: F401 2 | 3 | 4 | def test_import(): 5 | assert True 6 | -------------------------------------------------------------------------------- /tests/test_parse_methods.py: -------------------------------------------------------------------------------- 1 | from pdf_statement_reader.parse import format_negatives 2 | 3 | 4 | def test_format_negatives(): 5 | assert format_negatives(123.45) == "123.45" 6 | assert format_negatives(-123.45) == "-123.45" 7 | assert format_negatives(0) == "0" 8 | assert format_negatives("123.45") == "123.45" 9 | assert format_negatives("-123.45") == "-123.45" 10 | assert format_negatives("0") == "0" 11 | assert format_negatives("123.45-") == "-123.45" 12 | -------------------------------------------------------------------------------- /pdf_statement_reader/validate.py: -------------------------------------------------------------------------------- 1 | def validate_statement(df, config): 2 | """Checks that the dataframe statement has a valid rolling balance""" 3 | df = df.copy() 4 | debit = config["columns"]["debit"] 5 | credit = config["columns"]["credit"] 6 | balance = config["columns"]["balance"] 7 | 8 | df[[debit, credit, balance]] = df[[debit, credit, balance]].fillna(0) 9 | 10 | prev_balance = df[balance] - df[credit] + df[debit] 11 | 12 | invalid = (abs(prev_balance.shift(-1) - df[balance]) > 1e-6).any() 13 | 14 | return not invalid 15 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | 14 | - name: Set up Python 15 | uses: actions/setup-python@v2 16 | with: 17 | python-version: "3.13" 18 | 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v5 21 | with: 22 | enable-cache: true 23 | cache-dependency-glob: "uv.lock" 24 | 25 | - name: Install the project 26 | run: uv sync --all-extras --dev 27 | 28 | - name: Run tests 29 | run: uv run pytest -v 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Marlan Perumal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pdf_statement_reader/config/za/absa/cheque.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/marlanperumal/pdf_statement_reader/develop/pdf_statement_reader/config/psr_config.schema.json", 3 | "layout": { 4 | "default": { 5 | "area": [280, 27, 795, 576], 6 | "columns": [83, 263, 264, 344, 425, 485, 570] 7 | }, 8 | "first": { 9 | "area": [480, 27, 763, 576], 10 | "columns": [83, 263, 264, 344, 425, 485, 570] 11 | } 12 | }, 13 | "columns": { 14 | "trans_date": "Date", 15 | "trans_type": "Transaction Description", 16 | "trans_detail": "Transaction Detail", 17 | "charge": "Charge", 18 | "debit": "Debit Amount", 19 | "credit": "Credit Amount", 20 | "balance": "Balance" 21 | }, 22 | "order": [ 23 | "trans_date", 24 | "trans_type", 25 | "trans_detail", 26 | "charge", 27 | "debit", 28 | "credit", 29 | "balance" 30 | ], 31 | "cleaning": { 32 | "numeric": ["debit", "credit", "balance"], 33 | "date": ["trans_date"], 34 | "date_format": "%d/%m/%Y", 35 | "trans_detail": "below", 36 | "dropna": ["balance"] 37 | } 38 | } -------------------------------------------------------------------------------- /pdf_statement_reader/config/za/absa/cheque-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/marlanperumal/pdf_statement_reader/develop/pdf_statement_reader/config/psr_config.schema.json", 3 | "layout": { 4 | "default": { 5 | "area": [183, 78, 731, 530], 6 | "columns": [120, 289, 290, 330, 350, 415, 475, 530] 7 | }, 8 | "first": { 9 | "area": [419, 78, 731, 530], 10 | "columns": [120, 289, 290, 330, 350, 415, 475, 530] 11 | } 12 | }, 13 | "columns": { 14 | "trans_date": "Date", 15 | "trans_type": "Transaction Description", 16 | "trans_detail": "Transaction Detail", 17 | "charge": "Charge", 18 | "charge_type": "Charge Type", 19 | "debit": "Debit Amount", 20 | "credit": "Credit Amount", 21 | "balance": "Balance" 22 | }, 23 | "order": [ 24 | "trans_date", 25 | "trans_type", 26 | "trans_detail", 27 | "charge", 28 | "charge_type", 29 | "debit", 30 | "credit", 31 | "balance" 32 | ], 33 | "cleaning": { 34 | "numeric": ["charge", "debit", "credit", "balance"], 35 | "date": ["trans_date"], 36 | "date_format": "%d/%m/%Y", 37 | "trans_detail": "below", 38 | "dropna": ["balance"] 39 | } 40 | } -------------------------------------------------------------------------------- /pdf_statement_reader/decrypt.py: -------------------------------------------------------------------------------- 1 | from pikepdf import Pdf 2 | import click 3 | 4 | 5 | def decrypt_pdf(input_filename, output_filename=None, password=None): 6 | """Decrypts a pdf file 7 | 8 | Uses pikepdf to open an encrypted pdf file and then save the unencrypted version. 9 | If no output_filename is specified then overwrites the original file. 10 | If no password is supplied then checks if file is already unencrypted 11 | 12 | Args: 13 | input_filename: The source encrypted pdf file 14 | output_filename: The filename to write the decrypted file to 15 | password: The password on the encrypted pdf file 16 | 17 | Returns: 18 | True if decryption succeeded, False if the file was already unencrypted. 19 | If decryption fails, an error is raised 20 | 21 | Raises: 22 | FileNotFoundError: if output cannot be written 23 | PdfError: if the file is not a valid pdf 24 | PasswordError: if incorrect password supplied 25 | """ 26 | if password is None: 27 | pdf = Pdf.open(input_filename) 28 | click.echo("{} was already unencrypted") 29 | return False 30 | 31 | if output_filename is None: 32 | output_filename = input_filename 33 | 34 | pdf = Pdf.open(input_filename, password=password, allow_overwriting_input=True) 35 | pdf.save(output_filename) 36 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Test and Publish 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | 14 | - name: Set up Python 15 | uses: actions/setup-python@v2 16 | with: 17 | python-version: "3.13" 18 | 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v5 21 | with: 22 | enable-cache: true 23 | cache-dependency-glob: "uv.lock" 24 | 25 | - name: Install the project 26 | run: uv sync --all-extras --dev 27 | 28 | - name: Run tests 29 | run: uv run pytest -v 30 | 31 | publish: 32 | needs: test 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Checkout code 36 | uses: actions/checkout@v2 37 | 38 | - name: Set up Python 39 | uses: actions/setup-python@v2 40 | with: 41 | python-version: "3.13" 42 | 43 | - name: Install uv 44 | uses: astral-sh/setup-uv@v5 45 | with: 46 | enable-cache: true 47 | cache-dependency-glob: "uv.lock" 48 | 49 | - name: Install the project 50 | run: uv sync --all-extras --dev 51 | 52 | - name: Build the package 53 | run: uv build 54 | 55 | - name: Publish to PyPi 56 | env: 57 | UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }} 58 | run: uv publish 59 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | urls = { "issues" = "https://github.com/marlanperumal/pdf_statement_reader/issues", "source" = "https://github.com/marlanperumal/pdf_statement_reader", "homepage" = "https://github.com/marlanperumal/pdf_statement_reader" } 3 | name = "pdf-statement-reader" 4 | version = "0.3.4" 5 | description = "PDF Statement Reader" 6 | keywords = ["pdf", "statement", "reader", "bank statement", "digitise"] 7 | authors = [{ name = "Marlan Perumal", email = "marlan.perumal@gmail.com" }] 8 | classifiers = [ 9 | "Development Status :: 3 - Alpha", 10 | "Intended Audience :: Developers", 11 | "Topic :: Office/Business :: Financial", 12 | "Programming Language :: Python :: 3.13", 13 | "License :: OSI Approved :: MIT License", 14 | "Operating System :: OS Independent", 15 | ] 16 | license = { file = "LICENSE" } 17 | readme = "README.md" 18 | requires-python = ">=3.13" 19 | dependencies = ["click>=8.1.8", "pikepdf>=9.5.1", "tabula-py[jpype]>=2.10.0"] 20 | 21 | [dependency-groups] 22 | dev = [ 23 | "coverage>=7.6.10", 24 | "ipykernel>=6.29.5", 25 | "pytest-cov>=6.0.0", 26 | "pytest>=8.3.4", 27 | "coveralls>=4.0.1", 28 | ] 29 | 30 | [project.scripts] 31 | psr = "pdf_statement_reader:cli" 32 | 33 | [tool.pytest.ini_options] 34 | addopts = ["-v", "--cov=pdf_statement_reader", "--import-mode=importlib"] 35 | 36 | [tool.hatch.build] 37 | exclude = [ 38 | "tests/*", 39 | "docs", 40 | "docs/*", 41 | "**/.ipynb_checkpoints", 42 | "**/.ipynb_checkpoints/*", 43 | "tests", 44 | "tests/*", 45 | "*.yml", 46 | ".gitignore", 47 | "uv.lock", 48 | ] 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 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | .ruff_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | 107 | # pdfs and other data files 108 | *.pdf 109 | *.csv 110 | *.json 111 | *.ipynb 112 | 113 | !pdf_statement_reader/config/**/*.json 114 | -------------------------------------------------------------------------------- /pdf_statement_reader/parse.py: -------------------------------------------------------------------------------- 1 | from tabula import read_pdf 2 | from pikepdf import Pdf 3 | import pandas as pd 4 | import numpy as np 5 | 6 | 7 | def get_raw_df(filename, num_pages, config): 8 | dfs = [] 9 | 10 | for i in range(num_pages): 11 | if i == 0 and "first" in config["layout"]: 12 | area = config["layout"]["first"]["area"] 13 | columns = config["layout"]["first"]["columns"] 14 | else: 15 | area = config["layout"]["default"]["area"] 16 | columns = config["layout"]["default"]["columns"] 17 | 18 | df = read_pdf( 19 | filename, 20 | pages=i + 1, 21 | area=area, 22 | columns=columns, 23 | stream=True, 24 | guess=False, 25 | pandas_options={"dtype": str}, 26 | java_options=[ 27 | "-Dorg.slf4j.simpleLogger.defaultLogLevel=off", 28 | "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", 29 | ], 30 | ) 31 | if df is not None and len(df) > 0: 32 | dfs.extend(df) 33 | statement = pd.concat(dfs, sort=False).reset_index(drop=True) 34 | return statement 35 | 36 | 37 | def format_negatives(s): 38 | s = str(s) 39 | if s.endswith("-"): 40 | return "-" + s[:-1] 41 | else: 42 | return s 43 | 44 | 45 | def clean_numeric(df, config): 46 | numeric_cols = [config["columns"][col] for col in config["cleaning"]["numeric"]] 47 | 48 | for col in numeric_cols: 49 | df[col] = df[col].apply(format_negatives) 50 | df[col] = df[col].str.replace(" ", "") 51 | df[col] = pd.to_numeric(df[col], errors="coerce") 52 | 53 | 54 | def clean_date(df, config): 55 | date_cols = [config["columns"][col] for col in config["cleaning"]["date"]] 56 | if "date_format" in config["cleaning"]: 57 | date_format = config["cleaning"]["date_format"] 58 | else: 59 | date_format = None 60 | 61 | for col in date_cols: 62 | df[col] = pd.to_datetime(df[col], errors="coerce", format=date_format) 63 | 64 | 65 | def clean_trans_detail(df, config): 66 | trans_detail = config["columns"]["trans_detail"] 67 | trans_type = config["columns"]["trans_type"] 68 | balance = config["columns"]["balance"] 69 | 70 | for i, row in df.iterrows(): 71 | if i == 0: 72 | continue 73 | if np.isnan(row[balance]): 74 | df.loc[i - 1, trans_detail] = row[trans_type] 75 | 76 | 77 | def clean_dropna(df, config): 78 | drop_cols = [config["columns"][col] for col in config["cleaning"]["dropna"]] 79 | df.dropna(subset=drop_cols, inplace=True) 80 | 81 | 82 | def reorder_columns(df, config): 83 | column_mapper = {a: b for a, b in zip(df.columns, config["columns"].values())} 84 | ordered_columns = [config["columns"][col] for col in config["order"]] 85 | return df.rename(columns=column_mapper)[ordered_columns] 86 | 87 | 88 | def parse_statement(filename, config): 89 | pdf = Pdf.open(filename) 90 | num_pages = len(pdf.pages) 91 | 92 | statement = get_raw_df(filename, num_pages, config) 93 | 94 | if "numeric" in config["cleaning"]: 95 | clean_numeric(statement, config) 96 | 97 | if "date" in config["cleaning"]: 98 | clean_date(statement, config) 99 | 100 | if "order" in config: 101 | statement = reorder_columns(statement, config) 102 | 103 | if "trans_detail" in config["cleaning"]: 104 | clean_trans_detail(statement, config) 105 | 106 | if "dropna" in config["cleaning"]: 107 | clean_dropna(statement, config) 108 | 109 | return statement 110 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PDF Statement Reader 2 | 3 | Thank you for your interest in contributing to PDF Statement Reader! This document provides guidelines and information about contributing to this project. 4 | 5 | ## Getting Started 6 | 7 | 1. Fork the repository on GitHub 8 | 2. Clone your fork locally: 9 | ```bash 10 | git clone git@github.com:your-username/pdf_statement_reader.git 11 | ``` 12 | 3. Set up your development environment: 13 | ```bash 14 | uv sync 15 | ``` 16 | 17 | ## Development Process 18 | 19 | 1. Create a new branch for your feature or bugfix: 20 | ```bash 21 | git checkout -b feature-name 22 | ``` 23 | 24 | 2. Make your changes, following our coding standards 25 | 3. Write or update tests as needed 26 | 4. Run the test suite to ensure everything passes 27 | 5. Commit your changes with a clear commit message following conventional commits format 28 | 6. Push to your fork and submit a pull request 29 | 30 | ## Commit Messages 31 | 32 | We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification for commit messages. Each commit message should be structured as follows: 33 | 34 | ``` 35 | [optional scope]: 36 | 37 | [optional body] 38 | 39 | [optional footer(s)] 40 | ``` 41 | 42 | Types include: 43 | - `feat`: A new feature 44 | - `fix`: A bug fix 45 | - `docs`: Documentation only changes 46 | - `style`: Changes that do not affect the meaning of the code 47 | - `refactor`: A code change that neither fixes a bug nor adds a feature 48 | - `perf`: A code change that improves performance 49 | - `test`: Adding missing tests or correcting existing tests 50 | - `chore`: Changes to the build process or auxiliary tools 51 | 52 | Examples: 53 | ``` 54 | feat(parser): add ability to parse HSBC statements 55 | fix(config): correct column mapping for Absa statements 56 | docs: update configuration guide 57 | test(validation): add tests for balance validation 58 | ``` 59 | 60 | ## Adding New Statement Configurations 61 | 62 | One of the most valuable ways to contribute is by adding support for new bank statement formats: 63 | 64 | 1. Create a new configuration file in the appropriate location: 65 | ``` 66 | config > [country code] > [bank] > [statement type].json 67 | ``` 68 | 69 | 2. Follow the configuration format as described in the README.md 70 | 3. Test your configuration with sample statements 71 | 4. Include documentation about the new format in your pull request 72 | 73 | ## Pull Request Guidelines 74 | 75 | - Include a clear description of the changes 76 | - Add/update tests for any new functionality 77 | - Ensure all tests pass 78 | - Update documentation as needed 79 | - Follow existing code style and conventions 80 | 81 | ## Running Tests 82 | 83 | Before submitting a pull request, make sure all tests pass: 84 | 85 | ```bash 86 | # Run the test suite 87 | pytest 88 | 89 | # Check code coverage 90 | pytest --cov=pdf_statement_reader 91 | ``` 92 | 93 | ## Code Style 94 | 95 | - Follow PEP 8 guidelines for Python code 96 | - Use meaningful variable and function names 97 | - Add docstrings for functions and classes 98 | - Comment complex logic as needed 99 | 100 | ## Reporting Issues 101 | 102 | When reporting issues, please include: 103 | 104 | - A clear description of the problem 105 | - Steps to reproduce the issue 106 | - Expected vs actual behavior 107 | - Version information (Python version, package version) 108 | - Any relevant error messages or logs 109 | 110 | ## License 111 | 112 | By contributing to PDF Statement Reader, you agree that your contributions will be licensed under the same license as the project. 113 | 114 | ## Questions? 115 | 116 | If you have questions about contributing, feel free to open an issue for discussion. 117 | 118 | Thank you for contributing to PDF Statement Reader! -------------------------------------------------------------------------------- /pdf_statement_reader/config/psr_config.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://github.com/marlanperumal/pdf_statement_reader/psr_config.schema.json", 4 | "title": "PDF Statement Reader Config", 5 | "description": "Config file to be used by the PDF Statement Reader (psr) application to read pdf bank statements", 6 | "type": "object", 7 | "properties": { 8 | "layout": { 9 | "description": "Describes the page layout that should be scanned", 10 | "type": "object", 11 | "properties": { 12 | "default": { 13 | "description": "Default layout for all pages not otherwise defined", 14 | "type": "object", 15 | "properties": { 16 | "area": { 17 | "description": "The page coordinates containing the table in pts: [top, left, bottom, right]", 18 | "type": "array", 19 | "items": { 20 | "type": "integer" 21 | }, 22 | "minItems": 4, 23 | "maxItems": 4, 24 | "examples": [ 25 | [280, 27, 763, 576] 26 | ], 27 | "uniqueItems": true 28 | }, 29 | "columns": { 30 | "description": "The right x coordinate of each column in the table", 31 | "type": "array", 32 | "items": { 33 | "type": "integer" 34 | }, 35 | "minItems": 1, 36 | "examples": [ 37 | [83, 264, 344, 425, 485, 570] 38 | ], 39 | "uniqueItems": true 40 | } 41 | } 42 | }, 43 | "first": { 44 | "description": "Layout for the first page", 45 | "type": "object", 46 | "properties": { 47 | "area": { 48 | "description": "The page coordinates containing the table in pts: [top, left, bottom, right]", 49 | "type": "array", 50 | "items": { 51 | "type": "integer" 52 | }, 53 | "minItems": 4, 54 | "maxItems": 4, 55 | "examples": [ 56 | [280, 27, 763, 576] 57 | ], 58 | "uniqueItems": true 59 | }, 60 | "columns": { 61 | "description": "The right x coordinate of each column in the table", 62 | "type": "array", 63 | "items": { 64 | "type": "integer" 65 | }, 66 | "minItems": 1, 67 | "examples": [ 68 | [[83, 264, 344, 425, 485, 570]] 69 | ], 70 | "uniqueItems": true 71 | } 72 | } 73 | } 74 | }, 75 | "required": ["default"] 76 | }, 77 | "columns": { 78 | "description": "The columns names to be used as they exactly appear in the statement", 79 | "type": "object", 80 | "minProperties": 1, 81 | "examples": [ 82 | { 83 | "trans_date": "Date", 84 | "trans_type": "Transaction Description", 85 | "trans_detail": "Transaction Detail", 86 | "debit": "Debit Amount", 87 | "credit": "Credit Amount", 88 | "balance": "Balance" 89 | } 90 | ] 91 | }, 92 | "order": { 93 | "description": "The order of the columns to be output in the csv", 94 | "type": "array", 95 | "items": { 96 | "type": "string" 97 | }, 98 | "minItems": 1, 99 | "examples": [ 100 | [ 101 | "trans_date", 102 | "trans_type", 103 | "trans_detail", 104 | "debit", 105 | "credit", 106 | "balance" 107 | ] 108 | ], 109 | "uniqueItems": true 110 | }, 111 | "cleaning": { 112 | "description": "Specifies any cleaning operations required", 113 | "type": "object", 114 | "properties": { 115 | "numeric": { 116 | "description": "Convert these columns to numeric", 117 | "type": "array", 118 | "items": { 119 | "type": "string" 120 | }, 121 | "examples": [ 122 | ["debit", "credit", "balance"] 123 | ], 124 | "uniqueItems": true 125 | }, 126 | "date": { 127 | "description": "Convert these columns to date", 128 | "type": "array", 129 | "items": { 130 | "type": "string" 131 | }, 132 | "examples": [ 133 | ["trans_date"] 134 | ], 135 | "uniqueItems": true 136 | }, 137 | "date_format": { 138 | "description": "Use this date format to parse any date columns", 139 | "type": "string", 140 | "examples": ["%d/%m/%Y"] 141 | }, 142 | "trans_detail": { 143 | "description": "For cases where the transaction detail is stored in the next line below the transaction type", 144 | "type": "string", 145 | "examples": ["below"] 146 | }, 147 | "dropna": { 148 | "description": "Only keep the rows where these columns are populated", 149 | "type": "array", 150 | "items": { 151 | "type": "string" 152 | }, 153 | "examples": [ 154 | ["balance"] 155 | ], 156 | "uniqueItems": true 157 | } 158 | } 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /pdf_statement_reader/__init__.py: -------------------------------------------------------------------------------- 1 | import click 2 | import os 3 | from os import listdir, remove 4 | from os.path import isfile, join 5 | import json 6 | import pandas as pd 7 | 8 | from pdf_statement_reader.decrypt import decrypt_pdf 9 | from pdf_statement_reader.parse import parse_statement 10 | from pdf_statement_reader.validate import validate_statement 11 | 12 | 13 | def load_config(config_spec): 14 | """Loads configuration for a template statement 15 | 16 | For each type of bank statement, the exact format will be different. 17 | A config file holds the instructions for how to process the raw pdf. 18 | These config files are stored in a folder structure as follows: 19 | config > [country code] > [bank] > [statement type].json 20 | 21 | So for example the default config is stored in 22 | config > za > absa > cheque.json 23 | 24 | The config spec is a code of the form 25 | [country code].[bank].[statement type] 26 | 27 | Once again for the default this will be 28 | za.absa.cheque 29 | 30 | Args: 31 | config_spec: Code that resolves to a json file as explained above 32 | 33 | Returns: 34 | The configuration as a python object 35 | """ 36 | local_dir = os.path.dirname(__file__) 37 | config_dir = os.path.join(*config_spec.split(".")[:-1]) 38 | config_file = config_spec.split(".")[-1] + ".json" 39 | config_path = os.path.join(local_dir, "config", config_dir, config_file) 40 | with open(config_path) as f: 41 | config = json.load(f) 42 | return config 43 | 44 | 45 | @click.group() 46 | def cli(): 47 | """Utility for reading bank and other statements in pdf form""" 48 | pass 49 | 50 | 51 | @cli.command() 52 | @click.argument("input_filename", type=click.Path(exists=True)) 53 | @click.argument("output_filename", type=click.Path(), required=False) 54 | @click.option( 55 | "--password", 56 | "-p", 57 | prompt=True, 58 | hide_input=True, 59 | help="The pdf encryption password. If not supplied, it will be requested at the prompt", 60 | ) 61 | def decrypt(input_filename, output_filename=None, password=None): 62 | """Decrypts a pdf file 63 | 64 | Uses pikepdf to open an encrypted pdf file and then save the unencrypted version. 65 | If no output_filename is specified then overwrites the original file. 66 | """ 67 | 68 | decrypt_pdf(input_filename, output_filename, password) 69 | click.echo("Decrypted {} and saved as {}".format(input_filename, output_filename)) 70 | 71 | 72 | @cli.command() 73 | @click.argument("input_filename", type=click.Path(exists=True)) 74 | @click.argument("output_filename", type=click.Path(), required=False) 75 | @click.option( 76 | "--config", 77 | "-c", 78 | "config_spec", 79 | default="za.absa.cheque", 80 | show_default=True, 81 | help="The configuration code defining how the file should be parsed", 82 | ) 83 | def pdf2csv(input_filename, output_filename=None, config_spec=None): 84 | """Converts a pdf statement to a csv file using a given format""" 85 | 86 | config = load_config(config_spec) 87 | 88 | if output_filename is None: 89 | output_filename = input_filename.split(".pdf")[0] + ".csv" 90 | 91 | df = parse_statement(input_filename, config) 92 | df.to_csv(output_filename, index=False, float_format="%.2f") 93 | click.echo("Converted {} and saved as {}".format(input_filename, output_filename)) 94 | 95 | 96 | @cli.command() 97 | @click.argument("input_filename", type=click.Path(exists=True)) 98 | @click.option( 99 | "--config", 100 | "-c", 101 | "config_spec", 102 | default="za.absa.cheque", 103 | show_default=True, 104 | help="The configuration code defining how the file should be parsed", 105 | ) 106 | def validate(input_filename, output_filename=None, config_spec=None): 107 | """Validates the csv statement rolling balance""" 108 | 109 | config = load_config(config_spec) 110 | 111 | statement = pd.read_csv(input_filename) 112 | valid = validate_statement(statement, config) 113 | 114 | if valid: 115 | click.echo("Statement is valid") 116 | else: 117 | click.echo("Statement is invalid") 118 | 119 | 120 | @cli.command() 121 | @click.argument("folder", type=click.Path(exists=True)) 122 | @click.option( 123 | "--config", 124 | "-c", 125 | "config_spec", 126 | default="za.absa.cheque", 127 | show_default=True, 128 | help="The configuration code defining how the file should be parsed", 129 | ) 130 | @click.option( 131 | "--password", 132 | "-p", 133 | prompt=True, 134 | hide_input=True, 135 | help="The pdf encryption password. If not supplied, it will be requested at the prompt", 136 | ) 137 | @click.option( 138 | "--decrypt-suffix", 139 | "-d", 140 | default="_decrypted", 141 | show_default=True, 142 | help="The suffix to append to the decrypted pdf file when created", 143 | ) 144 | @click.option( 145 | "--keep-decrypted", 146 | "-k", 147 | is_flag=True, 148 | help="Keep the a copy of the decrypted file. It is removed by default", 149 | ) 150 | @click.option( 151 | "--verbose", "-v", is_flag=True, help="Print verbose output while running" 152 | ) 153 | def bulk( 154 | folder, config_spec, password, decrypt_suffix, keep_decrypted=False, verbose=False 155 | ): 156 | """Bulk converts all files in a folder""" 157 | 158 | config = load_config(config_spec) 159 | 160 | files = [f for f in listdir(folder) if isfile(join(folder, f))] 161 | 162 | for file in files: 163 | extension = file.split(".")[-1] 164 | if extension.lower() == "pdf": 165 | base_name = ".".join(file.split(".")[:-1]) 166 | valid = False 167 | 168 | try: 169 | if verbose: 170 | click.echo(file) 171 | decrypted_file = base_name + decrypt_suffix + "." + extension 172 | if verbose: 173 | click.echo("decrypting...") 174 | decrypt_pdf(join(folder, file), join(folder, decrypted_file), password) 175 | if verbose: 176 | click.echo("parsing...") 177 | df = parse_statement(join(folder, decrypted_file), config) 178 | if verbose: 179 | click.echo("validating...") 180 | valid = validate_statement(df, config) 181 | if verbose: 182 | click.echo("writing to csv...") 183 | df.to_csv( 184 | join(folder, base_name + ".csv"), index=False, float_format="%.2f" 185 | ) 186 | 187 | if not valid: 188 | click.echo(click.style("Statement not valid", fg="yellow")) 189 | raise 190 | 191 | click.echo(click.style("Converted {}".format(file), fg="green")) 192 | 193 | except Exception as e: 194 | if verbose: 195 | click.echo(click.style(str(e), fg="yellow")) 196 | click.echo(click.style("Error on {}".format(file), fg="red")) 197 | 198 | finally: 199 | if not keep_decrypted: 200 | if verbose: 201 | click.echo("removing decrypted file...") 202 | remove(join(folder, decrypted_file)) 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PDF Statement Reader 2 | [![PyPI version](https://badge.fury.io/py/pdf-statement-reader.svg)](https://badge.fury.io/py/pdf-statement-reader) 3 | [![Coverage Status](https://coveralls.io/repos/github/marlanperumal/pdf_statement_reader/badge.svg)](https://coveralls.io/github/marlanperumal/pdf_statement_reader) 4 | 5 | Python library and command line tool for parsing pdf bank statements 6 | 7 | Inspired by https://github.com/antonburger/pdf2csv 8 | 9 | ## Objectives 10 | 11 | Banks generally send account statements in pdf format. These pdfs are often encrypted, the pdf format is difficult to extract tables from and when you finally get the table out it's in a non tidy format. This package aims to help by providing a library of functions and a set of command line tools for converting these statements into more useful formats such as csv files and pandas dataframes. 12 | 13 | ## Installation 14 | 15 | Python and package management have been set up with [uv](https://docs.astral.sh/uv/). With uv installed and the repo cloned run 16 | 17 | ```bash 18 | uv sync 19 | ``` 20 | 21 | The CLI tool can then be invoked with 22 | 23 | ```bash 24 | uv run psr 25 | ``` 26 | 27 | Alternatively you can use the `uvx` command to run the tool without installing with 28 | 29 | ```bash 30 | uvx --from pdf-statement-reader psr 31 | ``` 32 | 33 | Or to be less verbose on each call first run 34 | 35 | ```bash 36 | uv tool install pdf-statement-reader 37 | ``` 38 | 39 | Then you'll be able to simply run 40 | 41 | ```bash 42 | psr 43 | ``` 44 | 45 | ### Troubleshooting 46 | 47 | This package uses [tabula-py](https://github.com/chezou/tabula-py) under the hood, which itself is a wrapper for [tabula-java](https://github.com/tabulapdf/tabula-java). You thus need to have java installed for it to work. If you have any errors complaining about java, checkout out the `tabula-py` page for troubleshooting advice. 48 | 49 | In the future, we hope to move to a pure python implementation. 50 | 51 | ## Usage 52 | 53 | The package provides a command line application `psr` 54 | 55 | ``` 56 | Usage: psr [OPTIONS] COMMAND [ARGS]... 57 | 58 | Utility for reading bank and other statements in pdf form 59 | 60 | Options: 61 | --help Show this message and exit. 62 | 63 | Commands: 64 | bulk Bulk converts all files in a folder 65 | decrypt Decrypts a pdf file Uses pikepdf to open an encrypted pdf file... 66 | pdf2csv Converts a pdf statement to a csv file using a given format 67 | validate Validates the csv statement rolling balance 68 | ``` 69 | 70 | ## Configuration 71 | 72 | PDF files are notoriously difficult to extract data from. (Here's a nice [blog post](https://www.propublica.org/nerds/heart-of-nerd-darkness-why-dollars-for-docs-was-so-difficult) on why). For a really good semi-manual GUI solution, check out [tabula](https://tabula.technology/). In fact this package uses tabula's pdf parsing library under the hood. 73 | 74 | Since bank statements are generally of the same (if inconvenient) format, we can set up a configuration to tell the tool how to grab the data. 75 | 76 | For each type of bank statement, the exact format will be different. A config file holds the instructions for how to process the raw pdf. For now the only config supported is for Cheque account statements from Absa bank in South Africa. 77 | 78 | To set up a different statement, you can simply add a new config file and then tell the `psr` tool to use it. These config files are stored in a folder structure as follows: 79 | 80 | config > [country code] > [bank] > [statement type].json 81 | 82 | So for example the default config is stored in 83 | 84 | config > za > absa > cheque.json 85 | 86 | The config spec is a code of the form 87 | 88 | [country code].[bank].[statement type] 89 | 90 | Once again for the default this will be 91 | 92 | za.absa.cheque 93 | 94 | The configuration file itself is in JSON format. Here's the Absa cheque account one with some commentary to explain what each field does. 95 | 96 | The dimensions to be supplied in the `area` and `columns` parameters are specified in pts, defined as 72 pts in 1 inch. For reference, letter size paper is 8.5 x 11.0 inches (612 x 792 pts) and A4 paper is 8.3 x 11.7 inches (597.6 x 842.4 pts). The origin (0, 0) is located at the top left corner of the page. This is probably most intuitive, however note that it is different to the PDF standard which places the origin at the *bottom* left of the page. 97 | 98 | ```json5 99 | { 100 | "$schema": "https://raw.githubusercontent.com/marlanperumal/pdf_statement_reader/develop/pdf_statement_reader/config/psr_config.schema.json", 101 | // Describes the page layout that should be scanned 102 | "layout": { 103 | // Default layout for all pages not otherwise defined 104 | "default": { 105 | // The page coordinates in containing the table in pts 106 | // [top, left, bottom, right] 107 | "area": [280, 27, 763, 576], 108 | // The right x coordinate of each column in the table in pts 109 | "columns": [83, 264, 344, 425, 485, 570] 110 | }, 111 | // Layout for the first page 112 | "first": { 113 | "area": [480, 27, 763, 576], 114 | "columns": [83, 264, 344, 425, 485, 570] 115 | } 116 | }, 117 | 118 | // The columns names to be used as they exactly appear 119 | // in the statement 120 | "columns": { 121 | "trans_date": "Date", 122 | "trans_type": "Transaction Description", 123 | "trans_detail": "Transaction Detail", 124 | "debit": "Debit Amount", 125 | "credit": "Credit Amount", 126 | "balance": "Balance" 127 | }, 128 | 129 | // The order of the columns to be output in the csv 130 | "order": [ 131 | "trans_date", 132 | "trans_type", 133 | "trans_detail", 134 | "debit", 135 | "credit", 136 | "balance" 137 | ], 138 | 139 | // Specifies any cleaning operations required 140 | "cleaning": { 141 | // Convert these columns to numeric 142 | "numeric": ["debit", "credit", "balance"], 143 | // Convert these columns to date 144 | "date": ["trans_date"], 145 | // Use this date format to parse any date columns 146 | "date_format": "%d/%m/%Y", 147 | // For cases where the transaction detail is stored 148 | // in the next line below the transaction type 149 | "trans_detail": "below", 150 | // Only keep the rows where these columns are populated 151 | "dropna": ["balance"] 152 | } 153 | } 154 | ``` 155 | 156 | These were the configuration options that were required for the default format. It is envisaged that as more formats are added, the list of options will grow. 157 | 158 | This format is also captured in `pdf_statement_rader/config/psr_config.schema.json` as a [json-schema](https://json-schema.org/understanding-json-schema/index.html). If you're using vscode or some other compatible text editor, you should get autocompletion hints as long as you include that `$schema` tag at the top of your json file. 159 | 160 | A key part in setting up a new configuration is getting the page coordinates for the area and columns. The easiest way to do this is to run the [tabula GUI](https://tabula.technology/), autodetect the page areas, save the settings as a template, then download and inspect json template file. It's not a one-to-one mapping to the psr config but hopefully it will be a good starting point. 161 | 162 | ## CLI API 163 | 164 | ### decrypt 165 | 166 | ``` 167 | Usage: psr decrypt [OPTIONS] INPUT_FILENAME [OUTPUT_FILENAME] 168 | 169 | Decrypts a pdf file 170 | 171 | Uses pikepdf to open an encrypted pdf file and then save the unencrypted 172 | version. If no output_filename is specified then overwrites the original 173 | file. 174 | 175 | Options: 176 | -p, --password TEXT The pdf encryption password. If not supplied, it will 177 | be requested at the prompt 178 | --help Show this message and exit. 179 | ``` 180 | 181 | ### pdf2csv 182 | 183 | ``` 184 | Usage: psr pdf2csv [OPTIONS] INPUT_FILENAME [OUTPUT_FILENAME] 185 | 186 | Converts a pdf statement to a csv file using a given format 187 | 188 | Options: 189 | -c, --config TEXT The configuration code defining how the file should be 190 | parsed [default: za.absa.cheque] 191 | --help Show this message and exit. 192 | ``` 193 | 194 | ### validate 195 | 196 | ``` 197 | Usage: psr validate [OPTIONS] INPUT_FILENAME 198 | 199 | Validates the csv statement rolling balance 200 | 201 | Options: 202 | -c, --config TEXT The configuration code defining how the file should be 203 | parsed [default: za.absa.cheque] 204 | --help Show this message and exit. 205 | ``` 206 | 207 | ### bulk 208 | 209 | ``` 210 | Usage: psr bulk [OPTIONS] FOLDER 211 | 212 | Bulk converts all files in a folder 213 | 214 | Options: 215 | -c, --config TEXT The configuration code defining how the file 216 | should be parsed [default: za.absa.cheque] 217 | -p, --password TEXT The pdf encryption password. If not supplied, it 218 | will be requested at the prompt 219 | -d, --decrypt-suffix TEXT The suffix to append to the decrypted pdf file 220 | when created [default: _decrypted] 221 | -k, --keep-decrypted Keep the a copy of the decrypted file. It is 222 | removed by default 223 | -v, --verbose Print verbose output while running 224 | --help Show this message and exit. 225 | ``` 226 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.13" 3 | 4 | [[package]] 5 | name = "appnope" 6 | version = "0.1.4" 7 | source = { registry = "https://pypi.org/simple" } 8 | sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } 9 | wheels = [ 10 | { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, 11 | ] 12 | 13 | [[package]] 14 | name = "asttokens" 15 | version = "3.0.0" 16 | source = { registry = "https://pypi.org/simple" } 17 | sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } 18 | wheels = [ 19 | { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, 20 | ] 21 | 22 | [[package]] 23 | name = "certifi" 24 | version = "2024.12.14" 25 | source = { registry = "https://pypi.org/simple" } 26 | sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } 27 | wheels = [ 28 | { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, 29 | ] 30 | 31 | [[package]] 32 | name = "cffi" 33 | version = "1.17.1" 34 | source = { registry = "https://pypi.org/simple" } 35 | dependencies = [ 36 | { name = "pycparser" }, 37 | ] 38 | sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } 39 | wheels = [ 40 | { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, 41 | { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, 42 | { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, 43 | { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, 44 | { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, 45 | { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, 46 | { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, 47 | { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, 48 | { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, 49 | { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, 50 | { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, 51 | ] 52 | 53 | [[package]] 54 | name = "charset-normalizer" 55 | version = "3.4.1" 56 | source = { registry = "https://pypi.org/simple" } 57 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } 58 | wheels = [ 59 | { 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 }, 60 | { 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 }, 61 | { 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 }, 62 | { 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 }, 63 | { 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 }, 64 | { 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 }, 65 | { 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 }, 66 | { 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 }, 67 | { 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 }, 68 | { 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 }, 69 | { 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 }, 70 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, 71 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, 72 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, 73 | ] 74 | 75 | [[package]] 76 | name = "click" 77 | version = "8.1.8" 78 | source = { registry = "https://pypi.org/simple" } 79 | dependencies = [ 80 | { name = "colorama", marker = "platform_system == 'Windows'" }, 81 | ] 82 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 83 | wheels = [ 84 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 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 = "comm" 98 | version = "0.2.2" 99 | source = { registry = "https://pypi.org/simple" } 100 | dependencies = [ 101 | { name = "traitlets" }, 102 | ] 103 | sdist = { url = "https://files.pythonhosted.org/packages/e9/a8/fb783cb0abe2b5fded9f55e5703015cdf1c9c85b3669087c538dd15a6a86/comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e", size = 6210 } 104 | wheels = [ 105 | { url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180 }, 106 | ] 107 | 108 | [[package]] 109 | name = "coverage" 110 | version = "7.6.10" 111 | source = { registry = "https://pypi.org/simple" } 112 | sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868 } 113 | wheels = [ 114 | { 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 }, 115 | { 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 }, 116 | { 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 }, 117 | { 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 }, 118 | { 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 }, 119 | { 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 }, 120 | { 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 }, 121 | { 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 }, 122 | { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965 }, 123 | { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719 }, 124 | { 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 }, 125 | { 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 }, 126 | { 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 }, 127 | { 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 }, 128 | { 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 }, 129 | { 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 }, 130 | { 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 }, 131 | { 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 }, 132 | { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 }, 133 | { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 }, 134 | ] 135 | 136 | [[package]] 137 | name = "coveralls" 138 | version = "4.0.1" 139 | source = { registry = "https://pypi.org/simple" } 140 | dependencies = [ 141 | { name = "coverage" }, 142 | { name = "docopt" }, 143 | { name = "requests" }, 144 | ] 145 | sdist = { url = "https://files.pythonhosted.org/packages/61/75/a454fb443eb6a053833f61603a432ffbd7dd6ae53a11159bacfadb9d6219/coveralls-4.0.1.tar.gz", hash = "sha256:7b2a0a2bcef94f295e3cf28dcc55ca40b71c77d1c2446b538e85f0f7bc21aa69", size = 12419 } 146 | wheels = [ 147 | { url = "https://files.pythonhosted.org/packages/63/e5/6708c75e2a4cfca929302d4d9b53b862c6dc65bd75e6933ea3d20016d41d/coveralls-4.0.1-py3-none-any.whl", hash = "sha256:7a6b1fa9848332c7b2221afb20f3df90272ac0167060f41b5fe90429b30b1809", size = 13599 }, 148 | ] 149 | 150 | [[package]] 151 | name = "debugpy" 152 | version = "1.8.11" 153 | source = { registry = "https://pypi.org/simple" } 154 | sdist = { url = "https://files.pythonhosted.org/packages/bc/e7/666f4c9b0e24796af50aadc28d36d21c2e01e831a934535f956e09b3650c/debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57", size = 1640124 } 155 | wheels = [ 156 | { url = "https://files.pythonhosted.org/packages/2e/66/931dc2479aa8fbf362dc6dcee707d895a84b0b2d7b64020135f20b8db1ed/debugpy-1.8.11-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3", size = 2483651 }, 157 | { url = "https://files.pythonhosted.org/packages/10/07/6c171d0fe6b8d237e35598b742f20ba062511b3a4631938cc78eefbbf847/debugpy-1.8.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e", size = 4213770 }, 158 | { url = "https://files.pythonhosted.org/packages/89/f1/0711da6ac250d4fe3bf7b3e9b14b4a86e82a98b7825075c07e19bab8da3d/debugpy-1.8.11-cp313-cp313-win32.whl", hash = "sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28", size = 5223911 }, 159 | { url = "https://files.pythonhosted.org/packages/56/98/5e27fa39050749ed460025bcd0034a0a5e78a580a14079b164cc3abdeb98/debugpy-1.8.11-cp313-cp313-win_amd64.whl", hash = "sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1", size = 5264166 }, 160 | { url = "https://files.pythonhosted.org/packages/77/0a/d29a5aacf47b4383ed569b8478c02d59ee3a01ad91224d2cff8562410e43/debugpy-1.8.11-py2.py3-none-any.whl", hash = "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920", size = 5226874 }, 161 | ] 162 | 163 | [[package]] 164 | name = "decorator" 165 | version = "5.1.1" 166 | source = { registry = "https://pypi.org/simple" } 167 | sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } 168 | wheels = [ 169 | { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, 170 | ] 171 | 172 | [[package]] 173 | name = "deprecated" 174 | version = "1.2.15" 175 | source = { registry = "https://pypi.org/simple" } 176 | dependencies = [ 177 | { name = "wrapt" }, 178 | ] 179 | sdist = { url = "https://files.pythonhosted.org/packages/2e/a3/53e7d78a6850ffdd394d7048a31a6f14e44900adedf190f9a165f6b69439/deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d", size = 2977612 } 180 | wheels = [ 181 | { url = "https://files.pythonhosted.org/packages/1d/8f/c7f227eb42cfeaddce3eb0c96c60cbca37797fa7b34f8e1aeadf6c5c0983/Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320", size = 9941 }, 182 | ] 183 | 184 | [[package]] 185 | name = "distro" 186 | version = "1.9.0" 187 | source = { registry = "https://pypi.org/simple" } 188 | sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } 189 | wheels = [ 190 | { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, 191 | ] 192 | 193 | [[package]] 194 | name = "docopt" 195 | version = "0.6.2" 196 | source = { registry = "https://pypi.org/simple" } 197 | sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } 198 | 199 | [[package]] 200 | name = "executing" 201 | version = "2.1.0" 202 | source = { registry = "https://pypi.org/simple" } 203 | sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/7d45f492c2c4a0e8e0fad57d081a7c8a0286cdd86372b070cca1ec0caa1e/executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab", size = 977485 } 204 | wheels = [ 205 | { url = "https://files.pythonhosted.org/packages/b5/fd/afcd0496feca3276f509df3dbd5dae726fcc756f1a08d9e25abe1733f962/executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", size = 25805 }, 206 | ] 207 | 208 | [[package]] 209 | name = "idna" 210 | version = "3.10" 211 | source = { registry = "https://pypi.org/simple" } 212 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 213 | wheels = [ 214 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 215 | ] 216 | 217 | [[package]] 218 | name = "iniconfig" 219 | version = "2.0.0" 220 | source = { registry = "https://pypi.org/simple" } 221 | sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } 222 | wheels = [ 223 | { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, 224 | ] 225 | 226 | [[package]] 227 | name = "ipykernel" 228 | version = "6.29.5" 229 | source = { registry = "https://pypi.org/simple" } 230 | dependencies = [ 231 | { name = "appnope", marker = "platform_system == 'Darwin'" }, 232 | { name = "comm" }, 233 | { name = "debugpy" }, 234 | { name = "ipython" }, 235 | { name = "jupyter-client" }, 236 | { name = "jupyter-core" }, 237 | { name = "matplotlib-inline" }, 238 | { name = "nest-asyncio" }, 239 | { name = "packaging" }, 240 | { name = "psutil" }, 241 | { name = "pyzmq" }, 242 | { name = "tornado" }, 243 | { name = "traitlets" }, 244 | ] 245 | sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/67594cb0c7055dc50814b21731c22a601101ea3b1b50a9a1b090e11f5d0f/ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215", size = 163367 } 246 | wheels = [ 247 | { url = "https://files.pythonhosted.org/packages/94/5c/368ae6c01c7628438358e6d337c19b05425727fbb221d2a3c4303c372f42/ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", size = 117173 }, 248 | ] 249 | 250 | [[package]] 251 | name = "ipython" 252 | version = "8.31.0" 253 | source = { registry = "https://pypi.org/simple" } 254 | dependencies = [ 255 | { name = "colorama", marker = "sys_platform == 'win32'" }, 256 | { name = "decorator" }, 257 | { name = "jedi" }, 258 | { name = "matplotlib-inline" }, 259 | { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, 260 | { name = "prompt-toolkit" }, 261 | { name = "pygments" }, 262 | { name = "stack-data" }, 263 | { name = "traitlets" }, 264 | ] 265 | sdist = { url = "https://files.pythonhosted.org/packages/01/35/6f90fdddff7a08b7b715fccbd2427b5212c9525cd043d26fdc45bee0708d/ipython-8.31.0.tar.gz", hash = "sha256:b6a2274606bec6166405ff05e54932ed6e5cfecaca1fc05f2cacde7bb074d70b", size = 5501011 } 266 | wheels = [ 267 | { url = "https://files.pythonhosted.org/packages/04/60/d0feb6b6d9fe4ab89fe8fe5b47cbf6cd936bfd9f1e7ffa9d0015425aeed6/ipython-8.31.0-py3-none-any.whl", hash = "sha256:46ec58f8d3d076a61d128fe517a51eb730e3aaf0c184ea8c17d16e366660c6a6", size = 821583 }, 268 | ] 269 | 270 | [[package]] 271 | name = "jedi" 272 | version = "0.19.2" 273 | source = { registry = "https://pypi.org/simple" } 274 | dependencies = [ 275 | { name = "parso" }, 276 | ] 277 | sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } 278 | wheels = [ 279 | { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, 280 | ] 281 | 282 | [[package]] 283 | name = "jpype1" 284 | version = "1.5.1" 285 | source = { registry = "https://pypi.org/simple" } 286 | dependencies = [ 287 | { name = "packaging" }, 288 | ] 289 | sdist = { url = "https://files.pythonhosted.org/packages/c2/50/cc34769452934a1342326c1eee23d4e70229164364e7cbbdf3f572699f31/jpype1-1.5.1.tar.gz", hash = "sha256:cbb8ea952bf0a0c6011f21a31a97baed241c4aad0e2499db386edfd5cf2adece", size = 856550 } 290 | wheels = [ 291 | { url = "https://files.pythonhosted.org/packages/74/5e/c10e9f68335fae39d0216c82fb31255cb1771b8f2a8899344dea57b735cb/jpype1-1.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6d17792936c36b475527217dc827f45d088c0de928f0021007e8216fb0008bf0", size = 583426 }, 292 | { url = "https://files.pythonhosted.org/packages/35/9c/3af1f95be832931d7a3e87de00913563b37daf78b9137da4e5b93ae99eda/jpype1-1.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d78dd96bd21e220f8be275393f20a64ec8e1ecf071fd2d7d44dac81653e8e78a", size = 466132 }, 293 | { url = "https://files.pythonhosted.org/packages/32/92/b5bf19cee69d6d6f0f3f9750369dc2ce9ba211316b867d22d3c252f658d6/jpype1-1.5.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bab2c0dd32ff7454d7af67e94f27f5be2fe49b687791cd853db9474ecb046eb2", size = 508987 }, 294 | { url = "https://files.pythonhosted.org/packages/53/09/c1d188d04fc308049163313419d3d606e2924005015174ba5e276a1b511c/jpype1-1.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bd441732ee864ddec56369d01b131aa851d43d6d722d9b236d09eda5c2c691a", size = 493200 }, 295 | { url = "https://files.pythonhosted.org/packages/5f/35/e88bc402f2e476449bb835e568c72e6dad6463ca30aa692df84afe5caa7d/jpype1-1.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:39bf24e4a2f1001906d235b731a3fd573e89164a569be592485bb54f394ccd5b", size = 355599 }, 296 | { url = "https://files.pythonhosted.org/packages/fc/83/c2506cd274b4694e4f734daa37c994e5c065e9ecc587779c65d29d3a186d/jpype1-1.5.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d038b7dcd7b16debba51df5d1ad9d60239a6dd873b13f4c2bd210a25465d5ae", size = 467940 }, 297 | { url = "https://files.pythonhosted.org/packages/e8/d2/45940d85e2e83822f73b72f687468f649aa6f4109ac9bdabfbcb017a378b/jpype1-1.5.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1952e2de26c66c2c7b6fc0a8a9546f596fa9afed5490acb6ab17d2b6d767ed4", size = 511001 }, 298 | { url = "https://files.pythonhosted.org/packages/0d/99/c9d6eef8eec9efbb64b776814866345caf498d20245c7ecbc28daa1484ea/jpype1-1.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:699b555cb549659679b69a8ec2d708ac416a08c0b148d0122d3ce133d37a4229", size = 494520 }, 299 | ] 300 | 301 | [[package]] 302 | name = "jupyter-client" 303 | version = "8.6.3" 304 | source = { registry = "https://pypi.org/simple" } 305 | dependencies = [ 306 | { name = "jupyter-core" }, 307 | { name = "python-dateutil" }, 308 | { name = "pyzmq" }, 309 | { name = "tornado" }, 310 | { name = "traitlets" }, 311 | ] 312 | sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019 } 313 | wheels = [ 314 | { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105 }, 315 | ] 316 | 317 | [[package]] 318 | name = "jupyter-core" 319 | version = "5.7.2" 320 | source = { registry = "https://pypi.org/simple" } 321 | dependencies = [ 322 | { name = "platformdirs" }, 323 | { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, 324 | { name = "traitlets" }, 325 | ] 326 | sdist = { url = "https://files.pythonhosted.org/packages/00/11/b56381fa6c3f4cc5d2cf54a7dbf98ad9aa0b339ef7a601d6053538b079a7/jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9", size = 87629 } 327 | wheels = [ 328 | { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 }, 329 | ] 330 | 331 | [[package]] 332 | name = "lxml" 333 | version = "5.3.0" 334 | source = { registry = "https://pypi.org/simple" } 335 | sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 } 336 | wheels = [ 337 | { url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 }, 338 | { url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 }, 339 | { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 }, 340 | { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 }, 341 | { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 }, 342 | { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046 }, 343 | { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597 }, 344 | { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071 }, 345 | { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213 }, 346 | { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749 }, 347 | { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901 }, 348 | { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447 }, 349 | { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 }, 350 | { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 }, 351 | { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 }, 352 | { url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 }, 353 | { url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 }, 354 | ] 355 | 356 | [[package]] 357 | name = "matplotlib-inline" 358 | version = "0.1.7" 359 | source = { registry = "https://pypi.org/simple" } 360 | dependencies = [ 361 | { name = "traitlets" }, 362 | ] 363 | sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } 364 | wheels = [ 365 | { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, 366 | ] 367 | 368 | [[package]] 369 | name = "nest-asyncio" 370 | version = "1.6.0" 371 | source = { registry = "https://pypi.org/simple" } 372 | sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } 373 | wheels = [ 374 | { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, 375 | ] 376 | 377 | [[package]] 378 | name = "numpy" 379 | version = "2.2.1" 380 | source = { registry = "https://pypi.org/simple" } 381 | sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/fdbf6a7871703df6160b5cf3dd774074b086d278172285c52c2758b76305/numpy-2.2.1.tar.gz", hash = "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918", size = 20227662 } 382 | wheels = [ 383 | { url = "https://files.pythonhosted.org/packages/20/d6/91a26e671c396e0c10e327b763485ee295f5a5a7a48c553f18417e5a0ed5/numpy-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780", size = 20896464 }, 384 | { url = "https://files.pythonhosted.org/packages/8c/40/5792ccccd91d45e87d9e00033abc4f6ca8a828467b193f711139ff1f1cd9/numpy-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821", size = 14111350 }, 385 | { url = "https://files.pythonhosted.org/packages/c0/2a/fb0a27f846cb857cef0c4c92bef89f133a3a1abb4e16bba1c4dace2e9b49/numpy-2.2.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e", size = 5111629 }, 386 | { url = "https://files.pythonhosted.org/packages/eb/e5/8e81bb9d84db88b047baf4e8b681a3e48d6390bc4d4e4453eca428ecbb49/numpy-2.2.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348", size = 6645865 }, 387 | { url = "https://files.pythonhosted.org/packages/7a/1a/a90ceb191dd2f9e2897c69dde93ccc2d57dd21ce2acbd7b0333e8eea4e8d/numpy-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59", size = 14043508 }, 388 | { url = "https://files.pythonhosted.org/packages/f1/5a/e572284c86a59dec0871a49cd4e5351e20b9c751399d5f1d79628c0542cb/numpy-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af", size = 16094100 }, 389 | { url = "https://files.pythonhosted.org/packages/0c/2c/a79d24f364788386d85899dd280a94f30b0950be4b4a545f4fa4ed1d4ca7/numpy-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51", size = 15239691 }, 390 | { url = "https://files.pythonhosted.org/packages/cf/79/1e20fd1c9ce5a932111f964b544facc5bb9bde7865f5b42f00b4a6a9192b/numpy-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716", size = 17856571 }, 391 | { url = "https://files.pythonhosted.org/packages/be/5b/cc155e107f75d694f562bdc84a26cc930569f3dfdfbccb3420b626065777/numpy-2.2.1-cp313-cp313-win32.whl", hash = "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e", size = 6270841 }, 392 | { url = "https://files.pythonhosted.org/packages/44/be/0e5cd009d2162e4138d79a5afb3b5d2341f0fe4777ab6e675aa3d4a42e21/numpy-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60", size = 12606618 }, 393 | { url = "https://files.pythonhosted.org/packages/a8/87/04ddf02dd86fb17c7485a5f87b605c4437966d53de1e3745d450343a6f56/numpy-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e", size = 20921004 }, 394 | { url = "https://files.pythonhosted.org/packages/6e/3e/d0e9e32ab14005425d180ef950badf31b862f3839c5b927796648b11f88a/numpy-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712", size = 14119910 }, 395 | { url = "https://files.pythonhosted.org/packages/b5/5b/aa2d1905b04a8fb681e08742bb79a7bddfc160c7ce8e1ff6d5c821be0236/numpy-2.2.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008", size = 5153612 }, 396 | { url = "https://files.pythonhosted.org/packages/ce/35/6831808028df0648d9b43c5df7e1051129aa0d562525bacb70019c5f5030/numpy-2.2.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84", size = 6668401 }, 397 | { url = "https://files.pythonhosted.org/packages/b1/38/10ef509ad63a5946cc042f98d838daebfe7eaf45b9daaf13df2086b15ff9/numpy-2.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631", size = 14014198 }, 398 | { url = "https://files.pythonhosted.org/packages/df/f8/c80968ae01df23e249ee0a4487fae55a4c0fe2f838dfe9cc907aa8aea0fa/numpy-2.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d", size = 16076211 }, 399 | { url = "https://files.pythonhosted.org/packages/09/69/05c169376016a0b614b432967ac46ff14269eaffab80040ec03ae1ae8e2c/numpy-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5", size = 15220266 }, 400 | { url = "https://files.pythonhosted.org/packages/f1/ff/94a4ce67ea909f41cf7ea712aebbe832dc67decad22944a1020bb398a5ee/numpy-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71", size = 17852844 }, 401 | { url = "https://files.pythonhosted.org/packages/46/72/8a5dbce4020dfc595592333ef2fbb0a187d084ca243b67766d29d03e0096/numpy-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2", size = 6326007 }, 402 | { url = "https://files.pythonhosted.org/packages/7b/9c/4fce9cf39dde2562584e4cfd351a0140240f82c0e3569ce25a250f47037d/numpy-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268", size = 12693107 }, 403 | ] 404 | 405 | [[package]] 406 | name = "packaging" 407 | version = "24.2" 408 | source = { registry = "https://pypi.org/simple" } 409 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 410 | wheels = [ 411 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 412 | ] 413 | 414 | [[package]] 415 | name = "pandas" 416 | version = "2.2.3" 417 | source = { registry = "https://pypi.org/simple" } 418 | dependencies = [ 419 | { name = "numpy" }, 420 | { name = "python-dateutil" }, 421 | { name = "pytz" }, 422 | { name = "tzdata" }, 423 | ] 424 | sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } 425 | wheels = [ 426 | { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, 427 | { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, 428 | { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, 429 | { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, 430 | { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, 431 | { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, 432 | { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, 433 | { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, 434 | { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, 435 | { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, 436 | { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, 437 | { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, 438 | { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, 439 | ] 440 | 441 | [[package]] 442 | name = "parso" 443 | version = "0.8.4" 444 | source = { registry = "https://pypi.org/simple" } 445 | sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } 446 | wheels = [ 447 | { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, 448 | ] 449 | 450 | [[package]] 451 | name = "pdf-statement-reader" 452 | version = "0.3.4" 453 | source = { editable = "." } 454 | dependencies = [ 455 | { name = "click" }, 456 | { name = "pikepdf" }, 457 | { name = "tabula-py", extra = ["jpype"] }, 458 | ] 459 | 460 | [package.dev-dependencies] 461 | dev = [ 462 | { name = "coverage" }, 463 | { name = "coveralls" }, 464 | { name = "ipykernel" }, 465 | { name = "pytest" }, 466 | { name = "pytest-cov" }, 467 | ] 468 | 469 | [package.metadata] 470 | requires-dist = [ 471 | { name = "click", specifier = ">=8.1.8" }, 472 | { name = "pikepdf", specifier = ">=9.5.1" }, 473 | { name = "tabula-py", extras = ["jpype"], specifier = ">=2.10.0" }, 474 | ] 475 | 476 | [package.metadata.requires-dev] 477 | dev = [ 478 | { name = "coverage", specifier = ">=7.6.10" }, 479 | { name = "coveralls", specifier = ">=4.0.1" }, 480 | { name = "ipykernel", specifier = ">=6.29.5" }, 481 | { name = "pytest", specifier = ">=8.3.4" }, 482 | { name = "pytest-cov", specifier = ">=6.0.0" }, 483 | ] 484 | 485 | [[package]] 486 | name = "pexpect" 487 | version = "4.9.0" 488 | source = { registry = "https://pypi.org/simple" } 489 | dependencies = [ 490 | { name = "ptyprocess" }, 491 | ] 492 | sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } 493 | wheels = [ 494 | { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, 495 | ] 496 | 497 | [[package]] 498 | name = "pikepdf" 499 | version = "9.5.1" 500 | source = { registry = "https://pypi.org/simple" } 501 | dependencies = [ 502 | { name = "deprecated" }, 503 | { name = "lxml" }, 504 | { name = "packaging" }, 505 | { name = "pillow" }, 506 | ] 507 | sdist = { url = "https://files.pythonhosted.org/packages/6f/9b/a03b9dd92c44779bfd189bd78ddb3357f53ca0100618e5f1d4b1650220a5/pikepdf-9.5.1.tar.gz", hash = "sha256:dccdab8c176956ab049bf527cf4f47b4f678ac77d65659cc2575a27e3965ce3f", size = 2916260 } 508 | wheels = [ 509 | { url = "https://files.pythonhosted.org/packages/48/20/3fb3f82269cfcd3b0424985387ab714473637e20fea9beb7841aec758d55/pikepdf-9.5.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:9aa11fbfc9b27a722b4ea6b7e766725676530b6ad1a12cc95393d2fd234fb431", size = 4811307 }, 510 | { url = "https://files.pythonhosted.org/packages/fe/b7/e0fa18575b3106f486645f41b077032f886cbfec5e031e7f8e7f99d850f8/pikepdf-9.5.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:10f5f0724ff2d5b2bf4ad33ca334d1053d7d7c8cd871abddf03df72e58f42aa4", size = 4489307 }, 511 | { url = "https://files.pythonhosted.org/packages/d8/fb/eecdf24ecb687c870b0c72941c9d943cdf2e129b17c269390e97c7666476/pikepdf-9.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17d1f5667cf19093e7b4861a1cfe8a7c44b8cc74179c117da492bbc8c0843109", size = 2262698 }, 512 | { url = "https://files.pythonhosted.org/packages/3d/08/1a9d00963ed0a9e7a085086a1850738601e228a8b1300931346b45c2f9fb/pikepdf-9.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdf7aed55487d72c213e9224aef49f0370dc3a6501baaaf89d4eedfb57f3ef8", size = 2411496 }, 513 | { url = "https://files.pythonhosted.org/packages/e3/c6/2afd1b2f88045e87117d44e17bd7f742956d5ef709f258050c7a1b5484ae/pikepdf-9.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44033a1908fc2bd2827e6b4f7e039eb8e9742488047112e4ca3991d636641761", size = 3307715 }, 514 | { url = "https://files.pythonhosted.org/packages/af/c6/02a77275a6b1dc27ffb1e0b53d12a072625ce6b2b7f4fa9fee8ac7cbdab4/pikepdf-9.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:48a258dc8f3ba5381d3000082264f4bca93e00b640c267844140fac4cfe3ec79", size = 3473011 }, 515 | { url = "https://files.pythonhosted.org/packages/49/7a/3c76ef6e3777cf40e29a001298be3bb8da8a3a9591d4a8de150aa995a3b7/pikepdf-9.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:ac14f0d99c996d437ac8f1e72f5e39c4534f8e341b0d84baf7e01ae154148a11", size = 3469968 }, 516 | ] 517 | 518 | [[package]] 519 | name = "pillow" 520 | version = "11.1.0" 521 | source = { registry = "https://pypi.org/simple" } 522 | sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } 523 | wheels = [ 524 | { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, 525 | { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, 526 | { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, 527 | { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, 528 | { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, 529 | { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, 530 | { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, 531 | { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, 532 | { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, 533 | { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, 534 | { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, 535 | { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, 536 | { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, 537 | { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, 538 | { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, 539 | { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, 540 | { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, 541 | { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, 542 | { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, 543 | ] 544 | 545 | [[package]] 546 | name = "platformdirs" 547 | version = "4.3.6" 548 | source = { registry = "https://pypi.org/simple" } 549 | sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } 550 | wheels = [ 551 | { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, 552 | ] 553 | 554 | [[package]] 555 | name = "pluggy" 556 | version = "1.5.0" 557 | source = { registry = "https://pypi.org/simple" } 558 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } 559 | wheels = [ 560 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, 561 | ] 562 | 563 | [[package]] 564 | name = "prompt-toolkit" 565 | version = "3.0.48" 566 | source = { registry = "https://pypi.org/simple" } 567 | dependencies = [ 568 | { name = "wcwidth" }, 569 | ] 570 | sdist = { url = "https://files.pythonhosted.org/packages/2d/4f/feb5e137aff82f7c7f3248267b97451da3644f6cdc218edfe549fb354127/prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", size = 424684 } 571 | wheels = [ 572 | { url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 }, 573 | ] 574 | 575 | [[package]] 576 | name = "psutil" 577 | version = "6.1.1" 578 | source = { registry = "https://pypi.org/simple" } 579 | sdist = { url = "https://files.pythonhosted.org/packages/1f/5a/07871137bb752428aa4b659f910b399ba6f291156bdea939be3e96cae7cb/psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", size = 508502 } 580 | wheels = [ 581 | { url = "https://files.pythonhosted.org/packages/61/99/ca79d302be46f7bdd8321089762dd4476ee725fce16fc2b2e1dbba8cac17/psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8", size = 247511 }, 582 | { url = "https://files.pythonhosted.org/packages/0b/6b/73dbde0dd38f3782905d4587049b9be64d76671042fdcaf60e2430c6796d/psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", size = 248985 }, 583 | { url = "https://files.pythonhosted.org/packages/17/38/c319d31a1d3f88c5b79c68b3116c129e5133f1822157dd6da34043e32ed6/psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", size = 284488 }, 584 | { url = "https://files.pythonhosted.org/packages/9c/39/0f88a830a1c8a3aba27fededc642da37613c57cbff143412e3536f89784f/psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", size = 287477 }, 585 | { url = "https://files.pythonhosted.org/packages/47/da/99f4345d4ddf2845cb5b5bd0d93d554e84542d116934fde07a0c50bd4e9f/psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", size = 289017 }, 586 | { url = "https://files.pythonhosted.org/packages/38/53/bd755c2896f4461fd4f36fa6a6dcb66a88a9e4b9fd4e5b66a77cf9d4a584/psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", size = 250602 }, 587 | { url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444 }, 588 | ] 589 | 590 | [[package]] 591 | name = "ptyprocess" 592 | version = "0.7.0" 593 | source = { registry = "https://pypi.org/simple" } 594 | sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } 595 | wheels = [ 596 | { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, 597 | ] 598 | 599 | [[package]] 600 | name = "pure-eval" 601 | version = "0.2.3" 602 | source = { registry = "https://pypi.org/simple" } 603 | sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } 604 | wheels = [ 605 | { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, 606 | ] 607 | 608 | [[package]] 609 | name = "pycparser" 610 | version = "2.22" 611 | source = { registry = "https://pypi.org/simple" } 612 | sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } 613 | wheels = [ 614 | { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, 615 | ] 616 | 617 | [[package]] 618 | name = "pygments" 619 | version = "2.19.1" 620 | source = { registry = "https://pypi.org/simple" } 621 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } 622 | wheels = [ 623 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, 624 | ] 625 | 626 | [[package]] 627 | name = "pytest" 628 | version = "8.3.4" 629 | source = { registry = "https://pypi.org/simple" } 630 | dependencies = [ 631 | { name = "colorama", marker = "sys_platform == 'win32'" }, 632 | { name = "iniconfig" }, 633 | { name = "packaging" }, 634 | { name = "pluggy" }, 635 | ] 636 | sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } 637 | wheels = [ 638 | { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, 639 | ] 640 | 641 | [[package]] 642 | name = "pytest-cov" 643 | version = "6.0.0" 644 | source = { registry = "https://pypi.org/simple" } 645 | dependencies = [ 646 | { name = "coverage" }, 647 | { name = "pytest" }, 648 | ] 649 | sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } 650 | wheels = [ 651 | { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, 652 | ] 653 | 654 | [[package]] 655 | name = "python-dateutil" 656 | version = "2.9.0.post0" 657 | source = { registry = "https://pypi.org/simple" } 658 | dependencies = [ 659 | { name = "six" }, 660 | ] 661 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } 662 | wheels = [ 663 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, 664 | ] 665 | 666 | [[package]] 667 | name = "pytz" 668 | version = "2024.2" 669 | source = { registry = "https://pypi.org/simple" } 670 | sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } 671 | wheels = [ 672 | { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, 673 | ] 674 | 675 | [[package]] 676 | name = "pywin32" 677 | version = "308" 678 | source = { registry = "https://pypi.org/simple" } 679 | wheels = [ 680 | { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 }, 681 | { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 }, 682 | { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 }, 683 | ] 684 | 685 | [[package]] 686 | name = "pyzmq" 687 | version = "26.2.0" 688 | source = { registry = "https://pypi.org/simple" } 689 | dependencies = [ 690 | { name = "cffi", marker = "implementation_name == 'pypy'" }, 691 | ] 692 | sdist = { url = "https://files.pythonhosted.org/packages/fd/05/bed626b9f7bb2322cdbbf7b4bd8f54b1b617b0d2ab2d3547d6e39428a48e/pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f", size = 271975 } 693 | wheels = [ 694 | { url = "https://files.pythonhosted.org/packages/04/a7/0f7e2f6c126fe6e62dbae0bc93b1bd3f1099cf7fea47a5468defebe3f39d/pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726", size = 1006564 }, 695 | { url = "https://files.pythonhosted.org/packages/31/b6/a187165c852c5d49f826a690857684333a6a4a065af0a6015572d2284f6a/pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3", size = 1340447 }, 696 | { url = "https://files.pythonhosted.org/packages/68/ba/f4280c58ff71f321602a6e24fd19879b7e79793fb8ab14027027c0fb58ef/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50", size = 665485 }, 697 | { url = "https://files.pythonhosted.org/packages/77/b5/c987a5c53c7d8704216f29fc3d810b32f156bcea488a940e330e1bcbb88d/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb", size = 903484 }, 698 | { url = "https://files.pythonhosted.org/packages/29/c9/07da157d2db18c72a7eccef8e684cefc155b712a88e3d479d930aa9eceba/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187", size = 859981 }, 699 | { url = "https://files.pythonhosted.org/packages/43/09/e12501bd0b8394b7d02c41efd35c537a1988da67fc9c745cae9c6c776d31/pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b", size = 860334 }, 700 | { url = "https://files.pythonhosted.org/packages/eb/ff/f5ec1d455f8f7385cc0a8b2acd8c807d7fade875c14c44b85c1bddabae21/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18", size = 1196179 }, 701 | { url = "https://files.pythonhosted.org/packages/ec/8a/bb2ac43295b1950fe436a81fc5b298be0b96ac76fb029b514d3ed58f7b27/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115", size = 1507668 }, 702 | { url = "https://files.pythonhosted.org/packages/a9/49/dbc284ebcfd2dca23f6349227ff1616a7ee2c4a35fe0a5d6c3deff2b4fed/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e", size = 1406539 }, 703 | { url = "https://files.pythonhosted.org/packages/00/68/093cdce3fe31e30a341d8e52a1ad86392e13c57970d722c1f62a1d1a54b6/pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5", size = 575567 }, 704 | { url = "https://files.pythonhosted.org/packages/92/ae/6cc4657148143412b5819b05e362ae7dd09fb9fe76e2a539dcff3d0386bc/pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad", size = 637551 }, 705 | { url = "https://files.pythonhosted.org/packages/6c/67/fbff102e201688f97c8092e4c3445d1c1068c2f27bbd45a578df97ed5f94/pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797", size = 540378 }, 706 | { url = "https://files.pythonhosted.org/packages/3f/fe/2d998380b6e0122c6c4bdf9b6caf490831e5f5e2d08a203b5adff060c226/pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a", size = 1007378 }, 707 | { url = "https://files.pythonhosted.org/packages/4a/f4/30d6e7157f12b3a0390bde94d6a8567cdb88846ed068a6e17238a4ccf600/pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc", size = 1329532 }, 708 | { url = "https://files.pythonhosted.org/packages/82/86/3fe917870e15ee1c3ad48229a2a64458e36036e64b4afa9659045d82bfa8/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5", size = 653242 }, 709 | { url = "https://files.pythonhosted.org/packages/50/2d/242e7e6ef6c8c19e6cb52d095834508cd581ffb925699fd3c640cdc758f1/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672", size = 888404 }, 710 | { url = "https://files.pythonhosted.org/packages/ac/11/7270566e1f31e4ea73c81ec821a4b1688fd551009a3d2bab11ec66cb1e8f/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797", size = 845858 }, 711 | { url = "https://files.pythonhosted.org/packages/91/d5/72b38fbc69867795c8711bdd735312f9fef1e3d9204e2f63ab57085434b9/pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386", size = 847375 }, 712 | { url = "https://files.pythonhosted.org/packages/dd/9a/10ed3c7f72b4c24e719c59359fbadd1a27556a28b36cdf1cd9e4fb7845d5/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306", size = 1183489 }, 713 | { url = "https://files.pythonhosted.org/packages/72/2d/8660892543fabf1fe41861efa222455811adac9f3c0818d6c3170a1153e3/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6", size = 1492932 }, 714 | { url = "https://files.pythonhosted.org/packages/7b/d6/32fd69744afb53995619bc5effa2a405ae0d343cd3e747d0fbc43fe894ee/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0", size = 1392485 }, 715 | ] 716 | 717 | [[package]] 718 | name = "requests" 719 | version = "2.32.3" 720 | source = { registry = "https://pypi.org/simple" } 721 | dependencies = [ 722 | { name = "certifi" }, 723 | { name = "charset-normalizer" }, 724 | { name = "idna" }, 725 | { name = "urllib3" }, 726 | ] 727 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } 728 | wheels = [ 729 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, 730 | ] 731 | 732 | [[package]] 733 | name = "six" 734 | version = "1.17.0" 735 | source = { registry = "https://pypi.org/simple" } 736 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } 737 | wheels = [ 738 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, 739 | ] 740 | 741 | [[package]] 742 | name = "stack-data" 743 | version = "0.6.3" 744 | source = { registry = "https://pypi.org/simple" } 745 | dependencies = [ 746 | { name = "asttokens" }, 747 | { name = "executing" }, 748 | { name = "pure-eval" }, 749 | ] 750 | sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } 751 | wheels = [ 752 | { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, 753 | ] 754 | 755 | [[package]] 756 | name = "tabula-py" 757 | version = "2.10.0" 758 | source = { registry = "https://pypi.org/simple" } 759 | dependencies = [ 760 | { name = "distro" }, 761 | { name = "numpy" }, 762 | { name = "pandas" }, 763 | ] 764 | sdist = { url = "https://files.pythonhosted.org/packages/e2/31/2a14a5048f681c404ae0b32a00d141128dd3065965190fdcae3b33e2bcae/tabula_py-2.10.0.tar.gz", hash = "sha256:75968a83fe978e5d56ccf23f0f0255a459c256b7b52db7cabe5ac795bb3b12df", size = 12459408 } 765 | wheels = [ 766 | { url = "https://files.pythonhosted.org/packages/2f/80/10bc6f303054d1a06eb8628f90e5997f4b1272956a477230f3fa95637c28/tabula_py-2.10.0-py3-none-any.whl", hash = "sha256:c7596c559fc813e313eb4fbc7aabe7e4290dbd04717c4cbe4aa4a2cafd00ab63", size = 12021009 }, 767 | ] 768 | 769 | [package.optional-dependencies] 770 | jpype = [ 771 | { name = "jpype1" }, 772 | ] 773 | 774 | [[package]] 775 | name = "tornado" 776 | version = "6.4.2" 777 | source = { registry = "https://pypi.org/simple" } 778 | sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 } 779 | wheels = [ 780 | { url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 }, 781 | { url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 }, 782 | { url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 }, 783 | { url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 }, 784 | { url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 }, 785 | { url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 }, 786 | { url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 }, 787 | { url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 }, 788 | { url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 }, 789 | { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, 790 | ] 791 | 792 | [[package]] 793 | name = "traitlets" 794 | version = "5.14.3" 795 | source = { registry = "https://pypi.org/simple" } 796 | sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } 797 | wheels = [ 798 | { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, 799 | ] 800 | 801 | [[package]] 802 | name = "tzdata" 803 | version = "2024.2" 804 | source = { registry = "https://pypi.org/simple" } 805 | sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } 806 | wheels = [ 807 | { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, 808 | ] 809 | 810 | [[package]] 811 | name = "urllib3" 812 | version = "2.3.0" 813 | source = { registry = "https://pypi.org/simple" } 814 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } 815 | wheels = [ 816 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, 817 | ] 818 | 819 | [[package]] 820 | name = "wcwidth" 821 | version = "0.2.13" 822 | source = { registry = "https://pypi.org/simple" } 823 | sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } 824 | wheels = [ 825 | { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, 826 | ] 827 | 828 | [[package]] 829 | name = "wrapt" 830 | version = "1.17.1" 831 | source = { registry = "https://pypi.org/simple" } 832 | sdist = { url = "https://files.pythonhosted.org/packages/c8/dd/35c573cc2b4b8d65ea96bba0247d05710f284857d30e2266d1874f1c727d/wrapt-1.17.1.tar.gz", hash = "sha256:16b2fdfa09a74a3930175b6d9d7d008022aa72a4f02de2b3eecafcc1adfd3cfe", size = 55552 } 833 | wheels = [ 834 | { url = "https://files.pythonhosted.org/packages/0e/16/82d25dd10e97eabb561d491487ff111ac272a4024f40df098bd61c96840b/wrapt-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:99e544e6ce26f89ad5acc6f407bc4daf7c1d42321e836f5c768f834100bdf35c", size = 38821 }, 835 | { url = "https://files.pythonhosted.org/packages/08/e2/c79dd3c9712988156ea86cd507a81f2b3f045eb84af2d0f7aedb42c709f9/wrapt-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:78da796b74f2c8e0af021ee99feb3bff7cb46f8e658fe25c20e66be1080db4a2", size = 38920 }, 836 | { url = "https://files.pythonhosted.org/packages/4d/d8/bc2bb9797543b31ef7311074583c83addbfc21f1bead66ca7c9d637de6fd/wrapt-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f1bc359f6c52e53565e7af24b423e7a1eea97d155f38ac9e90e95303514710b", size = 88691 }, 837 | { url = "https://files.pythonhosted.org/packages/e7/d3/8d64b5ced10eb0ef856ae864c806292de4891c4945db3444188d45a17b43/wrapt-1.17.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbead724daa13cae46e8ab3bb24938d8514d123f34345535b184f3eb1b7ad717", size = 80862 }, 838 | { url = "https://files.pythonhosted.org/packages/65/22/ee8e9a7014f7c011edac4a9babea4d0aa73a363dd618afc9b31669e478a8/wrapt-1.17.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdf7b0e3d3713331c0bb9daac47cd10e5aa60d060e53696f50de4e560bd5617f", size = 89174 }, 839 | { url = "https://files.pythonhosted.org/packages/fd/10/3d1610d0c220a9f09317d7c9c216889b9dd67329e23d2fcf1017f2d67fc9/wrapt-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f17e8d926f63aed65ff949682c922f96d00f65c2e852c24272232313fa7823d5", size = 86721 }, 840 | { url = "https://files.pythonhosted.org/packages/3c/c1/2f4b20057afcfbfad4886138a702ae2ffd79abbb43884b31e2388895e367/wrapt-1.17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9e04f3bd30e0b23c0ca7e1d4084e7d28b6d7d2feb8b7bc69b496fe881280579b", size = 79761 }, 841 | { url = "https://files.pythonhosted.org/packages/f2/c9/c6bde0a10a7108da0ffaa0a8337221e66636199b367e7d6f1d035e0b306a/wrapt-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5660e470edfa15ae7ef407272c642d29e9962777a6b30bfa8fc0da2173dc9afd", size = 87586 }, 842 | { url = "https://files.pythonhosted.org/packages/2a/ad/956a2db1196bde82088f5576eb1d7a290c4ffc0dec00bfc9d29fca440fff/wrapt-1.17.1-cp313-cp313-win32.whl", hash = "sha256:a992f9e019145e84616048556546edeaba68e05e1c1ffbe8391067a63cdadb0c", size = 36678 }, 843 | { url = "https://files.pythonhosted.org/packages/d7/3a/8bf805ab213f7830b5998027ada2a3fae8e93529df7b0c446946d7f8e9e9/wrapt-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:5c2e24ba455af4b0a237a890ea6ed9bafd01fac2c47095f87c53ea3344215d43", size = 38873 }, 844 | { url = "https://files.pythonhosted.org/packages/d7/76/878e3891ea25875608c5075b81240a4060e48eec786ff354b2a5d3eb87f1/wrapt-1.17.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88623fd957ba500d8bb0f7427a76496d99313ca2f9e932481c0882e034cf1add", size = 40060 }, 845 | { url = "https://files.pythonhosted.org/packages/76/4b/fdde9124f6f61a56e1982cd0f7f0bc8fe2ababb876a50da3308e9ea462a0/wrapt-1.17.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:162d5f15bdd3b8037e06540902227ef9e0f298496c0afaadd9e2875851446693", size = 40154 }, 846 | { url = "https://files.pythonhosted.org/packages/17/f2/e3d909ded67bd7d15b7f02f9cb05e111d2fef9499c1dc0f43690391b8c53/wrapt-1.17.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bb82447ddae4e3d9b51f40c494f66e6cbd8fb0e8e8b993678416535c67f9a0d", size = 113469 }, 847 | { url = "https://files.pythonhosted.org/packages/6f/96/2ba3bd9b2d81b139a5784bf997bffc54979b561c272a953af3a69c242e02/wrapt-1.17.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ce4cff3922707048d754e365c4ebf41a3bcbf29b329349bf85d51873c7c7e9e", size = 101207 }, 848 | { url = "https://files.pythonhosted.org/packages/eb/9a/c8e0275eeef83f0b8bf685034244fb0bf21d2e759fd7a6d54005de6b887f/wrapt-1.17.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fdc4e73a3fa0c25eed4d836d9732226f0326957cb075044a7f252b465299433", size = 109341 }, 849 | { url = "https://files.pythonhosted.org/packages/4b/e3/346259c335b04d342beddba6a97030932b53a8ae35d7ff8a319ab2204270/wrapt-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bca1c0824f824bcd97b4b179dd55dcad1dab419252be2b2faebbcacefa3b27b2", size = 110232 }, 850 | { url = "https://files.pythonhosted.org/packages/c3/aa/9611db2f50359b0b091e501405bc2497b7369185b342cae7bb2218a986e8/wrapt-1.17.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6d44b14f3a2f6343a07c90344850b7af5515538ce3a5d01f9c87d8bae9bd8724", size = 100474 }, 851 | { url = "https://files.pythonhosted.org/packages/33/c2/edbcad020deeb742bce83647a7d13e47c35fafcab4fba4a89ec006ad0385/wrapt-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:169033329022739c6f0d8cd3031a113953b0ba500f3d5978904bdd40baec4568", size = 106377 }, 852 | { url = "https://files.pythonhosted.org/packages/4f/bf/e2aa032cea63737cbabd4069c86d6aa4ba075ee19c44a165e1362a5b403b/wrapt-1.17.1-cp313-cp313t-win32.whl", hash = "sha256:52f0907287d9104112dbebda46af4db0793fcc4c64c8a867099212d116b6db64", size = 37987 }, 853 | { url = "https://files.pythonhosted.org/packages/77/fb/439f032c1b52a1750c304ff85253edfec3a50d4e39fa9a338ab0f837acb4/wrapt-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:7966f98fa36933333d8a1c3d8552aa3d0735001901a4aabcfbd5a502b4ef14fe", size = 40751 }, 854 | { url = "https://files.pythonhosted.org/packages/94/47/299f204e352655c117b9dec03fc585866df7eea72660515208ec67c185c4/wrapt-1.17.1-py3-none-any.whl", hash = "sha256:f3117feb1fc479eaf84b549d3f229d5d2abdb823f003bc2a1c6dd70072912fa0", size = 23589 }, 855 | ] 856 | --------------------------------------------------------------------------------