├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── deploy-docs.yml │ ├── draft-release.yml │ ├── pypi-deploy.yml │ └── python-tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── async_tkinter_loop ├── __init__.py ├── async_tkinter_loop.py └── mixins.py ├── docs ├── api.md ├── development.md ├── index.md ├── installation.md └── similar_projects.md ├── examples ├── README.md ├── custom_tkinter.py ├── download_html.py ├── download_image.py ├── ping.py ├── sparks.py ├── start_stop_counter.py └── while_true.py ├── mkdocs.yml ├── overrides └── partials │ └── integrations │ └── analytics │ └── yandex-metrika.html ├── poetry.lock ├── poetry.toml ├── pyproject.toml └── tests ├── __init__.py ├── test_async_tk_loop.py └── test_mixins.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | custom: https://www.donationalerts.com/r/insolor 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | groups: 8 | dev-dependencies: 9 | patterns: 10 | - "pytest*" 11 | - "ruff" 12 | examples: 13 | patterns: 14 | - "Pillow" 15 | - "httpx" 16 | - "customtkinter" 17 | docs: 18 | patterns: 19 | - "mkdocs*" 20 | 21 | - package-ecosystem: "github-actions" 22 | directory: "/" 23 | schedule: 24 | interval: "monthly" 25 | groups: 26 | github-actions: 27 | patterns: 28 | - "*" 29 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | paths: 8 | - '.github/workflows/deploy-docs.yml' 9 | - 'pyproject.toml' 10 | - 'poetry.lock' 11 | - 'mkdocs.yml' 12 | - 'docs/*' 13 | - 'async-tkinter-loop/*' 14 | 15 | permissions: 16 | contents: write 17 | 18 | jobs: 19 | deploy: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Install poetry 25 | run: pipx install poetry 26 | 27 | - name: Set up Python 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: 3.x 31 | 32 | - name: Install dependencies 33 | run: poetry install --only docs 34 | 35 | - name: Deploy docs 36 | run: poetry run mkdocs gh-deploy --force 37 | -------------------------------------------------------------------------------- /.github/workflows/draft-release.yml: -------------------------------------------------------------------------------- 1 | name: Draft Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | name: Draft Release 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Install poetry 15 | run: pipx install poetry 16 | 17 | - name: Set up Python 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: 3.x 21 | cache: "poetry" 22 | cache-dependency-path: poetry.lock 23 | 24 | - name: Install dependencies 25 | run: poetry install --without docs 26 | 27 | - name: Build project 28 | run: | 29 | poetry build 30 | echo "version=$(poetry version --short)" >> "$GITHUB_ENV" 31 | 32 | - name: Release 33 | uses: softprops/action-gh-release@v2 34 | with: 35 | files: dist/* 36 | tag_name: ${{ env.version }} 37 | draft: true 38 | -------------------------------------------------------------------------------- /.github/workflows/pypi-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy on pypi.org 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Install poetry 15 | run: pipx install poetry 16 | 17 | - name: Set up Python 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: 3.x 21 | cache: "poetry" 22 | cache-dependency-path: poetry.lock 23 | 24 | - name: Install dependencies 25 | run: poetry install --without docs 26 | 27 | - name: Build packages 28 | run: poetry build 29 | 30 | - name: Publish on pypi.org 31 | uses: pypa/gh-action-pypi-publish@v1.12.4 32 | -------------------------------------------------------------------------------- /.github/workflows/python-tests.yml: -------------------------------------------------------------------------------- 1 | name: Python tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: 7 | - 'pyproject.toml' 8 | - 'poetry.lock' 9 | - '**.py' 10 | - '.github/workflows/python-tests.yml' 11 | 12 | pull_request: 13 | branches: [ main ] 14 | paths: 15 | - 'pyproject.toml' 16 | - 'poetry.lock' 17 | - '**.py' 18 | - '.github/workflows/python-tests.yml' 19 | 20 | jobs: 21 | test: 22 | runs-on: windows-latest 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | 31 | - name: Install poetry 32 | run: pipx install poetry 33 | 34 | - name: Set up Python ${{ matrix.python-version }} 35 | uses: actions/setup-python@v5 36 | with: 37 | python-version: ${{ matrix.python-version }} 38 | cache: "poetry" 39 | cache-dependency-path: poetry.lock 40 | 41 | - name: Install dependencies 42 | run: poetry install --without docs 43 | 44 | - name: Lint with ruff 45 | run: poetry run ruff check . --statistics 46 | 47 | - name: Test with pytest 48 | run: poetry run pytest --cov=./ 49 | 50 | # - name: Upload coverage data to coveralls.io 51 | # run: | 52 | # pip install coveralls 53 | # coveralls --service=github 54 | # env: 55 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | *.egg-info 3 | .idea 4 | dist 5 | *.pyc 6 | .coverage 7 | htmlcov 8 | *.sh 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mike Manturov 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 | # Asynchronous Tkinter Mainloop 2 | 3 | [![Python tests](https://github.com/insolor/async-tkinter-loop/actions/workflows/python-tests.yml/badge.svg)](https://github.com/insolor/async-tkinter-loop/actions/workflows/python-tests.yml) 4 | [![Documentation](https://github.com/insolor/async-tkinter-loop/actions/workflows/deploy-docs.yml/badge.svg)](https://insolor.github.io/async-tkinter-loop/) 5 | [![Coverage Status](https://coveralls.io/repos/github/insolor/async-tkinter-loop/badge.svg?branch=main)](https://coveralls.io/github/insolor/async-tkinter-loop?branch=main) 6 | [![Maintainability](https://api.codeclimate.com/v1/badges/2566146b14ef72177613/maintainability)](https://codeclimate.com/github/insolor/async-tkinter-loop/maintainability) 7 | [![PyPI](https://img.shields.io/pypi/v/async-tkinter-loop)](https://pypi.org/project/async-tkinter-loop/) 8 | ![Supported Python versions](https://img.shields.io/pypi/pyversions/async-tkinter-loop) 9 | 10 | 11 | Implementation of asynchronous `mainloop` for tkinter, the use of which allows using `async` handler functions. 12 | It is intended to be as simple to use as possible. No fancy unusual syntax or constructions - just use an alternative 13 | function instead of `root.mainloop()` and wrap asynchronous handlers into a helper function. 14 | 15 | > **Note** 16 | > Please, fill free to [report bugs](https://github.com/insolor/async-tkinter-loop/issues), add [pull requests](https://github.com/insolor/async-tkinter-loop/pulls) or [share your thoughts / ask questions, etc.](https://github.com/insolor/async-tkinter-loop/discussions) about the module. 17 | 18 | Based on ideas from: 19 | 20 | * my answer on ru.stackoverflow.com: 21 | * answer of [Terry Jan Reedy](https://stackoverflow.com/users/722804) on stackoverflow.com: 22 | 23 | * answer of [jfs](https://ru.stackoverflow.com/users/23044) on ru.stackoverflow.com: 24 | 25 | 26 | ## Installation 27 | 28 | Install the package with the following command: 29 | 30 | ``` 31 | pip install async-tkinter-loop 32 | ``` 33 | or 34 | ``` 35 | pip install async-tkinter-loop[examples] 36 | ``` 37 | 38 | - `[examples]` part is needed to install optional dependencies (such as `httpx` and `pillow`) to run some of the 39 | examples. If you're not going to run examples, remove the `[examples]` part from the command 40 | - Use `pip3` instead of `pip` on Linux systems to install the package for python3 (not python2) 41 | - Probably you'll want to create a virtual environment for experiments with this library, but this is optional. 42 | - If you want to try examples, download the entire repository as an archive (green "code" button on 43 | [the GitHub page](https://github.com/insolor/async-tkinter-loop) → 44 | "Download ZIP"), unpack, run any example (of course, you need to install optional dependencies) 45 | 46 | ## Some examples 47 | 48 | Basic example: 49 | ```python 50 | import asyncio 51 | import tkinter as tk 52 | 53 | from async_tkinter_loop import async_handler, async_mainloop 54 | 55 | 56 | async def counter(): 57 | i = 0 58 | while True: 59 | i += 1 60 | label.config(text=str(i)) 61 | await asyncio.sleep(1.0) 62 | 63 | 64 | root = tk.Tk() 65 | 66 | label = tk.Label(root) 67 | label.pack() 68 | 69 | tk.Button(root, text="Start", command=async_handler(counter)).pack() 70 | 71 | async_mainloop(root) 72 | ``` 73 | 74 | Also, `async_handler` function can be used as a decorator (but it makes a decorated function syncroneous): 75 | 76 | ```python 77 | import asyncio 78 | import tkinter as tk 79 | 80 | from async_tkinter_loop import async_handler, async_mainloop 81 | 82 | 83 | @async_handler 84 | async def counter(): 85 | i = 0 86 | while True: 87 | i += 1 88 | label.config(text=str(i)) 89 | await asyncio.sleep(1.0) 90 | 91 | 92 | root = tk.Tk() 93 | 94 | label = tk.Label(root) 95 | label.pack() 96 | 97 | tk.Button(root, text="Start", command=counter).pack() 98 | 99 | async_mainloop(root) 100 | ``` 101 | 102 | A more practical example, downloading an image from the Internet with [httpx](https://github.com/encode/httpx) 103 | (you can use [aiohttp](https://github.com/aio-libs/aiohttp) as well) 104 | and displaying it in the Tkinter window: 105 | 106 | ```python 107 | import tkinter as tk 108 | from io import BytesIO 109 | 110 | import httpx 111 | from PIL import Image, ImageTk 112 | 113 | from async_tkinter_loop import async_handler, async_mainloop 114 | 115 | 116 | async def load_image(url): 117 | button.config(state=tk.DISABLED) 118 | label.config(text="Loading...", image="") 119 | 120 | async with httpx.AsyncClient() as client: 121 | response = await client.get(url, follow_redirects=True) 122 | if response.status_code != 200: 123 | label.config(text=f"HTTP error {response.status_code}") 124 | else: 125 | content = response.content 126 | pil_image = Image.open(BytesIO(content)) 127 | image = ImageTk.PhotoImage(pil_image) 128 | label.image = image 129 | label.config(image=image, text="") 130 | button.config(state=tk.NORMAL) 131 | 132 | 133 | url = "https://picsum.photos/800/640" 134 | 135 | root = tk.Tk() 136 | root.geometry("800x640") 137 | 138 | button = tk.Button(root, text="Load an image", command=async_handler(load_image, url)) 139 | button.pack() 140 | 141 | label = tk.Label(root) 142 | label.pack(expand=1, fill=tk.BOTH) 143 | 144 | async_mainloop(root) 145 | ``` 146 | 147 | More examples see in the [`examples`](https://github.com/insolor/async-tkinter-loop/tree/main/examples) directory. 148 | -------------------------------------------------------------------------------- /async_tkinter_loop/__init__.py: -------------------------------------------------------------------------------- 1 | from .async_tkinter_loop import async_handler, async_mainloop, get_event_loop, main_loop 2 | 3 | __all__ = ["async_handler", "async_mainloop", "get_event_loop", "main_loop"] 4 | -------------------------------------------------------------------------------- /async_tkinter_loop/async_tkinter_loop.py: -------------------------------------------------------------------------------- 1 | import _tkinter 2 | import asyncio 3 | import tkinter as tk 4 | from collections.abc import Coroutine 5 | from functools import wraps 6 | from typing import Any, Callable 7 | 8 | from typing_extensions import ParamSpec 9 | 10 | 11 | async def main_loop(root: tk.Tk) -> None: 12 | """ 13 | An asynchronous implementation of tkinter mainloop. The function is not intended to be called directly from your 14 | code. 15 | 16 | Args: 17 | ---- 18 | root: tkinter root window object 19 | 20 | """ 21 | while True: 22 | # Process all pending events 23 | while root.dooneevent(_tkinter.DONT_WAIT) > 0: 24 | pass 25 | 26 | try: 27 | root.winfo_exists() # Will throw TclError if the main window is destroyed 28 | except tk.TclError: 29 | break 30 | 31 | await asyncio.sleep(0.01) 32 | 33 | 34 | def get_event_loop() -> asyncio.AbstractEventLoop: 35 | """ 36 | A helper function which returns an event loop using current event loop policy. 37 | 38 | Returns 39 | ------- 40 | event loop 41 | 42 | """ 43 | return asyncio.get_event_loop_policy().get_event_loop() 44 | 45 | 46 | def async_mainloop(root: tk.Tk) -> None: 47 | """ 48 | A function, which is a substitute to the standard `root.mainloop()`. 49 | 50 | Args: 51 | ---- 52 | root: tkinter root object 53 | 54 | """ 55 | get_event_loop().run_until_complete(main_loop(root)) 56 | 57 | 58 | P = ParamSpec("P") 59 | 60 | 61 | def async_handler(async_function: Callable[P, Coroutine[Any, Any, None]], *args, **kwargs) -> Callable[P, None]: 62 | """ 63 | A helper function which allows to use async functions as command handlers (e.g. button click handlers) or event 64 | handlers. 65 | 66 | Args: 67 | ---- 68 | async_function: async function 69 | args: positional parameters which will be passed to the async function 70 | kwargs: keyword parameters which will be passed to the async function 71 | 72 | Returns: 73 | ------- 74 | A sync function, which runs the original async function in an async event loop. 75 | 76 | Usage examples: 77 | ```python 78 | import tkinter as tk 79 | from async_tkinter_loop import async_handler 80 | 81 | async def some_async_function(): 82 | print("Wait...") 83 | await asyncio.sleep(0.5) 84 | print("Done!") 85 | 86 | button = tk.Button("Press me", command=async_handler(some_async_function)) 87 | 88 | # ---- 89 | 90 | async def some_async_function(event): 91 | print("Wait...") 92 | await asyncio.sleep(0.5) 93 | print("Done!") 94 | 95 | root.bind("<1>", command=async_handler(some_async_function)) 96 | 97 | # ---- 98 | 99 | # Also, it can be used as a decorator 100 | @async_handler 101 | async def some_async_function(): 102 | print("Wait...") 103 | await asyncio.sleep(0.5) 104 | print("Done!") 105 | 106 | button = tk.Button("Press me", command=some_async_function) 107 | ``` 108 | 109 | """ 110 | 111 | @wraps(async_function) 112 | def wrapper(*handler_args) -> None: 113 | get_event_loop().create_task(async_function(*handler_args, *args, **kwargs)) 114 | 115 | return wrapper 116 | -------------------------------------------------------------------------------- /async_tkinter_loop/mixins.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import tkinter as tk 3 | 4 | from async_tkinter_loop import async_mainloop 5 | 6 | 7 | class AsyncTk: 8 | def async_mainloop(self: tk.Tk) -> None: 9 | async_mainloop(self) 10 | 11 | 12 | class AsyncCTk(AsyncTk): 13 | def async_mainloop(self) -> None: 14 | # Based on the code from CustomTkinter by Tom Schimansky 15 | # Source https://github.com/TomSchimansky/CustomTkinter/blob/d719950f80eb2768db96bd4cc627523e99603b1b/customtkinter/windows/ctk_tk.py#L155 16 | if not self._window_exists: 17 | if sys.platform.startswith("win"): 18 | self._windows_set_titlebar_color(self._get_appearance_mode()) 19 | 20 | if not self._withdraw_called_before_window_exists and not self._iconify_called_before_window_exists: 21 | self.deiconify() 22 | 23 | self._window_exists = True 24 | 25 | super().async_mainloop() 26 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # Library API 2 | 3 | :::async_tkinter_loop.async_tkinter_loop 4 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Installation for development 2 | 3 | 1. Install [Poetry](https://python-poetry.org), e.g., with `pip install poetry` (`pip3 install poetry`) command 4 | (other possible ways of installation see [here](https://python-poetry.org/docs/#installation)) 5 | 2. Download and unpack or clone [the repository](https://github.com/insolor/async-tkinter-loop). 6 | 3. Run the command `poetry install` or `poetry install -E examples` (the later command installs optional dependencies 7 | needed to run some examples). This command will create `.venv` directory with a virtual environment and 8 | install dependencies into it. 9 | - Run any example with `poetry run python examples/sparks.py` (insert a file name of an example). 10 | - Or activate the virtual environment with `poetry shell` and run an example with `python examples/sparks.py` 11 | command. You can also open the directory with the project in some IDE (e.g., PyCharm or VS Code) 12 | and select Python interpreter from the virtual environment as a project interpreter, 13 | then run examples directly from the IDE. 14 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | This site contains the project documentation for the 4 | `async-tkinter-loop` project. 5 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | Install the package with the following command: 2 | 3 | ``` 4 | pip install async-tkinter-loop 5 | ``` 6 | or 7 | ``` 8 | pip install async-tkinter-loop[examples] 9 | ``` 10 | 11 | - `[examples]` part is needed to install optional dependencies (such as `httpx` and `pillow`) to run some of the 12 | examples. If you're not going to run examples, remove the `[examples]` part from the command 13 | - Use `pip3` instead of `pip` on Linux systems to install the package for python3 (not python2) 14 | - Probably you'll want to create a virtual environment for experiments with this library, but this is optional. 15 | - If you want to try examples, download the entire repository as an archive (green "code" button on 16 | [the GitHub page](https://github.com/insolor/async-tkinter-loop) → 17 | "Download ZIP"), unpack, run any example (of course, you need to install optional dependencies) 18 | -------------------------------------------------------------------------------- /docs/similar_projects.md: -------------------------------------------------------------------------------- 1 | # Similar projects 2 | 3 | * [Starwort/asynctk](https://github.com/Starwort/asynctk) ([on PyPi](https://pypi.org/project/asynctk/)) - tries to wrap all widgets and make all their methods asyncroneous. 4 | Most recent commit: October 2021. 5 | GPL v3 license. 6 | * [gottadiveintopython/asynctkinter](https://github.com/gottadiveintopython/asynctkinter) ([on PyPi](https://pypi.org/project/asynctkinter/)) - looks like an asynchronous framework itself (like [trio](https://github.com/python-trio/trio)). 7 | Most recent commit: August 2023. 8 | MIT License. 9 | 10 | The latter two projects use old `asyncio` code (from before `async`/`await` syntax addition): 11 | 12 | * [Lucretiel/tkinter-async](https://github.com/Lucretiel/tkinter-async) - most recent commit: May 7, 2015 13 | * [fluentpython/asyncio-tkinter](https://github.com/fluentpython/asyncio-tkinter) - most recent commit: Apr 5, 2015. 14 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | - [while_true.py](while_true.py) - the most basic example how to do repeatable actions using non-blocking infinite `while True` loop in an asynchronous function; 4 | - [start_stop_counter.py](start_stop_counter.py) - an example how to start/pause background asynchronous task using `asyncio.Event()`, which is triggered with a button; 5 | - [download_html.py](download_html.py) - an example how to make asynchronous HTTP requests and get contents of a html page (for example); 6 | - [download_image.py](download_image.py) - similar example, but it downloads an image and displays it in the window; 7 | - [sparks.py](sparks.py) - it shoots sparks when you drag mouse over the window, every sparkle is moved by its own coroutine; 8 | - [ping.py](ping.py) - a demo, how to interact with command line tools asynchronously, and display their output in a Text widget; 9 | - [custom_tkinter.py](custom_tkinter.py) - a simple example how to use async code with [CustomTkinter](https://github.com/TomSchimansky/CustomTkinter) library. 10 | -------------------------------------------------------------------------------- /examples/custom_tkinter.py: -------------------------------------------------------------------------------- 1 | """A simple example how to use async code with CustomTkinter 2 | """ 3 | 4 | import asyncio 5 | 6 | import customtkinter 7 | 8 | from async_tkinter_loop import async_handler 9 | from async_tkinter_loop.mixins import AsyncCTk 10 | 11 | 12 | class App(customtkinter.CTk, AsyncCTk): # <-- add AsyncCTk as a second parent class 13 | def __init__(self): 14 | super().__init__() 15 | self.geometry("600x500") 16 | self.title("Async CTk example") 17 | 18 | # add widgets to app 19 | self.button = customtkinter.CTkButton(self, command=self.button_click) 20 | self.button.grid(row=0, column=0, padx=20, pady=10) 21 | 22 | self.label = customtkinter.CTkLabel(self, text="") 23 | self.label.grid(row=0, column=1, padx=20, pady=10) 24 | 25 | @async_handler # <-- add @async_handler decorator to use the method as a handler 26 | async def button_click(self): 27 | i = 0 28 | while True: 29 | i += 1 30 | self.label.configure(text=str(i)) 31 | await asyncio.sleep(1.0) 32 | 33 | 34 | app = App() 35 | app.async_mainloop() # <-- call .async_mainloop() method instead of .mainloop() 36 | -------------------------------------------------------------------------------- /examples/download_html.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | import tkinter as tk 3 | 4 | import httpx 5 | 6 | from async_tkinter_loop import async_handler, async_mainloop 7 | 8 | 9 | @async_handler 10 | async def load_data(): 11 | text.delete(1.0, tk.END) 12 | text.insert(tk.END, "Loading...") 13 | button.config(state=tk.DISABLED) 14 | 15 | async with httpx.AsyncClient(timeout=30) as client: 16 | response = await client.get("https://python.org", follow_redirects=True) 17 | text.delete(1.0, tk.END) 18 | 19 | text.insert(tk.END, f"Status: {response.status_code}\n") 20 | text.insert(tk.END, f"Content-type: {response.headers['content-type']}\n") 21 | 22 | html = response.text.replace("\r\n", "\n") 23 | text.insert(tk.END, f"Body:\n{html[:1000]}...") 24 | 25 | button.config(state=tk.NORMAL) 26 | 27 | 28 | root = tk.Tk() 29 | 30 | button = tk.Button(root, text="Load text", command=load_data) 31 | button.pack() 32 | 33 | text = tk.Text(root) 34 | text.pack() 35 | 36 | async_mainloop(root) 37 | -------------------------------------------------------------------------------- /examples/download_image.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | import tkinter as tk 3 | from io import BytesIO 4 | 5 | import httpx 6 | from PIL import Image, ImageTk 7 | 8 | from async_tkinter_loop import async_handler, async_mainloop 9 | 10 | 11 | async def load_image(url): 12 | button.config(state=tk.DISABLED) 13 | label.config(text="Loading...", image="") 14 | 15 | async with httpx.AsyncClient(timeout=30) as client: 16 | response = await client.get(url, follow_redirects=True) 17 | if response.status_code != 200: 18 | label.config(text=f"HTTP error {response.status_code}") 19 | else: 20 | content = response.content 21 | pil_image = Image.open(BytesIO(content)) 22 | image = ImageTk.PhotoImage(pil_image) 23 | label.image = image 24 | label.config(image=image, text="") 25 | button.config(state=tk.NORMAL) 26 | 27 | 28 | url = "https://picsum.photos/800/640" 29 | 30 | root = tk.Tk() 31 | root.geometry("800x640") 32 | 33 | button = tk.Button(root, text="Load an image", command=async_handler(load_image, url)) 34 | button.pack() 35 | 36 | label = tk.Label(root) 37 | label.pack(expand=1, fill=tk.BOTH) 38 | 39 | async_mainloop(root) 40 | -------------------------------------------------------------------------------- /examples/ping.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | """ 3 | An example how to interact with processes asynchronously and show their output in a tkinter window 4 | """ 5 | 6 | from __future__ import annotations 7 | 8 | import asyncio 9 | import platform 10 | import tkinter as tk 11 | from tkinter.scrolledtext import ScrolledText 12 | from typing import TYPE_CHECKING 13 | 14 | from async_tkinter_loop import async_handler, async_mainloop 15 | 16 | if TYPE_CHECKING: 17 | from asyncio.subprocess import Process 18 | 19 | root = tk.Tk() 20 | root.geometry("600x400") 21 | 22 | text = ScrolledText(root, width=1, height=1) 23 | text.pack(fill=tk.BOTH, expand=True) 24 | text.tag_config("red_text", foreground="red") 25 | 26 | ping_subprocess: Process | None = None 27 | 28 | ping_command = ["ping", "-t"] if platform.system() == "Windows" else ["ping"] 29 | 30 | console_encoding = "utf-8" 31 | 32 | if platform.system() == "Windows": 33 | from ctypes import windll 34 | 35 | console_code_page = windll.kernel32.GetConsoleOutputCP() 36 | if console_code_page != 65001: 37 | console_encoding = f"cp{console_code_page}" 38 | 39 | 40 | @async_handler 41 | async def ping(): 42 | global ping_subprocess 43 | address = entry.get() 44 | 45 | ping_subprocess = await asyncio.create_subprocess_exec( 46 | *ping_command, 47 | address, 48 | stdout=asyncio.subprocess.PIPE, 49 | stderr=asyncio.subprocess.PIPE, 50 | ) 51 | 52 | while ping_subprocess.returncode is None: 53 | stdout = asyncio.create_task(ping_subprocess.stdout.readline()) 54 | stderr = asyncio.create_task(ping_subprocess.stderr.readline()) 55 | 56 | done, pending = await asyncio.wait({stdout, stderr}, return_when=asyncio.FIRST_COMPLETED) 57 | 58 | if stdout in done: 59 | result_text = stdout.result().decode(console_encoding) 60 | text.insert(tk.END, result_text) 61 | 62 | if stderr in done: 63 | result_text = stderr.result().decode(console_encoding) 64 | text.insert(tk.END, result_text, "red_text") 65 | 66 | for item in pending: 67 | item.cancel() 68 | 69 | text.insert(tk.END, f"Finished with code {ping_subprocess.returncode}\n\n") 70 | ping_subprocess = None 71 | 72 | 73 | def stop(): 74 | if ping_subprocess is not None: 75 | ping_subprocess.kill() 76 | 77 | 78 | bottom_bar = tk.Frame(root) 79 | bottom_bar.pack(side=tk.BOTTOM, fill=tk.X) 80 | entry = tk.Entry(bottom_bar) 81 | entry.pack(fill=tk.X, expand=True, side=tk.LEFT) 82 | tk.Button(bottom_bar, text="Ping", command=ping).pack(side=tk.LEFT) 83 | tk.Button(bottom_bar, text="Stop", command=stop).pack(side=tk.LEFT) 84 | tk.Button(bottom_bar, text="Clear", command=lambda: text.delete(1.0, tk.END)).pack(side=tk.LEFT) 85 | 86 | async_mainloop(root) 87 | -------------------------------------------------------------------------------- /examples/sparks.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | import asyncio 3 | import math 4 | import random 5 | import tkinter as tk 6 | 7 | from async_tkinter_loop import async_handler, async_mainloop 8 | 9 | 10 | async def shoot_spark(x, y): 11 | direction = 2 * math.pi * random.random() 12 | vel = random.randint(5, 20) 13 | dx = math.cos(direction) 14 | dy = math.sin(direction) 15 | spark_len = 10 16 | 17 | spark = canvas.create_line(x, y, x + dx * spark_len, y + dy * spark_len, fill="yellow") 18 | 19 | for _ in range(5): 20 | await asyncio.sleep(0.05) 21 | x += dx * vel 22 | y += dy * vel 23 | canvas.coords(spark, x, y, x + dx * spark_len, y + dy * spark_len) 24 | 25 | canvas.delete(spark) 26 | 27 | 28 | @async_handler 29 | async def on_mouse_drag(event): 30 | x = event.x 31 | y = event.y 32 | await asyncio.wait([asyncio.create_task(shoot_spark(x, y)) for _ in range(5)]) 33 | 34 | 35 | root = tk.Tk() 36 | 37 | canvas = tk.Canvas(root, bg="black", highlightthickness=0) 38 | canvas.pack(expand=1, fill=tk.BOTH) 39 | 40 | canvas.bind("", on_mouse_drag) 41 | 42 | async_mainloop(root) 43 | -------------------------------------------------------------------------------- /examples/start_stop_counter.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | import asyncio 3 | import tkinter as tk 4 | 5 | from async_tkinter_loop import async_mainloop 6 | 7 | 8 | async def counter(): 9 | i = 0 10 | while True: 11 | await event.wait() 12 | i += 1 13 | label.config(text=str(i)) 14 | await asyncio.sleep(1.0) 15 | 16 | 17 | root = tk.Tk() 18 | 19 | label = tk.Label(root) 20 | label.pack() 21 | 22 | 23 | def start_stop(): 24 | if event.is_set(): 25 | event.clear() 26 | else: 27 | event.set() 28 | 29 | 30 | tk.Button(root, text="Start/stop", command=start_stop).pack() 31 | 32 | event = asyncio.Event() 33 | 34 | # Start background task 35 | loop = asyncio.new_event_loop() 36 | asyncio.set_event_loop(loop) 37 | task = loop.create_task(counter()) 38 | 39 | async_mainloop(root) 40 | -------------------------------------------------------------------------------- /examples/while_true.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | """ 3 | This is a simple example of how to do repeatable actions without blocking the GUI. 4 | Classic tkinter approach is based on the "after" method, but with async_tkinter_loop it is possible 5 | to create a non-blocking "infinite" loop. 6 | """ 7 | 8 | import asyncio 9 | import tkinter as tk 10 | 11 | from async_tkinter_loop import async_handler, async_mainloop 12 | 13 | 14 | @async_handler 15 | async def counter(): 16 | i = 0 17 | while True: 18 | i += 1 19 | label.config(text=str(i)) 20 | await asyncio.sleep(1.0) 21 | 22 | 23 | root = tk.Tk() 24 | 25 | label = tk.Label(root) 26 | label.pack() 27 | 28 | tk.Button(root, text="Start", command=counter).pack() 29 | 30 | async_mainloop(root) 31 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Async-tkinter-loop documentation 2 | 3 | theme: 4 | name: "material" 5 | custom_dir: overrides 6 | 7 | plugins: 8 | - mkdocstrings 9 | 10 | markdown_extensions: 11 | - pymdownx.highlight: 12 | anchor_linenums: true 13 | line_spans: __span 14 | pygments_lang_class: true 15 | - pymdownx.inlinehilite 16 | - pymdownx.snippets 17 | - pymdownx.superfences 18 | 19 | nav: 20 | - Home: index.md 21 | - Installation: installation.md 22 | - Installation for development: development.md 23 | - Library API: api.md 24 | - Similar projects: similar_projects.md 25 | 26 | extra: 27 | analytics: 28 | provider: yandex-metrika 29 | property: 94803377 30 | -------------------------------------------------------------------------------- /overrides/partials/integrations/analytics/yandex-metrika.html: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "anyio" 5 | version = "4.6.2.post1" 6 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 7 | optional = true 8 | python-versions = ">=3.9" 9 | groups = ["main"] 10 | markers = "extra == \"examples\"" 11 | files = [ 12 | {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, 13 | {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, 14 | ] 15 | 16 | [package.dependencies] 17 | exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} 18 | idna = ">=2.8" 19 | sniffio = ">=1.1" 20 | typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} 21 | 22 | [package.extras] 23 | doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] 24 | test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21.0b1) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""] 25 | trio = ["trio (>=0.26.1)"] 26 | 27 | [[package]] 28 | name = "babel" 29 | version = "2.16.0" 30 | description = "Internationalization utilities" 31 | optional = false 32 | python-versions = ">=3.8" 33 | groups = ["docs"] 34 | files = [ 35 | {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, 36 | {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, 37 | ] 38 | 39 | [package.extras] 40 | dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] 41 | 42 | [[package]] 43 | name = "backrefs" 44 | version = "5.8" 45 | description = "A wrapper around re and regex that adds additional back references." 46 | optional = false 47 | python-versions = ">=3.9" 48 | groups = ["docs"] 49 | files = [ 50 | {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"}, 51 | {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"}, 52 | {file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"}, 53 | {file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"}, 54 | {file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"}, 55 | {file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"}, 56 | ] 57 | 58 | [package.extras] 59 | extras = ["regex"] 60 | 61 | [[package]] 62 | name = "certifi" 63 | version = "2024.8.30" 64 | description = "Python package for providing Mozilla's CA Bundle." 65 | optional = false 66 | python-versions = ">=3.6" 67 | groups = ["main", "docs"] 68 | files = [ 69 | {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, 70 | {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, 71 | ] 72 | markers = {main = "extra == \"examples\""} 73 | 74 | [[package]] 75 | name = "charset-normalizer" 76 | version = "3.4.0" 77 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 78 | optional = false 79 | python-versions = ">=3.7.0" 80 | groups = ["docs"] 81 | files = [ 82 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, 83 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, 84 | {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, 85 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, 86 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, 87 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, 88 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, 89 | {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, 90 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, 91 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, 92 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, 93 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, 94 | {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, 95 | {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, 96 | {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, 97 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, 98 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, 99 | {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, 100 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, 101 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, 102 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, 103 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, 104 | {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, 105 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, 106 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, 107 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, 108 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, 109 | {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, 110 | {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, 111 | {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, 112 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, 113 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, 114 | {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, 115 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, 116 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, 117 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, 118 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, 119 | {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, 120 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, 121 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, 122 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, 123 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, 124 | {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, 125 | {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, 126 | {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, 127 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, 128 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, 129 | {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, 130 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, 131 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, 132 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, 133 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, 134 | {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, 135 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, 136 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, 137 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, 138 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, 139 | {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, 140 | {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, 141 | {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, 142 | {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, 143 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, 144 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, 145 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, 146 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, 147 | {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, 148 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, 149 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, 150 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, 151 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, 152 | {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, 153 | {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, 154 | {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, 155 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, 156 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, 157 | {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, 158 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, 159 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, 160 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, 161 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, 162 | {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, 163 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, 164 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, 165 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, 166 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, 167 | {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, 168 | {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, 169 | {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, 170 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, 171 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, 172 | {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, 173 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, 174 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, 175 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, 176 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, 177 | {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, 178 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, 179 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, 180 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, 181 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, 182 | {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, 183 | {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, 184 | {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, 185 | {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, 186 | {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, 187 | ] 188 | 189 | [[package]] 190 | name = "click" 191 | version = "8.1.7" 192 | description = "Composable command line interface toolkit" 193 | optional = false 194 | python-versions = ">=3.7" 195 | groups = ["docs"] 196 | files = [ 197 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 198 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 199 | ] 200 | 201 | [package.dependencies] 202 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 203 | 204 | [[package]] 205 | name = "colorama" 206 | version = "0.4.6" 207 | description = "Cross-platform colored terminal text." 208 | optional = false 209 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 210 | groups = ["dev", "docs"] 211 | files = [ 212 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 213 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 214 | ] 215 | markers = {dev = "sys_platform == \"win32\""} 216 | 217 | [[package]] 218 | name = "coverage" 219 | version = "7.6.8" 220 | description = "Code coverage measurement for Python" 221 | optional = false 222 | python-versions = ">=3.9" 223 | groups = ["dev"] 224 | files = [ 225 | {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, 226 | {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, 227 | {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, 228 | {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, 229 | {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, 230 | {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, 231 | {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, 232 | {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, 233 | {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, 234 | {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, 235 | {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, 236 | {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, 237 | {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, 238 | {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, 239 | {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, 240 | {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, 241 | {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, 242 | {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, 243 | {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, 244 | {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, 245 | {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, 246 | {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, 247 | {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, 248 | {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, 249 | {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, 250 | {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, 251 | {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, 252 | {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, 253 | {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, 254 | {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, 255 | {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, 256 | {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, 257 | {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, 258 | {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, 259 | {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, 260 | {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, 261 | {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, 262 | {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, 263 | {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, 264 | {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, 265 | {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, 266 | {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, 267 | {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, 268 | {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, 269 | {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, 270 | {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, 271 | {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, 272 | {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, 273 | {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, 274 | {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, 275 | {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, 276 | {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, 277 | {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, 278 | {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, 279 | {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, 280 | {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, 281 | {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, 282 | {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, 283 | {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, 284 | {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, 285 | {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, 286 | {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, 287 | ] 288 | 289 | [package.dependencies] 290 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 291 | 292 | [package.extras] 293 | toml = ["tomli ; python_full_version <= \"3.11.0a6\""] 294 | 295 | [[package]] 296 | name = "customtkinter" 297 | version = "5.2.2" 298 | description = "Create modern looking GUIs with Python" 299 | optional = false 300 | python-versions = ">=3.7" 301 | groups = ["main", "dev"] 302 | files = [ 303 | {file = "customtkinter-5.2.2-py3-none-any.whl", hash = "sha256:14ad3e7cd3cb3b9eb642b9d4e8711ae80d3f79fb82545ad11258eeffb2e6b37c"}, 304 | {file = "customtkinter-5.2.2.tar.gz", hash = "sha256:fd8db3bafa961c982ee6030dba80b4c2e25858630756b513986db19113d8d207"}, 305 | ] 306 | 307 | [package.dependencies] 308 | darkdetect = "*" 309 | packaging = "*" 310 | 311 | [[package]] 312 | name = "darkdetect" 313 | version = "0.8.0" 314 | description = "Detect OS Dark Mode from Python" 315 | optional = false 316 | python-versions = ">=3.6" 317 | groups = ["main", "dev"] 318 | files = [ 319 | {file = "darkdetect-0.8.0-py3-none-any.whl", hash = "sha256:a7509ccf517eaad92b31c214f593dbcf138ea8a43b2935406bbd565e15527a85"}, 320 | {file = "darkdetect-0.8.0.tar.gz", hash = "sha256:b5428e1170263eb5dea44c25dc3895edd75e6f52300986353cd63533fe7df8b1"}, 321 | ] 322 | 323 | [package.extras] 324 | macos-listener = ["pyobjc-framework-Cocoa ; platform_system == \"Darwin\""] 325 | 326 | [[package]] 327 | name = "exceptiongroup" 328 | version = "1.2.2" 329 | description = "Backport of PEP 654 (exception groups)" 330 | optional = false 331 | python-versions = ">=3.7" 332 | groups = ["main", "dev"] 333 | files = [ 334 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 335 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 336 | ] 337 | markers = {main = "extra == \"examples\" and python_version < \"3.11\"", dev = "python_version < \"3.11\""} 338 | 339 | [package.extras] 340 | test = ["pytest (>=6)"] 341 | 342 | [[package]] 343 | name = "ghp-import" 344 | version = "2.1.0" 345 | description = "Copy your docs directly to the gh-pages branch." 346 | optional = false 347 | python-versions = "*" 348 | groups = ["docs"] 349 | files = [ 350 | {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, 351 | {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, 352 | ] 353 | 354 | [package.dependencies] 355 | python-dateutil = ">=2.8.1" 356 | 357 | [package.extras] 358 | dev = ["flake8", "markdown", "twine", "wheel"] 359 | 360 | [[package]] 361 | name = "griffe" 362 | version = "1.7.1" 363 | description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." 364 | optional = false 365 | python-versions = ">=3.9" 366 | groups = ["docs"] 367 | files = [ 368 | {file = "griffe-1.7.1-py3-none-any.whl", hash = "sha256:37a7f15233937d723ddc969fa4117fdd03988885c16938dc43bccdfe8fa4d02d"}, 369 | {file = "griffe-1.7.1.tar.gz", hash = "sha256:464730d0e95d0afd038e699a5f7276d7438d0712db0c489a17e761f70e011507"}, 370 | ] 371 | 372 | [package.dependencies] 373 | colorama = ">=0.4" 374 | 375 | [[package]] 376 | name = "h11" 377 | version = "0.16.0" 378 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 379 | optional = true 380 | python-versions = ">=3.8" 381 | groups = ["main"] 382 | markers = "extra == \"examples\"" 383 | files = [ 384 | {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, 385 | {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, 386 | ] 387 | 388 | [[package]] 389 | name = "httpcore" 390 | version = "1.0.9" 391 | description = "A minimal low-level HTTP client." 392 | optional = true 393 | python-versions = ">=3.8" 394 | groups = ["main"] 395 | markers = "extra == \"examples\"" 396 | files = [ 397 | {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, 398 | {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, 399 | ] 400 | 401 | [package.dependencies] 402 | certifi = "*" 403 | h11 = ">=0.16" 404 | 405 | [package.extras] 406 | asyncio = ["anyio (>=4.0,<5.0)"] 407 | http2 = ["h2 (>=3,<5)"] 408 | socks = ["socksio (==1.*)"] 409 | trio = ["trio (>=0.22.0,<1.0)"] 410 | 411 | [[package]] 412 | name = "httpx" 413 | version = "0.28.1" 414 | description = "The next generation HTTP client." 415 | optional = true 416 | python-versions = ">=3.8" 417 | groups = ["main"] 418 | markers = "extra == \"examples\"" 419 | files = [ 420 | {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, 421 | {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, 422 | ] 423 | 424 | [package.dependencies] 425 | anyio = "*" 426 | certifi = "*" 427 | httpcore = "==1.*" 428 | idna = "*" 429 | 430 | [package.extras] 431 | brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] 432 | cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] 433 | http2 = ["h2 (>=3,<5)"] 434 | socks = ["socksio (==1.*)"] 435 | zstd = ["zstandard (>=0.18.0)"] 436 | 437 | [[package]] 438 | name = "idna" 439 | version = "3.10" 440 | description = "Internationalized Domain Names in Applications (IDNA)" 441 | optional = false 442 | python-versions = ">=3.6" 443 | groups = ["main", "docs"] 444 | files = [ 445 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 446 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 447 | ] 448 | markers = {main = "extra == \"examples\""} 449 | 450 | [package.extras] 451 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 452 | 453 | [[package]] 454 | name = "importlib-metadata" 455 | version = "8.5.0" 456 | description = "Read metadata from Python packages" 457 | optional = false 458 | python-versions = ">=3.8" 459 | groups = ["docs"] 460 | markers = "python_version < \"3.10\"" 461 | files = [ 462 | {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, 463 | {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, 464 | ] 465 | 466 | [package.dependencies] 467 | zipp = ">=3.20" 468 | 469 | [package.extras] 470 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 471 | cover = ["pytest-cov"] 472 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 473 | enabler = ["pytest-enabler (>=2.2)"] 474 | perf = ["ipython"] 475 | test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] 476 | type = ["pytest-mypy"] 477 | 478 | [[package]] 479 | name = "iniconfig" 480 | version = "2.0.0" 481 | description = "brain-dead simple config-ini parsing" 482 | optional = false 483 | python-versions = ">=3.7" 484 | groups = ["dev"] 485 | files = [ 486 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 487 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 488 | ] 489 | 490 | [[package]] 491 | name = "jinja2" 492 | version = "3.1.6" 493 | description = "A very fast and expressive template engine." 494 | optional = false 495 | python-versions = ">=3.7" 496 | groups = ["docs"] 497 | files = [ 498 | {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, 499 | {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, 500 | ] 501 | 502 | [package.dependencies] 503 | MarkupSafe = ">=2.0" 504 | 505 | [package.extras] 506 | i18n = ["Babel (>=2.7)"] 507 | 508 | [[package]] 509 | name = "markdown" 510 | version = "3.7" 511 | description = "Python implementation of John Gruber's Markdown." 512 | optional = false 513 | python-versions = ">=3.8" 514 | groups = ["docs"] 515 | files = [ 516 | {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, 517 | {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, 518 | ] 519 | 520 | [package.dependencies] 521 | importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} 522 | 523 | [package.extras] 524 | docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] 525 | testing = ["coverage", "pyyaml"] 526 | 527 | [[package]] 528 | name = "markupsafe" 529 | version = "3.0.2" 530 | description = "Safely add untrusted strings to HTML/XML markup." 531 | optional = false 532 | python-versions = ">=3.9" 533 | groups = ["docs"] 534 | files = [ 535 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, 536 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, 537 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, 538 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, 539 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, 540 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, 541 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, 542 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, 543 | {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, 544 | {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, 545 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, 546 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, 547 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, 548 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, 549 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, 550 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, 551 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, 552 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, 553 | {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, 554 | {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, 555 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, 556 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, 557 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, 558 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, 559 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, 560 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, 561 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, 562 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, 563 | {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, 564 | {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, 565 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, 566 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, 567 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, 568 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, 569 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, 570 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, 571 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, 572 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, 573 | {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, 574 | {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, 575 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, 576 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, 577 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, 578 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, 579 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, 580 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, 581 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, 582 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, 583 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, 584 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, 585 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, 586 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, 587 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, 588 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, 589 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, 590 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, 591 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, 592 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, 593 | {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, 594 | {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, 595 | {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, 596 | ] 597 | 598 | [[package]] 599 | name = "mergedeep" 600 | version = "1.3.4" 601 | description = "A deep merge function for 🐍." 602 | optional = false 603 | python-versions = ">=3.6" 604 | groups = ["docs"] 605 | files = [ 606 | {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, 607 | {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, 608 | ] 609 | 610 | [[package]] 611 | name = "mkdocs" 612 | version = "1.6.1" 613 | description = "Project documentation with Markdown." 614 | optional = false 615 | python-versions = ">=3.8" 616 | groups = ["docs"] 617 | files = [ 618 | {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, 619 | {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, 620 | ] 621 | 622 | [package.dependencies] 623 | click = ">=7.0" 624 | colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} 625 | ghp-import = ">=1.0" 626 | importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} 627 | jinja2 = ">=2.11.1" 628 | markdown = ">=3.3.6" 629 | markupsafe = ">=2.0.1" 630 | mergedeep = ">=1.3.4" 631 | mkdocs-get-deps = ">=0.2.0" 632 | packaging = ">=20.5" 633 | pathspec = ">=0.11.1" 634 | pyyaml = ">=5.1" 635 | pyyaml-env-tag = ">=0.1" 636 | watchdog = ">=2.0" 637 | 638 | [package.extras] 639 | i18n = ["babel (>=2.9.0)"] 640 | min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] 641 | 642 | [[package]] 643 | name = "mkdocs-autorefs" 644 | version = "1.4.0" 645 | description = "Automatically link across pages in MkDocs." 646 | optional = false 647 | python-versions = ">=3.9" 648 | groups = ["docs"] 649 | files = [ 650 | {file = "mkdocs_autorefs-1.4.0-py3-none-any.whl", hash = "sha256:bad19f69655878d20194acd0162e29a89c3f7e6365ffe54e72aa3fd1072f240d"}, 651 | {file = "mkdocs_autorefs-1.4.0.tar.gz", hash = "sha256:a9c0aa9c90edbce302c09d050a3c4cb7c76f8b7b2c98f84a7a05f53d00392156"}, 652 | ] 653 | 654 | [package.dependencies] 655 | Markdown = ">=3.3" 656 | markupsafe = ">=2.0.1" 657 | mkdocs = ">=1.1" 658 | 659 | [[package]] 660 | name = "mkdocs-get-deps" 661 | version = "0.2.0" 662 | description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" 663 | optional = false 664 | python-versions = ">=3.8" 665 | groups = ["docs"] 666 | files = [ 667 | {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, 668 | {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, 669 | ] 670 | 671 | [package.dependencies] 672 | importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} 673 | mergedeep = ">=1.3.4" 674 | platformdirs = ">=2.2.0" 675 | pyyaml = ">=5.1" 676 | 677 | [[package]] 678 | name = "mkdocs-material" 679 | version = "9.6.14" 680 | description = "Documentation that simply works" 681 | optional = false 682 | python-versions = ">=3.8" 683 | groups = ["docs"] 684 | files = [ 685 | {file = "mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b"}, 686 | {file = "mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754"}, 687 | ] 688 | 689 | [package.dependencies] 690 | babel = ">=2.10,<3.0" 691 | backrefs = ">=5.7.post1,<6.0" 692 | colorama = ">=0.4,<1.0" 693 | jinja2 = ">=3.1,<4.0" 694 | markdown = ">=3.2,<4.0" 695 | mkdocs = ">=1.6,<2.0" 696 | mkdocs-material-extensions = ">=1.3,<2.0" 697 | paginate = ">=0.5,<1.0" 698 | pygments = ">=2.16,<3.0" 699 | pymdown-extensions = ">=10.2,<11.0" 700 | requests = ">=2.26,<3.0" 701 | 702 | [package.extras] 703 | git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] 704 | imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] 705 | recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] 706 | 707 | [[package]] 708 | name = "mkdocs-material-extensions" 709 | version = "1.3.1" 710 | description = "Extension pack for Python Markdown and MkDocs Material." 711 | optional = false 712 | python-versions = ">=3.8" 713 | groups = ["docs"] 714 | files = [ 715 | {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, 716 | {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, 717 | ] 718 | 719 | [[package]] 720 | name = "mkdocstrings" 721 | version = "0.29.1" 722 | description = "Automatic documentation from sources, for MkDocs." 723 | optional = false 724 | python-versions = ">=3.9" 725 | groups = ["docs"] 726 | files = [ 727 | {file = "mkdocstrings-0.29.1-py3-none-any.whl", hash = "sha256:37a9736134934eea89cbd055a513d40a020d87dfcae9e3052c2a6b8cd4af09b6"}, 728 | {file = "mkdocstrings-0.29.1.tar.gz", hash = "sha256:8722f8f8c5cd75da56671e0a0c1bbed1df9946c0cef74794d6141b34011abd42"}, 729 | ] 730 | 731 | [package.dependencies] 732 | importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} 733 | Jinja2 = ">=2.11.1" 734 | Markdown = ">=3.6" 735 | MarkupSafe = ">=1.1" 736 | mkdocs = ">=1.6" 737 | mkdocs-autorefs = ">=1.4" 738 | mkdocstrings-python = {version = ">=1.16.2", optional = true, markers = "extra == \"python\""} 739 | pymdown-extensions = ">=6.3" 740 | 741 | [package.extras] 742 | crystal = ["mkdocstrings-crystal (>=0.3.4)"] 743 | python = ["mkdocstrings-python (>=1.16.2)"] 744 | python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] 745 | 746 | [[package]] 747 | name = "mkdocstrings-python" 748 | version = "1.16.8" 749 | description = "A Python handler for mkdocstrings." 750 | optional = false 751 | python-versions = ">=3.9" 752 | groups = ["docs"] 753 | files = [ 754 | {file = "mkdocstrings_python-1.16.8-py3-none-any.whl", hash = "sha256:211b7aaf776cd45578ecb531e5ad0d3a35a8be9101a6bfa10de38a69af9d8fd8"}, 755 | {file = "mkdocstrings_python-1.16.8.tar.gz", hash = "sha256:9453ccae69be103810c1cf6435ce71c8f714ae37fef4d87d16aa92a7c800fe1d"}, 756 | ] 757 | 758 | [package.dependencies] 759 | griffe = ">=1.6.2" 760 | mkdocs-autorefs = ">=1.4" 761 | mkdocstrings = ">=0.28.3" 762 | typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} 763 | 764 | [[package]] 765 | name = "packaging" 766 | version = "24.2" 767 | description = "Core utilities for Python packages" 768 | optional = false 769 | python-versions = ">=3.8" 770 | groups = ["main", "dev", "docs"] 771 | files = [ 772 | {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, 773 | {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, 774 | ] 775 | 776 | [[package]] 777 | name = "paginate" 778 | version = "0.5.7" 779 | description = "Divides large result sets into pages for easier browsing" 780 | optional = false 781 | python-versions = "*" 782 | groups = ["docs"] 783 | files = [ 784 | {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, 785 | {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, 786 | ] 787 | 788 | [package.extras] 789 | dev = ["pytest", "tox"] 790 | lint = ["black"] 791 | 792 | [[package]] 793 | name = "pathspec" 794 | version = "0.12.1" 795 | description = "Utility library for gitignore style pattern matching of file paths." 796 | optional = false 797 | python-versions = ">=3.8" 798 | groups = ["docs"] 799 | files = [ 800 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 801 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 802 | ] 803 | 804 | [[package]] 805 | name = "pillow" 806 | version = "11.2.1" 807 | description = "Python Imaging Library (Fork)" 808 | optional = true 809 | python-versions = ">=3.9" 810 | groups = ["main"] 811 | markers = "extra == \"examples\"" 812 | files = [ 813 | {file = "pillow-11.2.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047"}, 814 | {file = "pillow-11.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95"}, 815 | {file = "pillow-11.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4ba4be812c7a40280629e55ae0b14a0aafa150dd6451297562e1764808bbe61"}, 816 | {file = "pillow-11.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8bd62331e5032bc396a93609982a9ab6b411c05078a52f5fe3cc59234a3abd1"}, 817 | {file = "pillow-11.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:562d11134c97a62fe3af29581f083033179f7ff435f78392565a1ad2d1c2c45c"}, 818 | {file = "pillow-11.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c97209e85b5be259994eb5b69ff50c5d20cca0f458ef9abd835e262d9d88b39d"}, 819 | {file = "pillow-11.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0c3e6d0f59171dfa2e25d7116217543310908dfa2770aa64b8f87605f8cacc97"}, 820 | {file = "pillow-11.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc1c3bc53befb6096b84165956e886b1729634a799e9d6329a0c512ab651e579"}, 821 | {file = "pillow-11.2.1-cp310-cp310-win32.whl", hash = "sha256:312c77b7f07ab2139924d2639860e084ec2a13e72af54d4f08ac843a5fc9c79d"}, 822 | {file = "pillow-11.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9bc7ae48b8057a611e5fe9f853baa88093b9a76303937449397899385da06fad"}, 823 | {file = "pillow-11.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:2728567e249cdd939f6cc3d1f049595c66e4187f3c34078cbc0a7d21c47482d2"}, 824 | {file = "pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70"}, 825 | {file = "pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf"}, 826 | {file = "pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7"}, 827 | {file = "pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8"}, 828 | {file = "pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600"}, 829 | {file = "pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788"}, 830 | {file = "pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e"}, 831 | {file = "pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e"}, 832 | {file = "pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6"}, 833 | {file = "pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193"}, 834 | {file = "pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7"}, 835 | {file = "pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f"}, 836 | {file = "pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b"}, 837 | {file = "pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d"}, 838 | {file = "pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4"}, 839 | {file = "pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d"}, 840 | {file = "pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4"}, 841 | {file = "pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443"}, 842 | {file = "pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c"}, 843 | {file = "pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3"}, 844 | {file = "pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941"}, 845 | {file = "pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb"}, 846 | {file = "pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28"}, 847 | {file = "pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830"}, 848 | {file = "pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0"}, 849 | {file = "pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1"}, 850 | {file = "pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f"}, 851 | {file = "pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155"}, 852 | {file = "pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14"}, 853 | {file = "pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b"}, 854 | {file = "pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2"}, 855 | {file = "pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691"}, 856 | {file = "pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c"}, 857 | {file = "pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22"}, 858 | {file = "pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7"}, 859 | {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16"}, 860 | {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b"}, 861 | {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406"}, 862 | {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91"}, 863 | {file = "pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751"}, 864 | {file = "pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9"}, 865 | {file = "pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd"}, 866 | {file = "pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e"}, 867 | {file = "pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681"}, 868 | {file = "pillow-11.2.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:7491cf8a79b8eb867d419648fff2f83cb0b3891c8b36da92cc7f1931d46108c8"}, 869 | {file = "pillow-11.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b02d8f9cb83c52578a0b4beadba92e37d83a4ef11570a8688bbf43f4ca50909"}, 870 | {file = "pillow-11.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:014ca0050c85003620526b0ac1ac53f56fc93af128f7546623cc8e31875ab928"}, 871 | {file = "pillow-11.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3692b68c87096ac6308296d96354eddd25f98740c9d2ab54e1549d6c8aea9d79"}, 872 | {file = "pillow-11.2.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:f781dcb0bc9929adc77bad571b8621ecb1e4cdef86e940fe2e5b5ee24fd33b35"}, 873 | {file = "pillow-11.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2b490402c96f907a166615e9a5afacf2519e28295f157ec3a2bb9bd57de638cb"}, 874 | {file = "pillow-11.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd6b20b93b3ccc9c1b597999209e4bc5cf2853f9ee66e3fc9a400a78733ffc9a"}, 875 | {file = "pillow-11.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b835d89c08a6c2ee7781b8dd0a30209a8012b5f09c0a665b65b0eb3560b6f36"}, 876 | {file = "pillow-11.2.1-cp39-cp39-win32.whl", hash = "sha256:b10428b3416d4f9c61f94b494681280be7686bda15898a3a9e08eb66a6d92d67"}, 877 | {file = "pillow-11.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:6ebce70c3f486acf7591a3d73431fa504a4e18a9b97ff27f5f47b7368e4b9dd1"}, 878 | {file = "pillow-11.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:c27476257b2fdcd7872d54cfd119b3a9ce4610fb85c8e32b70b42e3680a29a1e"}, 879 | {file = "pillow-11.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156"}, 880 | {file = "pillow-11.2.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772"}, 881 | {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363"}, 882 | {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85d27ea4c889342f7e35f6d56e7e1cb345632ad592e8c51b693d7b7556043ce0"}, 883 | {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bf2c33d6791c598142f00c9c4c7d47f6476731c31081331664eb26d6ab583e01"}, 884 | {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e616e7154c37669fc1dfc14584f11e284e05d1c650e1c0f972f281c4ccc53193"}, 885 | {file = "pillow-11.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39ad2e0f424394e3aebc40168845fee52df1394a4673a6ee512d840d14ab3013"}, 886 | {file = "pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed"}, 887 | {file = "pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c"}, 888 | {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd"}, 889 | {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076"}, 890 | {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b"}, 891 | {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f"}, 892 | {file = "pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044"}, 893 | {file = "pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6"}, 894 | ] 895 | 896 | [package.extras] 897 | docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] 898 | fpx = ["olefile"] 899 | mic = ["olefile"] 900 | test-arrow = ["pyarrow"] 901 | tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] 902 | typing = ["typing-extensions ; python_version < \"3.10\""] 903 | xmp = ["defusedxml"] 904 | 905 | [[package]] 906 | name = "platformdirs" 907 | version = "4.3.6" 908 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 909 | optional = false 910 | python-versions = ">=3.8" 911 | groups = ["docs"] 912 | files = [ 913 | {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, 914 | {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, 915 | ] 916 | 917 | [package.extras] 918 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] 919 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] 920 | type = ["mypy (>=1.11.2)"] 921 | 922 | [[package]] 923 | name = "pluggy" 924 | version = "1.5.0" 925 | description = "plugin and hook calling mechanisms for python" 926 | optional = false 927 | python-versions = ">=3.8" 928 | groups = ["dev"] 929 | files = [ 930 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 931 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 932 | ] 933 | 934 | [package.extras] 935 | dev = ["pre-commit", "tox"] 936 | testing = ["pytest", "pytest-benchmark"] 937 | 938 | [[package]] 939 | name = "pygments" 940 | version = "2.18.0" 941 | description = "Pygments is a syntax highlighting package written in Python." 942 | optional = false 943 | python-versions = ">=3.8" 944 | groups = ["docs"] 945 | files = [ 946 | {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, 947 | {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, 948 | ] 949 | 950 | [package.extras] 951 | windows-terminal = ["colorama (>=0.4.6)"] 952 | 953 | [[package]] 954 | name = "pymdown-extensions" 955 | version = "10.12" 956 | description = "Extension pack for Python Markdown." 957 | optional = false 958 | python-versions = ">=3.8" 959 | groups = ["docs"] 960 | files = [ 961 | {file = "pymdown_extensions-10.12-py3-none-any.whl", hash = "sha256:49f81412242d3527b8b4967b990df395c89563043bc51a3d2d7d500e52123b77"}, 962 | {file = "pymdown_extensions-10.12.tar.gz", hash = "sha256:b0ee1e0b2bef1071a47891ab17003bfe5bf824a398e13f49f8ed653b699369a7"}, 963 | ] 964 | 965 | [package.dependencies] 966 | markdown = ">=3.6" 967 | pyyaml = "*" 968 | 969 | [package.extras] 970 | extra = ["pygments (>=2.12)"] 971 | 972 | [[package]] 973 | name = "pytest" 974 | version = "8.3.5" 975 | description = "pytest: simple powerful testing with Python" 976 | optional = false 977 | python-versions = ">=3.8" 978 | groups = ["dev"] 979 | files = [ 980 | {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, 981 | {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, 982 | ] 983 | 984 | [package.dependencies] 985 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 986 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 987 | iniconfig = "*" 988 | packaging = "*" 989 | pluggy = ">=1.5,<2" 990 | tomli = {version = ">=1", markers = "python_version < \"3.11\""} 991 | 992 | [package.extras] 993 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 994 | 995 | [[package]] 996 | name = "pytest-cov" 997 | version = "6.1.1" 998 | description = "Pytest plugin for measuring coverage." 999 | optional = false 1000 | python-versions = ">=3.9" 1001 | groups = ["dev"] 1002 | files = [ 1003 | {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"}, 1004 | {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"}, 1005 | ] 1006 | 1007 | [package.dependencies] 1008 | coverage = {version = ">=7.5", extras = ["toml"]} 1009 | pytest = ">=4.6" 1010 | 1011 | [package.extras] 1012 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] 1013 | 1014 | [[package]] 1015 | name = "pytest-timeout" 1016 | version = "2.4.0" 1017 | description = "pytest plugin to abort hanging tests" 1018 | optional = false 1019 | python-versions = ">=3.7" 1020 | groups = ["dev"] 1021 | files = [ 1022 | {file = "pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2"}, 1023 | {file = "pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a"}, 1024 | ] 1025 | 1026 | [package.dependencies] 1027 | pytest = ">=7.0.0" 1028 | 1029 | [[package]] 1030 | name = "python-dateutil" 1031 | version = "2.9.0.post0" 1032 | description = "Extensions to the standard Python datetime module" 1033 | optional = false 1034 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 1035 | groups = ["docs"] 1036 | files = [ 1037 | {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, 1038 | {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, 1039 | ] 1040 | 1041 | [package.dependencies] 1042 | six = ">=1.5" 1043 | 1044 | [[package]] 1045 | name = "pyyaml" 1046 | version = "6.0.2" 1047 | description = "YAML parser and emitter for Python" 1048 | optional = false 1049 | python-versions = ">=3.8" 1050 | groups = ["docs"] 1051 | files = [ 1052 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 1053 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 1054 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 1055 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 1056 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 1057 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 1058 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 1059 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 1060 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 1061 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 1062 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 1063 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 1064 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 1065 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 1066 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 1067 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 1068 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 1069 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 1070 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 1071 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 1072 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 1073 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 1074 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 1075 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 1076 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 1077 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 1078 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 1079 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 1080 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 1081 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 1082 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 1083 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 1084 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 1085 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 1086 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 1087 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 1088 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 1089 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 1090 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 1091 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 1092 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 1093 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 1094 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 1095 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 1096 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 1097 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 1098 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 1099 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 1100 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 1101 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 1102 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 1103 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 1104 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "pyyaml-env-tag" 1109 | version = "0.1" 1110 | description = "A custom YAML tag for referencing environment variables in YAML files. " 1111 | optional = false 1112 | python-versions = ">=3.6" 1113 | groups = ["docs"] 1114 | files = [ 1115 | {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, 1116 | {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, 1117 | ] 1118 | 1119 | [package.dependencies] 1120 | pyyaml = "*" 1121 | 1122 | [[package]] 1123 | name = "requests" 1124 | version = "2.32.3" 1125 | description = "Python HTTP for Humans." 1126 | optional = false 1127 | python-versions = ">=3.8" 1128 | groups = ["docs"] 1129 | files = [ 1130 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, 1131 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, 1132 | ] 1133 | 1134 | [package.dependencies] 1135 | certifi = ">=2017.4.17" 1136 | charset-normalizer = ">=2,<4" 1137 | idna = ">=2.5,<4" 1138 | urllib3 = ">=1.21.1,<3" 1139 | 1140 | [package.extras] 1141 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 1142 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 1143 | 1144 | [[package]] 1145 | name = "ruff" 1146 | version = "0.11.12" 1147 | description = "An extremely fast Python linter and code formatter, written in Rust." 1148 | optional = false 1149 | python-versions = ">=3.7" 1150 | groups = ["dev"] 1151 | files = [ 1152 | {file = "ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc"}, 1153 | {file = "ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3"}, 1154 | {file = "ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa"}, 1155 | {file = "ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012"}, 1156 | {file = "ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a"}, 1157 | {file = "ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7"}, 1158 | {file = "ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a"}, 1159 | {file = "ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13"}, 1160 | {file = "ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be"}, 1161 | {file = "ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd"}, 1162 | {file = "ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef"}, 1163 | {file = "ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5"}, 1164 | {file = "ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02"}, 1165 | {file = "ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c"}, 1166 | {file = "ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6"}, 1167 | {file = "ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832"}, 1168 | {file = "ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5"}, 1169 | {file = "ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603"}, 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "six" 1174 | version = "1.17.0" 1175 | description = "Python 2 and 3 compatibility utilities" 1176 | optional = false 1177 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 1178 | groups = ["docs"] 1179 | files = [ 1180 | {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, 1181 | {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, 1182 | ] 1183 | 1184 | [[package]] 1185 | name = "sniffio" 1186 | version = "1.3.1" 1187 | description = "Sniff out which async library your code is running under" 1188 | optional = true 1189 | python-versions = ">=3.7" 1190 | groups = ["main"] 1191 | markers = "extra == \"examples\"" 1192 | files = [ 1193 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, 1194 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "tomli" 1199 | version = "2.2.1" 1200 | description = "A lil' TOML parser" 1201 | optional = false 1202 | python-versions = ">=3.8" 1203 | groups = ["dev"] 1204 | markers = "python_full_version <= \"3.11.0a6\"" 1205 | files = [ 1206 | {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, 1207 | {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, 1208 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, 1209 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, 1210 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, 1211 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, 1212 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, 1213 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, 1214 | {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, 1215 | {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, 1216 | {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, 1217 | {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, 1218 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, 1219 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, 1220 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, 1221 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, 1222 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, 1223 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, 1224 | {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, 1225 | {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, 1226 | {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, 1227 | {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, 1228 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, 1229 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, 1230 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, 1231 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, 1232 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, 1233 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, 1234 | {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, 1235 | {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, 1236 | {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, 1237 | {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, 1238 | ] 1239 | 1240 | [[package]] 1241 | name = "typing-extensions" 1242 | version = "4.13.2" 1243 | description = "Backported and Experimental Type Hints for Python 3.8+" 1244 | optional = false 1245 | python-versions = ">=3.8" 1246 | groups = ["main", "docs"] 1247 | files = [ 1248 | {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, 1249 | {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "urllib3" 1254 | version = "2.2.3" 1255 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1256 | optional = false 1257 | python-versions = ">=3.8" 1258 | groups = ["docs"] 1259 | files = [ 1260 | {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, 1261 | {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, 1262 | ] 1263 | 1264 | [package.extras] 1265 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] 1266 | h2 = ["h2 (>=4,<5)"] 1267 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1268 | zstd = ["zstandard (>=0.18.0)"] 1269 | 1270 | [[package]] 1271 | name = "watchdog" 1272 | version = "6.0.0" 1273 | description = "Filesystem events monitoring" 1274 | optional = false 1275 | python-versions = ">=3.9" 1276 | groups = ["docs"] 1277 | files = [ 1278 | {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, 1279 | {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, 1280 | {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, 1281 | {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, 1282 | {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, 1283 | {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, 1284 | {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, 1285 | {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, 1286 | {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, 1287 | {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, 1288 | {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, 1289 | {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, 1290 | {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, 1291 | {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, 1292 | {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, 1293 | {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, 1294 | {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, 1295 | {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, 1296 | {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, 1297 | {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, 1298 | {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, 1299 | {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, 1300 | {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, 1301 | {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, 1302 | {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, 1303 | {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, 1304 | {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, 1305 | {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, 1306 | {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, 1307 | {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, 1308 | ] 1309 | 1310 | [package.extras] 1311 | watchmedo = ["PyYAML (>=3.10)"] 1312 | 1313 | [[package]] 1314 | name = "zipp" 1315 | version = "3.21.0" 1316 | description = "Backport of pathlib-compatible object wrapper for zip files" 1317 | optional = false 1318 | python-versions = ">=3.9" 1319 | groups = ["docs"] 1320 | markers = "python_version < \"3.10\"" 1321 | files = [ 1322 | {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, 1323 | {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, 1324 | ] 1325 | 1326 | [package.extras] 1327 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 1328 | cover = ["pytest-cov"] 1329 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 1330 | enabler = ["pytest-enabler (>=2.2)"] 1331 | test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] 1332 | type = ["pytest-mypy"] 1333 | 1334 | [extras] 1335 | examples = ["Pillow", "customtkinter", "httpx"] 1336 | 1337 | [metadata] 1338 | lock-version = "2.1" 1339 | python-versions = "^3.9" 1340 | content-hash = "80019eeba871bf255db2812cd09d3e59ea524c6f2dadd3fa4629b64513ed07b6" 1341 | -------------------------------------------------------------------------------- /poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | in-project = true 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "async-tkinter-loop" 3 | version = "0.9.3" 4 | description = "Asynchronous mainloop implementation for tkinter" 5 | authors = ["insolor "] 6 | license = "MIT" 7 | readme = "README.md" 8 | homepage = "https://github.com/insolor/async-tkinter-loop" 9 | repository = "https://github.com/insolor/async-tkinter-loop" 10 | documentation = "https://insolor.github.io/async-tkinter-loop/" 11 | keywords = ["tkinter", "async", "async-await", "asyncio", "aiohttp"] 12 | classifiers = [ 13 | "Development Status :: 4 - Beta", 14 | "Operating System :: OS Independent", 15 | "License :: OSI Approved :: MIT License", 16 | "Intended Audience :: Developers", 17 | "Topic :: Software Development :: Libraries :: Python Modules", 18 | ] 19 | 20 | [tool.poetry.dependencies] 21 | python = "^3.9" 22 | Pillow = {version = ">=10.3.0,<12.0.0", optional = true} 23 | httpx = {version = ">=0.23.1,<0.29.0", optional = true} 24 | customtkinter = {version = "^5.2.1", optional = true} 25 | typing-extensions = "*" 26 | 27 | [tool.poetry.group.dev.dependencies] 28 | pytest = ">=7.4,<9.0" 29 | pytest-cov = ">=4.1,<7.0" 30 | pytest-timeout = "^2.0.1" 31 | ruff = ">=0.4.7,<0.12.0" 32 | customtkinter = "^5.2.1" 33 | 34 | [tool.poetry.group.docs.dependencies] 35 | mkdocs = "^1.5.2" 36 | mkdocstrings = {extras = ["python"], version = ">=0.22,<0.30"} 37 | mkdocs-material = "^9.2.5" 38 | 39 | [tool.poetry.extras] 40 | examples = ["httpx", "Pillow", "customtkinter"] 41 | 42 | [build-system] 43 | requires = ["poetry-core>=1.0.0"] 44 | build-backend = "poetry.core.masonry.api" 45 | 46 | [tool.ruff] 47 | target-version = "py39" 48 | line-length = 120 49 | 50 | [tool.ruff.lint] 51 | select = ["ALL"] 52 | ignore = [ 53 | "D", # Warning about docstrings are suppressed for now 54 | "ANN002", 55 | "ANN003", 56 | ] 57 | fixable = ["ALL"] 58 | 59 | [tool.ruff.lint.per-file-ignores] 60 | "tests/*" = [ 61 | "S101", # Don't warn about using of asserts in tests 62 | "ANN001", 63 | "ANN201", 64 | "ANN202", 65 | "D", # Don't warn about missing documentation in tests 66 | ] 67 | 68 | "examples/*" = [ 69 | "ANN", 70 | "D", 71 | "S311", 72 | "PL", 73 | "INP001", 74 | "RUF006", 75 | ] -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insolor/async-tkinter-loop/f3f674ede5c6ee4e1187c9985798b5a44cc70660/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_async_tk_loop.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from tkinter import Tk 3 | from unittest.mock import Mock 4 | 5 | import pytest 6 | 7 | from async_tkinter_loop import async_handler, async_mainloop 8 | 9 | TIMEOUT = 60 10 | 11 | 12 | @pytest.mark.timeout(TIMEOUT) 13 | def test_destroy(): 14 | root = Tk() 15 | root.destroy() 16 | async_mainloop(root) 17 | 18 | 19 | @pytest.mark.timeout(TIMEOUT) 20 | def test_async_command(): 21 | root = Tk() 22 | 23 | # Simulate a click on a button which closes the window with some delay 24 | async def button_pressed(): 25 | await asyncio.sleep(0.1) 26 | root.destroy() 27 | 28 | async_handler(button_pressed)() 29 | 30 | async_mainloop(root) 31 | 32 | 33 | @pytest.mark.timeout(TIMEOUT) 34 | def test_async_event_handler(): 35 | root = Tk() 36 | 37 | async def on_click(_event): 38 | await asyncio.sleep(0.1) 39 | root.destroy() 40 | 41 | async_handler(on_click)(Mock("Event")) 42 | 43 | async_mainloop(root) 44 | 45 | 46 | @pytest.mark.timeout(TIMEOUT) 47 | def test_async_command_as_decorator(): 48 | root = Tk() 49 | 50 | # Simulate a click on a button which closes the window with some delay 51 | @async_handler 52 | async def button_pressed(): 53 | await asyncio.sleep(0.1) 54 | root.destroy() 55 | 56 | button_pressed() 57 | 58 | async_mainloop(root) 59 | 60 | 61 | @pytest.mark.timeout(TIMEOUT) 62 | def test_async_event_handler_as_decorator(): 63 | root = Tk() 64 | 65 | @async_handler 66 | async def on_click(_event): 67 | await asyncio.sleep(0.1) 68 | root.destroy() 69 | 70 | on_click(Mock("Event")) 71 | 72 | async_mainloop(root) 73 | -------------------------------------------------------------------------------- /tests/test_mixins.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | 3 | import customtkinter 4 | import pytest 5 | 6 | from async_tkinter_loop.mixins import AsyncCTk, AsyncTk 7 | 8 | TIMEOUT = 60 9 | 10 | 11 | @pytest.mark.timeout(TIMEOUT) 12 | def test_destroy_tk(): 13 | class App(tk.Tk, AsyncTk): 14 | pass 15 | 16 | app = App() 17 | app.destroy() 18 | app.async_mainloop() 19 | 20 | 21 | @pytest.mark.timeout(TIMEOUT) 22 | def test_destroy_ctk(): 23 | class App(customtkinter.CTk, AsyncCTk): 24 | pass 25 | 26 | app = App() 27 | app.after(100, app.destroy) 28 | app.async_mainloop() 29 | --------------------------------------------------------------------------------