├── examples ├── .gitignore ├── requirements.txt ├── online.py ├── sol2log.py ├── hello.py ├── sol2csvfile.py ├── sol2csv.py ├── addNewInjectionProfile.py ├── sol2mqtt.py ├── talk2solmate.py └── talk2solmatelocally.py ├── .gitignore ├── solmate_sdk ├── __init__.py ├── tests.py ├── connection.py ├── utils.py └── apiclient.py ├── .editorconfig ├── tasks.py ├── docs ├── conf.py └── index.rst ├── LICENSE.md ├── pyproject.toml ├── README.md ├── .pre-commit-config.yaml └── poetry.lock /examples/.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | *.csv 3 | -------------------------------------------------------------------------------- /examples/requirements.txt: -------------------------------------------------------------------------------- 1 | solmate-sdk 2 | paho-mqtt 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.pyc 3 | *credentials* 4 | .cache/ 5 | /dist/ 6 | /build/ 7 | .venv/ 8 | -------------------------------------------------------------------------------- /solmate_sdk/__init__.py: -------------------------------------------------------------------------------- 1 | """Software Development Kit for the EET SolMate""" 2 | from .apiclient import SolMateAPIClient, LocalSolMateAPIClient 3 | -------------------------------------------------------------------------------- /examples/online.py: -------------------------------------------------------------------------------- 1 | from solmate_sdk import SolMateAPIClient 2 | 3 | client = SolMateAPIClient("test1") 4 | client.connect() 5 | print(f"Your SolMate online status is: {client.check_online()}") 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = True 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | tab_width = 2 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = True 10 | max_line_length = 120 11 | 12 | [*.py] 13 | indent_size = 4 14 | -------------------------------------------------------------------------------- /examples/sol2log.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import solmate_sdk 4 | 5 | client = solmate_sdk.SolMateAPIClient("test1") 6 | client.quickstart() 7 | while True: 8 | print(f"Solmate {client.serialnum}: {client.get_live_values()}") 9 | sleep(10) 10 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | """A collection of development tasks for this library.""" 2 | import invoke 3 | 4 | 5 | @invoke.task 6 | def generate_docs(ctx, builder="html"): 7 | """Generate the Sphinx Documentation""" 8 | ctx.run(f"sphinx-build -M {builder} docs build/docs") 9 | -------------------------------------------------------------------------------- /examples/hello.py: -------------------------------------------------------------------------------- 1 | from solmate_sdk import SolMateAPIClient 2 | 3 | client = SolMateAPIClient("test1") 4 | client.connect() 5 | print(f"Your SolMate online status is: {client.check_online()}") 6 | 7 | # or for the protected API: 8 | client.quickstart() 9 | print(client.get_live_values()) 10 | -------------------------------------------------------------------------------- /examples/sol2csvfile.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from time import sleep 3 | 4 | import solmate_sdk 5 | 6 | client = solmate_sdk.SolMateAPIClient("test1") 7 | client.quickstart() 8 | vals = client.get_live_values() 9 | keys = vals.keys() 10 | with open(f"{client.serialnum}.csv", "w") as csvfile: 11 | writer = csv.DictWriter(csvfile, fieldnames=keys) 12 | writer.writeheader() 13 | writer.writerow(vals) 14 | while True: 15 | with open(f"{client.serialnum}.csv", "a") as csvfile: 16 | writer = csv.DictWriter(csvfile, fieldnames=keys) 17 | writer.writerow(client.get_live_values()) 18 | sleep(10) 19 | -------------------------------------------------------------------------------- /examples/sol2csv.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import solmate_sdk 4 | 5 | SEPERATOR = ";" 6 | 7 | client = solmate_sdk.SolMateAPIClient("test1") 8 | client.quickstart() 9 | 10 | vals = client.get_live_values() 11 | keys = vals.keys() 12 | print("serial_number", end=SEPERATOR) 13 | for k in vals.keys(): 14 | print(k, end=SEPERATOR) 15 | print() 16 | while True: 17 | vals = client.get_live_values() 18 | print(client.serialnum, end=SEPERATOR) 19 | for k in keys: 20 | if k in vals: 21 | print(vals[k], end=SEPERATOR) 22 | else: 23 | print(" ", end=SEPERATOR) 24 | print() 25 | sleep(10) 26 | -------------------------------------------------------------------------------- /solmate_sdk/tests.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the API Client.""" 2 | 3 | import unittest 4 | from .apiclient import SolMateAPIClient 5 | 6 | 7 | class BasicUsageTest(unittest.TestCase): 8 | """Basic Test Case""" 9 | 10 | serial_num = "test1" 11 | 12 | def test_online(self): 13 | """Try connecting to a client and check online status.""" 14 | print(f"Checking online status of SolMate {self.serial_num}") 15 | client = SolMateAPIClient(self.serial_num) 16 | client.connect() 17 | print(client.check_online()) 18 | 19 | def test_get_online_values(self): 20 | """Try initialising a client and get live values.""" 21 | print(f"Getting live values of SolMate {self.serial_num}") 22 | client = SolMateAPIClient(self.serial_num) 23 | client.quickstart() 24 | print(f"Live values: {client.get_live_values()}") 25 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | """Configuration of the docs for this package.""" 2 | # pylint: skip-file 3 | 4 | # Configuration file for the Sphinx documentation builder. 5 | # 6 | # For the full list of built-in configuration values, see the documentation: 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 8 | 9 | # -- Project information ----------------------------------------------------- 10 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 11 | 12 | project = "solmate-sdk" 13 | copyright = "2022, EET" 14 | author = "EET" 15 | release = "stable" 16 | 17 | # -- General configuration --------------------------------------------------- 18 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 19 | 20 | extensions = [] 21 | 22 | templates_path = ["_templates"] 23 | exclude_patterns = [] 24 | 25 | 26 | # -- Options for HTML output ------------------------------------------------- 27 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 28 | 29 | html_theme = "alabaster" 30 | html_static_path = ["_static"] 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 EET - Efficient Energy Technology GmbH 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 | -------------------------------------------------------------------------------- /examples/addNewInjectionProfile.py: -------------------------------------------------------------------------------- 1 | from solmate_sdk import SolMateAPIClient 2 | from solmate_sdk.utils import DATETIME_FORMAT_INJECTION_PROFILES 3 | 4 | import datetime 5 | 6 | client = SolMateAPIClient("") 7 | client.quickstart() 8 | 9 | # get saved injection profile settings on solmate 10 | injection_profiles_settings_from_solmate = client.get_injection_profiles() 11 | injection_profiles_from_solmate = injection_profiles_settings_from_solmate["injection_profiles"] 12 | # define new injection profile where key is the name displayed in the App 13 | new_injection_profile = { 14 | "my awesome test profile": { 15 | "min": [0.2 for _ in range(24)], 16 | "max": [0.8 for _ in range(24)], 17 | } 18 | } 19 | # extend injection profiles with new injection profile 20 | injection_profiles_from_solmate.update(new_injection_profile) 21 | # get new timestamp 22 | new_timestamp = datetime.datetime.now().strftime(DATETIME_FORMAT_INJECTION_PROFILES) 23 | # send old + new injection profile to solmate 24 | client.set_injection_profiles(injection_profiles_from_solmate, new_timestamp) 25 | # apply the newly created injection profile 26 | client.apply_injection_profile("my awesome test profile") 27 | -------------------------------------------------------------------------------- /examples/sol2mqtt.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import paho.mqtt.client as mqtt 4 | 5 | import solmate_sdk 6 | 7 | 8 | class config: 9 | mqtt_host = "mqtt.eclipseprojects.io" 10 | mqtt_port = 1883 11 | 12 | 13 | client = solmate_sdk.SolMateAPIClient("test1") 14 | client.quickstart() 15 | 16 | 17 | def on_connect(client, userdata, flags, rc): 18 | print("Connected with result code " + str(rc)) 19 | 20 | 21 | mqttClient = mqtt.Client() 22 | mqttClient.on_connect = on_connect 23 | mqttClient.connect(config.mqtt_host, config.mqtt_port, 60) 24 | while True: 25 | print(".", end="", flush=True) 26 | try: 27 | live_values = client.get_live_values() 28 | online = client.check_online() 29 | # mqttClient.publish(f"eet/solmate/{client.serialnum}/live_values", json.dumps(live_values), 1) 30 | # mqttClient.publish(f"eet/solmate/{client.serialnum}/online", online, 1) 31 | for property_name in live_values.keys(): 32 | if property_name == "pv_power": 33 | mqttClient.publish(f"eet/solmate/{client.serialnum}/{property_name}", live_values[property_name], 1) 34 | except Exception as exc: 35 | print(exc) 36 | sleep(10) 37 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.nitpick] 2 | style = ["github://MrP01/lint-me-now/nitpick-base-style.toml", "github://MrP01/lint-me-now/nitpick-python-style.toml"] 3 | 4 | [tool.poetry] 5 | name = "solmate-sdk" 6 | version = "0.1.11" 7 | description = "Software Development Kit for the EET SolMate" 8 | authors = ["EET"] 9 | license = "MIT" 10 | readme = "README.md" 11 | packages = [{ include = "solmate_sdk" }] 12 | repository = "https://github.com/eet-energy/solmate-sdk" 13 | documentation = "https://solmate-sdk.readthedocs.io/en/latest/" 14 | 15 | [tool.poetry.dependencies] 16 | python = "^3.8" 17 | websockets = "11.0.3" 18 | 19 | [tool.poetry.group.dev.dependencies] 20 | black = "^22.12.0" 21 | pylint = "^2.17.7" 22 | isort = "^5.12.0" 23 | Sphinx = "^5.3.0" 24 | invoke = "^1.7.3" 25 | 26 | [tool.black] 27 | line-length = 120 28 | 29 | [tool.isort] 30 | line_length = 120 31 | profile = "black" 32 | 33 | [tool.pylint] 34 | 35 | [tool.pylint.REPORTS] 36 | output-format = "colorized" 37 | 38 | [tool.pylint.FORMAT] 39 | max-line-length = 120 40 | indent-after-paren = 4 41 | 42 | [tool.bandit] 43 | skips = ["B101"] 44 | 45 | [build-system] 46 | requires = ["poetry_core>=1.0.0"] 47 | build-backend = "poetry.core.masonry.api" 48 | -------------------------------------------------------------------------------- /solmate_sdk/connection.py: -------------------------------------------------------------------------------- 1 | """The SolConnection class, used by the API Client.""" 2 | 3 | import asyncio 4 | import json 5 | 6 | import websockets.client 7 | 8 | from .utils import BadRequest, MoreCapableEncoder 9 | 10 | 11 | class SolConnection: 12 | """Underlying Connection Object to a Sol API endpoint. Do not use directly.""" 13 | 14 | def __init__(self, sock: websockets.client.WebSocketClientProtocol): 15 | """Initializes with a socket object and request id counter.""" 16 | self.sock = sock 17 | self._nextreqid = 0 18 | 19 | async def request(self, route, data, timeout=30): 20 | """Issues a request to Sol/SolMate. 21 | When the timeout passes, an asyncio.TimeoutError will be raised. 22 | """ 23 | reqid = self.reqid_counter() 24 | await self.sock.send(json.dumps({"route": route, "id": reqid, "data": data}, cls=MoreCapableEncoder)) 25 | response = json.loads(await asyncio.wait_for(self.sock.recv(), timeout)) 26 | if "error" in response: 27 | raise BadRequest(response["error"]) 28 | return response["data"] 29 | 30 | def reqid_counter(self): 31 | """Internal counter for request ids. In principle, this could be anything.""" 32 | self._nextreqid += 1 33 | return self._nextreqid 34 | 35 | async def close(self, reason=""): 36 | """Close the socket.""" 37 | await self.sock.close(code=1000, reason=reason) 38 | -------------------------------------------------------------------------------- /examples/talk2solmate.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | from websockets.exceptions import ConnectionClosedError 4 | 5 | import solmate_sdk 6 | from solmate_sdk.utils import retry 7 | from websockets.exceptions import ConnectionClosedError 8 | 9 | client = solmate_sdk.SolMateAPIClient("test1") 10 | 11 | 12 | @retry(10, ConnectionClosedError, 30) 13 | def run_continuously(): 14 | client.quickstart() 15 | while True: 16 | print(f"Solmate {client.serialnum}: {client.get_live_values()}") 17 | sleep(0.1) 18 | print(f"Solmate {client.serialnum}: {client.get_recent_logs()}") 19 | sleep(0.1) 20 | print(f"Solmate {client.serialnum}: {client.get_software_version()}") 21 | sleep(0.1) 22 | print(f"Solmate {client.serialnum}: {client.get_grid_mode()}") 23 | sleep(0.1) 24 | print(f"Solmate {client.serialnum}: {client.get_user_settings()}") 25 | sleep(0.1) 26 | print(f"Solmate {client.serialnum}: {client.get_injection_settings()}") 27 | sleep(0.1) 28 | print(f"Solmate {client.serialnum}: {client.set_min_injection(50)}") 29 | sleep(0.1) 30 | print(f"Solmate {client.serialnum}: {client.get_injection_settings()}") 31 | sleep(0.1) 32 | print(f"Solmate {client.serialnum}: {client.set_max_injection(50)}") 33 | sleep(0.1) 34 | print(f"Solmate {client.serialnum}: {client.get_injection_settings()}") 35 | sleep(10) 36 | 37 | 38 | if __name__ == "__main__": 39 | run_continuously() 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EET SolMate SDK 2 | 3 | All you need to integrate your [EET SolMate](https://www.eet.energy) into your home automation system - or really any Python-based system! 4 | Keep in mind that this is **work in progress**. 5 | 6 | This Python SDK provides a class-based API Client which lets you: 7 | 8 | 1. Login to your SolMate with serial number and password which returns an authentication token. 9 | 2. Connect to your SolMate with the authentication token. 10 | 3. Get live values of your SolMate. 11 | 4. Check if your SolMate is online. 12 | 13 | For any inquiries about, or problems with, the usage of this API endpoint, please create an issue in this repository. 14 | 15 | ## How to use 16 | 17 | Install the package via: 18 | 19 | `pip install solmate-sdk` 20 | 21 | Import the `SolMateAPIClient` class and connect to your SolMate: 22 | 23 | ```python 24 | from solmate_sdk import SolMateAPIClient 25 | 26 | client = SolMateAPIClient("") 27 | client.connect() 28 | print(f"Your SolMate online status is: {client.check_online()}") 29 | 30 | # or for the protected API: 31 | client.quickstart() # or client.quickstart(password="") 32 | print(client.get_live_values()) 33 | ``` 34 | 35 | The SolMate SDK communicates via a Websocket API with your SolMate. 36 | 37 | ## Roadmap 38 | 39 | - Quickstart supports multiple serial numbers (and multiple device ids?) 40 | - More Examples 41 | - Full Unit Testing 42 | - Car Charger Example 43 | 44 | ## Links 45 | 46 | - Our Homepage: [www.eet.energy](https://www.eet.energy) 47 | - The project on PyPi: [pypi.org/project/solmate-sdk](https://pypi.org/project/solmate-sdk/) 48 | - Read the docs page: https://solmate-sdk.readthedocs.io/en/latest/ 49 | 50 | ## Changelog 51 | 52 | ### Version 0.1.11 53 | 54 | - Added async methods and made them public 55 | - Added possibility to provide password to quickstart 56 | - Added possibility to initialize api client with async parameter 57 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/mirrors-prettier 3 | rev: v3.0.0-alpha.0 4 | hooks: 5 | - id: prettier 6 | stages: 7 | - commit 8 | - repo: https://github.com/pre-commit/pre-commit-hooks 9 | rev: v4.3.0 10 | hooks: 11 | - id: end-of-file-fixer 12 | - id: trailing-whitespace 13 | - repo: https://github.com/psf/black 14 | rev: 22.8.0 15 | hooks: 16 | - id: black 17 | args: 18 | - --safe 19 | - --quiet 20 | - repo: https://github.com/PyCQA/isort 21 | rev: 5.10.1 22 | hooks: 23 | - id: isort 24 | - repo: local 25 | hooks: 26 | - id: pylint 27 | name: pylint 28 | entry: pylint 29 | language: system 30 | exclude: tests/ 31 | types: 32 | - python 33 | - repo: local 34 | hooks: 35 | - id: bandit 36 | name: bandit 37 | entry: bandit 38 | language: system 39 | args: 40 | - -c 41 | - pyproject.toml 42 | exclude: tests/ 43 | types: 44 | - python 45 | - repo: https://github.com/pre-commit/pygrep-hooks 46 | rev: v1.9.0 47 | hooks: 48 | - id: python-check-blanket-noqa 49 | - id: python-no-eval 50 | - id: python-no-log-warn 51 | - repo: https://github.com/asottile/pyupgrade 52 | rev: v2.37.3 53 | hooks: 54 | - id: pyupgrade 55 | args: 56 | - --py38-plus 57 | - repo: local 58 | hooks: 59 | - id: pyright 60 | name: pyright 61 | entry: pyright 62 | language: node 63 | types: 64 | - python 65 | pass_filenames: false 66 | additional_dependencies: 67 | - pyright 68 | - repo: https://github.com/myint/autoflake 69 | rev: v1.5.3 70 | hooks: 71 | - id: autoflake 72 | args: 73 | - --in-place 74 | - --remove-all-unused-imports 75 | - --remove-unused-variables 76 | - --remove-duplicate-keys 77 | - --ignore-init-module-imports 78 | -------------------------------------------------------------------------------- /examples/talk2solmatelocally.py: -------------------------------------------------------------------------------- 1 | from getpass import getpass 2 | from time import sleep 3 | 4 | import solmate_sdk 5 | from solmate_sdk.utils import retry 6 | from websockets.exceptions import ConnectionClosedError 7 | from asyncio.exceptions import TimeoutError 8 | 9 | client = solmate_sdk.LocalSolMateAPIClient("X2S1K0506A00000001", uri = "ws://192.168.0.138:9124/") 10 | # Use your SolMates local IP Address here on port 9124 i.e. ws://:9124 if it is connected to your WLAN already 11 | # Use ws://192.168.4.1:9124/ if you are connected to your SolMate via its Access Point ("SolMate ") 12 | 13 | client.connect() 14 | print(client.check_online()) 15 | 16 | @retry(10, TimeoutError, 100) # to handle WLAN switching 17 | @retry(10, ConnectionClosedError, 30) 18 | def run_continuously(): 19 | client.quickstart() 20 | 21 | if input("Want to change wifi before we start\n\n Make sure to have SolMate FW >= 0.4.2-d").lower() == "y": 22 | # This will eventually trigger a TimeoutError an the whole process is redone in the new wifi 23 | print(f"Choose your desired WIFI {client.serialnum}: ") 24 | wifi = input(f"Type in your WIFI from: {client.list_wifis()}") 25 | password = getpass(f"Enter your WIFI key") 26 | if wifi != "" and password != "": 27 | print(f"Solmate {client.serialnum}: {client.connect_to_wifi(wifi, password)}") 28 | 29 | while True: 30 | print(f"Solmate {client.serialnum}: {client.list_wifis()}") 31 | sleep(0.1) 32 | print(f"Solmate {client.serialnum}: {client.get_live_values()}") 33 | sleep(0.1) 34 | print(f"Solmate {client.serialnum}: {client.get_recent_logs()}") 35 | sleep(0.1) 36 | print(f"Solmate {client.serialnum}: {client.get_software_version()}") 37 | sleep(0.1) 38 | print(f"Solmate {client.serialnum}: {client.get_grid_mode()}") 39 | sleep(0.1) 40 | print(f"Solmate {client.serialnum}: {client.get_user_settings()}") 41 | sleep(0.1) 42 | print(f"Solmate {client.serialnum}: {client.get_injection_settings()}") 43 | sleep(0.1) 44 | print(f"Solmate {client.serialnum}: {client.set_min_injection(0)}") 45 | sleep(0.1) 46 | print(f"Solmate {client.serialnum}: {client.get_injection_settings()}") 47 | sleep(0.1) 48 | print(f"Solmate {client.serialnum}: {client.set_max_injection(50)}") 49 | sleep(0.1) 50 | print(f"Solmate {client.serialnum}: {client.get_injection_settings()}") 51 | sleep(10) 52 | 53 | 54 | if __name__ == "__main__": 55 | run_continuously() 56 | -------------------------------------------------------------------------------- /solmate_sdk/utils.py: -------------------------------------------------------------------------------- 1 | """Utilities for the other modules. 2 | Methods included here should only depend on built-in and third party modules. 3 | """ 4 | 5 | import datetime 6 | import functools 7 | import json 8 | import time 9 | from websockets import ConnectionClosed 10 | 11 | DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S" 12 | DATETIME_FORMAT_INJECTION_PROFILES = "%Y-%m-%dT%H:%M:%S.%fZ" 13 | 14 | 15 | def bad_request_handling(): 16 | """A decorator that deals with BadRequest exceptions""" 17 | 18 | def decorator(func): 19 | @functools.wraps(func) 20 | def wrapper(*args, **kwargs): 21 | delay = 1 22 | r = 0 23 | num_retries = 2 24 | while True: 25 | try: 26 | return func(*args, **kwargs) 27 | except BadRequest as exc: 28 | r += 1 29 | if delay: 30 | time.sleep(delay) 31 | if r >= num_retries: 32 | print("This route is not supported on this SolMate yet") 33 | break 34 | return wrapper 35 | return decorator 36 | 37 | 38 | def retry(num_retries, exception_type, delay=0.0): 39 | """A decorator that retries a function num_retries times""" 40 | 41 | def decorator(func): 42 | @functools.wraps(func) 43 | def wrapper(*args, **kwargs): 44 | r = 0 45 | while True: 46 | try: 47 | return func(*args, **kwargs) 48 | except exception_type as exc: 49 | r += 1 50 | if delay: 51 | time.sleep(delay) 52 | if r >= num_retries: 53 | raise exc 54 | 55 | return wrapper 56 | 57 | return decorator 58 | 59 | 60 | class BadRequest(Exception): 61 | """Exception that the server may throw when receiving invalid requests. 62 | The API Client will throw this exception when it receives such an error. 63 | """ 64 | 65 | 66 | class ConnectionClosedOnPurpose(ConnectionClosed): 67 | """Exception to be thrown on purpose i.e. during switching of wlan routers""" 68 | 69 | 70 | class MoreCapableEncoder(json.JSONEncoder): 71 | """An extended JSONEncoder also capable of encoding datetime objects (using the DATETIME_FORMAT).""" 72 | 73 | def default(self, o): 74 | """Extends the method of the base class""" 75 | if isinstance(o, datetime.datetime): 76 | return o.strftime(DATETIME_FORMAT) 77 | return super().default(o) 78 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. solmate-sdk documentation master file, created by 2 | sphinx-quickstart on Thu Sep 1 16:52:14 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to solmate-sdk's documentation! 7 | ======================================= 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | 14 | 15 | 16 | Examples 17 | ======== 18 | 19 | For all examples please install solmate_sdk pypi package with: 20 | 21 | .. code-block:: bash 22 | 23 | pip install solmate_sdk 24 | 25 | Sol2Log 26 | ------- 27 | 28 | Log SolMate live values every 10 seconds on your terminal: 29 | 30 | .. code-block:: python 31 | 32 | from time import sleep 33 | import solmate_sdk 34 | 35 | client = solmate_sdk.SolMateAPIClient("test1") 36 | client.quickstart() 37 | while True: 38 | print(f"Solmate {client.serialnum}: {client.get_live_values()}") 39 | sleep(10) 40 | 41 | Sol2Csv 42 | ------- 43 | 44 | Log SolMate live values every 10 seconds in CSV format on your terminal: 45 | 46 | 47 | .. code-block:: python 48 | 49 | from time import sleep 50 | import solmate_sdk 51 | 52 | SEPERATOR=';' 53 | 54 | client = solmate_sdk.SolMateAPIClient("test1") 55 | client.quickstart() 56 | 57 | vals = client.get_live_values() 58 | keys = vals.keys() 59 | print("serial_number", end=SEPERATOR) 60 | for k in vals.keys(): 61 | print(k, end=SEPERATOR) 62 | print() 63 | while True: 64 | vals = client.get_live_values() 65 | print(client.serialnum, end=SEPERATOR) 66 | for k in keys: 67 | if k in vals: 68 | print(vals[k], end=SEPERATOR) 69 | else: 70 | print(" ", end=SEPERATOR) 71 | print() 72 | sleep(10) 73 | 74 | 75 | Sol2CsvFile 76 | ----------- 77 | 78 | Write SolMate live values every 10 seconds in CSV format into a CSV file: 79 | 80 | .. code-block:: python 81 | 82 | from time import sleep 83 | import solmate_sdk 84 | import csv 85 | 86 | client = solmate_sdk.SolMateAPIClient("test1") 87 | client.quickstart() 88 | vals = client.get_live_values() 89 | keys = vals.keys() 90 | with open(f'{client.serialnum}.csv', 'w') as csvfile: 91 | writer = csv.DictWriter(csvfile, fieldnames = keys) 92 | writer.writeheader() 93 | writer.writerow(vals) 94 | while True: 95 | with open(f'{client.serialnum}.csv', 'a') as csvfile: 96 | writer = csv.DictWriter(csvfile, fieldnames = keys) 97 | writer.writerow(client.get_live_values()) 98 | sleep(10) 99 | 100 | 101 | Sol2MQTT 102 | -------- 103 | 104 | Please install solmate_sdk and paho-mqtt pypi packages with: 105 | 106 | .. code-block:: bash 107 | 108 | pip install solmate_sdk paho-mqtt 109 | 110 | Publish SolMate PV Power every 10 seconds on the mqtt broker mqtt.eclipseprojects.io:1883 on the topic eet/solmate/test1/pv_power 111 | 112 | .. code-block:: python 113 | 114 | import solmate_sdk 115 | import paho.mqtt.client as mqtt 116 | from time import sleep 117 | import json 118 | 119 | client = solmate_sdk.SolMateAPIClient("test1") 120 | client.quickstart() 121 | 122 | mqttClient = mqtt.Client() 123 | mqttClient.connect("mqtt.eclipseprojects.io", 1883, 60) 124 | while True: 125 | print(".", end="", flush=True) 126 | try: 127 | live_values = client.get_live_values() 128 | online = client.check_online() 129 | for property_name in live_values.keys(): 130 | if property_name == 'pv_power': 131 | mqttClient.publish(f"eet/solmate/{client.serialnum}/{property_name}", live_values[property_name], 1) 132 | except Exception as exc: 133 | print(exc) 134 | sleep(10) 135 | 136 | 137 | .. include:: hello.py 138 | :lexer: 'python' 139 | :encoding: 'utf-8' 140 | :tab-width: 4 141 | 142 | Indices and tables 143 | ================== 144 | 145 | * :ref:`genindex` 146 | * :ref:`modindex` 147 | * :ref:`search` 148 | -------------------------------------------------------------------------------- /solmate_sdk/apiclient.py: -------------------------------------------------------------------------------- 1 | """Contains the high-level API client.""" 2 | 3 | import asyncio 4 | import base64 5 | import getpass 6 | import hashlib 7 | import json 8 | import pathlib 9 | import logging 10 | from typing import Optional 11 | 12 | import websockets.client 13 | 14 | from .connection import SolConnection 15 | from .utils import BadRequest, ConnectionClosedOnPurpose, bad_request_handling, retry 16 | 17 | CONFIG_DIRECTORY = pathlib.Path.home() / ".config" / "solmate-sdk" 18 | AUTHSTORE_FILE = CONFIG_DIRECTORY / "authstore.json" 19 | DEFAULT_DEVICE_ID = "solmate-sdk" 20 | LOCAL_AUTHSTORE_FILE = CONFIG_DIRECTORY / "local_authstore.json" 21 | LOCAL_ACCESS_DEVICE_ID = "local_webinterface" 22 | SOL_URI = "wss://sol.eet.energy:9124" 23 | 24 | 25 | class SolMateAPIClient: 26 | """Class-based Websocket API Client for the Sol and SolMate. 27 | This client provides synchronous and asynchronous endpoint methods. 28 | Asynchronous methods start with async_. 29 | """ 30 | 31 | def __init__(self, serialnum: str, uri=SOL_URI, asynchronous=False, logger=None): 32 | """Initializes the instance given a serial number and auth_token (signature). 33 | Leaves the underlying connection object uninitialised. 34 | """ 35 | self.serialnum: str = serialnum 36 | self.conn: Optional[SolConnection] = None 37 | self.authenticated: bool = False 38 | self.uri_verified: bool = False 39 | self.uri = uri 40 | self.device_id = DEFAULT_DEVICE_ID 41 | self.authstore_file = AUTHSTORE_FILE 42 | self.eventloop = None 43 | if logger is None: 44 | logger = logging.getLogger("SolMate API client.") 45 | self.logger = logger 46 | if not asynchronous: 47 | try: 48 | self.eventloop = asyncio.get_event_loop() 49 | if self.eventloop.is_closed(): 50 | raise RuntimeError("The current event loop is closed.") 51 | except RuntimeError: 52 | # No event loop is currently running, so create a new one 53 | self.eventloop = asyncio.new_event_loop() 54 | asyncio.set_event_loop(self.eventloop) 55 | 56 | async def async_connect(self): 57 | """Asynchronously attempts to connect to the server and initialize the client.""" 58 | if self.conn is not None: 59 | try: 60 | await self.conn.close("Got redirected by SWAG balancer.") 61 | self.logger.debug("Connection closed successfully.") 62 | except Exception as e: 63 | self.logger.error("Error while closing connection: %s", str(e)) 64 | 65 | self.logger.debug("Connecting to: %s", self.uri) 66 | sock = await websockets.client.connect(self.uri) 67 | self.logger.debug("Socket connected.") 68 | self.conn = SolConnection(sock) 69 | self.logger.info("Connected successfully.") 70 | 71 | def connect(self): 72 | """Synchronously attempts to connect to the server and initialize the client.""" 73 | self.eventloop.run_until_complete(self.async_connect()) 74 | 75 | async def async_request(self, route, data): 76 | """Synchronous method to make requests to the API.""" 77 | if self.conn is None: 78 | raise RuntimeError("Connection has not yet been initialised.") 79 | return await self.conn.request(route, data) 80 | 81 | def request(self, route, data): 82 | """Synchronous method to make requests to the API.""" 83 | return self.eventloop.run_until_complete(self.async_request(route, data)) 84 | 85 | async def async_login(self, user_password, device_id=DEFAULT_DEVICE_ID) -> str: 86 | """Generates the authentication token from the serialnumber + password.""" 87 | try: 88 | response = await self.async_request( 89 | "login", 90 | { 91 | "serial_num": self.serialnum, 92 | "user_password_hash": base64.encodebytes( 93 | hashlib.sha256(user_password.encode()).digest() 94 | ).decode(), # the stage-1 hash of the user password 95 | "device_id": device_id, 96 | }, 97 | ) 98 | except BadRequest as err: 99 | raise err 100 | if not response["success"]: 101 | raise RuntimeError("Unauthenticated :(") 102 | return response["signature"] 103 | 104 | def login(self, user_password, device_id=DEFAULT_DEVICE_ID) -> str: 105 | """Generates the authentication token from the serialnumber + password.""" 106 | return self.eventloop.run_until_complete(self.async_login(user_password, device_id)) 107 | 108 | async def async_check_uri(self, auth_token, device_id): 109 | """Handles redirections using verification of uri and dummy request gaining redirection info""" 110 | if not self.uri_verified: 111 | self.logger.debug("Verifiying uri") 112 | try: 113 | data = await self.async_request( 114 | "authenticate", 115 | { 116 | "serial_num": self.serialnum, 117 | "signature": auth_token, 118 | "device_id": device_id, 119 | }, 120 | ) 121 | self.logger.debug(data) 122 | if not data["redirect"] in (self.uri, None): 123 | self.logger.debug("Redirected - switching to new uri - uri of SolMate changed retry whatever you have done") 124 | self.uri = data["redirect"] 125 | self.logger.debug("New uri %s", self.uri) 126 | self.uri_verified = True 127 | else: 128 | self.uri_verified = True 129 | except BadRequest as err: 130 | raise ValueError("Invalid Serial Number?") from err 131 | 132 | def check_uri(self, auth_token, device_id): 133 | """Handles redirections using verification of uri and dummy request gaining redirection info""" 134 | return self.eventloop.run_until_complete(self.async_check_uri(auth_token, device_id)) 135 | 136 | async def async_authenticate(self, auth_token, device_id=DEFAULT_DEVICE_ID): 137 | """Given the authentication token, try to authenticate this websocket connection. 138 | Subsequent method calls to protected methods are unlocked this way. 139 | """ 140 | try: 141 | await self.async_request( 142 | "authenticate", 143 | { 144 | "serial_num": self.serialnum, 145 | "signature": auth_token, 146 | "device_id": device_id, 147 | }, 148 | ) 149 | except BadRequest as err: 150 | raise ValueError("Invalid Serial Number?") from err 151 | 152 | def authenticate(self, auth_token, device_id=DEFAULT_DEVICE_ID): 153 | """Given the authentication token, try to authenticate this websocket connection. 154 | Subsequent method calls to protected methods are unlocked this way. 155 | """ 156 | return self.eventloop.run_until_complete(self.async_authenticate(auth_token, device_id)) 157 | 158 | async def async_quickstart(self, password=None, device_id=DEFAULT_DEVICE_ID, store_auth_token_in_file=True): 159 | """Connect, login, authenticate and store the token for future use async!""" 160 | await self.async_connect() 161 | token: Optional[str] = None 162 | if self.authstore_file.exists(): 163 | with open(self.authstore_file, encoding="utf-8") as file: 164 | authstore = json.load(file) 165 | if self.serialnum in authstore: 166 | token = authstore[self.serialnum] 167 | else: 168 | authstore = {} 169 | if token is None: 170 | if not password: 171 | print(f"Please enter the user password of your SolMate {self.serialnum}.") 172 | print(f"The credentials will be stored for future use in {self.authstore_file}.") 173 | password = getpass.getpass("Your SolMate's user password: ") 174 | token = await self.async_login(password, device_id) 175 | if store_auth_token_in_file: 176 | CONFIG_DIRECTORY.mkdir(parents=True, exist_ok=True) 177 | with open(self.authstore_file, "w", encoding="utf-8") as file: 178 | authstore[self.serialnum] = token 179 | json.dump(authstore, file) 180 | self.logger.debug("Stored credentials of %s.", self.serialnum) 181 | self.logger.debug("Already stored credentials are: %s", [sn for sn in authstore.keys()]) 182 | if not self.uri_verified: 183 | self.logger.debug("Checking uri for redirection - SolMate might be on a different port") 184 | await self.async_check_uri(token, device_id) 185 | await self.async_connect() # Connect to redirection address 186 | await self.async_authenticate(token, device_id) 187 | 188 | def quickstart(self, password=None, device_id=DEFAULT_DEVICE_ID, store_auth_token_in_file=True): 189 | """Connect, login, authenticate and store the token for future use!""" 190 | self.eventloop.run_until_complete(self.async_quickstart(password, device_id, store_auth_token_in_file)) 191 | 192 | 193 | @bad_request_handling() 194 | def get_software_version(self): 195 | """Returns the actually installed software version""" 196 | return self.request("get_solmate_info", {}) 197 | 198 | @retry(2, BadRequest, 1) 199 | def get_live_values(self): 200 | """Return current live values of the respective SolMate as a dictionary (pv power, battery state, injection).""" 201 | return self.request("live_values", {}) 202 | 203 | def get_recent_logs(self, days=None, start_time=None): 204 | """Returns the latest logs on the sol server""" 205 | import datetime 206 | 207 | if not days: 208 | days = 1 209 | if not start_time: 210 | start_time = datetime.datetime.now() - datetime.timedelta(days) 211 | end_time = start_time + datetime.timedelta(days) 212 | return self.request( 213 | "logs", 214 | {"timeframes": [{"start": start_time.isoformat()[:19], "end": end_time.isoformat()[:19], "resolution": 4}]}, 215 | ) 216 | 217 | def get_milestones_savings(self, days=1): 218 | """Returns the latest milestones saving""" 219 | return self.request("milestones_savings", {"days": days}) 220 | 221 | def get_user_settings(self): 222 | """Returns user settings which are valid at the moment""" 223 | return self.request("get_user_settings", {}) 224 | 225 | def get_injection_settings(self): 226 | """Shows your injection settings.""" 227 | return self.request("get_injection_settings", {}) 228 | 229 | def get_grid_mode(self): 230 | """Returns grid mode i.e. Offgrid mode ('island mode') or Ongrid mode""" 231 | return self.request("get_grid_mode", {}) 232 | 233 | def check_online(self): 234 | """Check whether the respective SolMate is currently online.""" 235 | return self.request("check_online", {"serial_num": self.serialnum})["online"] 236 | 237 | def set_max_injection(self, maximum_power): 238 | """Sets user defined maximum injection power which is applied if SolMates battery is ok with it""" 239 | return self.request("set_user_maximum_injection", {"injection": maximum_power}) 240 | 241 | @bad_request_handling() 242 | def set_min_injection(self, minimum_power): 243 | """Sets user defined minimum injection power which is applied if SolMates battery is ok with it""" 244 | return self.request("set_user_minimum_injection", {"injection": minimum_power}) 245 | 246 | def set_min_battery_percentage(self, minimum_percentage): 247 | """Sets user defined minimum battery percentage""" 248 | return self.request("set_user_minimum_battery_percentage", {"battery_percentage": minimum_percentage}) 249 | 250 | def set_AP_mode(self): 251 | """This function opens the local Access Point (AP) of SolMate and leaves client (CLI) mode. This means you will 252 | have to connect to your SolMates WIFI "SolMate ". However, if SolMate has a wired connection as 253 | well online availability remains""" 254 | return self.request("revert_to_ap", {}) 255 | 256 | def get_api_info(self): 257 | """Query all the available api calls.""" 258 | self.request("get_api_info", {}) 259 | 260 | def get_boost_injection(self): 261 | """Get the boost injection settings from the solmate.""" 262 | return self.request("get_boost_injection", {}) 263 | 264 | def set_boost_injection(self, set_time_in_secs: int, set_wattage: int): 265 | """ 266 | Set the boost injection. If "set_time" is greater than 0 the boost will be activate. The actual wattage of 267 | the boost might differ due to inverter restrictions. 268 | """ 269 | return self.request("set_boost_injection", {"time": set_time_in_secs, "wattage": set_wattage}) 270 | 271 | def get_injection_profiles(self): 272 | """Get all the injection profiles from the solmate.""" 273 | return self.request("get_injection_profiles", {}) 274 | 275 | def set_injection_profiles(self, new_injection_profiles: dict, new_injection_profiles_timestamp: str): 276 | """ 277 | Set the injection profiles on the solmate. This will overwrite all existing profiles. The new timestamp 278 | needs to be newer than the local timestamp on the solmate in order to update them. Timestamp format needs to be 279 | in "%Y-%m-%dT%H:%M:%S.%fZ" which is defined in solmate-sdk/utils.py as DATETIME_FORMAT_INJECTION_PROFILES for 280 | convenience. 281 | """ 282 | return self.request( 283 | "set_injection_profiles", 284 | { 285 | "injection_profiles": new_injection_profiles, 286 | "injection_profiles_timestamp": new_injection_profiles_timestamp, 287 | }, 288 | ) 289 | 290 | def apply_injection_profile(self, injection_profile_id: str): 291 | """Apply the injection profile with the id of 'injection_profile_id'.""" 292 | return self.request("apply_injection_profile", {"id": injection_profile_id}) 293 | 294 | def close(self): 295 | """Correctly close the underlying connection.""" 296 | if self.conn is None: 297 | raise RuntimeError("Connection has not yet been initialised.") 298 | asyncio.get_event_loop().run_until_complete(self.conn.close()) 299 | 300 | 301 | class LocalSolMateAPIClient(SolMateAPIClient): 302 | """Like SolMateAPIClient, however in local mode some extra routes are available 303 | 304 | In the local mode there is no load_balancer between you and your SolMate - though self.uri_verified needs to be set 305 | 306 | Furthermore, it is necessary to authenticate again using a special device_id. You may need to clear your authstore 307 | file (if you tested the online API first) 308 | """ 309 | 310 | def __init__(self, *args, **kwargs): 311 | super().__init__(*args, **kwargs) 312 | self.device_id = LOCAL_ACCESS_DEVICE_ID 313 | self.uri_verified = True # on local access no redirection is possible and the test for it is misunderstood 314 | self.authstore_file = LOCAL_AUTHSTORE_FILE 315 | 316 | def list_wifis(self): 317 | """Lists actually available and non hidden SSIDs""" 318 | return self.request("list_wifis", {}) 319 | 320 | def connect_to_wifi(self, ssid, password): 321 | """Switches to other ssid or to the same - THE ACTUAL CONNECTION WILL BE BROKEN AFTER THAT 322 | A TimeOutError will be raised rather than the ConnectionClosedOnPurpose error""" 323 | self.request("connect_to_wifi", {"ssid": ssid, "password": password}) 324 | raise ConnectionClosedOnPurpose 325 | 326 | def check_online(self): 327 | """Check whether the respective SolMate is currently online. 328 | The local api has no check_online route, it is online if you can connect to the local uri. 329 | Please call connect() or quickstart(...) before.""" 330 | return self.conn != None 331 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "alabaster" 5 | version = "0.7.13" 6 | description = "A configurable sidebar-enabled Sphinx theme" 7 | optional = false 8 | python-versions = ">=3.6" 9 | files = [ 10 | {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, 11 | {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, 12 | ] 13 | 14 | [[package]] 15 | name = "astroid" 16 | version = "2.15.8" 17 | description = "An abstract syntax tree for Python with inference support." 18 | optional = false 19 | python-versions = ">=3.7.2" 20 | files = [ 21 | {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, 22 | {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, 23 | ] 24 | 25 | [package.dependencies] 26 | lazy-object-proxy = ">=1.4.0" 27 | typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} 28 | wrapt = [ 29 | {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, 30 | {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, 31 | ] 32 | 33 | [[package]] 34 | name = "babel" 35 | version = "2.13.0" 36 | description = "Internationalization utilities" 37 | optional = false 38 | python-versions = ">=3.7" 39 | files = [ 40 | {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, 41 | {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, 42 | ] 43 | 44 | [package.dependencies] 45 | pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} 46 | 47 | [package.extras] 48 | dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] 49 | 50 | [[package]] 51 | name = "black" 52 | version = "22.12.0" 53 | description = "The uncompromising code formatter." 54 | optional = false 55 | python-versions = ">=3.7" 56 | files = [ 57 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 58 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 59 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 60 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 61 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 62 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 63 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 64 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 65 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 66 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 67 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 68 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 69 | ] 70 | 71 | [package.dependencies] 72 | click = ">=8.0.0" 73 | mypy-extensions = ">=0.4.3" 74 | pathspec = ">=0.9.0" 75 | platformdirs = ">=2" 76 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 77 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 78 | 79 | [package.extras] 80 | colorama = ["colorama (>=0.4.3)"] 81 | d = ["aiohttp (>=3.7.4)"] 82 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 83 | uvloop = ["uvloop (>=0.15.2)"] 84 | 85 | [[package]] 86 | name = "certifi" 87 | version = "2023.7.22" 88 | description = "Python package for providing Mozilla's CA Bundle." 89 | optional = false 90 | python-versions = ">=3.6" 91 | files = [ 92 | {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, 93 | {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, 94 | ] 95 | 96 | [[package]] 97 | name = "charset-normalizer" 98 | version = "3.3.0" 99 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 100 | optional = false 101 | python-versions = ">=3.7.0" 102 | files = [ 103 | {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, 104 | {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, 105 | {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, 106 | {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, 107 | {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, 108 | {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, 109 | {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, 110 | {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, 111 | {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, 112 | {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, 113 | {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, 114 | {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, 115 | {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, 116 | {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, 117 | {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, 118 | {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, 119 | {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, 120 | {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, 121 | {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, 122 | {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, 123 | {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, 124 | {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, 125 | {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, 126 | {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, 127 | {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, 128 | {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, 129 | {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, 130 | {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, 131 | {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, 132 | {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, 133 | {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, 134 | {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, 135 | {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, 136 | {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, 137 | {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, 138 | {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, 139 | {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, 140 | {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, 141 | {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, 142 | {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, 143 | {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, 144 | {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, 145 | {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, 146 | {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, 147 | {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, 148 | {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, 149 | {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, 150 | {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, 151 | {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, 152 | {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, 153 | {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, 154 | {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, 155 | {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, 156 | {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, 157 | {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, 158 | {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, 159 | {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, 160 | {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, 161 | {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, 162 | {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, 163 | {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, 164 | {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, 165 | {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, 166 | {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, 167 | {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, 168 | {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, 169 | {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, 170 | {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, 171 | {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, 172 | {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, 173 | {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, 174 | {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, 175 | {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, 176 | {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, 177 | {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, 178 | {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, 179 | {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, 180 | {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, 181 | {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, 182 | {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, 183 | {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, 184 | {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, 185 | {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, 186 | {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, 187 | {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, 188 | {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, 189 | {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, 190 | {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, 191 | {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, 192 | {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, 193 | ] 194 | 195 | [[package]] 196 | name = "click" 197 | version = "8.1.7" 198 | description = "Composable command line interface toolkit" 199 | optional = false 200 | python-versions = ">=3.7" 201 | files = [ 202 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 203 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 204 | ] 205 | 206 | [package.dependencies] 207 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 208 | 209 | [[package]] 210 | name = "colorama" 211 | version = "0.4.6" 212 | description = "Cross-platform colored terminal text." 213 | optional = false 214 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 215 | files = [ 216 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 217 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 218 | ] 219 | 220 | [[package]] 221 | name = "dill" 222 | version = "0.3.7" 223 | description = "serialize all of Python" 224 | optional = false 225 | python-versions = ">=3.7" 226 | files = [ 227 | {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, 228 | {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, 229 | ] 230 | 231 | [package.extras] 232 | graph = ["objgraph (>=1.7.2)"] 233 | 234 | [[package]] 235 | name = "docutils" 236 | version = "0.19" 237 | description = "Docutils -- Python Documentation Utilities" 238 | optional = false 239 | python-versions = ">=3.7" 240 | files = [ 241 | {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, 242 | {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, 243 | ] 244 | 245 | [[package]] 246 | name = "idna" 247 | version = "3.4" 248 | description = "Internationalized Domain Names in Applications (IDNA)" 249 | optional = false 250 | python-versions = ">=3.5" 251 | files = [ 252 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, 253 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, 254 | ] 255 | 256 | [[package]] 257 | name = "imagesize" 258 | version = "1.4.1" 259 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 260 | optional = false 261 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 262 | files = [ 263 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 264 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 265 | ] 266 | 267 | [[package]] 268 | name = "importlib-metadata" 269 | version = "6.8.0" 270 | description = "Read metadata from Python packages" 271 | optional = false 272 | python-versions = ">=3.8" 273 | files = [ 274 | {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, 275 | {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, 276 | ] 277 | 278 | [package.dependencies] 279 | zipp = ">=0.5" 280 | 281 | [package.extras] 282 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 283 | perf = ["ipython"] 284 | testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] 285 | 286 | [[package]] 287 | name = "invoke" 288 | version = "1.7.3" 289 | description = "Pythonic task execution" 290 | optional = false 291 | python-versions = "*" 292 | files = [ 293 | {file = "invoke-1.7.3-py3-none-any.whl", hash = "sha256:d9694a865764dd3fd91f25f7e9a97fb41666e822bbb00e670091e3f43933574d"}, 294 | {file = "invoke-1.7.3.tar.gz", hash = "sha256:41b428342d466a82135d5ab37119685a989713742be46e42a3a399d685579314"}, 295 | ] 296 | 297 | [[package]] 298 | name = "isort" 299 | version = "5.12.0" 300 | description = "A Python utility / library to sort Python imports." 301 | optional = false 302 | python-versions = ">=3.8.0" 303 | files = [ 304 | {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, 305 | {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, 306 | ] 307 | 308 | [package.extras] 309 | colors = ["colorama (>=0.4.3)"] 310 | pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] 311 | plugins = ["setuptools"] 312 | requirements-deprecated-finder = ["pip-api", "pipreqs"] 313 | 314 | [[package]] 315 | name = "jinja2" 316 | version = "3.1.2" 317 | description = "A very fast and expressive template engine." 318 | optional = false 319 | python-versions = ">=3.7" 320 | files = [ 321 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 322 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 323 | ] 324 | 325 | [package.dependencies] 326 | MarkupSafe = ">=2.0" 327 | 328 | [package.extras] 329 | i18n = ["Babel (>=2.7)"] 330 | 331 | [[package]] 332 | name = "lazy-object-proxy" 333 | version = "1.9.0" 334 | description = "A fast and thorough lazy object proxy." 335 | optional = false 336 | python-versions = ">=3.7" 337 | files = [ 338 | {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, 339 | {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, 340 | {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, 341 | {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, 342 | {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, 343 | {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, 344 | {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, 345 | {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, 346 | {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, 347 | {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, 348 | {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, 349 | {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, 350 | {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, 351 | {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, 352 | {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, 353 | {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, 354 | {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, 355 | {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, 356 | {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, 357 | {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, 358 | {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, 359 | {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, 360 | {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, 361 | {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, 362 | {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, 363 | {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, 364 | {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, 365 | {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, 366 | {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, 367 | {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, 368 | {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, 369 | {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, 370 | {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, 371 | {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, 372 | {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, 373 | {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, 374 | ] 375 | 376 | [[package]] 377 | name = "markupsafe" 378 | version = "2.1.3" 379 | description = "Safely add untrusted strings to HTML/XML markup." 380 | optional = false 381 | python-versions = ">=3.7" 382 | files = [ 383 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, 384 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, 385 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, 386 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, 387 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, 388 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, 389 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, 390 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, 391 | {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, 392 | {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, 393 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, 394 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, 395 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, 396 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, 397 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, 398 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, 399 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, 400 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, 401 | {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, 402 | {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, 403 | {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, 404 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, 405 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, 406 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, 407 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, 408 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, 409 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, 410 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, 411 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, 412 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, 413 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, 414 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, 415 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, 416 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, 417 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, 418 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, 419 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, 420 | {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, 421 | {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, 422 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, 423 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, 424 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, 425 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, 426 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, 427 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, 428 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, 429 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, 430 | {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, 431 | {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, 432 | {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, 433 | ] 434 | 435 | [[package]] 436 | name = "mccabe" 437 | version = "0.7.0" 438 | description = "McCabe checker, plugin for flake8" 439 | optional = false 440 | python-versions = ">=3.6" 441 | files = [ 442 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 443 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 444 | ] 445 | 446 | [[package]] 447 | name = "mypy-extensions" 448 | version = "1.0.0" 449 | description = "Type system extensions for programs checked with the mypy type checker." 450 | optional = false 451 | python-versions = ">=3.5" 452 | files = [ 453 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 454 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 455 | ] 456 | 457 | [[package]] 458 | name = "packaging" 459 | version = "23.2" 460 | description = "Core utilities for Python packages" 461 | optional = false 462 | python-versions = ">=3.7" 463 | files = [ 464 | {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, 465 | {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, 466 | ] 467 | 468 | [[package]] 469 | name = "pathspec" 470 | version = "0.11.2" 471 | description = "Utility library for gitignore style pattern matching of file paths." 472 | optional = false 473 | python-versions = ">=3.7" 474 | files = [ 475 | {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, 476 | {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, 477 | ] 478 | 479 | [[package]] 480 | name = "platformdirs" 481 | version = "3.11.0" 482 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 483 | optional = false 484 | python-versions = ">=3.7" 485 | files = [ 486 | {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, 487 | {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, 488 | ] 489 | 490 | [package.extras] 491 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] 492 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] 493 | 494 | [[package]] 495 | name = "pygments" 496 | version = "2.16.1" 497 | description = "Pygments is a syntax highlighting package written in Python." 498 | optional = false 499 | python-versions = ">=3.7" 500 | files = [ 501 | {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, 502 | {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, 503 | ] 504 | 505 | [package.extras] 506 | plugins = ["importlib-metadata"] 507 | 508 | [[package]] 509 | name = "pylint" 510 | version = "2.17.7" 511 | description = "python code static checker" 512 | optional = false 513 | python-versions = ">=3.7.2" 514 | files = [ 515 | {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"}, 516 | {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"}, 517 | ] 518 | 519 | [package.dependencies] 520 | astroid = ">=2.15.8,<=2.17.0-dev0" 521 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 522 | dill = [ 523 | {version = ">=0.2", markers = "python_version < \"3.11\""}, 524 | {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, 525 | ] 526 | isort = ">=4.2.5,<6" 527 | mccabe = ">=0.6,<0.8" 528 | platformdirs = ">=2.2.0" 529 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 530 | tomlkit = ">=0.10.1" 531 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} 532 | 533 | [package.extras] 534 | spelling = ["pyenchant (>=3.2,<4.0)"] 535 | testutils = ["gitpython (>3)"] 536 | 537 | [[package]] 538 | name = "pytz" 539 | version = "2023.3.post1" 540 | description = "World timezone definitions, modern and historical" 541 | optional = false 542 | python-versions = "*" 543 | files = [ 544 | {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, 545 | {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, 546 | ] 547 | 548 | [[package]] 549 | name = "requests" 550 | version = "2.31.0" 551 | description = "Python HTTP for Humans." 552 | optional = false 553 | python-versions = ">=3.7" 554 | files = [ 555 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 556 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 557 | ] 558 | 559 | [package.dependencies] 560 | certifi = ">=2017.4.17" 561 | charset-normalizer = ">=2,<4" 562 | idna = ">=2.5,<4" 563 | urllib3 = ">=1.21.1,<3" 564 | 565 | [package.extras] 566 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 567 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 568 | 569 | [[package]] 570 | name = "snowballstemmer" 571 | version = "2.2.0" 572 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 573 | optional = false 574 | python-versions = "*" 575 | files = [ 576 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 577 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 578 | ] 579 | 580 | [[package]] 581 | name = "sphinx" 582 | version = "5.3.0" 583 | description = "Python documentation generator" 584 | optional = false 585 | python-versions = ">=3.6" 586 | files = [ 587 | {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, 588 | {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, 589 | ] 590 | 591 | [package.dependencies] 592 | alabaster = ">=0.7,<0.8" 593 | babel = ">=2.9" 594 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 595 | docutils = ">=0.14,<0.20" 596 | imagesize = ">=1.3" 597 | importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} 598 | Jinja2 = ">=3.0" 599 | packaging = ">=21.0" 600 | Pygments = ">=2.12" 601 | requests = ">=2.5.0" 602 | snowballstemmer = ">=2.0" 603 | sphinxcontrib-applehelp = "*" 604 | sphinxcontrib-devhelp = "*" 605 | sphinxcontrib-htmlhelp = ">=2.0.0" 606 | sphinxcontrib-jsmath = "*" 607 | sphinxcontrib-qthelp = "*" 608 | sphinxcontrib-serializinghtml = ">=1.1.5" 609 | 610 | [package.extras] 611 | docs = ["sphinxcontrib-websupport"] 612 | lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] 613 | test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] 614 | 615 | [[package]] 616 | name = "sphinxcontrib-applehelp" 617 | version = "1.0.4" 618 | description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" 619 | optional = false 620 | python-versions = ">=3.8" 621 | files = [ 622 | {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, 623 | {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, 624 | ] 625 | 626 | [package.extras] 627 | lint = ["docutils-stubs", "flake8", "mypy"] 628 | test = ["pytest"] 629 | 630 | [[package]] 631 | name = "sphinxcontrib-devhelp" 632 | version = "1.0.2" 633 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." 634 | optional = false 635 | python-versions = ">=3.5" 636 | files = [ 637 | {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, 638 | {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, 639 | ] 640 | 641 | [package.extras] 642 | lint = ["docutils-stubs", "flake8", "mypy"] 643 | test = ["pytest"] 644 | 645 | [[package]] 646 | name = "sphinxcontrib-htmlhelp" 647 | version = "2.0.1" 648 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 649 | optional = false 650 | python-versions = ">=3.8" 651 | files = [ 652 | {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, 653 | {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, 654 | ] 655 | 656 | [package.extras] 657 | lint = ["docutils-stubs", "flake8", "mypy"] 658 | test = ["html5lib", "pytest"] 659 | 660 | [[package]] 661 | name = "sphinxcontrib-jsmath" 662 | version = "1.0.1" 663 | description = "A sphinx extension which renders display math in HTML via JavaScript" 664 | optional = false 665 | python-versions = ">=3.5" 666 | files = [ 667 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 668 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 669 | ] 670 | 671 | [package.extras] 672 | test = ["flake8", "mypy", "pytest"] 673 | 674 | [[package]] 675 | name = "sphinxcontrib-qthelp" 676 | version = "1.0.3" 677 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." 678 | optional = false 679 | python-versions = ">=3.5" 680 | files = [ 681 | {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, 682 | {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, 683 | ] 684 | 685 | [package.extras] 686 | lint = ["docutils-stubs", "flake8", "mypy"] 687 | test = ["pytest"] 688 | 689 | [[package]] 690 | name = "sphinxcontrib-serializinghtml" 691 | version = "1.1.5" 692 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." 693 | optional = false 694 | python-versions = ">=3.5" 695 | files = [ 696 | {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, 697 | {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, 698 | ] 699 | 700 | [package.extras] 701 | lint = ["docutils-stubs", "flake8", "mypy"] 702 | test = ["pytest"] 703 | 704 | [[package]] 705 | name = "tomli" 706 | version = "2.0.1" 707 | description = "A lil' TOML parser" 708 | optional = false 709 | python-versions = ">=3.7" 710 | files = [ 711 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 712 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 713 | ] 714 | 715 | [[package]] 716 | name = "tomlkit" 717 | version = "0.12.1" 718 | description = "Style preserving TOML library" 719 | optional = false 720 | python-versions = ">=3.7" 721 | files = [ 722 | {file = "tomlkit-0.12.1-py3-none-any.whl", hash = "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899"}, 723 | {file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"}, 724 | ] 725 | 726 | [[package]] 727 | name = "typing-extensions" 728 | version = "4.8.0" 729 | description = "Backported and Experimental Type Hints for Python 3.8+" 730 | optional = false 731 | python-versions = ">=3.8" 732 | files = [ 733 | {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, 734 | {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, 735 | ] 736 | 737 | [[package]] 738 | name = "urllib3" 739 | version = "2.0.6" 740 | description = "HTTP library with thread-safe connection pooling, file post, and more." 741 | optional = false 742 | python-versions = ">=3.7" 743 | files = [ 744 | {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, 745 | {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, 746 | ] 747 | 748 | [package.extras] 749 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 750 | secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] 751 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 752 | zstd = ["zstandard (>=0.18.0)"] 753 | 754 | [[package]] 755 | name = "websockets" 756 | version = "11.0.3" 757 | description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" 758 | optional = false 759 | python-versions = ">=3.7" 760 | files = [ 761 | {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, 762 | {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, 763 | {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, 764 | {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, 765 | {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, 766 | {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, 767 | {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, 768 | {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, 769 | {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, 770 | {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, 771 | {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, 772 | {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, 773 | {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, 774 | {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, 775 | {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, 776 | {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, 777 | {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, 778 | {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, 779 | {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, 780 | {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, 781 | {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, 782 | {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, 783 | {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, 784 | {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, 785 | {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, 786 | {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, 787 | {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, 788 | {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, 789 | {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, 790 | {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, 791 | {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, 792 | {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, 793 | {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, 794 | {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, 795 | {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, 796 | {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, 797 | {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, 798 | {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, 799 | {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, 800 | {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, 801 | {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, 802 | {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, 803 | {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, 804 | {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, 805 | {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, 806 | {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, 807 | {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, 808 | {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, 809 | {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, 810 | {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, 811 | {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, 812 | {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, 813 | {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, 814 | {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, 815 | {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, 816 | {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, 817 | {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, 818 | {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, 819 | {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, 820 | {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, 821 | {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, 822 | {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, 823 | {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, 824 | {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, 825 | {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, 826 | {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, 827 | {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, 828 | {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, 829 | {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, 830 | {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, 831 | ] 832 | 833 | [[package]] 834 | name = "wrapt" 835 | version = "1.15.0" 836 | description = "Module for decorators, wrappers and monkey patching." 837 | optional = false 838 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 839 | files = [ 840 | {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, 841 | {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, 842 | {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, 843 | {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, 844 | {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, 845 | {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, 846 | {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, 847 | {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, 848 | {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, 849 | {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, 850 | {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, 851 | {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, 852 | {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, 853 | {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, 854 | {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, 855 | {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, 856 | {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, 857 | {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, 858 | {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, 859 | {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, 860 | {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, 861 | {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, 862 | {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, 863 | {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, 864 | {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, 865 | {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, 866 | {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, 867 | {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, 868 | {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, 869 | {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, 870 | {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, 871 | {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, 872 | {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, 873 | {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, 874 | {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, 875 | {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, 876 | {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, 877 | {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, 878 | {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, 879 | {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, 880 | {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, 881 | {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, 882 | {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, 883 | {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, 884 | {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, 885 | {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, 886 | {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, 887 | {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, 888 | {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, 889 | {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, 890 | {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, 891 | {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, 892 | {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, 893 | {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, 894 | {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, 895 | {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, 896 | {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, 897 | {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, 898 | {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, 899 | {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, 900 | {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, 901 | {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, 902 | {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, 903 | {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, 904 | {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, 905 | {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, 906 | {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, 907 | {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, 908 | {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, 909 | {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, 910 | {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, 911 | {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, 912 | {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, 913 | {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, 914 | {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, 915 | ] 916 | 917 | [[package]] 918 | name = "zipp" 919 | version = "3.17.0" 920 | description = "Backport of pathlib-compatible object wrapper for zip files" 921 | optional = false 922 | python-versions = ">=3.8" 923 | files = [ 924 | {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, 925 | {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, 926 | ] 927 | 928 | [package.extras] 929 | docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] 930 | testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] 931 | 932 | [metadata] 933 | lock-version = "2.0" 934 | python-versions = "^3.8" 935 | content-hash = "65921bf2912c14b201b074b866dcc2faecb44eaa36c6af63f14d4d12e3a527b8" 936 | --------------------------------------------------------------------------------