├── .github └── workflows │ ├── ci.yaml │ └── release.yaml ├── .gitignore ├── LICENSE ├── README.md ├── autobot ├── __init__.py ├── __main__.py ├── api.py ├── main.py ├── prompt.py ├── py.typed ├── refactor │ ├── __init__.py │ ├── patches.py │ └── refactor.py ├── review │ ├── __init__.py │ └── review.py ├── schematic.py ├── schematics │ ├── assert_equals │ │ ├── after.py │ │ └── before.py │ ├── convert_to_dataclass │ │ ├── after.py │ │ └── before.py │ ├── keyword_only_arguments │ │ ├── after.py │ │ └── before.py │ ├── numpy_builtin_aliases │ │ ├── after.py │ │ └── before.py │ ├── print_statement │ │ ├── after.py │ │ └── before.py │ ├── sorted_attributes │ │ ├── after.py │ │ └── before.py │ ├── standard_library_generics │ │ ├── after.py │ │ └── before.py │ ├── unittest_to_pytest │ │ ├── after.py │ │ └── before.py │ ├── unnecessary_f_strings │ │ ├── after.py │ │ └── before.py │ ├── use_generator │ │ ├── after.py │ │ └── before.py │ └── useless_object_inheritance │ │ ├── after.py │ │ └── before.py ├── snippet.py ├── transforms.py ├── utils │ ├── __init__.py │ ├── cache.py │ ├── filesystem.py │ └── getch.py └── version.py ├── mypy.ini ├── pyproject.toml ├── tests ├── __init__.py ├── integration │ ├── __init__.py │ └── test_prompt.py └── unit │ ├── __init__.py │ └── test_schematic.py └── uv.lock /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set up Python 3.12 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: "3.12" 19 | - uses: astral-sh/setup-uv@v1 20 | with: 21 | version: "latest" 22 | - name: Install dependencies 23 | run: uv sync -p 3.12 24 | - name: Mypy 25 | run: uv run mypy autobot tests 26 | - name: unittest 27 | run: uv run python -m unittest discover -s tests/unit 28 | - name: lint 29 | run: uv run ruff check autobot tests 30 | - name: format 31 | run: uv run ruff format --check autobot tests 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | create: 7 | tags: 8 | - v* 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up Python 3.12 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: "3.12" 20 | - uses: astral-sh/setup-uv@v1 21 | with: 22 | version: "latest" 23 | - name: Install dependencies 24 | run: uv sync -p 3.12 25 | - name: Build wheels 26 | run: uv tool run hatch build 27 | - name: Upload wheels 28 | uses: actions/upload-artifact@v2 29 | with: 30 | name: wheels 31 | path: dist 32 | 33 | release: 34 | name: Release 35 | runs-on: ubuntu-latest 36 | needs: 37 | - build 38 | if: "startsWith(github.ref, 'refs/tags/')" 39 | steps: 40 | - uses: actions/download-artifact@v2 41 | with: 42 | name: wheels 43 | - name: Set up Python 3.12 44 | uses: actions/setup-python@v4 45 | with: 46 | python-version: "3.12" 47 | - uses: hynek/setup-cached-uv@v1 48 | - name: Publish to PyPI 49 | env: 50 | TWINE_USERNAME: __token__ 51 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 52 | run: | 53 | uv tool run twine upload --skip-existing * 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local ignores 2 | .autobot_cache 3 | .autobot_patches 4 | autobot/schematics/**/targets 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # poetry 103 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 104 | # This is especially recommended for binary packages to ensure reproducibility, and is more 105 | # commonly ignored for libraries. 106 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 107 | #poetry.lock 108 | 109 | # pdm 110 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 111 | #pdm.lock 112 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 113 | # in version control. 114 | # https://pdm.fming.dev/#use-with-ide 115 | .pdm.toml 116 | 117 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 118 | __pypackages__/ 119 | 120 | # Celery stuff 121 | celerybeat-schedule 122 | celerybeat.pid 123 | 124 | # SageMath parsed files 125 | *.sage.py 126 | 127 | # Environments 128 | .env 129 | .venv 130 | env/ 131 | venv/ 132 | ENV/ 133 | env.bak/ 134 | venv.bak/ 135 | 136 | # Spyder project settings 137 | .spyderproject 138 | .spyproject 139 | 140 | # Rope project settings 141 | .ropeproject 142 | 143 | # mkdocs documentation 144 | /site 145 | 146 | # mypy 147 | .mypy_cache/ 148 | .dmypy.json 149 | dmypy.json 150 | 151 | # Pyre type checker 152 | .pyre/ 153 | 154 | # pytype static type analyzer 155 | .pytype/ 156 | 157 | # Cython debug symbols 158 | cython_debug/ 159 | 160 | # PyCharm 161 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 162 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 163 | # and can be added to the global gitignore or merged into this file. For a more nuclear 164 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 165 | .idea/ 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Charles Marsh 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # autobot 2 | 3 | [![Actions status](https://github.com/charliermarsh/autobot/workflows/CI/badge.svg)](https://github.com/charliermarsh/autobot/actions) 4 | [![PyPI version](https://badge.fury.io/py/autobot-ml.svg)](https://badge.fury.io/py/autobot-ml) 5 | 6 | An automated code refactoring tool powered by GPT-3. Like GitHub Copilot, for your existing 7 | codebase. 8 | 9 | Autobot takes an example change as input and generates patches for you to review by scanning your 10 | codebase for similar code blocks and "applying" that change to the existing source code. 11 | 12 |

13 | Sorting class attributes 14 |

15 | 16 | See more examples on 17 | Twitter, or read the 18 | blog post. 19 | 20 | _N.B. Autobot is a prototype and isn't recommended for use on large codebases. See: ["Limitations"](#Limitations)._ 21 | 22 | ## Getting started 23 | 24 | Autobot is available as [`autobot-ml`](https://pypi.org/project/autobot-ml/) on PyPI: 25 | 26 | ```shell 27 | pip install autobot-ml 28 | ``` 29 | 30 | Autobot depends on the [OpenAI API](https://openai.com/api/) and, in particular, expects your OpenAI 31 | organization ID and API key to be exposed as the `OPENAI_ORGANIZATION` and `OPENAI_API_KEY` 32 | environment variables, respectively. 33 | 34 | Autobot can also read from a `.env` file: 35 | 36 | ``` 37 | OPENAI_ORGANIZATION=${YOUR_OPENAI_ORGANIZATION} 38 | OPENAI_API_KEY=${YOUR_OPENAI_API_KEY} 39 | ``` 40 | 41 | From there, you can run any of Autobot's built-in refactors (called "schematics"): 42 | 43 | ```shell 44 | autobot run useless_object_inheritance /path/to/file.py 45 | ``` 46 | 47 | ## Example usage 48 | 49 | _TL;DR: Autobot is a command-line tool. To generate patches, use `autobot run`; to review the 50 | generated patches, use `autobot review`._ 51 | 52 | Autobot is designed around a two-step workflow. 53 | 54 | In the first step (`autobot run {schematic} {files_to_analyze}`), we point Autobot to (1) the 55 | "schematic" that defines our desired change and (2) the files to which the change should be 56 | applied. 57 | 58 | In the second step (`autobot review`), we review the patches that Autobot generated and, for each 59 | suggested change, either apply it to the codebase or reject the patch entirely. 60 | 61 | Autobot ships with several schematics that you can use out-of-the-box: 62 | 63 | - `assert_equals` 64 | - `convert_to_dataclass` 65 | - `numpy_builtin_aliases` 66 | - `print_statement` 67 | - `sorted_attributes` 68 | - `standard_library_generics` 69 | - `unnecessary_f_strings` 70 | - `use_generator` 71 | - `useless_object_inheritance` 72 | 73 | For example: to remove any usages of NumPy's deprecated `np.int` and associated aliases, we'd first 74 | run `autobot run numpy_builtin_aliases /path/to/file.py`, followed by `autobot review`. 75 | 76 | The `schematic` argument to `autobot run` can either reference a directory within `schematics` (like 77 | `numpy_builtin_aliases`, above) or a path to a user-defined schematic directory on-disk. 78 | 79 | ### Implementing a new refactor ("schematic") 80 | 81 | Every refactor facilitated by Autobot requires a "schematic". Autobot ships with a few schematics 82 | in the `schematics` directory, but it's intended to be used with user-provided schematics. 83 | 84 | A schematic is a directory containing two files: 85 | 86 | 1. `before.py`: A code snippet demonstrating the "before" state of the refactor. 87 | 2. `after.py`: A code snippet demonstrating the "after" state of the refactor. 88 | 89 | Each file is expected to consist of a brief top-level docstring describing the "before" or "after" 90 | state, followed by a single function or class. 91 | 92 | For example: in Python 3, `class Foo(object)` is equivalent to `class Foo`. To automatically remove 93 | those useless object inheritances from our codebase, we'd create a `useless_object_inheritance` 94 | directory, and add the following two files: 95 | 96 | ```python 97 | # before.py 98 | """...with object inheritance.""" 99 | class Foo(Bar, object): 100 | def __init__(self, x: int) -> None: 101 | self.x = x 102 | 103 | ``` 104 | 105 | ```python 106 | # after.py 107 | """...without object inheritance.""" 108 | class Foo(Bar): 109 | def __init__(self, x: int) -> None: 110 | self.x = x 111 | 112 | ``` 113 | 114 | We'd then run `autobot run ./useless_object_inheritance /path/to/file/or/directory` to generate 115 | patches, followed by `autobot review` to apply or reject the suggested changes. 116 | 117 | ## Limitations 118 | 119 | 1. Running Autobot consumes OpenAI credits and thus could cost you money. Be careful! 120 | 2. By default, Autobot uses OpenAI's `text-davinci-002` model, though `autobot run` accepts a 121 | `--model` parameter, allowing you to select an alternative OpenAI model. Note, though, that 122 | OpenAI's Codex models are currently in a private beta, so `code-davinci-002` and friends may 123 | error for you. 124 | 4. To speed up execution, Autobot calls out to the OpenAI API in parallel. If you haven't upgraded 125 | to a paid account, you may hit rate-limit errors. You can pass `--nthreads 1` to `autobot run` 126 | to disable multi-threading. Running Autobot over large codebases is not recommended (yet). 127 | 5. Depending on the transform type, Autobot will attempt to generate a patch for every function or 128 | every 129 | class. Any function or class that's "too long" for GPT-3's maximum prompt size will be skipped. 130 | 6. Autobot isn't smart enough to handle nested functions (or nested classes), so nested functions 131 | will likely be processed and appear twice. 132 | 7. Autobot only supports Python code for now. (Autobot relies on parsing the AST to extract relevant 133 | code snippets, so additional languages require extending AST support.) 134 | 135 | ## Roadmap 136 | 137 | 1. **Multi-language support.** Autobot only supports Python code for now. Extending to 138 | multi-language support, at least with the current algorithm, will require supporting additional 139 | AST parsers. The most likely outcome here will either be to leverage [`tree-sitter`](https://github.com/tree-sitter/tree-sitter). 140 | 2. **Supporting large codebases.** What would it take to run Autobot over hundreds of thousands of 141 | lines of code? 142 | 143 | ## License 144 | 145 | MIT 146 | -------------------------------------------------------------------------------- /autobot/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliermarsh/autobot/e42c66659bf97b90ca9ff305a19cc99952d0d43f/autobot/__init__.py -------------------------------------------------------------------------------- /autobot/__main__.py: -------------------------------------------------------------------------------- 1 | from autobot.main import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /autobot/api.py: -------------------------------------------------------------------------------- 1 | """Interface to the OpenAI API.""" 2 | 3 | from __future__ import annotations 4 | 5 | import hashlib 6 | import json 7 | import logging 8 | import os 9 | 10 | import openai 11 | 12 | from autobot.utils import cache 13 | 14 | 15 | def init() -> None: 16 | openai.organization = os.environ["OPENAI_ORGANIZATION"] 17 | openai.api_key = os.environ["OPENAI_API_KEY"] 18 | 19 | 20 | def create_completion( 21 | prompt: str, 22 | max_tokens: int, 23 | *, 24 | temperature: int = 0, 25 | model: str = "text-davinci-002", 26 | stop: str | list[str] | None = None, 27 | ) -> openai.Completion: 28 | request_hash = hashlib.md5( 29 | json.dumps({ 30 | "prompt": prompt, 31 | "max_tokens": max_tokens, 32 | "temperature": temperature, 33 | "model": model, 34 | "stop": stop, 35 | }).encode("utf-8") 36 | ).hexdigest() 37 | 38 | if response := cache.get_from_cache(request_hash): 39 | logging.info("Reading response from cache...") 40 | return response 41 | 42 | response = openai.Completion.create( 43 | model=model, 44 | prompt=prompt, 45 | temperature=temperature, 46 | max_tokens=max_tokens, 47 | stop=stop, 48 | ) 49 | cache.set_in_cache(request_hash, response) 50 | return response 51 | -------------------------------------------------------------------------------- /autobot/main.py: -------------------------------------------------------------------------------- 1 | """Entrypoint to the autobot CLI.""" 2 | 3 | from __future__ import annotations 4 | 5 | import argparse 6 | import logging 7 | from typing import Any 8 | 9 | from dotenv import load_dotenv 10 | from rich.console import Console 11 | from rich.logging import RichHandler 12 | 13 | from autobot.version import __version__ 14 | 15 | 16 | def run(options: Any) -> None: 17 | from autobot import api 18 | from autobot.refactor import run_refactor 19 | from autobot.schematic import Schematic, SchematicDefinitionException 20 | from autobot.utils import filesystem 21 | 22 | api.init() 23 | 24 | model: str = options.model 25 | nthreads: int = options.nthreads 26 | verbose: bool = options.verbose 27 | 28 | logging.basicConfig( 29 | format="%(asctime)s %(levelname)-8s %(message)s", 30 | datefmt="%m-%d %H:%M:%S", 31 | level=logging.INFO if verbose else logging.WARNING, 32 | handlers=[RichHandler()], 33 | ) 34 | 35 | console = Console() 36 | 37 | try: 38 | schematic: Schematic = Schematic.from_directory(options.schematic) 39 | except SchematicDefinitionException as error: 40 | console.print(f"[bold red]error[/] {error}") 41 | exit(1) 42 | 43 | targets = filesystem.collect_python_files(options.files) 44 | if not targets: 45 | console.print("[bold red]error[/] No Python files found") 46 | exit(1) 47 | 48 | run_refactor( 49 | schematic=schematic, 50 | targets=targets, 51 | nthreads=nthreads, 52 | model=model, 53 | ) 54 | 55 | 56 | def review(options: Any) -> None: 57 | from autobot.review import run_review 58 | 59 | run_review() 60 | 61 | 62 | def main() -> None: 63 | load_dotenv() 64 | 65 | parser = argparse.ArgumentParser( 66 | prog="autobot", description="An automated code refactoring tool." 67 | ) 68 | parser.add_argument( 69 | "--version", action="version", version=f"%(prog)s {__version__}" 70 | ) 71 | subparsers = parser.add_subparsers() 72 | 73 | # autobot run 74 | parser_run = subparsers.add_parser( 75 | "run", 76 | description="An automated code refactoring tool.", 77 | usage="autobot run [schematic] [files [files ...]]", 78 | ) 79 | parser_run.add_argument( 80 | "schematic", type=str, help="Path to the autobot schematic." 81 | ) 82 | parser_run.add_argument( 83 | "files", type=str, nargs="+", help="Path to the files to refactor." 84 | ) 85 | parser_run.add_argument( 86 | "--model", 87 | type=str, 88 | default="text-davinci-002", 89 | choices=( 90 | "text-davinci-002", 91 | "text-curie-001", 92 | "text-babbage-001", 93 | "text-ada-001", 94 | "code-davinci-002", 95 | "code-cushman-001", 96 | ), 97 | help=( 98 | "The OpenAI model to use when generating completions. " 99 | "(Note: OpenAI's Codex models are currently in private beta.)" 100 | ), 101 | ) 102 | parser_run.add_argument( 103 | "--nthreads", 104 | type=int, 105 | default=8, 106 | help="The number of threads to use when generating completions.", 107 | ) 108 | parser_run.add_argument( 109 | "--verbose", 110 | action="store_true", 111 | help="Show verbose output.", 112 | ) 113 | parser_run.set_defaults(func=run) 114 | 115 | # autobot review 116 | parser_review = subparsers.add_parser( 117 | "review", 118 | description="An automated code refactoring tool.", 119 | usage="autobot review", 120 | ) 121 | parser_review.set_defaults(func=review) 122 | 123 | args = parser.parse_args() 124 | if hasattr(args, "func"): 125 | args.func(args) 126 | else: 127 | console = Console() 128 | console.print( 129 | "[bold white]Usage: autobot run \[schematic] \[files \[files ...]]" 130 | ) 131 | console.print() 132 | console.print("[bold white]An automated code refactoring tool.") 133 | -------------------------------------------------------------------------------- /autobot/prompt.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, NamedTuple, cast 4 | 5 | from autobot import api 6 | 7 | if TYPE_CHECKING: 8 | from autobot.transforms import TransformType 9 | 10 | 11 | class Prompt(NamedTuple): 12 | text: str 13 | max_tokens: int 14 | stop: str | list[str] | None 15 | 16 | 17 | def make_prompt( 18 | snippet: str, 19 | *, 20 | transform_type: TransformType, 21 | before_text: str, 22 | after_text: str, 23 | before_description: str, 24 | after_description: str, 25 | ) -> Prompt: 26 | """Construct a Prompt object from a source snippet.""" 27 | node_name = transform_type.plaintext_name() 28 | return Prompt( 29 | f"""### Python {node_name} {before_description} 30 | {before_text} 31 | 32 | ### The same Python {node_name} {after_description} 33 | {after_text} 34 | 35 | ### Python {node_name} {before_description} 36 | {snippet} 37 | ### End of {node_name} 38 | 39 | ### Now rewrite the Python {node_name} {after_description} 40 | """, 41 | max_tokens=len(snippet) // 2, 42 | stop=f"### End of {node_name}", 43 | ) 44 | 45 | 46 | def resolve_prompt(prompt: Prompt, *, model: str = "text-davinci-002") -> str: 47 | """Generate a completion for a prompt.""" 48 | response = api.create_completion( 49 | prompt=prompt.text, 50 | max_tokens=prompt.max_tokens, 51 | stop=prompt.stop, 52 | model=model, 53 | temperature=0, 54 | ) 55 | for choice in response["choices"]: 56 | return cast(str, choice["text"]) 57 | else: 58 | raise Exception("Request failed to generate choices.") 59 | -------------------------------------------------------------------------------- /autobot/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliermarsh/autobot/e42c66659bf97b90ca9ff305a19cc99952d0d43f/autobot/py.typed -------------------------------------------------------------------------------- /autobot/refactor/__init__.py: -------------------------------------------------------------------------------- 1 | from .refactor import run_refactor 2 | 3 | __all__ = ["run_refactor"] 4 | -------------------------------------------------------------------------------- /autobot/refactor/patches.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | import subprocess 5 | 6 | PATCH_DIR = os.path.join(os.getcwd(), ".autobot_patches") 7 | 8 | 9 | def save(patch: str, *, target: str, lineno: int) -> None: 10 | """Save a patch to disk.""" 11 | (target_filename, _) = os.path.splitext(target) 12 | patch_filename = os.path.join( 13 | PATCH_DIR, 14 | f"{target_filename}-{lineno}.patch", 15 | ) 16 | os.makedirs(os.path.dirname(patch_filename), exist_ok=True) 17 | with open(patch_filename, "w") as fp: 18 | fp.write(patch) 19 | 20 | 21 | def can_apply(patch_file: str) -> bool: 22 | """Return True if a patch file can be applied to its target.""" 23 | result = subprocess.run( 24 | ["git", "apply", "--check", patch_file], 25 | stdout=subprocess.DEVNULL, 26 | stderr=subprocess.DEVNULL, 27 | ) 28 | return result.returncode == 0 29 | 30 | 31 | def apply(patch_file: str) -> None: 32 | """Apply a patch file to its target.""" 33 | subprocess.check_call(["git", "apply", patch_file]) 34 | -------------------------------------------------------------------------------- /autobot/refactor/refactor.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import difflib 4 | import functools 5 | import logging 6 | import os.path 7 | from multiprocessing.pool import ThreadPool 8 | from typing import TYPE_CHECKING 9 | 10 | from rich.console import Console 11 | from rich.progress import Progress 12 | 13 | from autobot import prompt 14 | from autobot.refactor import patches 15 | from autobot.snippet import Snippet, iter_snippets, recontextualize 16 | 17 | if TYPE_CHECKING: 18 | from autobot.schematic import Schematic 19 | 20 | 21 | def _fix_text( 22 | text: str, 23 | *, 24 | schematic: Schematic, 25 | model: str, 26 | ) -> tuple[str, str]: 27 | """Generate a fix for a piece of source code. 28 | 29 | Returns: a tuple of (input, suggested fix), to play nicely with multiprocessing. 30 | """ 31 | return text, prompt.resolve_prompt( 32 | prompt.make_prompt( 33 | text, 34 | transform_type=schematic.transform_type, 35 | before_text=schematic.before_text, 36 | after_text=schematic.after_text, 37 | before_description=schematic.before_description, 38 | after_description=schematic.after_description, 39 | ), 40 | model=model, 41 | ) 42 | 43 | 44 | def run_refactor( 45 | *, 46 | schematic: Schematic, 47 | targets: list[str], 48 | nthreads: int, 49 | model: str, 50 | ) -> None: 51 | console = Console() 52 | 53 | console.print( 54 | f"[bold]Running [bold cyan]{schematic.title}[/] based on user-provided example" 55 | ) 56 | 57 | console.print( 58 | "-" * len(f"Running {schematic.title} based on user-provided example") 59 | ) 60 | schematic.print_diff() 61 | console.print( 62 | "-" * len(f"Running {schematic.title} based on user-provided example") 63 | ) 64 | console.print() 65 | 66 | # Deduplicate targets, such that if we need to apply the same fix to a bunch of 67 | # snippets, we only make a single API call. 68 | console.print("[bold]1. Extracting AST nodes...") 69 | filename_to_snippets: dict[str, list[Snippet]] = {} 70 | all_snippet_texts: set[str] = set() 71 | for filename in targets: 72 | with open(filename, "r") as fp: 73 | source_code = fp.read() 74 | 75 | filename_to_snippets[filename] = [] 76 | for snippet in iter_snippets( 77 | source_code, schematic.transform_type.ast_node_type() 78 | ): 79 | max_snippet_len = 1600 80 | if len(snippet.text) > max_snippet_len: 81 | logging.warning( 82 | f"Snippet at {filename}:{snippet.lineno} is too long " 83 | f"({len(snippet.text)} > {max_snippet_len}); skipping..." 84 | ) 85 | continue 86 | filename_to_snippets[filename].append(snippet) 87 | all_snippet_texts.add(snippet.text) 88 | 89 | # Map from snippet text to suggested fix. 90 | console.print("[bold]2. Generating completions...") 91 | snippet_text_to_completion: dict[str, str] = {} 92 | with Progress(transient=True, console=console) as progress: 93 | task = progress.add_task("", total=len(all_snippet_texts)) 94 | with ThreadPool(processes=nthreads) as pool: 95 | for text, completion in pool.imap_unordered( 96 | functools.partial( 97 | _fix_text, 98 | schematic=schematic, 99 | model=model, 100 | ), 101 | all_snippet_texts, 102 | ): 103 | progress.update(task, advance=1) 104 | snippet_text_to_completion[text] = completion 105 | 106 | # Format each suggestion as a patch. 107 | console.print("[bold]3. Constructing patches...") 108 | count: int = 0 109 | for target in filename_to_snippets: 110 | with open(target, "r") as fp: 111 | source = fp.read() 112 | 113 | for text, padding, lineno in filename_to_snippets[target]: 114 | before_text = text 115 | after_text = snippet_text_to_completion[text] 116 | patch: str = "" 117 | for line in difflib.unified_diff( 118 | recontextualize(Snippet(before_text, padding, lineno), source), 119 | recontextualize(Snippet(after_text, padding, lineno), source), 120 | lineterm="", 121 | fromfile=os.path.join("a", target), 122 | tofile=os.path.join("b", target), 123 | ): 124 | # TODO(charlie): Why is this necessary? Without it, blank lines contain 125 | # a single space. 126 | stripped = line.strip() 127 | if len(stripped) == 0: 128 | line = stripped 129 | 130 | patch += line 131 | patch += "\n" 132 | 133 | # Save the patch. 134 | if patch: 135 | patches.save(patch, target=target, lineno=lineno) 136 | count += 1 137 | 138 | console.print() 139 | if count == 0: 140 | console.print("[bold white]✨ Done! No suggestions found.") 141 | elif count == 1: 142 | console.print(f"[bold white]✨ Done! Generated {count} patch.") 143 | else: 144 | console.print(f"[bold white]✨ Done! Generated {count} patches.") 145 | -------------------------------------------------------------------------------- /autobot/review/__init__.py: -------------------------------------------------------------------------------- 1 | from .review import run_review 2 | 3 | __all__ = ["run_review"] 4 | -------------------------------------------------------------------------------- /autobot/review/review.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import enum 4 | import os 5 | 6 | from colorama import Fore 7 | from rich.console import Console 8 | 9 | from autobot.refactor import patches 10 | from autobot.utils.getch import getch 11 | 12 | 13 | class Resolution(enum.Enum): 14 | ACCEPT = "a" 15 | REJECT = "r" 16 | SKIP = "s" 17 | 18 | @classmethod 19 | def from_code(cls, code: str) -> Resolution | None: 20 | try: 21 | return cls(code) 22 | except ValueError: 23 | return None 24 | 25 | 26 | def run_review() -> None: 27 | patch_files: list[str] = [] 28 | for root, _, filenames in os.walk(patches.PATCH_DIR): 29 | for filename in filenames: 30 | if filename.endswith(".patch"): 31 | patch_files.append(os.path.join(root, filename)) 32 | 33 | console = Console() 34 | 35 | patches_by_resolution: dict[Resolution, list[str]] = { 36 | Resolution.ACCEPT: [], 37 | Resolution.REJECT: [], 38 | Resolution.SKIP: [], 39 | } 40 | num_patches = len(patch_files) 41 | for i, patch_file in enumerate(patch_files): 42 | if patches.can_apply(patch_file): 43 | with console.screen(hide_cursor=False): 44 | with open(patch_file, "r") as fp: 45 | contents = fp.read() 46 | 47 | console.print( 48 | f"[bold][white]Reviewing [[yellow]{i + 1}/{num_patches}[/yellow]]" 49 | ) 50 | console.print(f"Patch file: [cyan]{os.path.basename(patch_file)}") 51 | 52 | console.print() 53 | for line in contents.splitlines(): 54 | stripped = line.strip() 55 | if len(stripped) == 0: 56 | line = stripped 57 | if line.startswith("-"): 58 | print(f"{Fore.RED}{line}{Fore.RESET}") 59 | elif line.startswith("+"): 60 | print(f"{Fore.GREEN}{line}{Fore.RESET}") 61 | else: 62 | print(line) 63 | console.print() 64 | 65 | console.print(" [bold green]a[/] accept [grey46]apply the patch[/]") 66 | console.print(" [bold red]r[/] reject [grey46]reject the patch[/]") 67 | console.print(" [bold yellow]s[/] skip [grey46]skip the patch[/]") 68 | 69 | try: 70 | while (resolution := Resolution(getch())) is None: 71 | pass 72 | except KeyboardInterrupt: 73 | exit(0) 74 | 75 | patches_by_resolution[resolution].append(patch_file) 76 | if resolution == Resolution.ACCEPT: 77 | # Apply the patch. 78 | patches.apply(patch_file) 79 | os.remove(patch_file) 80 | elif resolution == Resolution.REJECT: 81 | # Reject the patch. 82 | os.remove(patch_file) 83 | elif resolution == Resolution.SKIP: 84 | # Do nothing. 85 | pass 86 | else: 87 | raise ValueError(f"Unexpected resolution: {resolution}") 88 | 89 | if num_patches > 0: 90 | if num_patches == 1: 91 | console.print(f"[bold]Done![/] Reviewed {len(patch_files)} patch.") 92 | else: 93 | console.print(f"[bold]Done![/] Reviewed {len(patch_files)} patches.") 94 | for resolution in patches_by_resolution: 95 | if patches_by_resolution[resolution]: 96 | if resolution == Resolution.ACCEPT: 97 | console.print("[green]Accepted:") 98 | elif resolution == Resolution.REJECT: 99 | console.print("[red]Rejected:") 100 | elif resolution == Resolution.SKIP: 101 | console.print("[yellow]Skipped:") 102 | else: 103 | raise ValueError(f"Unexpected resolution: {resolution}") 104 | 105 | for patch_file in patches_by_resolution[resolution]: 106 | print(f" {os.path.relpath(patch_file, patches.PATCH_DIR)}") 107 | else: 108 | console.print("[bold]Done![/] No patches to review.") 109 | -------------------------------------------------------------------------------- /autobot/schematic.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import ast 4 | import difflib 5 | import os 6 | from typing import NamedTuple 7 | 8 | from autobot.transforms import TransformType 9 | 10 | BEFORE_FILENAME: str = "before.py" 11 | AFTER_FILENAME: str = "after.py" 12 | 13 | 14 | class SchematicDefinitionException(Exception): 15 | pass 16 | 17 | 18 | def extract_transform_type(source_code: str) -> TransformType | None: 19 | for node in ast.walk(ast.parse(source_code)): 20 | for transform_type in TransformType: 21 | if isinstance(node, transform_type.ast_node_type()): 22 | return transform_type 23 | else: 24 | return None 25 | 26 | 27 | def extract_source(source_code: str) -> str | None: 28 | for node in ast.walk(ast.parse(source_code)): 29 | for transform_type in TransformType: 30 | if isinstance(node, transform_type.ast_node_type()): 31 | return ast.get_source_segment(source_code, node) 32 | else: 33 | return None 34 | 35 | 36 | def extract_description(source_code: str) -> str | None: 37 | for node in ast.walk(ast.parse(source_code)): 38 | if isinstance(node, ast.Module): 39 | return ast.get_docstring(node) 40 | else: 41 | return None 42 | 43 | 44 | class Schematic(NamedTuple): 45 | title: str 46 | before_text: str 47 | after_text: str 48 | before_description: str 49 | after_description: str 50 | transform_type: TransformType 51 | 52 | @classmethod 53 | def from_directory(cls, dirname: str) -> Schematic: 54 | """Load a Schematic from a directory.""" 55 | dirname = dirname.rstrip("/") 56 | title = os.path.basename(dirname) 57 | 58 | if not os.path.isdir(dirname): 59 | # Fallback: this could be a schematic that ships with autobot (i.e. a path 60 | # relative to ./schematics). 61 | bundled_dirname = os.path.join( 62 | os.path.dirname(__file__), "schematics", dirname 63 | ) 64 | 65 | if not os.path.isdir(bundled_dirname): 66 | raise SchematicDefinitionException(f"Directory not found: {dirname}") 67 | 68 | dirname = bundled_dirname 69 | 70 | before_filename = os.path.join(dirname, BEFORE_FILENAME) 71 | if not os.path.isfile(before_filename): 72 | raise SchematicDefinitionException( 73 | f"Unable to find file: {before_filename}" 74 | ) 75 | 76 | with open(before_filename, "r") as fp: 77 | source_code = fp.read() 78 | if not (transform_type := extract_transform_type(source_code)): 79 | raise SchematicDefinitionException( 80 | f"Invalid transform type found in: {before_filename}" 81 | ) 82 | if not (before_text := extract_source(source_code)): 83 | raise SchematicDefinitionException( 84 | f"No source node found in: {before_filename}" 85 | ) 86 | if not (before_description := extract_description(source_code)): 87 | raise SchematicDefinitionException( 88 | f"No description found in: {before_filename}" 89 | ) 90 | before_description = before_description.lstrip(".").rstrip(".") 91 | 92 | after_filename = os.path.join(dirname, AFTER_FILENAME) 93 | if not os.path.isfile(after_filename): 94 | raise SchematicDefinitionException(f"Unable to find file: {after_filename}") 95 | 96 | with open(after_filename, "r") as fp: 97 | source_code = fp.read() 98 | if not (after_text := extract_source(source_code)): 99 | raise SchematicDefinitionException( 100 | f"No source node found in: {after_filename}" 101 | ) 102 | if not (after_description := extract_description(source_code)): 103 | raise SchematicDefinitionException( 104 | f"No description found in: {after_filename}" 105 | ) 106 | after_description = after_description.lstrip(".").rstrip(".") 107 | 108 | return cls( 109 | title=title, 110 | before_text=before_text, 111 | after_text=after_text, 112 | before_description=before_description, 113 | after_description=after_description, 114 | transform_type=transform_type, 115 | ) 116 | 117 | def print_diff(self) -> None: 118 | from colorama import Fore 119 | 120 | for line in difflib.unified_diff( 121 | self.before_text.splitlines(), 122 | self.after_text.splitlines(), 123 | lineterm="", 124 | fromfile=os.path.join("a", self.title, BEFORE_FILENAME), 125 | tofile=os.path.join("b", self.title, AFTER_FILENAME), 126 | ): 127 | # TODO(charlie): Why is this necessary? Without it, blank lines contain 128 | # a single space. 129 | if len(line.strip()) == 0: 130 | line = line.strip() 131 | 132 | if line.startswith("-"): 133 | print(f"{Fore.RED}{line}{Fore.RESET}") 134 | elif line.startswith("+"): 135 | print(f"{Fore.GREEN}{line}{Fore.RESET}") 136 | else: 137 | print(line) 138 | -------------------------------------------------------------------------------- /autobot/schematics/assert_equals/after.py: -------------------------------------------------------------------------------- 1 | """...without self.assertEquals.""" 2 | 3 | 4 | class MemoryCacheTest(unittest.TestCase): 5 | def test_roundtrip(self) -> None: 6 | cache = MemoryCache(scope="test_roundtrip") 7 | 8 | expected = uuid.uuid4().hex.encode("utf-8") 9 | cache.set("VALUE", io.BytesIO(expected)) 10 | actual = cache.get("VALUE") or io.BytesIO() 11 | 12 | self.assertEqual(expected, actual.getvalue()) 13 | -------------------------------------------------------------------------------- /autobot/schematics/assert_equals/before.py: -------------------------------------------------------------------------------- 1 | """...with self.assertEquals.""" 2 | 3 | 4 | class MemoryCacheTest(unittest.TestCase): 5 | def test_roundtrip(self) -> None: 6 | cache = MemoryCache(scope="test_roundtrip") 7 | 8 | expected = uuid.uuid4().hex.encode("utf-8") 9 | cache.set("VALUE", io.BytesIO(expected)) 10 | actual = cache.get("VALUE") or io.BytesIO() 11 | 12 | self.assertEquals(expected, actual.getvalue()) 13 | -------------------------------------------------------------------------------- /autobot/schematics/convert_to_dataclass/after.py: -------------------------------------------------------------------------------- 1 | """...as a @dataclass.""" 2 | 3 | 4 | @dataclass 5 | class MyClass: 6 | x: int 7 | y: int 8 | z: int 9 | 10 | def __post_init__(self) -> None: 11 | print("Initialized MyClass!") 12 | -------------------------------------------------------------------------------- /autobot/schematics/convert_to_dataclass/before.py: -------------------------------------------------------------------------------- 1 | """...as a standard class.""" 2 | 3 | 4 | class MyClass: 5 | def __init__(self, x: int, y: int, z: int): 6 | self.x = x 7 | self.y = y 8 | self.z = z 9 | 10 | print("Initialized MyClass!") 11 | -------------------------------------------------------------------------------- /autobot/schematics/keyword_only_arguments/after.py: -------------------------------------------------------------------------------- 1 | """...with keyword-only arguments.""" 2 | 3 | 4 | def f(x: int, y: int, *, z: int = 1) -> None: 5 | ... 6 | -------------------------------------------------------------------------------- /autobot/schematics/keyword_only_arguments/before.py: -------------------------------------------------------------------------------- 1 | """...without keyword-only arguments.""" 2 | 3 | 4 | def f(x: int, y: int, z: int = 1) -> None: 5 | ... 6 | -------------------------------------------------------------------------------- /autobot/schematics/numpy_builtin_aliases/after.py: -------------------------------------------------------------------------------- 1 | """...without NumPy's deprecated aliases (so int instead of np.int).""" 2 | 3 | 4 | def f() -> None: 5 | a = np.array(dtype=int) 6 | b = np.dtype(str) 7 | c = np.dtype(object) 8 | d = float(123) 9 | -------------------------------------------------------------------------------- /autobot/schematics/numpy_builtin_aliases/before.py: -------------------------------------------------------------------------------- 1 | """...with NumPy's builtin aliases (like np.int).""" 2 | 3 | 4 | def f() -> None: 5 | a = np.array(dtype=np.int) 6 | b = np.dtype(np.unicode) 7 | c = np.dtype(np.object) 8 | d = np.float(123) 9 | -------------------------------------------------------------------------------- /autobot/schematics/print_statement/after.py: -------------------------------------------------------------------------------- 1 | """...without print statements.""" 2 | 3 | 4 | def square(x: int) -> int: 5 | return x * x 6 | -------------------------------------------------------------------------------- /autobot/schematics/print_statement/before.py: -------------------------------------------------------------------------------- 1 | """...with print statements.""" 2 | 3 | 4 | def square(x: int) -> int: 5 | print(f"Computing square of: {x}") 6 | return x * x 7 | -------------------------------------------------------------------------------- /autobot/schematics/sorted_attributes/after.py: -------------------------------------------------------------------------------- 1 | """...with alphabetically sorted class attributes.""" 2 | 3 | 4 | class MyUnsortedConstants: 5 | A = "zzz123" 6 | B = "aaa234" 7 | Daaa = "banana" 8 | cab = "foo bar" 9 | z = "hehehe" 10 | -------------------------------------------------------------------------------- /autobot/schematics/sorted_attributes/before.py: -------------------------------------------------------------------------------- 1 | """...with unsorted class attributes.""" 2 | 3 | 4 | class MyUnsortedConstants: 5 | z = "hehehe" 6 | B = "aaa234" 7 | A = "zzz123" 8 | cab = "foo bar" 9 | Daaa = "banana" 10 | -------------------------------------------------------------------------------- /autobot/schematics/standard_library_generics/after.py: -------------------------------------------------------------------------------- 1 | """...with standard library generics.""" 2 | 3 | 4 | def func( 5 | x: list[int] | None, 6 | y: int | None, 7 | z: int | list[int] | None, 8 | ) -> None: 9 | a: list[int] = [] 10 | b: set[int] = set() 11 | c: int | None = 0 12 | d: int | str = 0 13 | -------------------------------------------------------------------------------- /autobot/schematics/standard_library_generics/before.py: -------------------------------------------------------------------------------- 1 | """...with typing module generics.""" 2 | 3 | 4 | def func( 5 | x: Optional[List[int]], 6 | y: Optional[int], 7 | z: Optional[Union[int, List[int]]], 8 | ) -> None: 9 | a: List[int] = [] 10 | b: Set[int] = set() 11 | c: Optional[int] = 0 12 | d: Union[int, str] = 0 13 | -------------------------------------------------------------------------------- /autobot/schematics/unittest_to_pytest/after.py: -------------------------------------------------------------------------------- 1 | """...as a pytest test suite instead.""" 2 | 3 | 4 | def test_method() -> None: 5 | expected = 3 6 | actual = 1 + 2 7 | 8 | assert expected == actual 9 | -------------------------------------------------------------------------------- /autobot/schematics/unittest_to_pytest/before.py: -------------------------------------------------------------------------------- 1 | """...as a unittest.TestCase.""" 2 | 3 | 4 | def test_method(self) -> None: 5 | expected = 3 6 | actual = 1 + 2 7 | 8 | self.assertEqual(expected, actual) 9 | -------------------------------------------------------------------------------- /autobot/schematics/unnecessary_f_strings/after.py: -------------------------------------------------------------------------------- 1 | """...without useless f-string prefixes.""" 2 | 3 | 4 | def func() -> None: 5 | a = "world" 6 | b = f"Hello, {a}!" 7 | c = "Hello world" 8 | d = "Hello" + ", " + f"{a}!" 9 | -------------------------------------------------------------------------------- /autobot/schematics/unnecessary_f_strings/before.py: -------------------------------------------------------------------------------- 1 | """...with useless f-string prefixes.""" 2 | 3 | 4 | def func() -> None: 5 | a = f"world" 6 | b = f"Hello, {a}!" 7 | c = f"Hello world" 8 | d = f"Hello" + f", " + f"{a}!" 9 | -------------------------------------------------------------------------------- /autobot/schematics/use_generator/after.py: -------------------------------------------------------------------------------- 1 | """...as a generator.""" 2 | 3 | 4 | def compute_squares(n: int) -> Generator[int, None, None]: 5 | for i in range(n): 6 | yield i * i 7 | -------------------------------------------------------------------------------- /autobot/schematics/use_generator/before.py: -------------------------------------------------------------------------------- 1 | """...as a list builder.""" 2 | 3 | 4 | def compute_squares(n: int) -> list[int]: 5 | squares: list[int] = [] 6 | for i in range(n): 7 | squares.append(i * i) 8 | return squares 9 | -------------------------------------------------------------------------------- /autobot/schematics/useless_object_inheritance/after.py: -------------------------------------------------------------------------------- 1 | """...without object inheritance.""" 2 | 3 | 4 | class Foo(Bar): 5 | def __init__(self, x: int) -> None: 6 | self.x = x 7 | -------------------------------------------------------------------------------- /autobot/schematics/useless_object_inheritance/before.py: -------------------------------------------------------------------------------- 1 | """...with object inheritance.""" 2 | 3 | 4 | class Foo(Bar, object): 5 | def __init__(self, x: int) -> None: 6 | self.x = x 7 | -------------------------------------------------------------------------------- /autobot/snippet.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import ast 4 | import re 5 | from typing import Generator, NamedTuple, Type 6 | 7 | 8 | class Snippet(NamedTuple): 9 | """A snippet extracted from source code.""" 10 | 11 | text: str 12 | padding: str 13 | lineno: int 14 | 15 | @classmethod 16 | def from_node(cls, source_code: str, node: ast.AST) -> Snippet: 17 | return decontextualize(source_code, node) 18 | 19 | 20 | def decontextualize(source_code: str, node: ast.AST) -> Snippet: 21 | """Decontextualize a snippet from its originating source code. 22 | 23 | Takes the originating source code as input, along with the node to decontextualize, 24 | and extracts the code as a snippet, removing any indentation. 25 | """ 26 | # Extract the source segment. 27 | source_segment = ast.get_source_segment(source_code, node, padded=True) 28 | assert source_segment, "Unable to find source segment." 29 | 30 | # Dedent the code:. 31 | # TODO(charlie): This isn't safe. For example, there could be multi-line strings 32 | # within a function that need this padding. 33 | lines = source_segment.splitlines() 34 | if m := re.match(r"(\s+)", lines[0]): 35 | padding = m.group() 36 | source_segment = "\n".join([line.removeprefix(padding) for line in lines]) 37 | else: 38 | padding = "" 39 | 40 | lineno: int = node.lineno # type: ignore[attr-defined] 41 | 42 | return Snippet(source_segment, padding, lineno) 43 | 44 | 45 | def recontextualize(snippet: Snippet, source_code: str) -> list[str]: 46 | """Recontextualize a snippet within its originating source code. 47 | 48 | Takes the originating source code and snippet as input, and outputs the lines of the 49 | source code up to and including the snippet, with the snippet adjusted to match the 50 | indentation of its originating context. 51 | """ 52 | lines: list[str] = [] 53 | 54 | # Prepend any lines of the originating source code that precede the snippet. 55 | source_lines = source_code.splitlines() 56 | if snippet.lineno > 1: 57 | for i in range(snippet.lineno - 1): 58 | lines.append(source_lines[i]) 59 | 60 | # Tack on the snippet itself, with indentation re-applied. 61 | for line in snippet.text.splitlines(): 62 | lines.append(snippet.padding + line) 63 | 64 | return lines 65 | 66 | 67 | def iter_snippets( 68 | source_code: str, 69 | node_type: Type[ast.AST] | tuple[Type[ast.AST], ...], 70 | ) -> Generator[Snippet, None, None]: 71 | """Generate all snippets from the provided source code. 72 | 73 | Returns: a tuple of (text to fix, any indentation that was removed from the 74 | snippet, line number in the source file). 75 | """ 76 | for node in ast.walk(ast.parse(source_code)): 77 | if isinstance(node, node_type): 78 | yield Snippet.from_node(source_code, node) 79 | -------------------------------------------------------------------------------- /autobot/transforms.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import ast 4 | import enum 5 | from typing import Type 6 | 7 | 8 | class TransformType(enum.Enum): 9 | CLASS = "Class" 10 | FUNCTION = "Function" 11 | 12 | def plaintext_name(self) -> str: 13 | if self == TransformType.CLASS: 14 | return "class" 15 | if self == TransformType.FUNCTION: 16 | return "function" 17 | raise NotImplementedError(f"Unhandled transform type: {self}") 18 | 19 | def ast_node_type(self) -> Type[ast.AST] | tuple[Type[ast.AST], ...]: 20 | if self == TransformType.CLASS: 21 | return ast.ClassDef 22 | if self == TransformType.FUNCTION: 23 | return ast.FunctionDef, ast.AsyncFunctionDef 24 | raise NotImplementedError(f"Unhandled transform type: {self}") 25 | -------------------------------------------------------------------------------- /autobot/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliermarsh/autobot/e42c66659bf97b90ca9ff305a19cc99952d0d43f/autobot/utils/__init__.py -------------------------------------------------------------------------------- /autobot/utils/cache.py: -------------------------------------------------------------------------------- 1 | """Filesystem-based cache for storing JSON objects.""" 2 | 3 | from __future__ import annotations 4 | 5 | import json 6 | import os 7 | from typing import TypeVar, cast 8 | 9 | CACHE_DIR = os.path.join(os.getcwd(), ".autobot_cache") 10 | 11 | T = TypeVar("T") 12 | 13 | 14 | def cache_filename(key: str) -> str: 15 | return os.path.join(CACHE_DIR, key) 16 | 17 | 18 | def has_in_cache(key: str) -> bool: 19 | os.makedirs(os.path.dirname(cache_filename(key)), exist_ok=True) 20 | return os.path.exists(cache_filename(key)) 21 | 22 | 23 | def get_from_cache(key: str) -> T | None: 24 | os.makedirs(os.path.dirname(cache_filename(key)), exist_ok=True) 25 | try: 26 | with open(cache_filename(key), "r") as fp: 27 | return cast(T, json.load(fp)) 28 | except FileNotFoundError: 29 | return None 30 | 31 | 32 | def set_in_cache(key: str, value: T) -> None: 33 | os.makedirs(os.path.dirname(cache_filename(key)), exist_ok=True) 34 | with open(cache_filename(key), "w") as fp: 35 | json.dump(value, fp) 36 | 37 | 38 | def delete_from_cache(key: str) -> bool: 39 | os.makedirs(os.path.dirname(cache_filename(key)), exist_ok=True) 40 | try: 41 | os.remove(cache_filename(key)) 42 | return True 43 | except FileNotFoundError: 44 | return False 45 | -------------------------------------------------------------------------------- /autobot/utils/filesystem.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import glob 4 | import os 5 | 6 | 7 | def is_python_file(filename: str) -> bool: 8 | """Return True if a file appears to contain Python source code.""" 9 | return filename.endswith(".py") or filename.endswith(".pyi") 10 | 11 | 12 | def collect_python_files(targets: list[str]) -> list[str]: 13 | """Enumerate all Python files in a target.""" 14 | collected: set[str] = set() 15 | for target in targets: 16 | for file_or_directory in glob.iglob(target): 17 | if os.path.isdir(file_or_directory): 18 | for root, dirnames, filenames in os.walk(file_or_directory): 19 | for filename in filenames: 20 | if is_python_file(filename): 21 | collected.add(os.path.join(root, filename)) 22 | elif os.path.isfile(file_or_directory): 23 | if is_python_file(file_or_directory): 24 | collected.add(file_or_directory) 25 | 26 | return sorted(collected) 27 | -------------------------------------------------------------------------------- /autobot/utils/getch.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | import termios 5 | import tty 6 | 7 | 8 | def getch() -> str: 9 | """Get a single character from standard input.""" 10 | fd = sys.stdin.fileno() 11 | old_settings = termios.tcgetattr(fd) 12 | try: 13 | tty.setcbreak(sys.stdin.fileno()) 14 | ch = sys.stdin.read(1) 15 | finally: 16 | termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 17 | return ch 18 | -------------------------------------------------------------------------------- /autobot/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.0.16" 2 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | files = autobot, tests 3 | exclude = autobot/schematics 4 | 5 | [mypy-colorama.*] 6 | ignore_missing_imports = True 7 | 8 | [mypy-dotenv.*] 9 | ignore_missing_imports = True 10 | 11 | [mypy-openai.*] 12 | ignore_missing_imports = True 13 | 14 | [mypy-rich.*] 15 | ignore_missing_imports = True 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "autobot-ml" 7 | version = "0.0.16" 8 | requires-python = ">=3.9" 9 | description = "An automated code refactoring tool powered by GPT-3." 10 | authors = [{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" }] 11 | license = "MIT" 12 | readme = "README.md" 13 | repository = "https://github.com/charliermarsh/autobot" 14 | classifiers = [ 15 | "Development Status :: 3 - Alpha", 16 | "Environment :: Console", 17 | "Intended Audience :: Developers", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | "Programming Language :: Python :: 3 :: Only", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.8", 23 | "Programming Language :: Python :: 3.9", 24 | "Programming Language :: Python", 25 | "Topic :: Software Development :: Libraries :: Python Modules", 26 | "Topic :: Software Development :: Quality Assurance", 27 | ] 28 | packages = [{ include = "autobot" }] 29 | dependencies = [ 30 | "colorama>=0.4.5", 31 | "openai>=0.23.0,<0.24.0", 32 | "python-dotenv>=0.21.0", 33 | "rich>=12.5.1", 34 | ] 35 | 36 | [project.scripts] 37 | autobot = "autobot.main:main" 38 | 39 | [tool.uv] 40 | dev-dependencies = [ 41 | "black>=22.8.0", 42 | "isort>=5.10.1", 43 | "mypy>=0.981", 44 | "twine>=4.0.1", 45 | "ruff>=0.0.48", 46 | ] 47 | 48 | [tool.ruff] 49 | preview = true 50 | extend-exclude = ["autobot/schematics"] 51 | 52 | [tool.ruff.lint] 53 | extend-select = ["I"] 54 | 55 | [tool.hatch.build.targets.wheel] 56 | packages = ["autobot"] 57 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliermarsh/autobot/e42c66659bf97b90ca9ff305a19cc99952d0d43f/tests/__init__.py -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliermarsh/autobot/e42c66659bf97b90ca9ff305a19cc99952d0d43f/tests/integration/__init__.py -------------------------------------------------------------------------------- /tests/integration/test_prompt.py: -------------------------------------------------------------------------------- 1 | """Test cases to assess prompt completion. 2 | 3 | Note that these tests rely on the OpenAI API. 4 | """ 5 | 6 | from __future__ import annotations 7 | 8 | import unittest 9 | 10 | from dotenv import load_dotenv 11 | 12 | from autobot import api, prompt 13 | from autobot.transforms import TransformType 14 | 15 | 16 | class PromptIntegrationTest(unittest.TestCase): 17 | def setUp(self) -> None: 18 | load_dotenv() 19 | api.init() 20 | 21 | def test_useless_object_inheritance(self) -> None: 22 | before_text = """ 23 | class Foo(Bar, object): 24 | def __init__(self, x: int) -> None: 25 | self.x = x 26 | """ 27 | 28 | after_text = """ 29 | class Foo(Bar): 30 | def __init__(self, x: int) -> None: 31 | self.x = x 32 | """ 33 | 34 | snippet = """ 35 | class CreateTaskResponse(object): 36 | task_id: str 37 | """ 38 | 39 | expected = """ 40 | class CreateTaskResponse: 41 | task_id: str 42 | """ 43 | actual = prompt.resolve_prompt( 44 | prompt.make_prompt( 45 | snippet.strip(), 46 | transform_type=TransformType.CLASS, 47 | before_text=before_text.strip(), 48 | after_text=after_text.strip(), 49 | before_description="with object inheritance", 50 | after_description="without object inheritance", 51 | ) 52 | ) 53 | 54 | self.assertEqual(expected.strip(), actual.strip()) 55 | 56 | def test_sorted_attributes(self) -> None: 57 | before_text = """ 58 | class MyUnsortedConstants: 59 | z = "hehehe" 60 | B = "aaa234" 61 | A = "zzz123" 62 | cab = "foo bar" 63 | Daaa = "banana" 64 | """ 65 | 66 | after_text = """ 67 | class MyUnsortedConstants: 68 | A = "zzz123" 69 | B = "aaa234" 70 | Daaa = "banana" 71 | cab = "foo bar" 72 | z = "hehehe" 73 | """ 74 | 75 | snippet = """ 76 | @dataclass 77 | class Circle: 78 | \"\"\"A circle in an image, with all its pixel indices/radius/center.\"\"\" 79 | 80 | # All the row coordinates of pixels in the circle, suitable for logical 81 | # indexing with numpy. 82 | rr: np.ndarray 83 | 84 | # All the column coordinates of pixels in the circle, suitable for logical 85 | # indexing with numpy. 86 | cc: np.ndarray 87 | 88 | # Radius of the circle 89 | radius: int 90 | 91 | # Center of the circle (row, column) 92 | center: Tuple[int, int] 93 | """ 94 | 95 | expected = """ 96 | @dataclass 97 | class Circle: 98 | \"\"\"A circle in an image, with all its pixel indices/radius/center.\"\"\" 99 | 100 | # All the column coordinates of pixels in the circle, suitable for logical 101 | # indexing with numpy. 102 | cc: np.ndarray 103 | 104 | # Center of the circle (row, column) 105 | center: Tuple[int, int] 106 | 107 | # Radius of the circle 108 | radius: int 109 | 110 | # All the row coordinates of pixels in the circle, suitable for logical 111 | # indexing with numpy. 112 | rr: np.ndarray 113 | """ 114 | actual = prompt.resolve_prompt( 115 | prompt.make_prompt( 116 | snippet.strip(), 117 | transform_type=TransformType.CLASS, 118 | before_text=before_text.strip(), 119 | after_text=after_text.strip(), 120 | before_description="with unsorted class attributes", 121 | after_description="with alphabetically sorted class attributes", 122 | ) 123 | ) 124 | 125 | self.assertEqual(expected.strip(), actual.strip()) 126 | 127 | def test_standard_library_generics(self) -> None: 128 | before_text = """ 129 | def func( 130 | x: Optional[List[int]], 131 | y: Optional[int], 132 | z: Optional[Union[int, List[int]]], 133 | ) -> None: 134 | a: List[int] = [] 135 | b: Set[int] = set() 136 | c: Optional[int] = 0 137 | d: Union[int, str] = 0 138 | """ 139 | 140 | after_text = """ 141 | def func( 142 | x: list[int] | None, 143 | y: int | None, 144 | z: int | list[int] | None, 145 | ) -> None: 146 | a: list[int] = [] 147 | b: set[int] = set() 148 | c: int | None = 0 149 | d: int | str = 0 150 | """ 151 | 152 | snippet = """ 153 | def compute_squares(n: int) -> List[int]: 154 | x: List[int] = [] 155 | for i in range(n): 156 | x.append(i * i) 157 | return x 158 | """ 159 | 160 | expected = """ 161 | def compute_squares(n: int) -> list[int]: 162 | x: list[int] = [] 163 | for i in range(n): 164 | x.append(i * i) 165 | return x 166 | """ 167 | actual = prompt.resolve_prompt( 168 | prompt.make_prompt( 169 | snippet.strip(), 170 | transform_type=TransformType.CLASS, 171 | before_text=before_text.strip(), 172 | after_text=after_text.strip(), 173 | before_description="with typing module generics", 174 | after_description="with standard library generics", 175 | ) 176 | ) 177 | 178 | self.assertEqual(expected.strip(), actual.strip()) 179 | 180 | def test_use_generator(self) -> None: 181 | before_text = """ 182 | def compute_squares(n: int) -> list[int]: 183 | squares: list[int] = [] 184 | for i in range(n): 185 | squares.append(i * i) 186 | return squares 187 | """ 188 | 189 | after_text = """ 190 | def compute_squares(n: int) -> Generator[int, None, None]: 191 | for i in range(n): 192 | yield i * i 193 | """ 194 | 195 | snippet = """ 196 | def run(self, parameter_name: str, value: pd.DataFrame) -> list[SpecViolation]: 197 | violations: list[SpecViolation] = [] 198 | cols = value.columns if isinstance(self.columns, _All) else self.columns 199 | for col in cols: 200 | if col not in value.columns: 201 | violations.append( 202 | SpecViolation( 203 | parameter_name, 204 | f"column {col} missing from dataframe", 205 | pd.DataFrame, 206 | value, 207 | ) 208 | ) 209 | elif value[col].isnull().values.any(): 210 | violations.append( 211 | SpecViolation( 212 | parameter_name, 213 | f"column {col} had null values", 214 | pd.DataFrame, 215 | value, 216 | ) 217 | ) 218 | return violations 219 | """ 220 | 221 | expected = """ 222 | def run(self, parameter_name: str, value: pd.DataFrame) -> Generator[SpecViolation, None, None]: 223 | cols = value.columns if isinstance(self.columns, _All) else self.columns 224 | for col in cols: 225 | if col not in value.columns: 226 | yield SpecViolation( 227 | parameter_name, 228 | f"column {col} missing from dataframe", 229 | pd.DataFrame, 230 | value, 231 | ) 232 | elif value[col].isnull().values.any(): 233 | yield SpecViolation( 234 | parameter_name, 235 | f"column {col} had null values", 236 | pd.DataFrame, 237 | value, 238 | ) 239 | """ # noqa: E501 240 | 241 | actual = prompt.resolve_prompt( 242 | prompt.make_prompt( 243 | snippet.strip(), 244 | transform_type=TransformType.FUNCTION, 245 | before_text=before_text.strip(), 246 | after_text=after_text.strip(), 247 | before_description="as a list builder", 248 | after_description="as a generator", 249 | ) 250 | ) 251 | 252 | self.assertEqual(expected.strip(), actual.strip()) 253 | 254 | 255 | if __name__ == "__main__": 256 | unittest.main() 257 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliermarsh/autobot/e42c66659bf97b90ca9ff305a19cc99952d0d43f/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/test_schematic.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os.path 4 | import unittest 5 | 6 | from autobot.schematic import Schematic 7 | from autobot.transforms import TransformType 8 | 9 | 10 | class SchematicTest(unittest.TestCase): 11 | def test_from_directory__class(self) -> None: 12 | expected = Schematic( 13 | title="assert_equals", 14 | before_text="""class MemoryCacheTest(unittest.TestCase): 15 | def test_roundtrip(self) -> None: 16 | cache = MemoryCache(scope="test_roundtrip") 17 | 18 | expected = uuid.uuid4().hex.encode("utf-8") 19 | cache.set("VALUE", io.BytesIO(expected)) 20 | actual = cache.get("VALUE") or io.BytesIO() 21 | 22 | self.assertEquals(expected, actual.getvalue())""", 23 | after_text="""class MemoryCacheTest(unittest.TestCase): 24 | def test_roundtrip(self) -> None: 25 | cache = MemoryCache(scope="test_roundtrip") 26 | 27 | expected = uuid.uuid4().hex.encode("utf-8") 28 | cache.set("VALUE", io.BytesIO(expected)) 29 | actual = cache.get("VALUE") or io.BytesIO() 30 | 31 | self.assertEqual(expected, actual.getvalue())""", 32 | before_description="with self.assertEquals", 33 | after_description="without self.assertEquals", 34 | transform_type=TransformType.CLASS, 35 | ) 36 | actual = Schematic.from_directory( 37 | os.path.join( 38 | os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 39 | "autobot", 40 | "schematics", 41 | "assert_equals", 42 | ) 43 | ) 44 | 45 | self.assertEqual(expected, actual) 46 | 47 | def test_from_directory__function(self) -> None: 48 | expected = Schematic( 49 | title="numpy_builtin_aliases", 50 | before_text="""def f() -> None: 51 | a = np.array(dtype=np.int) 52 | b = np.dtype(np.unicode) 53 | c = np.dtype(np.object) 54 | d = np.float(123)""", 55 | after_text="""def f() -> None: 56 | a = np.array(dtype=int) 57 | b = np.dtype(str) 58 | c = np.dtype(object) 59 | d = float(123)""", 60 | before_description="with NumPy's builtin aliases (like np.int)", 61 | after_description=( 62 | "without NumPy's deprecated aliases (so int instead of np.int)" 63 | ), 64 | transform_type=TransformType.FUNCTION, 65 | ) 66 | actual = Schematic.from_directory( 67 | os.path.join( 68 | os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 69 | "autobot", 70 | "schematics", 71 | "numpy_builtin_aliases", 72 | ) 73 | ) 74 | 75 | self.assertEqual(expected, actual) 76 | 77 | 78 | if __name__ == "__main__": 79 | unittest.main() 80 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.9" 3 | resolution-markers = [ 4 | "python_full_version < '3.11'", 5 | "python_full_version == '3.11.*'", 6 | "python_full_version >= '3.12'", 7 | ] 8 | 9 | [[package]] 10 | name = "autobot-ml" 11 | version = "0.0.16" 12 | source = { editable = "." } 13 | dependencies = [ 14 | { name = "colorama" }, 15 | { name = "openai" }, 16 | { name = "python-dotenv" }, 17 | { name = "rich" }, 18 | ] 19 | 20 | [package.dev-dependencies] 21 | dev = [ 22 | { name = "black" }, 23 | { name = "isort" }, 24 | { name = "mypy" }, 25 | { name = "ruff" }, 26 | { name = "twine" }, 27 | ] 28 | 29 | [package.metadata] 30 | requires-dist = [ 31 | { name = "colorama", specifier = ">=0.4.5" }, 32 | { name = "openai", specifier = ">=0.23.0,<0.24.0" }, 33 | { name = "python-dotenv", specifier = ">=0.21.0" }, 34 | { name = "rich", specifier = ">=12.5.1" }, 35 | ] 36 | 37 | [package.metadata.requires-dev] 38 | dev = [ 39 | { name = "black", specifier = ">=22.8.0" }, 40 | { name = "isort", specifier = ">=5.10.1" }, 41 | { name = "mypy", specifier = ">=0.981" }, 42 | { name = "ruff", specifier = ">=0.0.48" }, 43 | { name = "twine", specifier = ">=4.0.1" }, 44 | ] 45 | 46 | [[package]] 47 | name = "backports-tarfile" 48 | version = "1.2.0" 49 | source = { registry = "https://pypi.org/simple" } 50 | sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406 } 51 | wheels = [ 52 | { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181 }, 53 | ] 54 | 55 | [[package]] 56 | name = "black" 57 | version = "24.8.0" 58 | source = { registry = "https://pypi.org/simple" } 59 | dependencies = [ 60 | { name = "click" }, 61 | { name = "mypy-extensions" }, 62 | { name = "packaging" }, 63 | { name = "pathspec" }, 64 | { name = "platformdirs" }, 65 | { name = "tomli", marker = "python_full_version < '3.11'" }, 66 | { name = "typing-extensions", marker = "python_full_version < '3.11'" }, 67 | ] 68 | sdist = { url = "https://files.pythonhosted.org/packages/04/b0/46fb0d4e00372f4a86a6f8efa3cb193c9f64863615e39010b1477e010578/black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", size = 644810 } 69 | wheels = [ 70 | { url = "https://files.pythonhosted.org/packages/47/6e/74e29edf1fba3887ed7066930a87f698ffdcd52c5dbc263eabb06061672d/black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6", size = 1632092 }, 71 | { url = "https://files.pythonhosted.org/packages/ab/49/575cb6c3faee690b05c9d11ee2e8dba8fbd6d6c134496e644c1feb1b47da/black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb", size = 1457529 }, 72 | { url = "https://files.pythonhosted.org/packages/7a/b4/d34099e95c437b53d01c4aa37cf93944b233066eb034ccf7897fa4e5f286/black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42", size = 1757443 }, 73 | { url = "https://files.pythonhosted.org/packages/87/a0/6d2e4175ef364b8c4b64f8441ba041ed65c63ea1db2720d61494ac711c15/black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a", size = 1418012 }, 74 | { url = "https://files.pythonhosted.org/packages/08/a6/0a3aa89de9c283556146dc6dbda20cd63a9c94160a6fbdebaf0918e4a3e1/black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1", size = 1615080 }, 75 | { url = "https://files.pythonhosted.org/packages/db/94/b803d810e14588bb297e565821a947c108390a079e21dbdcb9ab6956cd7a/black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", size = 1438143 }, 76 | { url = "https://files.pythonhosted.org/packages/a5/b5/f485e1bbe31f768e2e5210f52ea3f432256201289fd1a3c0afda693776b0/black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", size = 1738774 }, 77 | { url = "https://files.pythonhosted.org/packages/a8/69/a000fc3736f89d1bdc7f4a879f8aaf516fb03613bb51a0154070383d95d9/black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af", size = 1427503 }, 78 | { url = "https://files.pythonhosted.org/packages/a2/a8/05fb14195cfef32b7c8d4585a44b7499c2a4b205e1662c427b941ed87054/black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368", size = 1646132 }, 79 | { url = "https://files.pythonhosted.org/packages/41/77/8d9ce42673e5cb9988f6df73c1c5c1d4e9e788053cccd7f5fb14ef100982/black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed", size = 1448665 }, 80 | { url = "https://files.pythonhosted.org/packages/cc/94/eff1ddad2ce1d3cc26c162b3693043c6b6b575f538f602f26fe846dfdc75/black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018", size = 1762458 }, 81 | { url = "https://files.pythonhosted.org/packages/28/ea/18b8d86a9ca19a6942e4e16759b2fa5fc02bbc0eb33c1b866fcd387640ab/black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2", size = 1436109 }, 82 | { url = "https://files.pythonhosted.org/packages/13/b2/b3f24fdbb46f0e7ef6238e131f13572ee8279b70f237f221dd168a9dba1a/black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c", size = 1631706 }, 83 | { url = "https://files.pythonhosted.org/packages/d9/35/31010981e4a05202a84a3116423970fd1a59d2eda4ac0b3570fbb7029ddc/black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e", size = 1457429 }, 84 | { url = "https://files.pythonhosted.org/packages/27/25/3f706b4f044dd569a20a4835c3b733dedea38d83d2ee0beb8178a6d44945/black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47", size = 1756488 }, 85 | { url = "https://files.pythonhosted.org/packages/63/72/79375cd8277cbf1c5670914e6bd4c1b15dea2c8f8e906dc21c448d0535f0/black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb", size = 1417721 }, 86 | { url = "https://files.pythonhosted.org/packages/27/1e/83fa8a787180e1632c3d831f7e58994d7aaf23a0961320d21e84f922f919/black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed", size = 206504 }, 87 | ] 88 | 89 | [[package]] 90 | name = "certifi" 91 | version = "2024.7.4" 92 | source = { registry = "https://pypi.org/simple" } 93 | sdist = { url = "https://files.pythonhosted.org/packages/c2/02/a95f2b11e207f68bc64d7aae9666fed2e2b3f307748d5123dffb72a1bbea/certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", size = 164065 } 94 | wheels = [ 95 | { url = "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90", size = 162960 }, 96 | ] 97 | 98 | [[package]] 99 | name = "cffi" 100 | version = "1.17.0" 101 | source = { registry = "https://pypi.org/simple" } 102 | dependencies = [ 103 | { name = "pycparser" }, 104 | ] 105 | sdist = { url = "https://files.pythonhosted.org/packages/1e/bf/82c351342972702867359cfeba5693927efe0a8dd568165490144f554b18/cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76", size = 516073 } 106 | wheels = [ 107 | { url = "https://files.pythonhosted.org/packages/00/2a/9071bf1e20bf9f695643b6c3e0f838f340b95ee29de0d1bb7968772409be/cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb", size = 181841 }, 108 | { url = "https://files.pythonhosted.org/packages/4b/42/60116f10466d692b64aef32ac40fd79b11344ab6ef889ff8e3d047f2fcb2/cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a", size = 178242 }, 109 | { url = "https://files.pythonhosted.org/packages/26/8e/a53f844454595c6e9215e56cda123db3427f8592f2c7b5ef1be782f620d6/cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42", size = 425676 }, 110 | { url = "https://files.pythonhosted.org/packages/60/ac/6402563fb40b64c7ccbea87836d9c9498b374629af3449f3d8ff34df187d/cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d", size = 447842 }, 111 | { url = "https://files.pythonhosted.org/packages/b2/e7/e2ffdb8de59f48f17b196813e9c717fbed2364e39b10bdb3836504e89486/cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2", size = 455224 }, 112 | { url = "https://files.pythonhosted.org/packages/59/55/3e8968e92fe35c1c368959a070a1276c10cae29cdad0fd0daa36c69e237e/cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab", size = 436341 }, 113 | { url = "https://files.pythonhosted.org/packages/7f/df/700aaf009dfbfa04acb1ed487586c03c788c6a312f0361ad5f298c5f5a7d/cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b", size = 445861 }, 114 | { url = "https://files.pythonhosted.org/packages/5a/70/637f070aae533ea11ab77708a820f3935c0edb4fbcef9393b788e6f426a5/cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206", size = 460982 }, 115 | { url = "https://files.pythonhosted.org/packages/f7/1a/7d4740fa1ccc4fcc888963fc3165d69ef1a2c8d42c8911c946703ff5d4a5/cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa", size = 438434 }, 116 | { url = "https://files.pythonhosted.org/packages/d0/d9/c48cc38aaf6f53a8b5d2dbf6fe788410fcbab33b15a69c56c01d2b08f6a2/cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f", size = 461219 }, 117 | { url = "https://files.pythonhosted.org/packages/26/ec/b6a7f660a7f27bd2bb53fe99a2ccafa279088395ec8639b25b8950985b2d/cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc", size = 171406 }, 118 | { url = "https://files.pythonhosted.org/packages/08/42/8c00824787e6f5ec55194f5cd30c4ba4b9d9d5bb0d4d0007b1bb948d4ad4/cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2", size = 180809 }, 119 | { url = "https://files.pythonhosted.org/packages/53/cc/9298fb6235522e00e47d78d6aa7f395332ef4e5f6fe124f9a03aa60600f7/cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720", size = 181912 }, 120 | { url = "https://files.pythonhosted.org/packages/e7/79/dc5334fbe60635d0846c56597a8d2af078a543ff22bc48d36551a0de62c2/cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9", size = 178297 }, 121 | { url = "https://files.pythonhosted.org/packages/39/d7/ef1b6b16b51ccbabaced90ff0d821c6c23567fc4b2e4a445aea25d3ceb92/cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb", size = 444909 }, 122 | { url = "https://files.pythonhosted.org/packages/29/b8/6e3c61885537d985c78ef7dd779b68109ba256263d74a2f615c40f44548d/cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424", size = 468854 }, 123 | { url = "https://files.pythonhosted.org/packages/0b/49/adad1228e19b931e523c2731e6984717d5f9e33a2f9971794ab42815b29b/cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d", size = 476890 }, 124 | { url = "https://files.pythonhosted.org/packages/76/54/c00f075c3e7fd14d9011713bcdb5b4f105ad044c5ad948db7b1a0a7e4e78/cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8", size = 459374 }, 125 | { url = "https://files.pythonhosted.org/packages/f3/b9/f163bb3fa4fbc636ee1f2a6a4598c096cdef279823ddfaa5734e556dd206/cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6", size = 466891 }, 126 | { url = "https://files.pythonhosted.org/packages/31/52/72bbc95f6d06ff2e88a6fa13786be4043e542cb24748e1351aba864cb0a7/cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91", size = 477658 }, 127 | { url = "https://files.pythonhosted.org/packages/67/20/d694811457eeae0c7663fa1a7ca201ce495533b646c1180d4ac25684c69c/cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8", size = 453890 }, 128 | { url = "https://files.pythonhosted.org/packages/dc/79/40cbf5739eb4f694833db5a27ce7f63e30a9b25b4a836c4f25fb7272aacc/cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb", size = 478254 }, 129 | { url = "https://files.pythonhosted.org/packages/e9/eb/2c384c385cca5cae67ca10ac4ef685277680b8c552b99aedecf4ea23ff7e/cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9", size = 171285 }, 130 | { url = "https://files.pythonhosted.org/packages/ca/42/74cb1e0f1b79cb64672f3cb46245b506239c1297a20c0d9c3aeb3929cb0c/cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0", size = 180842 }, 131 | { url = "https://files.pythonhosted.org/packages/1a/1f/7862231350cc959a3138889d2c8d33da7042b22e923457dfd4cd487d772a/cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc", size = 182826 }, 132 | { url = "https://files.pythonhosted.org/packages/8b/8c/26119bf8b79e05a1c39812064e1ee7981e1f8a5372205ba5698ea4dd958d/cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59", size = 178494 }, 133 | { url = "https://files.pythonhosted.org/packages/61/94/4882c47d3ad396d91f0eda6ef16d45be3d752a332663b7361933039ed66a/cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb", size = 454459 }, 134 | { url = "https://files.pythonhosted.org/packages/0f/7c/a6beb119ad515058c5ee1829742d96b25b2b9204ff920746f6e13bf574eb/cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195", size = 478502 }, 135 | { url = "https://files.pythonhosted.org/packages/61/8a/2575cd01a90e1eca96a30aec4b1ac101a6fae06c49d490ac2704fa9bc8ba/cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e", size = 485381 }, 136 | { url = "https://files.pythonhosted.org/packages/cd/66/85899f5a9f152db49646e0c77427173e1b77a1046de0191ab3b0b9a5e6e3/cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828", size = 470907 }, 137 | { url = "https://files.pythonhosted.org/packages/00/13/150924609bf377140abe6e934ce0a57f3fc48f1fd956ec1f578ce97a4624/cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150", size = 479074 }, 138 | { url = "https://files.pythonhosted.org/packages/17/fd/7d73d7110155c036303b0a6462c56250e9bc2f4119d7591d27417329b4d1/cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a", size = 484225 }, 139 | { url = "https://files.pythonhosted.org/packages/fc/83/8353e5c9b01bb46332dac3dfb18e6c597a04ceb085c19c814c2f78a8c0d0/cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885", size = 488388 }, 140 | { url = "https://files.pythonhosted.org/packages/73/0c/f9d5ca9a095b1fc88ef77d1f8b85d11151c374144e4606da33874e17b65b/cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492", size = 172096 }, 141 | { url = "https://files.pythonhosted.org/packages/72/21/8c5d285fe20a6e31d29325f1287bb0e55f7d93630a5a44cafdafb5922495/cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2", size = 181478 }, 142 | { url = "https://files.pythonhosted.org/packages/17/8f/581f2f3c3464d5f7cf87c2f7a5ba9acc6976253e02d73804240964243ec2/cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118", size = 182638 }, 143 | { url = "https://files.pythonhosted.org/packages/8d/1c/c9afa66684b7039f48018eb11b229b659dfb32b7a16b88251bac106dd1ff/cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7", size = 178453 }, 144 | { url = "https://files.pythonhosted.org/packages/cc/b6/1a134d479d3a5a1ff2fabbee551d1d3f1dd70f453e081b5f70d604aae4c0/cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377", size = 454441 }, 145 | { url = "https://files.pythonhosted.org/packages/b1/b4/e1569475d63aad8042b0935dbf62ae2a54d1e9142424e2b0e924d2d4a529/cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb", size = 478543 }, 146 | { url = "https://files.pythonhosted.org/packages/d2/40/a9ad03fbd64309dec5bb70bc803a9a6772602de0ee164d7b9a6ca5a89249/cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555", size = 485463 }, 147 | { url = "https://files.pythonhosted.org/packages/a6/1a/f10be60e006dd9242a24bcc2b1cd55c34c578380100f742d8c610f7a5d26/cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204", size = 470854 }, 148 | { url = "https://files.pythonhosted.org/packages/cc/b3/c035ed21aa3d39432bd749fe331ee90e4bc83ea2dbed1f71c4bc26c41084/cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f", size = 479096 }, 149 | { url = "https://files.pythonhosted.org/packages/00/cb/6f7edde01131de9382c89430b8e253b8c8754d66b63a62059663ceafeab2/cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0", size = 484013 }, 150 | { url = "https://files.pythonhosted.org/packages/b9/83/8e4e8c211ea940210d293e951bf06b1bfb90f2eeee590e9778e99b4a8676/cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4", size = 488119 }, 151 | { url = "https://files.pythonhosted.org/packages/5e/52/3f7cfbc4f444cb4f73ff17b28690d12436dde665f67d68f1e1687908ab6c/cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a", size = 172122 }, 152 | { url = "https://files.pythonhosted.org/packages/94/19/cf5baa07ee0f0e55eab7382459fbddaba0fdb0ba45973dd92556ae0d02db/cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7", size = 181504 }, 153 | { url = "https://files.pythonhosted.org/packages/96/22/7866bf5450d6a5b8cf4123abde25b2126fce03ac4efc1244a44367b01c65/cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2", size = 181868 }, 154 | { url = "https://files.pythonhosted.org/packages/0c/03/934cd50132c1637a52ab41c093ff89b93086181f6cdc40d43185083818c1/cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759", size = 178261 }, 155 | { url = "https://files.pythonhosted.org/packages/4a/1e/06c7bc7ed387e42f0ecdef2477a5b291455c2158bb7a565848ef96bba113/cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4", size = 424564 }, 156 | { url = "https://files.pythonhosted.org/packages/b7/9b/43f26a558d192bb0691051153add44404af0adf6e3e35d5ce83340d41a92/cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82", size = 446854 }, 157 | { url = "https://files.pythonhosted.org/packages/b5/5c/7777c4b0fc212caf180b20ec51da3d9fa00910d40f042004d33679f39ec7/cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf", size = 454217 }, 158 | { url = "https://files.pythonhosted.org/packages/8f/90/a40b9821755bd3dfd2dd9a341b660cd57dfa2fc3bb9d8c4499477fa27ae3/cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058", size = 435285 }, 159 | { url = "https://files.pythonhosted.org/packages/e1/d3/36e54b85f670400ff0440ab743fa0de66bdd89b8f54b7d2370708cdcb03f/cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932", size = 444868 }, 160 | { url = "https://files.pythonhosted.org/packages/15/aa/62f87ceb24b03e42061050b1139864347fd73291d2b70b3daefd0c4fdaa8/cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693", size = 460136 }, 161 | { url = "https://files.pythonhosted.org/packages/d4/b6/7abfb922035cc03d2a6c05b6e90f55d60bfea26ef97a2d10357b3f0bdbf3/cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3", size = 437565 }, 162 | { url = "https://files.pythonhosted.org/packages/83/a8/306c52a4625eef30a6d7828c0c7ecaf9a11e1fc83efe506d6fcf980b68c7/cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4", size = 460284 }, 163 | { url = "https://files.pythonhosted.org/packages/a8/05/4daca3a5d2af2af95828b35e65221d4f8afb6155c9d80a1ebda7a11348ab/cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb", size = 171382 }, 164 | { url = "https://files.pythonhosted.org/packages/89/2d/ec3ae32daf8713681ded997aa2e6d68306c11a41627fb351201111ea0d24/cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29", size = 180820 }, 165 | ] 166 | 167 | [[package]] 168 | name = "charset-normalizer" 169 | version = "3.3.2" 170 | source = { registry = "https://pypi.org/simple" } 171 | sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } 172 | wheels = [ 173 | { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 }, 174 | { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 }, 175 | { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 }, 176 | { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 }, 177 | { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 }, 178 | { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 }, 179 | { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 }, 180 | { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 }, 181 | { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 }, 182 | { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 }, 183 | { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 }, 184 | { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 }, 185 | { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 }, 186 | { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 }, 187 | { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 }, 188 | { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, 189 | { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, 190 | { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, 191 | { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, 192 | { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, 193 | { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, 194 | { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 }, 195 | { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 }, 196 | { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 }, 197 | { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 }, 198 | { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, 199 | { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, 200 | { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, 201 | { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 }, 202 | { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 }, 203 | { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, 204 | { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, 205 | { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, 206 | { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, 207 | { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, 208 | { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, 209 | { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, 210 | { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, 211 | { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, 212 | { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, 213 | { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, 214 | { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, 215 | { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, 216 | { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, 217 | { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, 218 | { url = "https://files.pythonhosted.org/packages/f7/9d/bcf4a449a438ed6f19790eee543a86a740c77508fbc5ddab210ab3ba3a9a/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", size = 194198 }, 219 | { url = "https://files.pythonhosted.org/packages/66/fe/c7d3da40a66a6bf2920cce0f436fa1f62ee28aaf92f412f0bf3b84c8ad6c/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", size = 122494 }, 220 | { url = "https://files.pythonhosted.org/packages/2a/9d/a6d15bd1e3e2914af5955c8eb15f4071997e7078419328fee93dfd497eb7/charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", size = 120393 }, 221 | { url = "https://files.pythonhosted.org/packages/3d/85/5b7416b349609d20611a64718bed383b9251b5a601044550f0c8983b8900/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", size = 138331 }, 222 | { url = "https://files.pythonhosted.org/packages/79/66/8946baa705c588521afe10b2d7967300e49380ded089a62d38537264aece/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", size = 148097 }, 223 | { url = "https://files.pythonhosted.org/packages/44/80/b339237b4ce635b4af1c73742459eee5f97201bd92b2371c53e11958392e/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", size = 140711 }, 224 | { url = "https://files.pythonhosted.org/packages/98/69/5d8751b4b670d623aa7a47bef061d69c279e9f922f6705147983aa76c3ce/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", size = 142251 }, 225 | { url = "https://files.pythonhosted.org/packages/1f/8d/33c860a7032da5b93382cbe2873261f81467e7b37f4ed91e25fed62fd49b/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", size = 144636 }, 226 | { url = "https://files.pythonhosted.org/packages/c2/65/52aaf47b3dd616c11a19b1052ce7fa6321250a7a0b975f48d8c366733b9f/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", size = 139514 }, 227 | { url = "https://files.pythonhosted.org/packages/51/fd/0ee5b1c2860bb3c60236d05b6e4ac240cf702b67471138571dad91bcfed8/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", size = 145528 }, 228 | { url = "https://files.pythonhosted.org/packages/e1/9c/60729bf15dc82e3aaf5f71e81686e42e50715a1399770bcde1a9e43d09db/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", size = 149804 }, 229 | { url = "https://files.pythonhosted.org/packages/53/cd/aa4b8a4d82eeceb872f83237b2d27e43e637cac9ffaef19a1321c3bafb67/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", size = 141708 }, 230 | { url = "https://files.pythonhosted.org/packages/54/7f/cad0b328759630814fcf9d804bfabaf47776816ad4ef2e9938b7e1123d04/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561", size = 142708 }, 231 | { url = "https://files.pythonhosted.org/packages/c1/9d/254a2f1bcb0ce9acad838e94ed05ba71a7cb1e27affaa4d9e1ca3958cdb6/charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", size = 92830 }, 232 | { url = "https://files.pythonhosted.org/packages/2f/0e/d7303ccae9735ff8ff01e36705ad6233ad2002962e8668a970fc000c5e1b/charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", size = 100376 }, 233 | { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, 234 | ] 235 | 236 | [[package]] 237 | name = "click" 238 | version = "8.1.7" 239 | source = { registry = "https://pypi.org/simple" } 240 | dependencies = [ 241 | { name = "colorama", marker = "platform_system == 'Windows'" }, 242 | ] 243 | sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } 244 | wheels = [ 245 | { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, 246 | ] 247 | 248 | [[package]] 249 | name = "colorama" 250 | version = "0.4.6" 251 | source = { registry = "https://pypi.org/simple" } 252 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 253 | wheels = [ 254 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 255 | ] 256 | 257 | [[package]] 258 | name = "cryptography" 259 | version = "43.0.0" 260 | source = { registry = "https://pypi.org/simple" } 261 | dependencies = [ 262 | { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, 263 | ] 264 | sdist = { url = "https://files.pythonhosted.org/packages/69/ec/9fb9dcf4f91f0e5e76de597256c43eedefd8423aa59be95c70c4c3db426a/cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e", size = 686873 } 265 | wheels = [ 266 | { url = "https://files.pythonhosted.org/packages/d3/46/dcd2eb6840b9452e7fbc52720f3dc54a85eb41e68414733379e8f98e3275/cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74", size = 6239718 }, 267 | { url = "https://files.pythonhosted.org/packages/e8/23/b0713319edff1d8633775b354f8b34a476e4dd5f4cd4b91e488baec3361a/cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895", size = 3808466 }, 268 | { url = "https://files.pythonhosted.org/packages/77/9d/0b98c73cebfd41e4fb0439fe9ce08022e8d059f51caa7afc8934fc1edcd9/cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22", size = 3998060 }, 269 | { url = "https://files.pythonhosted.org/packages/ae/71/e073795d0d1624847f323481f7d84855f699172a632aa37646464b0e1712/cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47", size = 3792596 }, 270 | { url = "https://files.pythonhosted.org/packages/83/25/439a8ddd8058e7f898b7d27c36f94b66c8c8a2d60e1855d725845f4be0bc/cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf", size = 4008355 }, 271 | { url = "https://files.pythonhosted.org/packages/c7/a2/1607f1295eb2c30fcf2c07d7fd0c3772d21dcdb827de2b2730b02df0af51/cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55", size = 3899133 }, 272 | { url = "https://files.pythonhosted.org/packages/5e/64/f41f42ddc9c583737c9df0093affb92c61de7d5b0d299bf644524afe31c1/cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431", size = 4096946 }, 273 | { url = "https://files.pythonhosted.org/packages/cd/cd/d165adcf3e707d6a049d44ade6ca89973549bed0ab3686fa49efdeefea53/cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc", size = 2616826 }, 274 | { url = "https://files.pythonhosted.org/packages/f9/b7/38924229e84c41b0e88d7a5eed8a29d05a44364f85fbb9ddb3984b746fd2/cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778", size = 3078700 }, 275 | { url = "https://files.pythonhosted.org/packages/66/d7/397515233e6a861f921bd0365b162b38e0cc513fcf4f1bdd9cc7bc5a3384/cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66", size = 6242814 }, 276 | { url = "https://files.pythonhosted.org/packages/58/aa/99b2c00a4f54c60d210d6d1759c720ecf28305aa32d6fb1bb1853f415be6/cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5", size = 3809467 }, 277 | { url = "https://files.pythonhosted.org/packages/76/eb/ab783b47b3b9b55371b4361c7ec695144bde1a3343ff2b7a8c1d8fe617bb/cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e", size = 3998617 }, 278 | { url = "https://files.pythonhosted.org/packages/a3/62/62770f34290ebb1b6542bd3f13b3b102875b90aed4804e296f8d2a5ac6d7/cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5", size = 3794003 }, 279 | { url = "https://files.pythonhosted.org/packages/0f/6c/b42660b3075ff543065b2c1c5a3d9bedaadcff8ebce2ee981be2babc2934/cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f", size = 4008774 }, 280 | { url = "https://files.pythonhosted.org/packages/f7/74/028cea86db9315ba3f991e307adabf9f0aa15067011137c38b2fb2aa16eb/cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0", size = 3900098 }, 281 | { url = "https://files.pythonhosted.org/packages/bd/f6/e4387edb55563e2546028ba4c634522fe727693d3cdd9ec0ecacedc75411/cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b", size = 4096867 }, 282 | { url = "https://files.pythonhosted.org/packages/ce/61/55560405e75432bdd9f6cf72fa516cab623b83a3f6d230791bc8fc4afeee/cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf", size = 2616481 }, 283 | { url = "https://files.pythonhosted.org/packages/e6/3d/696e7a0f04555c58a2813d47aaa78cb5ba863c1f453c74a4f45ae772b054/cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709", size = 3081462 }, 284 | { url = "https://files.pythonhosted.org/packages/c6/3a/9c7d864bbcca2df77a601366a6ae3937cd78d0f21ad98441f3424592aea7/cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70", size = 3156882 }, 285 | { url = "https://files.pythonhosted.org/packages/17/cd/d43859b09d726a905d882b6e464ccf02aa2dca2c3e76c44a0c5b169f0144/cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66", size = 3722095 }, 286 | { url = "https://files.pythonhosted.org/packages/2e/ce/c7b912d95f0ded80ad3b50a0a6b31de813c25d9ffadbe1b26bf22d2c4518/cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f", size = 3928750 }, 287 | { url = "https://files.pythonhosted.org/packages/ca/25/7b53082e4c373127c1fb190f70c5aca7bf7a03ac11f67ba15473bc6d9a0e/cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f", size = 3002487 }, 288 | { url = "https://files.pythonhosted.org/packages/ba/2a/1bf25f4fa1fd1d315e7ab429539850526b4fbaba0d2eba7813bec242ce6a/cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2", size = 3161239 }, 289 | { url = "https://files.pythonhosted.org/packages/0e/aa/fba13d5fcfeaa11dc57ff7b7357b4cc05529a94b29753097e31dde8bcb0d/cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947", size = 3726861 }, 290 | { url = "https://files.pythonhosted.org/packages/62/9e/d8c84c24f5c42c7595e975101969009efc440259b59a0a9732cfd4fc4108/cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069", size = 3930709 }, 291 | { url = "https://files.pythonhosted.org/packages/ae/35/64282489b9a1a49b79a5543124f24a34242d6daed30f0df7995564c01cf5/cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1", size = 3003354 }, 292 | ] 293 | 294 | [[package]] 295 | name = "docutils" 296 | version = "0.20.1" 297 | source = { registry = "https://pypi.org/simple" } 298 | sdist = { url = "https://files.pythonhosted.org/packages/1f/53/a5da4f2c5739cf66290fac1431ee52aff6851c7c8ffd8264f13affd7bcdd/docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b", size = 2058365 } 299 | wheels = [ 300 | { url = "https://files.pythonhosted.org/packages/26/87/f238c0670b94533ac0353a4e2a1a771a0cc73277b88bff23d3ae35a256c1/docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6", size = 572666 }, 301 | ] 302 | 303 | [[package]] 304 | name = "et-xmlfile" 305 | version = "1.1.0" 306 | source = { registry = "https://pypi.org/simple" } 307 | sdist = { url = "https://files.pythonhosted.org/packages/3d/5d/0413a31d184a20c763ad741cc7852a659bf15094c24840c5bdd1754765cd/et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c", size = 3218 } 308 | wheels = [ 309 | { url = "https://files.pythonhosted.org/packages/96/c2/3dd434b0108730014f1b96fd286040dc3bcb70066346f7e01ec2ac95865f/et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada", size = 4688 }, 310 | ] 311 | 312 | [[package]] 313 | name = "idna" 314 | version = "3.7" 315 | source = { registry = "https://pypi.org/simple" } 316 | sdist = { url = "https://files.pythonhosted.org/packages/21/ed/f86a79a07470cb07819390452f178b3bef1d375f2ec021ecfc709fc7cf07/idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", size = 189575 } 317 | wheels = [ 318 | { url = "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0", size = 66836 }, 319 | ] 320 | 321 | [[package]] 322 | name = "importlib-metadata" 323 | version = "8.2.0" 324 | source = { registry = "https://pypi.org/simple" } 325 | dependencies = [ 326 | { name = "zipp" }, 327 | ] 328 | sdist = { url = "https://files.pythonhosted.org/packages/f6/a1/db39a513aa99ab3442010a994eef1cb977a436aded53042e69bee6959f74/importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d", size = 53907 } 329 | wheels = [ 330 | { url = "https://files.pythonhosted.org/packages/82/47/bb25ec04985d0693da478797c3d8c1092b140f3a53ccb984fbbd38affa5b/importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369", size = 25920 }, 331 | ] 332 | 333 | [[package]] 334 | name = "isort" 335 | version = "5.13.2" 336 | source = { registry = "https://pypi.org/simple" } 337 | sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } 338 | wheels = [ 339 | { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, 340 | ] 341 | 342 | [[package]] 343 | name = "jaraco-classes" 344 | version = "3.4.0" 345 | source = { registry = "https://pypi.org/simple" } 346 | dependencies = [ 347 | { name = "more-itertools" }, 348 | ] 349 | sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780 } 350 | wheels = [ 351 | { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777 }, 352 | ] 353 | 354 | [[package]] 355 | name = "jaraco-context" 356 | version = "5.3.0" 357 | source = { registry = "https://pypi.org/simple" } 358 | dependencies = [ 359 | { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, 360 | ] 361 | sdist = { url = "https://files.pythonhosted.org/packages/c9/60/e83781b07f9a66d1d102a0459e5028f3a7816fdd0894cba90bee2bbbda14/jaraco.context-5.3.0.tar.gz", hash = "sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2", size = 13345 } 362 | wheels = [ 363 | { url = "https://files.pythonhosted.org/packages/d2/40/11b7bc1898cf1dcb87ccbe09b39f5088634ac78bb25f3383ff541c2b40aa/jaraco.context-5.3.0-py3-none-any.whl", hash = "sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266", size = 6527 }, 364 | ] 365 | 366 | [[package]] 367 | name = "jaraco-functools" 368 | version = "4.0.2" 369 | source = { registry = "https://pypi.org/simple" } 370 | dependencies = [ 371 | { name = "more-itertools" }, 372 | ] 373 | sdist = { url = "https://files.pythonhosted.org/packages/03/b1/6ca3c2052e584e9908a2c146f00378939b3c51b839304ab8ef4de067f042/jaraco_functools-4.0.2.tar.gz", hash = "sha256:3460c74cd0d32bf82b9576bbb3527c4364d5b27a21f5158a62aed6c4b42e23f5", size = 18319 } 374 | wheels = [ 375 | { url = "https://files.pythonhosted.org/packages/b1/54/7623e24ffc63730c3a619101361b08860c6b7c7cfc1aef6edb66d80ed708/jaraco.functools-4.0.2-py3-none-any.whl", hash = "sha256:c9d16a3ed4ccb5a889ad8e0b7a343401ee5b2a71cee6ed192d3f68bc351e94e3", size = 9883 }, 376 | ] 377 | 378 | [[package]] 379 | name = "jeepney" 380 | version = "0.8.0" 381 | source = { registry = "https://pypi.org/simple" } 382 | sdist = { url = "https://files.pythonhosted.org/packages/d6/f4/154cf374c2daf2020e05c3c6a03c91348d59b23c5366e968feb198306fdf/jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", size = 106005 } 383 | wheels = [ 384 | { url = "https://files.pythonhosted.org/packages/ae/72/2a1e2290f1ab1e06f71f3d0f1646c9e4634e70e1d37491535e19266e8dc9/jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755", size = 48435 }, 385 | ] 386 | 387 | [[package]] 388 | name = "keyring" 389 | version = "25.3.0" 390 | source = { registry = "https://pypi.org/simple" } 391 | dependencies = [ 392 | { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, 393 | { name = "jaraco-classes" }, 394 | { name = "jaraco-context" }, 395 | { name = "jaraco-functools" }, 396 | { name = "jeepney", marker = "sys_platform == 'linux'" }, 397 | { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, 398 | { name = "secretstorage", marker = "sys_platform == 'linux'" }, 399 | ] 400 | sdist = { url = "https://files.pythonhosted.org/packages/32/30/bfdde7294ba6bb2f519950687471dc6a0996d4f77ab30d75c841fa4994ed/keyring-25.3.0.tar.gz", hash = "sha256:8d85a1ea5d6db8515b59e1c5d1d1678b03cf7fc8b8dcfb1651e8c4a524eb42ef", size = 61495 } 401 | wheels = [ 402 | { url = "https://files.pythonhosted.org/packages/63/42/ea8c9726e5ee5ff0731978aaf7cd5fa16674cf549c46279b279d7167c2b4/keyring-25.3.0-py3-none-any.whl", hash = "sha256:8d963da00ccdf06e356acd9bf3b743208878751032d8599c6cc89eb51310ffae", size = 38742 }, 403 | ] 404 | 405 | [[package]] 406 | name = "markdown-it-py" 407 | version = "3.0.0" 408 | source = { registry = "https://pypi.org/simple" } 409 | dependencies = [ 410 | { name = "mdurl" }, 411 | ] 412 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } 413 | wheels = [ 414 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, 415 | ] 416 | 417 | [[package]] 418 | name = "mdurl" 419 | version = "0.1.2" 420 | source = { registry = "https://pypi.org/simple" } 421 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 422 | wheels = [ 423 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, 424 | ] 425 | 426 | [[package]] 427 | name = "more-itertools" 428 | version = "10.4.0" 429 | source = { registry = "https://pypi.org/simple" } 430 | sdist = { url = "https://files.pythonhosted.org/packages/92/0d/ad6a82320cb8eba710fd0dceb0f678d5a1b58d67d03ae5be14874baa39e0/more-itertools-10.4.0.tar.gz", hash = "sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923", size = 120755 } 431 | wheels = [ 432 | { url = "https://files.pythonhosted.org/packages/d8/0b/6a51175e1395774449fca317fb8861379b7a2d59be411b8cce3d19d6ce78/more_itertools-10.4.0-py3-none-any.whl", hash = "sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27", size = 60935 }, 433 | ] 434 | 435 | [[package]] 436 | name = "mypy" 437 | version = "1.11.1" 438 | source = { registry = "https://pypi.org/simple" } 439 | dependencies = [ 440 | { name = "mypy-extensions" }, 441 | { name = "tomli", marker = "python_full_version < '3.11'" }, 442 | { name = "typing-extensions" }, 443 | ] 444 | sdist = { url = "https://files.pythonhosted.org/packages/b6/9c/a4b3bda53823439cf395db8ecdda6229a83f9bf201714a68a15190bb2919/mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08", size = 3078369 } 445 | wheels = [ 446 | { url = "https://files.pythonhosted.org/packages/33/ba/858cc9631c24a349c1c63814edc16448da7d6b8716b2c83a10aa20f5ee89/mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c", size = 10937885 }, 447 | { url = "https://files.pythonhosted.org/packages/2d/88/2ae81f7489da8313d0f2043dd657ba847650b00a0fb8e07f40e716ed8c58/mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411", size = 10111978 }, 448 | { url = "https://files.pythonhosted.org/packages/df/4b/d211d6036366f9ea5ee9fb949e80d133b4b8496cdde78c7119f518c49734/mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03", size = 12498441 }, 449 | { url = "https://files.pythonhosted.org/packages/94/d2/973278d03ad11e006d71d4c858bfe45cf571ae061f3997911925c70a59f0/mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4", size = 13020595 }, 450 | { url = "https://files.pythonhosted.org/packages/0b/c2/7f4285eda528883c5c34cb4b8d88080792967f7f7f24256ad8090d303702/mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58", size = 9568307 }, 451 | { url = "https://files.pythonhosted.org/packages/0b/b1/62d8ce619493a5364dda4f410912aa12c27126926e8fb8393edca0664640/mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5", size = 10858723 }, 452 | { url = "https://files.pythonhosted.org/packages/fe/aa/2ad15a318bc6a17b7f23e1641a624603949904f6131e09681f40340fb875/mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca", size = 10038078 }, 453 | { url = "https://files.pythonhosted.org/packages/4d/7f/77feb389d91603f55b3c4e3e16ccf8752bce007ed73ca921e42c9a5dff12/mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de", size = 12420213 }, 454 | { url = "https://files.pythonhosted.org/packages/bc/5b/907b4681f68e7ee2e2e88eed65c514cf6406b8f2f83b243ea79bd4eddb97/mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809", size = 12898278 }, 455 | { url = "https://files.pythonhosted.org/packages/5b/b3/2a83be637825d7432b8e6a51e45d02de4f463b6c7ec7164a45009a7cf477/mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72", size = 9564438 }, 456 | { url = "https://files.pythonhosted.org/packages/3a/34/69638cee2e87303f19a0c35e80d42757e14d9aba328f272fdcdc0bf3c9b8/mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8", size = 10995789 }, 457 | { url = "https://files.pythonhosted.org/packages/c4/3c/3e0611348fc53a4a7c80485959478b4f6eae706baf3b7c03cafa22639216/mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a", size = 10002696 }, 458 | { url = "https://files.pythonhosted.org/packages/1c/21/a6b46c91b4c9d1918ee59c305f46850cde7cbea748635a352e7c3c8ed204/mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417", size = 12505772 }, 459 | { url = "https://files.pythonhosted.org/packages/c4/55/07904d4c8f408e70308015edcbff067eaa77514475938a9dd81b063de2a8/mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e", size = 12954190 }, 460 | { url = "https://files.pythonhosted.org/packages/1e/b7/3a50f318979c8c541428c2f1ee973cda813bcc89614de982dafdd0df2b3e/mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525", size = 9663138 }, 461 | { url = "https://files.pythonhosted.org/packages/d9/3d/cca659545bd83c67ab784ac2221f8264b3b74e98b2a5aee4f454276ecaf2/mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe", size = 10936143 }, 462 | { url = "https://files.pythonhosted.org/packages/83/ff/ec6f1015813adb183925b6f9696062b7fb60b1d60761cc06cea85dd7bcb0/mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c", size = 10105432 }, 463 | { url = "https://files.pythonhosted.org/packages/08/a3/ffc2ee9b8689920a524a8013bf05482f51d191d73a8a3939f3d47d9a485a/mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69", size = 12493869 }, 464 | { url = "https://files.pythonhosted.org/packages/7b/6b/830fbf3066dcf79831f360c5b1ae3923cfc248aa6a32ea217f179a210126/mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74", size = 13016443 }, 465 | { url = "https://files.pythonhosted.org/packages/0a/ae/8ebca638f6bd9e914501a567a56e3ae64fa4d9c8dfff298618e9409d1156/mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b", size = 9567604 }, 466 | { url = "https://files.pythonhosted.org/packages/f8/d4/4960d0df55f30a7625d9c3c9414dfd42f779caabae137ef73ffaed0c97b9/mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54", size = 2619257 }, 467 | ] 468 | 469 | [[package]] 470 | name = "mypy-extensions" 471 | version = "1.0.0" 472 | source = { registry = "https://pypi.org/simple" } 473 | sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } 474 | wheels = [ 475 | { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, 476 | ] 477 | 478 | [[package]] 479 | name = "nh3" 480 | version = "0.2.18" 481 | source = { registry = "https://pypi.org/simple" } 482 | sdist = { url = "https://files.pythonhosted.org/packages/62/73/10df50b42ddb547a907deeb2f3c9823022580a7a47281e8eae8e003a9639/nh3-0.2.18.tar.gz", hash = "sha256:94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4", size = 15028 } 483 | wheels = [ 484 | { url = "https://files.pythonhosted.org/packages/b3/89/1daff5d9ba5a95a157c092c7c5f39b8dd2b1ddb4559966f808d31cfb67e0/nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86", size = 1374474 }, 485 | { url = "https://files.pythonhosted.org/packages/2c/b6/42fc3c69cabf86b6b81e4c051a9b6e249c5ba9f8155590222c2622961f58/nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811", size = 694573 }, 486 | { url = "https://files.pythonhosted.org/packages/45/b9/833f385403abaf0023c6547389ec7a7acf141ddd9d1f21573723a6eab39a/nh3-0.2.18-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200", size = 844082 }, 487 | { url = "https://files.pythonhosted.org/packages/05/2b/85977d9e11713b5747595ee61f381bc820749daf83f07b90b6c9964cf932/nh3-0.2.18-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164", size = 782460 }, 488 | { url = "https://files.pythonhosted.org/packages/72/f2/5c894d5265ab80a97c68ca36f25c8f6f0308abac649aaf152b74e7e854a8/nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189", size = 879827 }, 489 | { url = "https://files.pythonhosted.org/packages/ab/a7/375afcc710dbe2d64cfbd69e31f82f3e423d43737258af01f6a56d844085/nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad", size = 841080 }, 490 | { url = "https://files.pythonhosted.org/packages/c2/a8/3bb02d0c60a03ad3a112b76c46971e9480efa98a8946677b5a59f60130ca/nh3-0.2.18-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b", size = 924144 }, 491 | { url = "https://files.pythonhosted.org/packages/1b/63/6ab90d0e5225ab9780f6c9fb52254fa36b52bb7c188df9201d05b647e5e1/nh3-0.2.18-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307", size = 769192 }, 492 | { url = "https://files.pythonhosted.org/packages/a4/17/59391c28580e2c32272761629893e761442fc7666da0b1cdb479f3b67b88/nh3-0.2.18-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6955369e4d9f48f41e3f238a9e60f9410645db7e07435e62c6a9ea6135a4907f", size = 791042 }, 493 | { url = "https://files.pythonhosted.org/packages/a3/da/0c4e282bc3cff4a0adf37005fa1fb42257673fbc1bbf7d1ff639ec3d255a/nh3-0.2.18-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe", size = 1010073 }, 494 | { url = "https://files.pythonhosted.org/packages/de/81/c291231463d21da5f8bba82c8167a6d6893cc5419b0639801ee5d3aeb8a9/nh3-0.2.18-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a", size = 1029782 }, 495 | { url = "https://files.pythonhosted.org/packages/63/1d/842fed85cf66c973be0aed8770093d6a04741f65e2c388ddd4c07fd3296e/nh3-0.2.18-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50", size = 942504 }, 496 | { url = "https://files.pythonhosted.org/packages/eb/61/73a007c74c37895fdf66e0edcd881f5eaa17a348ff02f4bb4bc906d61085/nh3-0.2.18-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204", size = 941541 }, 497 | { url = "https://files.pythonhosted.org/packages/78/48/54a788fc9428e481b2f58e0cd8564f6c74ffb6e9ef73d39e8acbeae8c629/nh3-0.2.18-cp37-abi3-win32.whl", hash = "sha256:a7f1b5b2c15866f2db413a3649a8fe4fd7b428ae58be2c0f6bca5eefd53ca2be", size = 573750 }, 498 | { url = "https://files.pythonhosted.org/packages/26/8d/53c5b19c4999bdc6ba95f246f4ef35ca83d7d7423e5e38be43ad66544e5d/nh3-0.2.18-cp37-abi3-win_amd64.whl", hash = "sha256:8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844", size = 579012 }, 499 | ] 500 | 501 | [[package]] 502 | name = "numpy" 503 | version = "2.0.1" 504 | source = { registry = "https://pypi.org/simple" } 505 | sdist = { url = "https://files.pythonhosted.org/packages/1c/8a/0db635b225d2aa2984e405dc14bd2b0c324a0c312ea1bc9d283f2b83b038/numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3", size = 18872007 } 506 | wheels = [ 507 | { url = "https://files.pythonhosted.org/packages/86/c0/025580db782c9be4c7de992e2cc4b2930c12ef8e0f26389c88089e2f8028/numpy-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fbb536eac80e27a2793ffd787895242b7f18ef792563d742c2d673bfcb75134", size = 21234466 }, 508 | { url = "https://files.pythonhosted.org/packages/d6/47/58ad880b4a6ae4ec01e212d68d298cc5425814496e8fce7b35575880984c/numpy-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69ff563d43c69b1baba77af455dd0a839df8d25e8590e79c90fcbe1499ebde42", size = 13369204 }, 509 | { url = "https://files.pythonhosted.org/packages/5f/f7/d9db3db9ff23af6ed1d948441a4805cfcb24119a3edbe1d5d8bcc89b2496/numpy-2.0.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1b902ce0e0a5bb7704556a217c4f63a7974f8f43e090aff03fcf262e0b135e02", size = 5300247 }, 510 | { url = "https://files.pythonhosted.org/packages/b5/ec/d8faaa60f6e39227b3c7286db9db1de48b6f51227d7e64223d22d2374e3e/numpy-2.0.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:f1659887361a7151f89e79b276ed8dff3d75877df906328f14d8bb40bb4f5101", size = 6899834 }, 511 | { url = "https://files.pythonhosted.org/packages/08/7a/71b12fe40147b1a6146ffd07afbe57d3c0ea98560032fae7c6d81c861b09/numpy-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4658c398d65d1b25e1760de3157011a80375da861709abd7cef3bad65d6543f9", size = 13907521 }, 512 | { url = "https://files.pythonhosted.org/packages/ac/9c/703d6775b99ae37c3d4fc32984953572fb20e23e61c0f4154f05e5758a30/numpy-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4127d4303b9ac9f94ca0441138acead39928938660ca58329fe156f84b9f3015", size = 19530562 }, 513 | { url = "https://files.pythonhosted.org/packages/be/53/4c639cc2e0331ec42b54368648f23330f9ff406c74f2f0ccca6702ba6b71/numpy-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e5eeca8067ad04bc8a2a8731183d51d7cbaac66d86085d5f4766ee6bf19c7f87", size = 19929256 }, 514 | { url = "https://files.pythonhosted.org/packages/91/ec/18a1b896e25f0bba8aa3c3b010e7aaf62387583db368b29a571519c2dd45/numpy-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9adbd9bb520c866e1bfd7e10e1880a1f7749f1f6e5017686a5fbb9b72cf69f82", size = 14413740 }, 515 | { url = "https://files.pythonhosted.org/packages/3c/f6/00efe4505061b100a907c55d60765b020be3b08accff37d1444d02ae108a/numpy-2.0.1-cp310-cp310-win32.whl", hash = "sha256:7b9853803278db3bdcc6cd5beca37815b133e9e77ff3d4733c247414e78eb8d1", size = 6469150 }, 516 | { url = "https://files.pythonhosted.org/packages/16/9f/1fcb7fdb6ec988052b42c7f4ed6d92d89c541d640f8f39f01bf141d17426/numpy-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81b0893a39bc5b865b8bf89e9ad7807e16717f19868e9d234bdaf9b1f1393868", size = 16554905 }, 517 | { url = "https://files.pythonhosted.org/packages/29/d6/ff66f4f87518a435538e15cc9e0477a88398512a18783e748914f0daf5ea/numpy-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268", size = 21238269 }, 518 | { url = "https://files.pythonhosted.org/packages/c5/64/853cfc37494471e64ea9f7bf3bc3b4bb39450e6db5beeb05e2a66beef612/numpy-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e", size = 13334324 }, 519 | { url = "https://files.pythonhosted.org/packages/d1/d8/597b4b2e396a77cbec677c9de33bb1789d5c3b66d653cb723d00eb331e99/numpy-2.0.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343", size = 5298376 }, 520 | { url = "https://files.pythonhosted.org/packages/64/58/8664ff3747ac719ae1a5b9c0020533435158180a27f2f88a2b7a253bb623/numpy-2.0.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b", size = 6903817 }, 521 | { url = "https://files.pythonhosted.org/packages/72/44/71ac0090d4ccb512fcac0ef0e5208248423a1ce30381541700470ac09b75/numpy-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe", size = 13916254 }, 522 | { url = "https://files.pythonhosted.org/packages/ef/27/39622993e8688a1f05898a3c3b2836b856f79c06637ebd4b71cb35cc9b18/numpy-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67", size = 19540260 }, 523 | { url = "https://files.pythonhosted.org/packages/6a/26/a32b5a6b3f090860aeefb3619bfea09f717d73908bd65e69e8ab0cac9c07/numpy-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7", size = 19938602 }, 524 | { url = "https://files.pythonhosted.org/packages/34/b6/a88a9953d0be231c67aa0b3714d6138507490753beaa927f0b33f20cdca2/numpy-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55", size = 14425300 }, 525 | { url = "https://files.pythonhosted.org/packages/e4/e2/e763e102bea9c188b43ea144a91c22bec669736889a6e0be0235d64666d7/numpy-2.0.1-cp311-cp311-win32.whl", hash = "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4", size = 6467652 }, 526 | { url = "https://files.pythonhosted.org/packages/3d/67/928e8f0d5c7fd32f32fb5caf92b186a1b3826dbaf5a294e13a976d6c38b6/numpy-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8", size = 16559280 }, 527 | { url = "https://files.pythonhosted.org/packages/64/1c/401489a7e92c30db413362756c313b9353fb47565015986c55582593e2ae/numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac", size = 20965374 }, 528 | { url = "https://files.pythonhosted.org/packages/08/61/460fb524bb2d1a8bd4bbcb33d9b0971f9837fdedcfda8478d4c8f5cfd7ee/numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4", size = 13102536 }, 529 | { url = "https://files.pythonhosted.org/packages/c2/da/3d8debb409bc97045b559f408d2b8cefa6a077a73df14dbf4d8780d976b1/numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1", size = 5037809 }, 530 | { url = "https://files.pythonhosted.org/packages/6d/59/851609f533e7bf5f4af6264a7c5149ab07be9c8db2b0eb064794f8a7bf6d/numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587", size = 6631813 }, 531 | { url = "https://files.pythonhosted.org/packages/5e/e3/944b70438d3b7e2742fece7da8dfba6f7ef7dccdd163d1a613f7027f4d5b/numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8", size = 13623742 }, 532 | { url = "https://files.pythonhosted.org/packages/2c/f3/61eeef119beb37decb58e7cb29940f19a1464b8608f2cab8a8616aba75fd/numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a", size = 19242336 }, 533 | { url = "https://files.pythonhosted.org/packages/77/b5/c74cc1c91754436114c1de5912cdb475145245f6e645a6a1a29b5d08c774/numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7", size = 19637264 }, 534 | { url = "https://files.pythonhosted.org/packages/da/89/c8856d3fd5fce12e0b3f6af371ccb90d604600923b08050c58f0cd26eac9/numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b", size = 14108911 }, 535 | { url = "https://files.pythonhosted.org/packages/15/96/310c6f3f2447f6d146518479b0a6ee6eb92a537954ec3b1acfa2894d1347/numpy-2.0.1-cp312-cp312-win32.whl", hash = "sha256:173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf", size = 6171379 }, 536 | { url = "https://files.pythonhosted.org/packages/b5/59/f6ad30785a6578ad85ed9c2785f271b39c3e5b6412c66e810d2c60934c9f/numpy-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171", size = 16255757 }, 537 | { url = "https://files.pythonhosted.org/packages/e9/8b/405e7dba00aa96ff03f5fef3b6fb3b6f113b13311f70faa72185696fbe0a/numpy-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc085b28d62ff4009364e7ca34b80a9a080cbd97c2c0630bb5f7f770dae9414", size = 21239254 }, 538 | { url = "https://files.pythonhosted.org/packages/a5/e4/280150a359c0c3039d7965f6ade0c9324961b318f5e07cb36dc28915c0a6/numpy-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fae4ebbf95a179c1156fab0b142b74e4ba4204c87bde8d3d8b6f9c34c5825ef", size = 13342134 }, 539 | { url = "https://files.pythonhosted.org/packages/cc/4e/3f7e4fed86ec3c940ba84fe819eb5f2288de85a5f29679a9ed5fbb55dc43/numpy-2.0.1-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:72dc22e9ec8f6eaa206deb1b1355eb2e253899d7347f5e2fae5f0af613741d06", size = 5306698 }, 540 | { url = "https://files.pythonhosted.org/packages/97/de/9bf6c37a8f760847172b13691ac83c404c8485a090b349244e2758c8436d/numpy-2.0.1-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:ec87f5f8aca726117a1c9b7083e7656a9d0d606eec7299cc067bb83d26f16e0c", size = 6904663 }, 541 | { url = "https://files.pythonhosted.org/packages/bd/54/15a0ba87e6335d02475201c9767a6a424ee39ed438ebdb6438f34abc2c25/numpy-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f682ea61a88479d9498bf2091fdcd722b090724b08b31d63e022adc063bad59", size = 13913787 }, 542 | { url = "https://files.pythonhosted.org/packages/b1/e3/24d289c5a3255bf52824bd52295e9a7923cad8ae5ec29539fc971e1122f6/numpy-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8efc84f01c1cd7e34b3fb310183e72fcdf55293ee736d679b6d35b35d80bba26", size = 19533058 }, 543 | { url = "https://files.pythonhosted.org/packages/0a/ea/a0c96ffd46214e5fdb4e12cfa34824468503295d6e2fdeef2e2b1de30cd7/numpy-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3fdabe3e2a52bc4eff8dc7a5044342f8bd9f11ef0934fcd3289a788c0eb10018", size = 19934441 }, 544 | { url = "https://files.pythonhosted.org/packages/c3/c4/763efe6af1925baa6c433ee926fef6996eedc7c2cb2485593de01a24e8f5/numpy-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:24a0e1befbfa14615b49ba9659d3d8818a0f4d8a1c5822af8696706fbda7310c", size = 14417003 }, 545 | { url = "https://files.pythonhosted.org/packages/35/a0/2efec9d64c64db60200909800ca04ade088bc9942641e121cf9933a53d11/numpy-2.0.1-cp39-cp39-win32.whl", hash = "sha256:f9cf5ea551aec449206954b075db819f52adc1638d46a6738253a712d553c7b4", size = 6473865 }, 546 | { url = "https://files.pythonhosted.org/packages/52/87/bb45780eb4b9ed1e4710c2f2b42ed7224071aef6f08152f2520df0ec2ee5/numpy-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9e81fa9017eaa416c056e5d9e71be93d05e2c3c2ab308d23307a8bc4443c368", size = 16560729 }, 547 | { url = "https://files.pythonhosted.org/packages/2a/a3/0a6e85731c9a5ac646cf873d02dca843c6c00fc98ed979bc59ade283ad31/numpy-2.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61728fba1e464f789b11deb78a57805c70b2ed02343560456190d0501ba37b0f", size = 21094924 }, 548 | { url = "https://files.pythonhosted.org/packages/1a/e8/ba6f8f5ee68c7459820236a1e2a76ab4eccad6b6b7f090dabffb7c62ca55/numpy-2.0.1-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:12f5d865d60fb9734e60a60f1d5afa6d962d8d4467c120a1c0cda6eb2964437d", size = 6760873 }, 549 | { url = "https://files.pythonhosted.org/packages/3b/f1/d156a9f3adbdbfdf97e8d0f25e32ee943200ed743c8af7a207d92ebcf61d/numpy-2.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eacf3291e263d5a67d8c1a581a8ebbcfd6447204ef58828caf69a5e3e8c75990", size = 19337879 }, 550 | { url = "https://files.pythonhosted.org/packages/41/30/260f1848653080d7ed1780e85547cdca50d858c6d38e835b2c8d7ac7cad9/numpy-2.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c3a346ae20cfd80b6cfd3e60dc179963ef2ea58da5ec074fd3d9e7a1e7ba97f", size = 16477335 }, 551 | ] 552 | 553 | [[package]] 554 | name = "openai" 555 | version = "0.23.1" 556 | source = { registry = "https://pypi.org/simple" } 557 | dependencies = [ 558 | { name = "numpy" }, 559 | { name = "openpyxl" }, 560 | { name = "pandas" }, 561 | { name = "pandas-stubs" }, 562 | { name = "requests" }, 563 | { name = "tqdm" }, 564 | { name = "typing-extensions" }, 565 | ] 566 | sdist = { url = "https://files.pythonhosted.org/packages/be/92/91da375955142026e05dd0936f425c324924d55ac726d71bcfff5eb368c6/openai-0.23.1.tar.gz", hash = "sha256:16703a2433c1fc71b913057062d00b86dd2ad714dfd7b2b1ab803ac9b31fba5f", size = 43793 } 567 | 568 | [[package]] 569 | name = "openpyxl" 570 | version = "3.1.5" 571 | source = { registry = "https://pypi.org/simple" } 572 | dependencies = [ 573 | { name = "et-xmlfile" }, 574 | ] 575 | sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 } 576 | wheels = [ 577 | { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 }, 578 | ] 579 | 580 | [[package]] 581 | name = "packaging" 582 | version = "24.1" 583 | source = { registry = "https://pypi.org/simple" } 584 | sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } 585 | wheels = [ 586 | { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, 587 | ] 588 | 589 | [[package]] 590 | name = "pandas" 591 | version = "2.2.2" 592 | source = { registry = "https://pypi.org/simple" } 593 | dependencies = [ 594 | { name = "numpy" }, 595 | { name = "python-dateutil" }, 596 | { name = "pytz" }, 597 | { name = "tzdata" }, 598 | ] 599 | sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 } 600 | wheels = [ 601 | { url = "https://files.pythonhosted.org/packages/d1/2d/39600d073ea70b9cafdc51fab91d69c72b49dd92810f24cb5ac6631f387f/pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce", size = 12551798 }, 602 | { url = "https://files.pythonhosted.org/packages/fd/4b/0cd38e68ab690b9df8ef90cba625bf3f93b82d1c719703b8e1b333b2c72d/pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238", size = 11287392 }, 603 | { url = "https://files.pythonhosted.org/packages/01/c6/d3d2612aea9b9f28e79a30b864835dad8f542dcf474eee09afeee5d15d75/pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08", size = 15634823 }, 604 | { url = "https://files.pythonhosted.org/packages/89/1b/12521efcbc6058e2673583bb096c2b5046a9df39bd73eca392c1efed24e5/pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0", size = 13032214 }, 605 | { url = "https://files.pythonhosted.org/packages/e4/d7/303dba73f1c3a9ef067d23e5afbb6175aa25e8121be79be354dcc740921a/pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51", size = 16278302 }, 606 | { url = "https://files.pythonhosted.org/packages/ba/df/8ff7c5ed1cc4da8c6ab674dc8e4860a4310c3880df1283e01bac27a4333d/pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99", size = 13892866 }, 607 | { url = "https://files.pythonhosted.org/packages/69/a6/81d5dc9a612cf0c1810c2ebc4f2afddb900382276522b18d128213faeae3/pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772", size = 11621592 }, 608 | { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 }, 609 | { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 }, 610 | { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 }, 611 | { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 }, 612 | { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 }, 613 | { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 }, 614 | { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 }, 615 | { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 }, 616 | { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 }, 617 | { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 }, 618 | { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 }, 619 | { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 }, 620 | { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 }, 621 | { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 }, 622 | { url = "https://files.pythonhosted.org/packages/1b/cc/eb6ce83667131667c6561e009823e72aa5c76698e75552724bdfc8d1ef0b/pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2", size = 12566406 }, 623 | { url = "https://files.pythonhosted.org/packages/96/08/9ad65176f854fd5eb806a27da6e8b6c12d5ddae7ef3bd80d8b3009099333/pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd", size = 11304008 }, 624 | { url = "https://files.pythonhosted.org/packages/aa/30/5987c82fea318ac7d6bcd083c5b5259d4000e99dd29ae7a9357c65a1b17a/pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863", size = 15662279 }, 625 | { url = "https://files.pythonhosted.org/packages/bb/30/f6f1f1ac36250f50c421b1b6af08c35e5a8b5a84385ef928625336b93e6f/pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921", size = 13069490 }, 626 | { url = "https://files.pythonhosted.org/packages/b5/27/76c1509f505d1f4cb65839352d099c90a13019371e90347166811aa6a075/pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a", size = 16299412 }, 627 | { url = "https://files.pythonhosted.org/packages/5d/11/a5a2f52936fba3afc42de35b19cae941284d973649cb6949bc41cc2e5901/pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57", size = 13920884 }, 628 | { url = "https://files.pythonhosted.org/packages/bf/2c/a0cee9c392a4c9227b835af27f9260582b994f9a2b5ec23993b596e5deb7/pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4", size = 11637580 }, 629 | ] 630 | 631 | [[package]] 632 | name = "pandas-stubs" 633 | version = "2.2.2.240807" 634 | source = { registry = "https://pypi.org/simple" } 635 | dependencies = [ 636 | { name = "numpy" }, 637 | { name = "types-pytz" }, 638 | ] 639 | sdist = { url = "https://files.pythonhosted.org/packages/1f/df/0da95bc75c76f1e012e0bc0b76da31faaf4254e94b9870f25e6311145e98/pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93", size = 103095 } 640 | wheels = [ 641 | { url = "https://files.pythonhosted.org/packages/0a/f9/22c91632ea1b4c6165952f677bf9ad95f9ac36ffd7ef3e6450144e6d8b1a/pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e", size = 157069 }, 642 | ] 643 | 644 | [[package]] 645 | name = "pathspec" 646 | version = "0.12.1" 647 | source = { registry = "https://pypi.org/simple" } 648 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } 649 | wheels = [ 650 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, 651 | ] 652 | 653 | [[package]] 654 | name = "pkginfo" 655 | version = "1.10.0" 656 | source = { registry = "https://pypi.org/simple" } 657 | sdist = { url = "https://files.pythonhosted.org/packages/2f/72/347ec5be4adc85c182ed2823d8d1c7b51e13b9a6b0c1aae59582eca652df/pkginfo-1.10.0.tar.gz", hash = "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297", size = 378457 } 658 | wheels = [ 659 | { url = "https://files.pythonhosted.org/packages/56/09/054aea9b7534a15ad38a363a2bd974c20646ab1582a387a95b8df1bfea1c/pkginfo-1.10.0-py3-none-any.whl", hash = "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097", size = 30392 }, 660 | ] 661 | 662 | [[package]] 663 | name = "platformdirs" 664 | version = "4.2.2" 665 | source = { registry = "https://pypi.org/simple" } 666 | sdist = { url = "https://files.pythonhosted.org/packages/f5/52/0763d1d976d5c262df53ddda8d8d4719eedf9594d046f117c25a27261a19/platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3", size = 20916 } 667 | wheels = [ 668 | { url = "https://files.pythonhosted.org/packages/68/13/2aa1f0e1364feb2c9ef45302f387ac0bd81484e9c9a4c5688a322fbdfd08/platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", size = 18146 }, 669 | ] 670 | 671 | [[package]] 672 | name = "pycparser" 673 | version = "2.22" 674 | source = { registry = "https://pypi.org/simple" } 675 | sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } 676 | wheels = [ 677 | { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, 678 | ] 679 | 680 | [[package]] 681 | name = "pygments" 682 | version = "2.18.0" 683 | source = { registry = "https://pypi.org/simple" } 684 | sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } 685 | wheels = [ 686 | { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, 687 | ] 688 | 689 | [[package]] 690 | name = "python-dateutil" 691 | version = "2.9.0.post0" 692 | source = { registry = "https://pypi.org/simple" } 693 | dependencies = [ 694 | { name = "six" }, 695 | ] 696 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } 697 | wheels = [ 698 | { 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 }, 699 | ] 700 | 701 | [[package]] 702 | name = "python-dotenv" 703 | version = "1.0.1" 704 | source = { registry = "https://pypi.org/simple" } 705 | sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } 706 | wheels = [ 707 | { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, 708 | ] 709 | 710 | [[package]] 711 | name = "pytz" 712 | version = "2024.1" 713 | source = { registry = "https://pypi.org/simple" } 714 | sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214 } 715 | wheels = [ 716 | { url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474 }, 717 | ] 718 | 719 | [[package]] 720 | name = "pywin32-ctypes" 721 | version = "0.2.2" 722 | source = { registry = "https://pypi.org/simple" } 723 | sdist = { url = "https://files.pythonhosted.org/packages/10/3d/0cfbca45201351fe8c09cca743403e6c2407892e256e25d126ad64dc6bb7/pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60", size = 26950 } 724 | wheels = [ 725 | { url = "https://files.pythonhosted.org/packages/a4/bc/78b2c00cc64c31dbb3be42a0e8600bcebc123ad338c3b714754d668c7c2d/pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7", size = 30152 }, 726 | ] 727 | 728 | [[package]] 729 | name = "readme-renderer" 730 | version = "43.0" 731 | source = { registry = "https://pypi.org/simple" } 732 | dependencies = [ 733 | { name = "docutils" }, 734 | { name = "nh3" }, 735 | { name = "pygments" }, 736 | ] 737 | sdist = { url = "https://files.pythonhosted.org/packages/fe/b5/536c775084d239df6345dccf9b043419c7e3308bc31be4c7882196abc62e/readme_renderer-43.0.tar.gz", hash = "sha256:1818dd28140813509eeed8d62687f7cd4f7bad90d4db586001c5dc09d4fde311", size = 31768 } 738 | wheels = [ 739 | { url = "https://files.pythonhosted.org/packages/45/be/3ea20dc38b9db08387cf97997a85a7d51527ea2057d71118feb0aa8afa55/readme_renderer-43.0-py3-none-any.whl", hash = "sha256:19db308d86ecd60e5affa3b2a98f017af384678c63c88e5d4556a380e674f3f9", size = 13301 }, 740 | ] 741 | 742 | [[package]] 743 | name = "requests" 744 | version = "2.32.3" 745 | source = { registry = "https://pypi.org/simple" } 746 | dependencies = [ 747 | { name = "certifi" }, 748 | { name = "charset-normalizer" }, 749 | { name = "idna" }, 750 | { name = "urllib3" }, 751 | ] 752 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } 753 | wheels = [ 754 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, 755 | ] 756 | 757 | [[package]] 758 | name = "requests-toolbelt" 759 | version = "1.0.0" 760 | source = { registry = "https://pypi.org/simple" } 761 | dependencies = [ 762 | { name = "requests" }, 763 | ] 764 | sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 } 765 | wheels = [ 766 | { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, 767 | ] 768 | 769 | [[package]] 770 | name = "rfc3986" 771 | version = "2.0.0" 772 | source = { registry = "https://pypi.org/simple" } 773 | sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026 } 774 | wheels = [ 775 | { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326 }, 776 | ] 777 | 778 | [[package]] 779 | name = "rich" 780 | version = "13.7.1" 781 | source = { registry = "https://pypi.org/simple" } 782 | dependencies = [ 783 | { name = "markdown-it-py" }, 784 | { name = "pygments" }, 785 | ] 786 | sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 } 787 | wheels = [ 788 | { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 }, 789 | ] 790 | 791 | [[package]] 792 | name = "ruff" 793 | version = "0.5.6" 794 | source = { registry = "https://pypi.org/simple" } 795 | sdist = { url = "https://files.pythonhosted.org/packages/f7/69/96766da2cdb5605e6a31ef2734aff0be17901cefb385b885c2ab88896d76/ruff-0.5.6.tar.gz", hash = "sha256:07c9e3c2a8e1fe377dd460371c3462671a728c981c3205a5217291422209f642", size = 2444466 } 796 | wheels = [ 797 | { url = "https://files.pythonhosted.org/packages/85/54/af603686de0e9ba6c1798bd83d335e345e7c9ce4deed95a1071448bb6563/ruff-0.5.6-py3-none-linux_armv6l.whl", hash = "sha256:a0ef5930799a05522985b9cec8290b185952f3fcd86c1772c3bdbd732667fdcd", size = 9543715 }, 798 | { url = "https://files.pythonhosted.org/packages/81/86/b22d250e1be2dcd11c749817d0cfe8be37efce6e6862c92ea037bedf6df9/ruff-0.5.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b652dc14f6ef5d1552821e006f747802cc32d98d5509349e168f6bf0ee9f8f42", size = 8662814 }, 799 | { url = "https://files.pythonhosted.org/packages/d2/e2/f97c58797fac1c7dd8226becfed55056fa7bbfb6d9c2e93900f2a27b540e/ruff-0.5.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:80521b88d26a45e871f31e4b88938fd87db7011bb961d8afd2664982dfc3641a", size = 8230427 }, 800 | { url = "https://files.pythonhosted.org/packages/24/70/9f00cf1e2b50476751808ac6ebbfaa6dab3abab5514058f6bbca28a790bc/ruff-0.5.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9bc8f328a9f1309ae80e4d392836e7dbc77303b38ed4a7112699e63d3b066ab", size = 9976713 }, 801 | { url = "https://files.pythonhosted.org/packages/9b/02/309b171d867d819b7c76c46489597341ce85d70fefa05102a95130440005/ruff-0.5.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d394940f61f7720ad371ddedf14722ee1d6250fd8d020f5ea5a86e7be217daf", size = 9345335 }, 802 | { url = "https://files.pythonhosted.org/packages/37/42/74b84b97815a0b940f1438ff554ead1405d2556ff8e2da58d3c3068a2093/ruff-0.5.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111a99cdb02f69ddb2571e2756e017a1496c2c3a2aeefe7b988ddab38b416d36", size = 10140380 }, 803 | { url = "https://files.pythonhosted.org/packages/47/0c/705b979fe814498dfc3977bcdbf8c47059d2ed37b1c2128f1b77612eea82/ruff-0.5.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e395daba77a79f6dc0d07311f94cc0560375ca20c06f354c7c99af3bf4560c5d", size = 10853490 }, 804 | { url = "https://files.pythonhosted.org/packages/56/f1/400cc9a533fadba676783a2116526d1c26063d3228105bc75fa1c32a092b/ruff-0.5.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c476acb43c3c51e3c614a2e878ee1589655fa02dab19fe2db0423a06d6a5b1b6", size = 10425500 }, 805 | { url = "https://files.pythonhosted.org/packages/f3/cd/f2cf6bc23a4293e3f8c365138f7279eea9179d1c4d3e09546d64daea40da/ruff-0.5.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2ff8003f5252fd68425fd53d27c1f08b201d7ed714bb31a55c9ac1d4c13e2eb", size = 11407463 }, 806 | { url = "https://files.pythonhosted.org/packages/f8/cc/227428f39f8834d281b6d18f84f53e08a840ed63dec259e5e5e502284cec/ruff-0.5.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c94e084ba3eaa80c2172918c2ca2eb2230c3f15925f4ed8b6297260c6ef179ad", size = 10160402 }, 807 | { url = "https://files.pythonhosted.org/packages/f3/61/218890d960ca146d3822e981a34834995081f3b741b82ca2585fd5ebc521/ruff-0.5.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f77c1c3aa0669fb230b06fb24ffa3e879391a3ba3f15e3d633a752da5a3e670", size = 9971344 }, 808 | { url = "https://files.pythonhosted.org/packages/f1/20/bd444745ff7b51e497332ecf3f550bb62871dd24d61960997049fdbed268/ruff-0.5.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f908148c93c02873210a52cad75a6eda856b2cbb72250370ce3afef6fb99b1ed", size = 9417963 }, 809 | { url = "https://files.pythonhosted.org/packages/30/03/69b6d3735f284d93324ca31e36316574501130393f1a8e328a74775f4fb5/ruff-0.5.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:563a7ae61ad284187d3071d9041c08019975693ff655438d8d4be26e492760bd", size = 9769870 }, 810 | { url = "https://files.pythonhosted.org/packages/7f/19/1c86dc40abef457124bb4abf90c68688eb2c7f9944903240c3b93d511360/ruff-0.5.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:94fe60869bfbf0521e04fd62b74cbca21cbc5beb67cbb75ab33fe8c174f54414", size = 10229991 }, 811 | { url = "https://files.pythonhosted.org/packages/65/a7/60a19f3cfccdea5815bc31971f523f5b2ecd08162b05d1a0d5b8c37dff59/ruff-0.5.6-py3-none-win32.whl", hash = "sha256:e6a584c1de6f8591c2570e171cc7ce482bb983d49c70ddf014393cd39e9dfaed", size = 7789030 }, 812 | { url = "https://files.pythonhosted.org/packages/27/c6/b51987072bba4a56537b6fc7036d9bf0694a9de6cceddaa0761475658632/ruff-0.5.6-py3-none-win_amd64.whl", hash = "sha256:d7fe7dccb1a89dc66785d7aa0ac283b2269712d8ed19c63af908fdccca5ccc1a", size = 8690520 }, 813 | { url = "https://files.pythonhosted.org/packages/c0/b1/712801cf487fd78554029b02f980e24531f5994e2bf79b7ac55c1659dd52/ruff-0.5.6-py3-none-win_arm64.whl", hash = "sha256:57c6c0dd997b31b536bff49b9eee5ed3194d60605a4427f735eeb1f9c1b8d264", size = 8101938 }, 814 | ] 815 | 816 | [[package]] 817 | name = "secretstorage" 818 | version = "3.3.3" 819 | source = { registry = "https://pypi.org/simple" } 820 | dependencies = [ 821 | { name = "cryptography" }, 822 | { name = "jeepney" }, 823 | ] 824 | sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739 } 825 | wheels = [ 826 | { url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221 }, 827 | ] 828 | 829 | [[package]] 830 | name = "six" 831 | version = "1.16.0" 832 | source = { registry = "https://pypi.org/simple" } 833 | sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } 834 | wheels = [ 835 | { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, 836 | ] 837 | 838 | [[package]] 839 | name = "tomli" 840 | version = "2.0.1" 841 | source = { registry = "https://pypi.org/simple" } 842 | sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 } 843 | wheels = [ 844 | { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, 845 | ] 846 | 847 | [[package]] 848 | name = "tqdm" 849 | version = "4.66.5" 850 | source = { registry = "https://pypi.org/simple" } 851 | dependencies = [ 852 | { name = "colorama", marker = "platform_system == 'Windows'" }, 853 | ] 854 | sdist = { url = "https://files.pythonhosted.org/packages/58/83/6ba9844a41128c62e810fddddd72473201f3eacde02046066142a2d96cc5/tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad", size = 169504 } 855 | wheels = [ 856 | { url = "https://files.pythonhosted.org/packages/48/5d/acf5905c36149bbaec41ccf7f2b68814647347b72075ac0b1fe3022fdc73/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", size = 78351 }, 857 | ] 858 | 859 | [[package]] 860 | name = "twine" 861 | version = "5.1.1" 862 | source = { registry = "https://pypi.org/simple" } 863 | dependencies = [ 864 | { name = "importlib-metadata" }, 865 | { name = "keyring" }, 866 | { name = "pkginfo" }, 867 | { name = "readme-renderer" }, 868 | { name = "requests" }, 869 | { name = "requests-toolbelt" }, 870 | { name = "rfc3986" }, 871 | { name = "rich" }, 872 | { name = "urllib3" }, 873 | ] 874 | sdist = { url = "https://files.pythonhosted.org/packages/77/68/bd982e5e949ef8334e6f7dcf76ae40922a8750aa2e347291ae1477a4782b/twine-5.1.1.tar.gz", hash = "sha256:9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db", size = 225531 } 875 | wheels = [ 876 | { url = "https://files.pythonhosted.org/packages/5d/ec/00f9d5fd040ae29867355e559a94e9a8429225a0284a3f5f091a3878bfc0/twine-5.1.1-py3-none-any.whl", hash = "sha256:215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997", size = 38650 }, 877 | ] 878 | 879 | [[package]] 880 | name = "types-pytz" 881 | version = "2024.1.0.20240417" 882 | source = { registry = "https://pypi.org/simple" } 883 | sdist = { url = "https://files.pythonhosted.org/packages/9b/b0/079f6f340c0051fbe03ac3a6d9fce323c9797b85380d455e1566eaf2716b/types-pytz-2024.1.0.20240417.tar.gz", hash = "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981", size = 5459 } 884 | wheels = [ 885 | { url = "https://files.pythonhosted.org/packages/e8/8d/f5dc5239d59bb4a7b58e2b6d0dc6f2c2ba797b110f83cdda8479508c63dd/types_pytz-2024.1.0.20240417-py3-none-any.whl", hash = "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659", size = 5246 }, 886 | ] 887 | 888 | [[package]] 889 | name = "typing-extensions" 890 | version = "4.12.2" 891 | source = { registry = "https://pypi.org/simple" } 892 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 893 | wheels = [ 894 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 895 | ] 896 | 897 | [[package]] 898 | name = "tzdata" 899 | version = "2024.1" 900 | source = { registry = "https://pypi.org/simple" } 901 | sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 } 902 | wheels = [ 903 | { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 }, 904 | ] 905 | 906 | [[package]] 907 | name = "urllib3" 908 | version = "2.2.2" 909 | source = { registry = "https://pypi.org/simple" } 910 | sdist = { url = "https://files.pythonhosted.org/packages/43/6d/fa469ae21497ddc8bc93e5877702dca7cb8f911e337aca7452b5724f1bb6/urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168", size = 292266 } 911 | wheels = [ 912 | { url = "https://files.pythonhosted.org/packages/ca/1c/89ffc63a9605b583d5df2be791a27bc1a42b7c32bab68d3c8f2f73a98cd4/urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", size = 121444 }, 913 | ] 914 | 915 | [[package]] 916 | name = "zipp" 917 | version = "3.19.2" 918 | source = { registry = "https://pypi.org/simple" } 919 | sdist = { url = "https://files.pythonhosted.org/packages/d3/20/b48f58857d98dcb78f9e30ed2cfe533025e2e9827bbd36ea0a64cc00cbc1/zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19", size = 22922 } 920 | wheels = [ 921 | { url = "https://files.pythonhosted.org/packages/20/38/f5c473fe9b90c8debdd29ea68d5add0289f1936d6f923b6b9cc0b931194c/zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c", size = 9039 }, 922 | ] 923 | --------------------------------------------------------------------------------